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