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