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