7cef50b04dd88f65249004521593f79a13411832
[dragonfly.git] / sbin / ip6fw / ip6fw.c
1 /*      $KAME: ip6fw.c,v 1.13 2001/06/22 05:51:16 itojun Exp $  */
2
3 /*
4  * Copyright (C) 1998, 1999, 2000 and 2001 WIDE Project.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the project nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 /*
33  * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
34  * Copyright (c) 1994 Ugen J.S.Antsilevich
35  *
36  * Idea and grammar partially left from:
37  * Copyright (c) 1993 Daniel Boulet
38  *
39  * Redistribution and use in source forms, with and without modification,
40  * are permitted provided that this entire comment appears intact.
41  *
42  * Redistribution in binary form may occur without any restrictions.
43  * Obviously, it would be nice if you gave credit where credit is due
44  * but requiring it would be too onerous.
45  *
46  * This software is provided ``AS IS'' without any warranties of any kind.
47  *
48  * NEW command line interface for IP firewall facility
49  *
50  * $Id: ip6fw.c,v 1.1.2.2.2.2 1999/05/14 05:13:50 shin Exp $
51  * $FreeBSD: src/sbin/ip6fw/ip6fw.c,v 1.1.2.9 2003/04/05 10:54:51 ume Exp $
52  * $DragonFly: src/sbin/ip6fw/ip6fw.c,v 1.7 2004/02/04 17:39:59 joerg Exp $
53  */
54
55 #include <sys/types.h>
56 #include <sys/queue.h>
57 #include <sys/socket.h>
58 #include <sys/sockio.h>
59 #include <sys/time.h>
60 #include <sys/ioctl.h>
61 #include <sys/wait.h>
62
63 #include <ctype.h>
64 #include <err.h>
65 #include <limits.h>
66 #include <netdb.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <stdarg.h>
70 #include <string.h>
71 #include <time.h>
72 #include <unistd.h>
73 #include <errno.h>
74 #include <signal.h>
75 #include <sysexits.h>
76
77 #include <net/if.h>
78 #include <netinet/in.h>
79 #include <netinet/in_systm.h>
80 #include <netinet/ip6.h>
81 #include <netinet/icmp6.h>
82 #include <net/ip6fw/ip6_fw.h>
83 #include <netinet/tcp.h>
84 #include <arpa/inet.h>
85
86 int             lineno = -1;
87
88 int             s;                              /* main RAW socket         */
89 int             do_resolv=0;                    /* Would try to resolv all */
90 int             do_acct=0;                      /* Show packet/byte count  */
91 int             do_time=0;                      /* Show time stamps        */
92 int             do_quiet=0;                     /* Be quiet in add and flush  */
93 int             do_force=0;                     /* Don't ask for confirmation */
94
95 struct icmpcode {
96         int     code;
97         char    *str;
98 };
99
100 static struct icmpcode icmp6codes[] = {
101       { ICMP6_DST_UNREACH_NOROUTE,      "noroute" },
102       { ICMP6_DST_UNREACH_ADMIN,        "admin" },
103       { ICMP6_DST_UNREACH_NOTNEIGHBOR,  "notneighbor" },
104       { ICMP6_DST_UNREACH_ADDR,         "addr" },
105       { ICMP6_DST_UNREACH_NOPORT,       "noport" },
106       { 0, NULL }
107 };
108
109 static char ntop_buf[INET6_ADDRSTRLEN];
110
111 static void show_usage(const char *fmt, ...);
112
113 static int
114 mask_bits(u_char *m_ad, int m_len)
115 {
116         int h_num = 0,i;
117
118         for (i = 0; i < m_len; i++, m_ad++) {
119                 if (*m_ad != 0xff)
120                         break;
121                 h_num += 8;
122         }
123         if (i < m_len) {
124                 switch (*m_ad) {
125 #define MASKLEN(m, l)   case m: h_num += l; break
126                 MASKLEN(0xfe, 7);
127                 MASKLEN(0xfc, 6);
128                 MASKLEN(0xf8, 5);
129                 MASKLEN(0xf0, 4);
130                 MASKLEN(0xe0, 3);
131                 MASKLEN(0xc0, 2);
132                 MASKLEN(0x80, 1);
133 #undef  MASKLEN
134                 }
135         }
136         return h_num;
137 }
138
139 static int pl2m[9] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
140
141 struct in6_addr *plen2mask(int n)
142 {
143         static struct in6_addr ia;
144         u_char  *p;
145         int     i;
146
147         memset(&ia, 0, sizeof(struct in6_addr));
148         p = (u_char *)&ia;
149         for (i = 0; i < 16; i++, p++, n -= 8) {
150                 if (n >= 8) {
151                         *p = 0xff;
152                         continue;
153                 }
154                 *p = pl2m[n];
155                 break;
156         }
157         return &ia;
158 }
159
160 void
161 print_port(u_char prot, u_short port, const char *comma)
162 {
163         struct servent *se;
164         struct protoent *pe;
165         const char *protocol;
166         int printed = 0;
167
168         if (do_resolv) {
169                 pe = getprotobynumber(prot);
170                 if (pe)
171                         protocol = pe->p_name;
172                 else
173                         protocol = NULL;
174
175                 se = getservbyport(htons(port), protocol);
176                 if (se) {
177                         printf("%s%s", comma, se->s_name);
178                         printed = 1;
179                 }
180         }
181         if (!printed)
182                 printf("%s%d",comma,port);
183 }
184
185 static void
186 print_iface(char *key, union ip6_fw_if *un, int byname)
187 {
188
189         if (byname) {
190                 printf(" %s %s", key, un->fu_via_if.name);
191         } else if (!IN6_IS_ADDR_UNSPECIFIED(&un->fu_via_ip6)) {
192                 printf(" %s %s", key, inet_ntop(AF_INET6,&un->fu_via_ip6,ntop_buf,sizeof(ntop_buf)));
193         } else
194                 printf(" %s any", key);
195 }
196
197 static void
198 print_reject_code(int code)
199 {
200         struct icmpcode *ic;
201
202         for (ic = icmp6codes; ic->str; ic++)
203                 if (ic->code == code) {
204                         printf("%s", ic->str);
205                         return;
206                 }
207         printf("%u", code);
208 }
209
210 static void
211 show_ip6fw(struct ip6_fw *chain)
212 {
213         char *comma;
214         struct hostent *he;
215         struct protoent *pe;
216         int i, mb;
217         int nsp = IPV6_FW_GETNSRCP(chain);
218         int ndp = IPV6_FW_GETNDSTP(chain);
219
220         if (do_resolv)
221                 setservent(1/*stayopen*/);
222
223         printf("%05u ", chain->fw_number);
224
225         if (do_acct)
226                 printf("%10lu %10lu ",chain->fw_pcnt,chain->fw_bcnt);
227
228         if (do_time)
229         {
230                 if (chain->timestamp)
231                 {
232                         char timestr[30];
233
234                         strcpy(timestr, ctime((time_t *)&chain->timestamp));
235                         *strchr(timestr, '\n') = '\0';
236                         printf("%s ", timestr);
237                 }
238                 else
239                         printf("                         ");
240         }
241
242         switch (chain->fw_flg & IPV6_FW_F_COMMAND)
243         {
244                 case IPV6_FW_F_ACCEPT:
245                         printf("allow");
246                         break;
247                 case IPV6_FW_F_DENY:
248                         printf("deny");
249                         break;
250                 case IPV6_FW_F_COUNT:
251                         printf("count");
252                         break;
253                 case IPV6_FW_F_DIVERT:
254                         printf("divert %u", chain->fw_divert_port);
255                         break;
256                 case IPV6_FW_F_TEE:
257                         printf("tee %u", chain->fw_divert_port);
258                         break;
259                 case IPV6_FW_F_SKIPTO:
260                         printf("skipto %u", chain->fw_skipto_rule);
261                         break;
262                 case IPV6_FW_F_REJECT:
263                         if (chain->fw_reject_code == IPV6_FW_REJECT_RST)
264                                 printf("reset");
265                         else {
266                                 printf("unreach ");
267                                 print_reject_code(chain->fw_reject_code);
268                         }
269                         break;
270                 default:
271                         errx(EX_OSERR, "impossible");
272         }
273
274         if (chain->fw_flg & IPV6_FW_F_PRN)
275                 printf(" log");
276
277         pe = getprotobynumber(chain->fw_prot);
278         if (pe)
279                 printf(" %s", pe->p_name);
280         else
281                 printf(" %u", chain->fw_prot);
282
283         printf(" from %s", chain->fw_flg & IPV6_FW_F_INVSRC ? "not " : "");
284
285         mb=mask_bits((u_char *)&chain->fw_smsk,sizeof(chain->fw_smsk));
286         if (mb==128 && do_resolv) {
287                 he=gethostbyaddr((char *)&(chain->fw_src),sizeof(chain->fw_src),AF_INET6);
288                 if (he==NULL) {
289                         printf(inet_ntop(AF_INET6,&chain->fw_src,ntop_buf,sizeof(ntop_buf)));
290                 } else
291                         printf("%s",he->h_name);
292         } else {
293                 if (mb!=128) {
294                         if (mb == 0) {
295                                 printf("any");
296                         } else {
297                                 printf(inet_ntop(AF_INET6,&chain->fw_src,ntop_buf,sizeof(ntop_buf)));
298                                 printf("/%d",mb);
299                         }
300                 } else
301                         printf(inet_ntop(AF_INET6,&chain->fw_src,ntop_buf,sizeof(ntop_buf)));
302         }
303
304         if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) {
305                 comma = " ";
306                 for (i = 0; i < nsp; i++) {
307                         print_port(chain->fw_prot, chain->fw_pts[i], comma);
308                         if (i==0 && (chain->fw_flg & IPV6_FW_F_SRNG))
309                                 comma = "-";
310                         else
311                                 comma = ",";
312                 }
313         }
314
315         printf(" to %s", chain->fw_flg & IPV6_FW_F_INVDST ? "not " : "");
316
317         mb=mask_bits((u_char *)&chain->fw_dmsk,sizeof(chain->fw_dmsk));
318         if (mb==128 && do_resolv) {
319                 he=gethostbyaddr((char *)&(chain->fw_dst),sizeof(chain->fw_dst),AF_INET6);
320                 if (he==NULL) {
321                         printf(inet_ntop(AF_INET6,&chain->fw_dst,ntop_buf,sizeof(ntop_buf)));
322                 } else
323                         printf("%s",he->h_name);
324         } else {
325                 if (mb!=128) {
326                         if (mb == 0) {
327                                 printf("any");
328                         } else {
329                                 printf(inet_ntop(AF_INET6,&chain->fw_dst,ntop_buf,sizeof(ntop_buf)));
330                                 printf("/%d",mb);
331                         }
332                 } else
333                         printf(inet_ntop(AF_INET6,&chain->fw_dst,ntop_buf,sizeof(ntop_buf)));
334         }
335
336         if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) {
337                 comma = " ";
338                 for (i = 0; i < ndp; i++) {
339                         print_port(chain->fw_prot, chain->fw_pts[nsp+i], comma);
340                         if (i==0 && (chain->fw_flg & IPV6_FW_F_DRNG))
341                                 comma = "-";
342                         else
343                                 comma = ",";
344                 }
345         }
346
347         /* Direction */
348         if ((chain->fw_flg & IPV6_FW_F_IN) && !(chain->fw_flg & IPV6_FW_F_OUT))
349                 printf(" in");
350         if (!(chain->fw_flg & IPV6_FW_F_IN) && (chain->fw_flg & IPV6_FW_F_OUT))
351                 printf(" out");
352
353         /* Handle hack for "via" backwards compatibility */
354         if ((chain->fw_flg & IF6_FW_F_VIAHACK) == IF6_FW_F_VIAHACK) {
355                 print_iface("via",
356                     &chain->fw_in_if, chain->fw_flg & IPV6_FW_F_IIFNAME);
357         } else {
358                 /* Receive interface specified */
359                 if (chain->fw_flg & IPV6_FW_F_IIFACE)
360                         print_iface("recv", &chain->fw_in_if,
361                             chain->fw_flg & IPV6_FW_F_IIFNAME);
362                 /* Transmit interface specified */
363                 if (chain->fw_flg & IPV6_FW_F_OIFACE)
364                         print_iface("xmit", &chain->fw_out_if,
365                             chain->fw_flg & IPV6_FW_F_OIFNAME);
366         }
367
368         if (chain->fw_flg & IPV6_FW_F_FRAG)
369                 printf(" frag");
370
371         if (chain->fw_ip6opt || chain->fw_ip6nopt) {
372                 int     _opt_printed = 0;
373 #define PRINTOPT(x)     {if (_opt_printed) printf(",");\
374                         printf(x); _opt_printed = 1;}
375
376                 printf(" ip6opt ");
377                 if (chain->fw_ip6opt  & IPV6_FW_IP6OPT_HOPOPT) PRINTOPT("hopopt");
378                 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_HOPOPT) PRINTOPT("!hopopt");
379                 if (chain->fw_ip6opt  & IPV6_FW_IP6OPT_ROUTE)  PRINTOPT("route");
380                 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_ROUTE)  PRINTOPT("!route");
381                 if (chain->fw_ip6opt  & IPV6_FW_IP6OPT_FRAG)  PRINTOPT("frag");
382                 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_FRAG)  PRINTOPT("!frag");
383                 if (chain->fw_ip6opt  & IPV6_FW_IP6OPT_ESP)    PRINTOPT("esp");
384                 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_ESP)    PRINTOPT("!esp");
385                 if (chain->fw_ip6opt  & IPV6_FW_IP6OPT_AH)     PRINTOPT("ah");
386                 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_AH)     PRINTOPT("!ah");
387                 if (chain->fw_ip6opt  & IPV6_FW_IP6OPT_NONXT)  PRINTOPT("nonxt");
388                 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_NONXT)  PRINTOPT("!nonxt");
389                 if (chain->fw_ip6opt  & IPV6_FW_IP6OPT_OPTS)   PRINTOPT("opts");
390                 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_OPTS)   PRINTOPT("!opts");
391         }
392
393         if (chain->fw_ipflg & IPV6_FW_IF_TCPEST)
394                 printf(" established");
395         else if (chain->fw_tcpf == IPV6_FW_TCPF_SYN &&
396             chain->fw_tcpnf == IPV6_FW_TCPF_ACK)
397                 printf(" setup");
398         else if (chain->fw_tcpf || chain->fw_tcpnf) {
399                 int     _flg_printed = 0;
400 #define PRINTFLG(x)     {if (_flg_printed) printf(",");\
401                         printf(x); _flg_printed = 1;}
402
403                 printf(" tcpflg ");
404                 if (chain->fw_tcpf  & IPV6_FW_TCPF_FIN)  PRINTFLG("fin");
405                 if (chain->fw_tcpnf & IPV6_FW_TCPF_FIN)  PRINTFLG("!fin");
406                 if (chain->fw_tcpf  & IPV6_FW_TCPF_SYN)  PRINTFLG("syn");
407                 if (chain->fw_tcpnf & IPV6_FW_TCPF_SYN)  PRINTFLG("!syn");
408                 if (chain->fw_tcpf  & IPV6_FW_TCPF_RST)  PRINTFLG("rst");
409                 if (chain->fw_tcpnf & IPV6_FW_TCPF_RST)  PRINTFLG("!rst");
410                 if (chain->fw_tcpf  & IPV6_FW_TCPF_PSH)  PRINTFLG("psh");
411                 if (chain->fw_tcpnf & IPV6_FW_TCPF_PSH)  PRINTFLG("!psh");
412                 if (chain->fw_tcpf  & IPV6_FW_TCPF_ACK)  PRINTFLG("ack");
413                 if (chain->fw_tcpnf & IPV6_FW_TCPF_ACK)  PRINTFLG("!ack");
414                 if (chain->fw_tcpf  & IPV6_FW_TCPF_URG)  PRINTFLG("urg");
415                 if (chain->fw_tcpnf & IPV6_FW_TCPF_URG)  PRINTFLG("!urg");
416         }
417         if (chain->fw_flg & IPV6_FW_F_ICMPBIT) {
418                 int type_index;
419                 int first = 1;
420
421                 printf(" icmptype");
422
423                 for (type_index = 0; type_index < IPV6_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8; ++type_index)
424                         if (chain->fw_icmp6types[type_index / (sizeof(unsigned) * 8)] &
425                                 (1U << (type_index % (sizeof(unsigned) * 8)))) {
426                                 printf("%c%d", first == 1 ? ' ' : ',', type_index);
427                                 first = 0;
428                         }
429         }
430         printf("\n");
431         if (do_resolv)
432                 endservent();
433 }
434
435 void
436 list(int ac, char **av)
437 {
438         struct ip6_fw *r, *rules, *n;
439         int l,i;
440         unsigned long rulenum;
441         int nalloc, bytes, maxbytes;
442
443         /* extract rules from kernel, resizing array as necessary */
444         rules = NULL;
445         nalloc = sizeof *rules;
446         bytes = nalloc;
447         maxbytes = 65536 * sizeof *rules;
448         while (bytes >= nalloc) {
449                 if ((n = realloc(rules, nalloc * 2 + 200)) == NULL)
450                         err(EX_OSERR, "realloc");
451                 bytes = nalloc = nalloc * 2 + 200;
452                 rules = n;
453                 i = getsockopt(s, IPPROTO_IPV6, IPV6_FW_GET, rules, &bytes);
454                 if ((i < 0 && errno != EINVAL) || nalloc > maxbytes)
455                         err(EX_OSERR, "getsockopt(IPV6_FW_GET)");
456         }
457         if (!ac) {
458                 /* display all rules */
459                 for (r = rules, l = bytes; l >= sizeof rules[0];
460                          r++, l-=sizeof rules[0])
461                         show_ip6fw(r);
462         }
463         else {
464                 /* display specific rules requested on command line */
465                 int exitval = 0;
466
467                 while (ac--) {
468                         char *endptr;
469                         int seen;
470
471                         /* convert command line rule # */
472                         rulenum = strtoul(*av++, &endptr, 10);
473                         if (*endptr) {
474                                 exitval = 1;
475                                 warn("invalid rule number: %s", av[-1]);
476                                 continue;
477                         }
478                         seen = 0;
479                         for (r = rules, l = bytes;
480                                  l >= sizeof rules[0] && r->fw_number <= rulenum;
481                                  r++, l-=sizeof rules[0])
482                                 if (rulenum == r->fw_number) {
483                                         show_ip6fw(r);
484                                         seen = 1;
485                                 }
486                         if (!seen) {
487                                 exitval = 1;
488                                 warnx("rule %lu does not exist", rulenum);
489                         }
490                 }
491                 if (exitval != 0)
492                         exit(exitval);
493         }
494 }
495
496 static void
497 show_usage(const char *fmt, ...)
498 {
499         if (fmt) {
500                 char buf[100];
501                 va_list args;
502
503                 va_start(args, fmt);
504                 vsnprintf(buf, sizeof(buf), fmt, args);
505                 va_end(args);
506                 warnx("error: %s", buf);
507         }
508         fprintf(stderr, "usage: ip6fw [options]\n"
509 "    flush\n"
510 "    add [number] rule\n"
511 "    delete number ...\n"
512 "    list [number ...]\n"
513 "    show [number ...]\n"
514 "    zero [number ...]\n"
515 "  rule:  action proto src dst extras...\n"
516 "    action:\n"
517 "      {allow|permit|accept|pass|deny|drop|reject|unreach code|\n"
518 "       reset|count|skipto num} [log]\n"
519 "    proto: {ipv6|tcp|udp|ipv6-icmp|<number>}\n"
520 "    src: from [not] {any|ipv6[/prefixlen]} [{port|port-port},[port],...]\n"
521 "    dst: to [not] {any|ipv6[/prefixlen]} [{port|port-port},[port],...]\n"
522 "  extras:\n"
523 "    fragment     (may not be used with ports or tcpflags)\n"
524 "    in\n"
525 "    out\n"
526 "    {xmit|recv|via} {iface|ipv6|any}\n"
527 "    {established|setup}\n"
528 "    tcpflags [!]{syn|fin|rst|ack|psh|urg},...\n"
529 "    ipv6options [!]{hopopt|route|frag|esp|ah|nonxt|opts},...\n"
530 "    icmptypes {type[,type]}...\n");
531
532         exit(1);
533 }
534
535 static int
536 lookup_host (const char *host, u_char *addr, int family)
537 {
538         struct hostent *he;
539
540         if (inet_pton(family, host, addr) != 1) {
541                 if ((he = gethostbyname2(host, family)) == NULL)
542                         return(-1);
543                 memcpy(addr, he->h_addr_list[0], he->h_length);
544         }       
545         return(0);
546 }
547
548 void
549 fill_ip6(struct in6_addr *ipno, struct in6_addr *mask, int *acp, char ***avp)
550 {
551         int ac = *acp;
552         char **av = *avp;
553         char *p = 0, md = 0;
554         int i;
555
556         if (ac && !strncmp(*av,"any",strlen(*av))) {
557                 *ipno = *mask = in6addr_any; av++; ac--;
558         } else {
559                 p = strchr(*av, '/');
560                 if (p) {
561                         md = *p;
562                         *p++ = '\0';
563                 }
564
565                 if (lookup_host(*av, (u_char *)ipno, AF_INET6) != 0)
566                         show_usage("hostname ``%s'' unknown", *av);
567                 switch (md) {
568                         case '/':
569                                 if (atoi(p) == 0) {
570                                         *mask = in6addr_any;
571                                 } else if (atoi(p) > 128) {
572                                         show_usage("bad width ``%s''", p);
573                                 } else {
574                                         *mask = *(plen2mask(atoi(p)));
575                                 }
576                                 break;
577                         default:
578                                 *mask = *(plen2mask(128));
579                                 break;
580                 }
581                 for (i = 0; i < sizeof(*ipno); i++)
582                         ipno->s6_addr[i] &= mask->s6_addr[i];
583                 av++;
584                 ac--;
585         }
586         *acp = ac;
587         *avp = av;
588 }
589
590 static void
591 fill_reject_code6(u_short *codep, char *str)
592 {
593         struct icmpcode *ic;
594         u_long val;
595         char *s;
596
597         val = strtoul(str, &s, 0);
598         if (s != str && *s == '\0' && val < 0x100) {
599                 *codep = val;
600                 return;
601         }
602         for (ic = icmp6codes; ic->str; ic++)
603                 if (!strcasecmp(str, ic->str)) {
604                         *codep = ic->code;
605                         return;
606                 }
607         show_usage("unknown ICMP6 unreachable code ``%s''", str);
608 }
609
610 static void
611 add_port(u_short *cnt, u_short *ptr, u_short off, u_short port)
612 {
613         if (off + *cnt >= IPV6_FW_MAX_PORTS)
614                 errx(1, "too many ports (max is %d)", IPV6_FW_MAX_PORTS);
615         ptr[off+*cnt] = port;
616         (*cnt)++;
617 }
618
619 static int
620 lookup_port(const char *arg, int test, int nodash)
621 {
622         int             val;
623         char            *earg, buf[32];
624         struct servent  *s;
625
626         snprintf(buf, sizeof(buf), "%s", arg);
627         buf[strcspn(arg, nodash ? "-," : ",")] = 0;
628         val = (int) strtoul(buf, &earg, 0);
629         if (!*buf || *earg) {
630                 setservent(1);
631                 if ((s = getservbyname(buf, NULL))) {
632                         val = htons(s->s_port);
633                 } else {
634                         if (!test) {
635                                 errx(1, "unknown port ``%s''", arg);
636                         }
637                         val = -1;
638                 }
639         } else {
640                 if (val < 0 || val > 0xffff) {
641                         if (!test) {
642                                 errx(1, "port ``%s'' out of range", arg);
643                         }
644                         val = -1;
645                 }
646         }
647         return(val);
648 }
649
650 int
651 fill_port(u_short *cnt, u_short *ptr, u_short off, char *arg)
652 {
653         char *s;
654         int initial_range = 0;
655
656         s = arg + strcspn(arg, "-,");   /* first port name can't have a dash */
657         if (*s == '-') {
658                 *s++ = '\0';
659                 if (strchr(arg, ','))
660                         errx(1, "port range must be first in list");
661                 add_port(cnt, ptr, off, *arg ? lookup_port(arg, 0, 0) : 0x0000);
662                 arg = s;
663                 s = strchr(arg,',');
664                 if (s)
665                         *s++ = '\0';
666                 add_port(cnt, ptr, off, *arg ? lookup_port(arg, 0, 0) : 0xffff);
667                 arg = s;
668                 initial_range = 1;
669         }
670         while (arg != NULL) {
671                 s = strchr(arg,',');
672                 if (s)
673                         *s++ = '\0';
674                 add_port(cnt, ptr, off, lookup_port(arg, 0, 0));
675                 arg = s;
676         }
677         return initial_range;
678 }
679
680 void
681 fill_tcpflag(u_char *set, u_char *reset, char **vp)
682 {
683         char *p = *vp,*q;
684         u_char *d;
685
686         while (p && *p) {
687                 struct tpcflags {
688                         char * name;
689                         u_char value;
690                 } flags[] = {
691                         { "syn", IPV6_FW_TCPF_SYN },
692                         { "fin", IPV6_FW_TCPF_FIN },
693                         { "ack", IPV6_FW_TCPF_ACK },
694                         { "psh", IPV6_FW_TCPF_PSH },
695                         { "rst", IPV6_FW_TCPF_RST },
696                         { "urg", IPV6_FW_TCPF_URG }
697                 };
698                 int i;
699
700                 if (*p == '!') {
701                         p++;
702                         d = reset;
703                 } else {
704                         d = set;
705                 }
706                 q = strchr(p, ',');
707                 if (q)
708                         *q++ = '\0';
709                 for (i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i)
710                         if (!strncmp(p, flags[i].name, strlen(p))) {
711                                 *d |= flags[i].value;
712                                 break;
713                         }
714                 if (i == sizeof(flags) / sizeof(flags[0]))
715                         show_usage("invalid tcp flag ``%s''", p);
716                 p = q;
717         }
718 }
719
720 static void
721 fill_ip6opt(u_char *set, u_char *reset, char **vp)
722 {
723         char *p = *vp,*q;
724         u_char *d;
725
726         while (p && *p) {
727                 if (*p == '!') {
728                         p++;
729                         d = reset;
730                 } else {
731                         d = set;
732                 }
733                 q = strchr(p, ',');
734                 if (q)
735                         *q++ = '\0';
736                 if (!strncmp(p,"hopopt",strlen(p))) *d |= IPV6_FW_IP6OPT_HOPOPT;
737                 if (!strncmp(p,"route",strlen(p)))  *d |= IPV6_FW_IP6OPT_ROUTE;
738                 if (!strncmp(p,"frag",strlen(p)))   *d |= IPV6_FW_IP6OPT_FRAG;
739                 if (!strncmp(p,"esp",strlen(p)))    *d |= IPV6_FW_IP6OPT_ESP;
740                 if (!strncmp(p,"ah",strlen(p)))     *d |= IPV6_FW_IP6OPT_AH;
741                 if (!strncmp(p,"nonxt",strlen(p)))  *d |= IPV6_FW_IP6OPT_NONXT;
742                 if (!strncmp(p,"opts",strlen(p)))   *d |= IPV6_FW_IP6OPT_OPTS;
743                 p = q;
744         }
745 }
746
747 void
748 fill_icmptypes(unsigned *types, char **vp, u_short *fw_flg)
749 {
750         char *c = *vp;
751
752         while (*c)
753         {
754                 unsigned long icmptype;
755
756                 if ( *c == ',' )
757                         ++c;
758
759                 icmptype = strtoul(c, &c, 0);
760
761                 if ( *c != ',' && *c != '\0' )
762                         show_usage("invalid ICMP6 type");
763
764                 if (icmptype >= IPV6_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8)
765                         show_usage("ICMP6 type out of range");
766
767                 types[icmptype / (sizeof(unsigned) * 8)] |=
768                         1 << (icmptype % (sizeof(unsigned) * 8));
769                 *fw_flg |= IPV6_FW_F_ICMPBIT;
770         }
771 }
772
773 void
774 delete(int ac, char **av)
775 {
776         struct ip6_fw rule;
777         int i;
778         int exitval = 0;
779
780         memset(&rule, 0, sizeof rule);
781
782         av++; ac--;
783
784         /* Rule number */
785         while (ac && isdigit(**av)) {
786                 rule.fw_number = atoi(*av); av++; ac--;
787                 i = setsockopt(s, IPPROTO_IPV6, IPV6_FW_DEL, &rule, sizeof rule);
788                 if (i) {
789                         exitval = 1;
790                         warn("rule %u: setsockopt(%s)", rule.fw_number, "IPV6_FW_DEL");
791                 }
792         }
793         if (exitval != 0)
794                 exit(exitval);
795 }
796
797 static void
798 verify_interface(union ip6_fw_if *ifu)
799 {
800         struct ifreq ifr;
801
802         strlcpy(ifr.ifr_name, ifu->fu_via_if.name, sizeof(ifr.ifr_name));
803
804         if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
805                 warnx("warning: interface ``%s'' does not exist", ifr.ifr_name);
806 }
807
808 static void
809 fill_iface(char *which, union ip6_fw_if *ifu, int *byname, int ac, char *arg)
810 {
811         if (!ac)
812             show_usage("missing argument for ``%s''", which);
813
814         /* Parse the interface or address */
815         if (!strcmp(arg, "any")) {
816                 ifu->fu_via_ip6 = in6addr_any;
817                 *byname = 0;
818         } else if (!isdigit(*arg)) {
819                 *byname = 1;
820                 strlcpy(ifu->fu_via_if.name, arg, sizeof(ifu->fu_via_if.name));
821                 /*
822                  * We assume that strings containing '*', '?', or '['
823                  * are ment to be shell pattern.
824                  */
825                 if (strpbrk(arg, "*?[") != NULL) {
826                         ifu->fu_via_if.glob = 1;
827                 } else {
828                         ifu->fu_via_if.glob = 0;
829                         verify_interface(ifu);
830                 }
831         } else if (inet_pton(AF_INET6, arg, &ifu->fu_via_ip6) != 1) {
832                 show_usage("bad ip6 address ``%s''", arg);
833         } else
834                 *byname = 0;
835 }
836
837 static void
838 add(int ac, char **av)
839 {
840         struct ip6_fw rule;
841         int i;
842         u_char proto;
843         struct protoent *pe;
844         int saw_xmrc = 0, saw_via = 0;
845
846         memset(&rule, 0, sizeof rule);
847
848         av++; ac--;
849
850         /* Rule number */
851         if (ac && isdigit(**av)) {
852                 rule.fw_number = atoi(*av); av++; ac--;
853         }
854
855         /* Action */
856         if (ac == 0)
857                 show_usage("missing action");
858         if (!strncmp(*av,"accept",strlen(*av))
859                     || !strncmp(*av,"pass",strlen(*av))
860                     || !strncmp(*av,"allow",strlen(*av))
861                     || !strncmp(*av,"permit",strlen(*av))) {
862                 rule.fw_flg |= IPV6_FW_F_ACCEPT; av++; ac--;
863         } else if (!strncmp(*av,"count",strlen(*av))) {
864                 rule.fw_flg |= IPV6_FW_F_COUNT; av++; ac--;
865         }
866 #if 0
867         else if (!strncmp(*av,"divert",strlen(*av))) {
868                 rule.fw_flg |= IPV6_FW_F_DIVERT; av++; ac--;
869                 if (!ac)
870                         show_usage("missing divert port");
871                 rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
872                 if (rule.fw_divert_port == 0) {
873                         struct servent *s;
874                         setservent(1);
875                         s = getservbyname(av[-1], "divert");
876                         if (s != NULL)
877                                 rule.fw_divert_port = ntohs(s->s_port);
878                         else
879                                 show_usage("illegal divert port");
880                 }
881         } else if (!strncmp(*av,"tee",strlen(*av))) {
882                 rule.fw_flg |= IPV6_FW_F_TEE; av++; ac--;
883                 if (!ac)
884                         show_usage("missing divert port");
885                 rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
886                 if (rule.fw_divert_port == 0) {
887                         struct servent *s;
888                         setservent(1);
889                         s = getservbyname(av[-1], "divert");
890                         if (s != NULL)
891                                 rule.fw_divert_port = ntohs(s->s_port);
892                         else
893                                 show_usage("illegal divert port");
894                 }
895         }
896 #endif
897         else if (!strncmp(*av,"skipto",strlen(*av))) {
898                 rule.fw_flg |= IPV6_FW_F_SKIPTO; av++; ac--;
899                 if (!ac)
900                         show_usage("missing skipto rule number");
901                 rule.fw_skipto_rule = strtoul(*av, NULL, 0); av++; ac--;
902         } else if ((!strncmp(*av,"deny",strlen(*av))
903                     || !strncmp(*av,"drop",strlen(*av)))) {
904                 rule.fw_flg |= IPV6_FW_F_DENY; av++; ac--;
905         } else if (!strncmp(*av,"reject",strlen(*av))) {
906                 rule.fw_flg |= IPV6_FW_F_REJECT; av++; ac--;
907                 rule.fw_reject_code = ICMP6_DST_UNREACH_NOROUTE;
908         } else if (!strncmp(*av,"reset",strlen(*av))) {
909                 rule.fw_flg |= IPV6_FW_F_REJECT; av++; ac--;
910                 rule.fw_reject_code = IPV6_FW_REJECT_RST;       /* check TCP later */
911         } else if (!strncmp(*av,"unreach",strlen(*av))) {
912                 rule.fw_flg |= IPV6_FW_F_REJECT; av++; ac--;
913                 fill_reject_code6(&rule.fw_reject_code, *av); av++; ac--;
914         } else {
915                 show_usage("invalid action ``%s''", *av);
916         }
917
918         /* [log] */
919         if (ac && !strncmp(*av,"log",strlen(*av))) {
920                 rule.fw_flg |= IPV6_FW_F_PRN; av++; ac--;
921         }
922
923         /* protocol */
924         if (ac == 0)
925                 show_usage("missing protocol");
926         if ((proto = atoi(*av)) > 0) {
927                 rule.fw_prot = proto; av++; ac--;
928         } else if (!strncmp(*av,"all",strlen(*av))) {
929                 rule.fw_prot = IPPROTO_IPV6; av++; ac--;
930         } else if ((pe = getprotobyname(*av)) != NULL) {
931                 rule.fw_prot = pe->p_proto; av++; ac--;
932         } else {
933                 show_usage("invalid protocol ``%s''", *av);
934         }
935
936         if (rule.fw_prot != IPPROTO_TCP
937             && (rule.fw_flg & IPV6_FW_F_COMMAND) == IPV6_FW_F_REJECT
938             && rule.fw_reject_code == IPV6_FW_REJECT_RST)
939                 show_usage("``reset'' is only valid for tcp packets");
940
941         /* from */
942         if (ac && !strncmp(*av,"from",strlen(*av))) { av++; ac--; }
943         else
944                 show_usage("missing ``from''");
945
946         if (ac && !strncmp(*av,"not",strlen(*av))) {
947                 rule.fw_flg |= IPV6_FW_F_INVSRC;
948                 av++; ac--;
949         }
950         if (!ac)
951                 show_usage("missing arguments");
952
953         fill_ip6(&rule.fw_src, &rule.fw_smsk, &ac, &av);
954
955         if (ac && (isdigit(**av) || lookup_port(*av, 1, 1) >= 0)) {
956                 u_short nports = 0;
957
958                 if (fill_port(&nports, rule.fw_pts, 0, *av))
959                         rule.fw_flg |= IPV6_FW_F_SRNG;
960                 IPV6_FW_SETNSRCP(&rule, nports);
961                 av++; ac--;
962         }
963
964         /* to */
965         if (ac && !strncmp(*av,"to",strlen(*av))) { av++; ac--; }
966         else
967                 show_usage("missing ``to''");
968
969         if (ac && !strncmp(*av,"not",strlen(*av))) {
970                 rule.fw_flg |= IPV6_FW_F_INVDST;
971                 av++; ac--;
972         }
973         if (!ac)
974                 show_usage("missing arguments");
975
976         fill_ip6(&rule.fw_dst, &rule.fw_dmsk, &ac, &av);
977
978         if (ac && (isdigit(**av) || lookup_port(*av, 1, 1) >= 0)) {
979                 u_short nports = 0;
980
981                 if (fill_port(&nports,
982                     rule.fw_pts, IPV6_FW_GETNSRCP(&rule), *av))
983                         rule.fw_flg |= IPV6_FW_F_DRNG;
984                 IPV6_FW_SETNDSTP(&rule, nports);
985                 av++; ac--;
986         }
987
988         if ((rule.fw_prot != IPPROTO_TCP) && (rule.fw_prot != IPPROTO_UDP)
989             && (IPV6_FW_GETNSRCP(&rule) || IPV6_FW_GETNDSTP(&rule))) {
990                 show_usage("only TCP and UDP protocols are valid"
991                     " with port specifications");
992         }
993
994         while (ac) {
995                 if (!strncmp(*av,"in",strlen(*av))) {
996                         rule.fw_flg |= IPV6_FW_F_IN;
997                         av++; ac--; continue;
998                 }
999                 if (!strncmp(*av,"out",strlen(*av))) {
1000                         rule.fw_flg |= IPV6_FW_F_OUT;
1001                         av++; ac--; continue;
1002                 }
1003                 if (ac && !strncmp(*av,"xmit",strlen(*av))) {
1004                         union ip6_fw_if ifu;
1005                         int byname;
1006
1007                         if (saw_via) {
1008 badviacombo:
1009                                 show_usage("``via'' is incompatible"
1010                                     " with ``xmit'' and ``recv''");
1011                         }
1012                         saw_xmrc = 1;
1013                         av++; ac--;
1014                         fill_iface("xmit", &ifu, &byname, ac, *av);
1015                         rule.fw_out_if = ifu;
1016                         rule.fw_flg |= IPV6_FW_F_OIFACE;
1017                         if (byname)
1018                                 rule.fw_flg |= IPV6_FW_F_OIFNAME;
1019                         av++; ac--; continue;
1020                 }
1021                 if (ac && !strncmp(*av,"recv",strlen(*av))) {
1022                         union ip6_fw_if ifu;
1023                         int byname;
1024
1025                         if (saw_via)
1026                                 goto badviacombo;
1027                         saw_xmrc = 1;
1028                         av++; ac--;
1029                         fill_iface("recv", &ifu, &byname, ac, *av);
1030                         rule.fw_in_if = ifu;
1031                         rule.fw_flg |= IPV6_FW_F_IIFACE;
1032                         if (byname)
1033                                 rule.fw_flg |= IPV6_FW_F_IIFNAME;
1034                         av++; ac--; continue;
1035                 }
1036                 if (ac && !strncmp(*av,"via",strlen(*av))) {
1037                         union ip6_fw_if ifu;
1038                         int byname = 0;
1039
1040                         if (saw_xmrc)
1041                                 goto badviacombo;
1042                         saw_via = 1;
1043                         av++; ac--;
1044                         fill_iface("via", &ifu, &byname, ac, *av);
1045                         rule.fw_out_if = rule.fw_in_if = ifu;
1046                         if (byname)
1047                                 rule.fw_flg |=
1048                                     (IPV6_FW_F_IIFNAME | IPV6_FW_F_OIFNAME);
1049                         av++; ac--; continue;
1050                 }
1051                 if (!strncmp(*av,"fragment",strlen(*av))) {
1052                         rule.fw_flg |= IPV6_FW_F_FRAG;
1053                         av++; ac--; continue;
1054                 }
1055                 if (!strncmp(*av,"ipv6options",strlen(*av))) {
1056                         av++; ac--;
1057                         if (!ac)
1058                                 show_usage("missing argument"
1059                                     " for ``ipv6options''");
1060                         fill_ip6opt(&rule.fw_ip6opt, &rule.fw_ip6nopt, av);
1061                         av++; ac--; continue;
1062                 }
1063                 if (rule.fw_prot == IPPROTO_TCP) {
1064                         if (!strncmp(*av,"established",strlen(*av))) {
1065                                 rule.fw_ipflg |= IPV6_FW_IF_TCPEST;
1066                                 av++; ac--; continue;
1067                         }
1068                         if (!strncmp(*av,"setup",strlen(*av))) {
1069                                 rule.fw_tcpf  |= IPV6_FW_TCPF_SYN;
1070                                 rule.fw_tcpnf  |= IPV6_FW_TCPF_ACK;
1071                                 av++; ac--; continue;
1072                         }
1073                         if (!strncmp(*av,"tcpflags",strlen(*av))) {
1074                                 av++; ac--;
1075                                 if (!ac)
1076                                         show_usage("missing argument"
1077                                             " for ``tcpflags''");
1078                                 fill_tcpflag(&rule.fw_tcpf, &rule.fw_tcpnf, av);
1079                                 av++; ac--; continue;
1080                         }
1081                 }
1082                 if (rule.fw_prot == IPPROTO_ICMPV6) {
1083                         if (!strncmp(*av,"icmptypes",strlen(*av))) {
1084                                 av++; ac--;
1085                                 if (!ac)
1086                                         show_usage("missing argument"
1087                                             " for ``icmptypes''");
1088                                 fill_icmptypes(rule.fw_icmp6types,
1089                                     av, &rule.fw_flg);
1090                                 av++; ac--; continue;
1091                         }
1092                 }
1093                 show_usage("unknown argument ``%s''", *av);
1094         }
1095
1096         /* No direction specified -> do both directions */
1097         if (!(rule.fw_flg & (IPV6_FW_F_OUT|IPV6_FW_F_IN)))
1098                 rule.fw_flg |= (IPV6_FW_F_OUT|IPV6_FW_F_IN);
1099
1100         /* Sanity check interface check, but handle "via" case separately */
1101         if (saw_via) {
1102                 if (rule.fw_flg & IPV6_FW_F_IN)
1103                         rule.fw_flg |= IPV6_FW_F_IIFACE;
1104                 if (rule.fw_flg & IPV6_FW_F_OUT)
1105                         rule.fw_flg |= IPV6_FW_F_OIFACE;
1106         } else if ((rule.fw_flg & IPV6_FW_F_OIFACE) && (rule.fw_flg & IPV6_FW_F_IN))
1107                 show_usage("can't check xmit interface of incoming packets");
1108
1109         /* frag may not be used in conjunction with ports or TCP flags */
1110         if (rule.fw_flg & IPV6_FW_F_FRAG) {
1111                 if (rule.fw_tcpf || rule.fw_tcpnf)
1112                         show_usage("can't mix 'frag' and tcpflags");
1113
1114                 if (rule.fw_nports)
1115                         show_usage("can't mix 'frag' and port specifications");
1116         }
1117
1118         if (!do_quiet)
1119                 show_ip6fw(&rule);
1120         i = setsockopt(s, IPPROTO_IPV6, IPV6_FW_ADD, &rule, sizeof rule);
1121         if (i)
1122                 err(EX_UNAVAILABLE, "setsockopt(%s)", "IPV6_FW_ADD");
1123 }
1124
1125 static void
1126 zero (int ac, char **av)
1127 {
1128         av++; ac--;
1129
1130         if (!ac) {
1131                 /* clear all entries */
1132                 if (setsockopt(s,IPPROTO_IPV6,IPV6_FW_ZERO,NULL,0)<0)
1133                         err(EX_UNAVAILABLE, "setsockopt(%s)", "IPV6_FW_ZERO");
1134                 if (!do_quiet)
1135                         printf("Accounting cleared.\n");
1136         } else {
1137                 struct ip6_fw rule;
1138                 int failed = 0;
1139
1140                 memset(&rule, 0, sizeof rule);
1141                 while (ac) {
1142                         /* Rule number */
1143                         if (isdigit(**av)) {
1144                                 rule.fw_number = atoi(*av); av++; ac--;
1145                                 if (setsockopt(s, IPPROTO_IPV6,
1146                                     IPV6_FW_ZERO, &rule, sizeof rule)) {
1147                                         warn("rule %u: setsockopt(%s)", rule.fw_number,
1148                                                  "IPV6_FW_ZERO");
1149                                         failed = 1;
1150                                 }
1151                                 else if (!do_quiet)
1152                                         printf("Entry %d cleared\n",
1153                                             rule.fw_number);
1154                         } else
1155                                 show_usage("invalid rule number ``%s''", *av);
1156                 }
1157                 if (failed != 0)
1158                         exit(failed);
1159         }
1160 }
1161
1162 int
1163 ip6fw_main(int ac, char **av)
1164 {
1165         int             ch;
1166         extern int      optind;
1167
1168         /* init optind to 1 */
1169         optind = 1;
1170
1171         if ( ac == 1 ) {
1172                 show_usage(NULL);
1173         }
1174
1175         /* Set the force flag for non-interactive processes */
1176         do_force = !isatty(STDIN_FILENO);
1177
1178         while ((ch = getopt(ac, av ,"afqtN")) != -1)
1179         switch(ch) {
1180                 case 'a':
1181                         do_acct=1;
1182                         break;
1183                 case 'f':
1184                         do_force=1;
1185                         break;
1186                 case 'q':
1187                         do_quiet=1;
1188                         break;
1189                 case 't':
1190                         do_time=1;
1191                         break;
1192                 case 'N':
1193                         do_resolv=1;
1194                         break;
1195                 default:
1196                         show_usage(NULL);
1197         }
1198
1199         ac -= optind;
1200         if (*(av+=optind)==NULL) {
1201                  show_usage("Bad arguments");
1202         }
1203
1204         if (!strncmp(*av, "add", strlen(*av))) {
1205                 add(ac,av);
1206         } else if (!strncmp(*av, "delete", strlen(*av))) {
1207                 delete(ac,av);
1208         } else if (!strncmp(*av, "flush", strlen(*av))) {
1209                 int do_flush = 0;
1210
1211                 if ( do_force || do_quiet )
1212                         do_flush = 1;
1213                 else {
1214                         int c;
1215
1216                         /* Ask the user */
1217                         printf("Are you sure? [yn] ");
1218                         do {
1219                                 fflush(stdout);
1220                                 c = toupper(getc(stdin));
1221                                 while (c != '\n' && getc(stdin) != '\n')
1222                                         if (feof(stdin))
1223                                                 return (0);
1224                         } while (c != 'Y' && c != 'N');
1225                         printf("\n");
1226                         if (c == 'Y')
1227                                 do_flush = 1;
1228                 }
1229                 if ( do_flush ) {
1230                         if (setsockopt(s,IPPROTO_IPV6,IPV6_FW_FLUSH,NULL,0) < 0)
1231                                 err(EX_UNAVAILABLE, "setsockopt(%s)", "IPV6_FW_FLUSH");
1232                         if (!do_quiet)
1233                                 printf("Flushed all rules.\n");
1234                 }
1235         } else if (!strncmp(*av, "zero", strlen(*av))) {
1236                 zero(ac,av);
1237         } else if (!strncmp(*av, "print", strlen(*av))) {
1238                 list(--ac,++av);
1239         } else if (!strncmp(*av, "list", strlen(*av))) {
1240                 list(--ac,++av);
1241         } else if (!strncmp(*av, "show", strlen(*av))) {
1242                 do_acct++;
1243                 list(--ac,++av);
1244         } else {
1245                 show_usage("Bad arguments");
1246         }
1247         return 0;
1248 }
1249
1250 int
1251 main(int ac, char **av)
1252 {
1253 #define MAX_ARGS        32
1254 #define WHITESP         " \t\f\v\n\r"
1255         char    buf[BUFSIZ];
1256         char    *a, *p, *args[MAX_ARGS], *cmd = NULL;
1257         char    linename[10];
1258         int     i, c, lineno, qflag, pflag, status;
1259         FILE    *f = NULL;
1260         pid_t   preproc = 0;
1261
1262         s = socket( AF_INET6, SOCK_RAW, IPPROTO_RAW );
1263         if ( s < 0 )
1264                 err(EX_UNAVAILABLE, "socket");
1265
1266         setbuf(stdout,0);
1267
1268         /*
1269          * Only interpret the last command line argument as a file to
1270          * be preprocessed if it is specified as an absolute pathname.
1271          */
1272
1273         if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0) {
1274                 qflag = pflag = i = 0;
1275                 lineno = 0;
1276
1277                 while ((c = getopt(ac, av, "D:U:p:q")) != -1)
1278                         switch(c) {
1279                         case 'D':
1280                                 if (!pflag)
1281                                         errx(EX_USAGE, "-D requires -p");
1282                                 if (i > MAX_ARGS - 2)
1283                                         errx(EX_USAGE,
1284                                              "too many -D or -U options");
1285                                 args[i++] = "-D";
1286                                 args[i++] = optarg;
1287                                 break;
1288
1289                         case 'U':
1290                                 if (!pflag)
1291                                         errx(EX_USAGE, "-U requires -p");
1292                                 if (i > MAX_ARGS - 2)
1293                                         errx(EX_USAGE,
1294                                              "too many -D or -U options");
1295                                 args[i++] = "-U";
1296                                 args[i++] = optarg;
1297                                 break;
1298
1299                         case 'p':
1300                                 pflag = 1;
1301                                 cmd = optarg;
1302                                 args[0] = cmd;
1303                                 i = 1;
1304                                 break;
1305
1306                         case 'q':
1307                                 qflag = 1;
1308                                 break;
1309
1310                         default:
1311                                 show_usage(NULL);
1312                         }
1313
1314                 av += optind;
1315                 ac -= optind;
1316                 if (ac != 1)
1317                         show_usage("extraneous filename arguments");
1318
1319                 if ((f = fopen(av[0], "r")) == NULL)
1320                         err(EX_UNAVAILABLE, "fopen: %s", av[0]);
1321
1322                 if (pflag) {
1323                         /* pipe through preprocessor (cpp or m4) */
1324                         int pipedes[2];
1325
1326                         args[i] = 0;
1327
1328                         if (pipe(pipedes) == -1)
1329                                 err(EX_OSERR, "cannot create pipe");
1330
1331                         switch((preproc = fork())) {
1332                         case -1:
1333                                 err(EX_OSERR, "cannot fork");
1334
1335                         case 0:
1336                                 /* child */
1337                                 if (dup2(fileno(f), 0) == -1 ||
1338                                     dup2(pipedes[1], 1) == -1)
1339                                         err(EX_OSERR, "dup2()");
1340                                 fclose(f);
1341                                 close(pipedes[1]);
1342                                 close(pipedes[0]);
1343                                 execvp(cmd, args);
1344                                 err(EX_OSERR, "execvp(%s) failed", cmd);
1345
1346                         default:
1347                                 /* parent */
1348                                 fclose(f);
1349                                 close(pipedes[1]);
1350                                 if ((f = fdopen(pipedes[0], "r")) == NULL) {
1351                                         int savederrno = errno;
1352
1353                                         (void)kill(preproc, SIGTERM);
1354                                         errno = savederrno;
1355                                         err(EX_OSERR, "fdopen()");
1356                                 }
1357                         }
1358                 }
1359
1360                 while (fgets(buf, BUFSIZ, f)) {
1361                         lineno++;
1362                         sprintf(linename, "Line %d", lineno);
1363                         args[0] = linename;
1364
1365                         if (*buf == '#')
1366                                 continue;
1367                         if ((p = strchr(buf, '#')) != NULL)
1368                                 *p = '\0';
1369                         i=1;
1370                         if (qflag) args[i++]="-q";
1371                         for (a = strtok(buf, WHITESP);
1372                             a && i < MAX_ARGS; a = strtok(NULL, WHITESP), i++)
1373                                 args[i] = a;
1374                         if (i == (qflag? 2: 1))
1375                                 continue;
1376                         if (i == MAX_ARGS)
1377                                 errx(EX_USAGE, "%s: too many arguments", linename);
1378                         args[i] = NULL;
1379
1380                         ip6fw_main(i, args);
1381                 }
1382                 fclose(f);
1383                 if (pflag) {
1384                         if (waitpid(preproc, &status, 0) != -1) {
1385                                 if (WIFEXITED(status)) {
1386                                         if (WEXITSTATUS(status) != EX_OK)
1387                                                 errx(EX_UNAVAILABLE,
1388                                                      "preprocessor exited with status %d",
1389                                                      WEXITSTATUS(status));
1390                                 } else if (WIFSIGNALED(status)) {
1391                                         errx(EX_UNAVAILABLE,
1392                                              "preprocessor exited with signal %d",
1393                                              WTERMSIG(status));
1394                                 }
1395                         }
1396                 }
1397         } else
1398                 ip6fw_main(ac,av);
1399         return 0;
1400 }