1 /* $KAME: ip6fw.c,v 1.13 2001/06/22 05:51:16 itojun Exp $ */
4 * Copyright (C) 1998, 1999, 2000 and 2001 WIDE Project.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
33 * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
34 * Copyright (c) 1994 Ugen J.S.Antsilevich
36 * Idea and grammar partially left from:
37 * Copyright (c) 1993 Daniel Boulet
39 * Redistribution and use in source forms, with and without modification,
40 * are permitted provided that this entire comment appears intact.
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.
46 * This software is provided ``AS IS'' without any warranties of any kind.
48 * NEW command line interface for IP firewall facility
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 $
54 #include <sys/types.h>
55 #include <sys/queue.h>
56 #include <sys/socket.h>
57 #include <sys/sockio.h>
59 #include <sys/ioctl.h>
77 #include <netinet/in.h>
78 #include <netinet/in_systm.h>
79 #include <netinet/ip6.h>
80 #include <netinet/icmp6.h>
81 #include <netinet6/ip6_fw.h>
82 #include <netinet/tcp.h>
83 #include <arpa/inet.h>
87 int s; /* main RAW socket */
88 int do_resolv=0; /* Would try to resolv all */
89 int do_acct=0; /* Show packet/byte count */
90 int do_time=0; /* Show time stamps */
91 int do_quiet=0; /* Be quiet in add and flush */
92 int do_force=0; /* Don't ask for confirmation */
99 static struct icmpcode icmp6codes[] = {
100 { ICMP6_DST_UNREACH_NOROUTE, "noroute" },
101 { ICMP6_DST_UNREACH_ADMIN, "admin" },
102 { ICMP6_DST_UNREACH_NOTNEIGHBOR, "notneighbor" },
103 { ICMP6_DST_UNREACH_ADDR, "addr" },
104 { ICMP6_DST_UNREACH_NOPORT, "noport" },
108 static char ntop_buf[INET6_ADDRSTRLEN];
110 static void show_usage(const char *fmt, ...);
113 mask_bits(u_char *m_ad, int m_len)
117 for (i = 0; i < m_len; i++, m_ad++) {
124 #define MASKLEN(m, l) case m: h_num += l; break
138 static int pl2m[9] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
140 struct in6_addr *plen2mask(int n)
142 static struct in6_addr ia;
146 memset(&ia, 0, sizeof(struct in6_addr));
148 for (i = 0; i < 16; i++, p++, n -= 8) {
160 print_port(prot, port, comma)
167 const char *protocol;
171 pe = getprotobynumber(prot);
173 protocol = pe->p_name;
177 se = getservbyport(htons(port), protocol);
179 printf("%s%s", comma, se->s_name);
184 printf("%s%d",comma,port);
188 print_iface(char *key, union ip6_fw_if *un, int byname)
190 char ifnb[IP6FW_IFNLEN+1];
193 strncpy(ifnb, un->fu_via_if.name, IP6FW_IFNLEN);
194 ifnb[IP6FW_IFNLEN]='\0';
195 if (un->fu_via_if.unit == -1)
196 printf(" %s %s*", key, ifnb);
198 printf(" %s %s%d", key, ifnb, un->fu_via_if.unit);
199 } else if (!IN6_IS_ADDR_UNSPECIFIED(&un->fu_via_ip6)) {
200 printf(" %s %s", key, inet_ntop(AF_INET6,&un->fu_via_ip6,ntop_buf,sizeof(ntop_buf)));
202 printf(" %s any", key);
206 print_reject_code(int code)
210 for (ic = icmp6codes; ic->str; ic++)
211 if (ic->code == code) {
212 printf("%s", ic->str);
219 show_ip6fw(struct ip6_fw *chain)
225 int nsp = IPV6_FW_GETNSRCP(chain);
226 int ndp = IPV6_FW_GETNDSTP(chain);
229 setservent(1/*stayopen*/);
231 printf("%05u ", chain->fw_number);
234 printf("%10lu %10lu ",chain->fw_pcnt,chain->fw_bcnt);
238 if (chain->timestamp)
242 strcpy(timestr, ctime((time_t *)&chain->timestamp));
243 *strchr(timestr, '\n') = '\0';
244 printf("%s ", timestr);
250 switch (chain->fw_flg & IPV6_FW_F_COMMAND)
252 case IPV6_FW_F_ACCEPT:
258 case IPV6_FW_F_COUNT:
261 case IPV6_FW_F_DIVERT:
262 printf("divert %u", chain->fw_divert_port);
265 printf("tee %u", chain->fw_divert_port);
267 case IPV6_FW_F_SKIPTO:
268 printf("skipto %u", chain->fw_skipto_rule);
270 case IPV6_FW_F_REJECT:
271 if (chain->fw_reject_code == IPV6_FW_REJECT_RST)
275 print_reject_code(chain->fw_reject_code);
279 errx(EX_OSERR, "impossible");
282 if (chain->fw_flg & IPV6_FW_F_PRN)
285 pe = getprotobynumber(chain->fw_prot);
287 printf(" %s", pe->p_name);
289 printf(" %u", chain->fw_prot);
291 printf(" from %s", chain->fw_flg & IPV6_FW_F_INVSRC ? "not " : "");
293 mb=mask_bits((u_char *)&chain->fw_smsk,sizeof(chain->fw_smsk));
294 if (mb==128 && do_resolv) {
295 he=gethostbyaddr((char *)&(chain->fw_src),sizeof(chain->fw_src),AF_INET6);
297 printf(inet_ntop(AF_INET6,&chain->fw_src,ntop_buf,sizeof(ntop_buf)));
299 printf("%s",he->h_name);
305 printf(inet_ntop(AF_INET6,&chain->fw_src,ntop_buf,sizeof(ntop_buf)));
309 printf(inet_ntop(AF_INET6,&chain->fw_src,ntop_buf,sizeof(ntop_buf)));
312 if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) {
314 for (i = 0; i < nsp; i++) {
315 print_port(chain->fw_prot, chain->fw_pts[i], comma);
316 if (i==0 && (chain->fw_flg & IPV6_FW_F_SRNG))
323 printf(" to %s", chain->fw_flg & IPV6_FW_F_INVDST ? "not " : "");
325 mb=mask_bits((u_char *)&chain->fw_dmsk,sizeof(chain->fw_dmsk));
326 if (mb==128 && do_resolv) {
327 he=gethostbyaddr((char *)&(chain->fw_dst),sizeof(chain->fw_dst),AF_INET6);
329 printf(inet_ntop(AF_INET6,&chain->fw_dst,ntop_buf,sizeof(ntop_buf)));
331 printf("%s",he->h_name);
337 printf(inet_ntop(AF_INET6,&chain->fw_dst,ntop_buf,sizeof(ntop_buf)));
341 printf(inet_ntop(AF_INET6,&chain->fw_dst,ntop_buf,sizeof(ntop_buf)));
344 if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) {
346 for (i = 0; i < ndp; i++) {
347 print_port(chain->fw_prot, chain->fw_pts[nsp+i], comma);
348 if (i==0 && (chain->fw_flg & IPV6_FW_F_DRNG))
356 if ((chain->fw_flg & IPV6_FW_F_IN) && !(chain->fw_flg & IPV6_FW_F_OUT))
358 if (!(chain->fw_flg & IPV6_FW_F_IN) && (chain->fw_flg & IPV6_FW_F_OUT))
361 /* Handle hack for "via" backwards compatibility */
362 if ((chain->fw_flg & IF6_FW_F_VIAHACK) == IF6_FW_F_VIAHACK) {
364 &chain->fw_in_if, chain->fw_flg & IPV6_FW_F_IIFNAME);
366 /* Receive interface specified */
367 if (chain->fw_flg & IPV6_FW_F_IIFACE)
368 print_iface("recv", &chain->fw_in_if,
369 chain->fw_flg & IPV6_FW_F_IIFNAME);
370 /* Transmit interface specified */
371 if (chain->fw_flg & IPV6_FW_F_OIFACE)
372 print_iface("xmit", &chain->fw_out_if,
373 chain->fw_flg & IPV6_FW_F_OIFNAME);
376 if (chain->fw_flg & IPV6_FW_F_FRAG)
379 if (chain->fw_ip6opt || chain->fw_ip6nopt) {
380 int _opt_printed = 0;
381 #define PRINTOPT(x) {if (_opt_printed) printf(",");\
382 printf(x); _opt_printed = 1;}
385 if (chain->fw_ip6opt & IPV6_FW_IP6OPT_HOPOPT) PRINTOPT("hopopt");
386 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_HOPOPT) PRINTOPT("!hopopt");
387 if (chain->fw_ip6opt & IPV6_FW_IP6OPT_ROUTE) PRINTOPT("route");
388 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_ROUTE) PRINTOPT("!route");
389 if (chain->fw_ip6opt & IPV6_FW_IP6OPT_FRAG) PRINTOPT("frag");
390 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_FRAG) PRINTOPT("!frag");
391 if (chain->fw_ip6opt & IPV6_FW_IP6OPT_ESP) PRINTOPT("esp");
392 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_ESP) PRINTOPT("!esp");
393 if (chain->fw_ip6opt & IPV6_FW_IP6OPT_AH) PRINTOPT("ah");
394 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_AH) PRINTOPT("!ah");
395 if (chain->fw_ip6opt & IPV6_FW_IP6OPT_NONXT) PRINTOPT("nonxt");
396 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_NONXT) PRINTOPT("!nonxt");
397 if (chain->fw_ip6opt & IPV6_FW_IP6OPT_OPTS) PRINTOPT("opts");
398 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_OPTS) PRINTOPT("!opts");
401 if (chain->fw_ipflg & IPV6_FW_IF_TCPEST)
402 printf(" established");
403 else if (chain->fw_tcpf == IPV6_FW_TCPF_SYN &&
404 chain->fw_tcpnf == IPV6_FW_TCPF_ACK)
406 else if (chain->fw_tcpf || chain->fw_tcpnf) {
407 int _flg_printed = 0;
408 #define PRINTFLG(x) {if (_flg_printed) printf(",");\
409 printf(x); _flg_printed = 1;}
412 if (chain->fw_tcpf & IPV6_FW_TCPF_FIN) PRINTFLG("fin");
413 if (chain->fw_tcpnf & IPV6_FW_TCPF_FIN) PRINTFLG("!fin");
414 if (chain->fw_tcpf & IPV6_FW_TCPF_SYN) PRINTFLG("syn");
415 if (chain->fw_tcpnf & IPV6_FW_TCPF_SYN) PRINTFLG("!syn");
416 if (chain->fw_tcpf & IPV6_FW_TCPF_RST) PRINTFLG("rst");
417 if (chain->fw_tcpnf & IPV6_FW_TCPF_RST) PRINTFLG("!rst");
418 if (chain->fw_tcpf & IPV6_FW_TCPF_PSH) PRINTFLG("psh");
419 if (chain->fw_tcpnf & IPV6_FW_TCPF_PSH) PRINTFLG("!psh");
420 if (chain->fw_tcpf & IPV6_FW_TCPF_ACK) PRINTFLG("ack");
421 if (chain->fw_tcpnf & IPV6_FW_TCPF_ACK) PRINTFLG("!ack");
422 if (chain->fw_tcpf & IPV6_FW_TCPF_URG) PRINTFLG("urg");
423 if (chain->fw_tcpnf & IPV6_FW_TCPF_URG) PRINTFLG("!urg");
425 if (chain->fw_flg & IPV6_FW_F_ICMPBIT) {
431 for (type_index = 0; type_index < IPV6_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8; ++type_index)
432 if (chain->fw_icmp6types[type_index / (sizeof(unsigned) * 8)] &
433 (1U << (type_index % (sizeof(unsigned) * 8)))) {
434 printf("%c%d", first == 1 ? ' ' : ',', type_index);
448 struct ip6_fw *r, *rules;
450 unsigned long rulenum;
451 int nalloc, bytes, maxbytes;
453 /* extract rules from kernel, resizing array as necessary */
455 nalloc = sizeof *rules;
457 maxbytes = 65536 * sizeof *rules;
458 while (bytes >= nalloc) {
459 nalloc = nalloc * 2 + 200;
461 if ((rules = realloc(rules, bytes)) == NULL)
462 err(EX_OSERR, "realloc");
463 i = getsockopt(s, IPPROTO_IPV6, IPV6_FW_GET, rules, &bytes);
464 if ((i < 0 && errno != EINVAL) || nalloc > maxbytes)
465 err(EX_OSERR, "getsockopt(IPV6_FW_GET)");
468 /* display all rules */
469 for (r = rules, l = bytes; l >= sizeof rules[0];
470 r++, l-=sizeof rules[0])
474 /* display specific rules requested on command line */
481 /* convert command line rule # */
482 rulenum = strtoul(*av++, &endptr, 10);
485 warn("invalid rule number: %s", av[-1]);
489 for (r = rules, l = bytes;
490 l >= sizeof rules[0] && r->fw_number <= rulenum;
491 r++, l-=sizeof rules[0])
492 if (rulenum == r->fw_number) {
498 warnx("rule %lu does not exist", rulenum);
507 show_usage(const char *fmt, ...)
514 vsnprintf(buf, sizeof(buf), fmt, args);
516 warnx("error: %s", buf);
518 fprintf(stderr, "usage: ip6fw [options]\n"
520 " add [number] rule\n"
521 " delete number ...\n"
522 " list [number ...]\n"
523 " show [number ...]\n"
524 " zero [number ...]\n"
525 " rule: action proto src dst extras...\n"
527 " {allow|permit|accept|pass|deny|drop|reject|unreach code|\n"
528 " reset|count|skipto num} [log]\n"
529 " proto: {ipv6|tcp|udp|ipv6-icmp|<number>}\n"
530 " src: from [not] {any|ipv6[/prefixlen]} [{port|port-port},[port],...]\n"
531 " dst: to [not] {any|ipv6[/prefixlen]} [{port|port-port},[port],...]\n"
533 " fragment (may not be used with ports or tcpflags)\n"
536 " {xmit|recv|via} {iface|ipv6|any}\n"
537 " {established|setup}\n"
538 " tcpflags [!]{syn|fin|rst|ack|psh|urg},...\n"
539 " ipv6options [!]{hopopt|route|frag|esp|ah|nonxt|opts},...\n"
540 " icmptypes {type[,type]}...\n");
546 lookup_host (host, addr, family)
552 if (inet_pton(family, host, addr) != 1) {
553 if ((he = gethostbyname2(host, family)) == NULL)
555 memcpy(addr, he->h_addr_list[0], he->h_length);
561 fill_ip6(ipno, mask, acp, avp)
562 struct in6_addr *ipno, *mask;
571 if (ac && !strncmp(*av,"any",strlen(*av))) {
572 *ipno = *mask = in6addr_any; av++; ac--;
574 p = strchr(*av, '/');
580 if (lookup_host(*av, ipno, AF_INET6) != 0)
581 show_usage("hostname ``%s'' unknown", *av);
586 } else if (atoi(p) > 128) {
587 show_usage("bad width ``%s''", p);
589 *mask = *(plen2mask(atoi(p)));
593 *mask = *(plen2mask(128));
596 for (i = 0; i < sizeof(*ipno); i++)
597 ipno->s6_addr[i] &= mask->s6_addr[i];
606 fill_reject_code6(u_short *codep, char *str)
612 val = strtoul(str, &s, 0);
613 if (s != str && *s == '\0' && val < 0x100) {
617 for (ic = icmp6codes; ic->str; ic++)
618 if (!strcasecmp(str, ic->str)) {
622 show_usage("unknown ICMP6 unreachable code ``%s''", str);
626 add_port(cnt, ptr, off, port)
627 u_short *cnt, *ptr, off, port;
629 if (off + *cnt >= IPV6_FW_MAX_PORTS)
630 errx(1, "too many ports (max is %d)", IPV6_FW_MAX_PORTS);
631 ptr[off+*cnt] = port;
636 lookup_port(const char *arg, int test, int nodash)
642 snprintf(buf, sizeof(buf), "%s", arg);
643 buf[strcspn(arg, nodash ? "-," : ",")] = 0;
644 val = (int) strtoul(buf, &earg, 0);
645 if (!*buf || *earg) {
647 if ((s = getservbyname(buf, NULL))) {
648 val = htons(s->s_port);
651 errx(1, "unknown port ``%s''", arg);
656 if (val < 0 || val > 0xffff) {
658 errx(1, "port ``%s'' out of range", arg);
667 fill_port(cnt, ptr, off, arg)
668 u_short *cnt, *ptr, off;
672 int initial_range = 0;
674 s = arg + strcspn(arg, "-,"); /* first port name can't have a dash */
677 if (strchr(arg, ','))
678 errx(1, "port range must be first in list");
679 add_port(cnt, ptr, off, *arg ? lookup_port(arg, 0, 0) : 0x0000);
684 add_port(cnt, ptr, off, *arg ? lookup_port(arg, 0, 0) : 0xffff);
688 while (arg != NULL) {
692 add_port(cnt, ptr, off, lookup_port(arg, 0, 0));
695 return initial_range;
699 fill_tcpflag(set, reset, vp)
711 { "syn", IPV6_FW_TCPF_SYN },
712 { "fin", IPV6_FW_TCPF_FIN },
713 { "ack", IPV6_FW_TCPF_ACK },
714 { "psh", IPV6_FW_TCPF_PSH },
715 { "rst", IPV6_FW_TCPF_RST },
716 { "urg", IPV6_FW_TCPF_URG }
729 for (i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i)
730 if (!strncmp(p, flags[i].name, strlen(p))) {
731 *d |= flags[i].value;
734 if (i == sizeof(flags) / sizeof(flags[0]))
735 show_usage("invalid tcp flag ``%s''", p);
741 fill_ip6opt(u_char *set, u_char *reset, char **vp)
756 if (!strncmp(p,"hopopt",strlen(p))) *d |= IPV6_FW_IP6OPT_HOPOPT;
757 if (!strncmp(p,"route",strlen(p))) *d |= IPV6_FW_IP6OPT_ROUTE;
758 if (!strncmp(p,"frag",strlen(p))) *d |= IPV6_FW_IP6OPT_FRAG;
759 if (!strncmp(p,"esp",strlen(p))) *d |= IPV6_FW_IP6OPT_ESP;
760 if (!strncmp(p,"ah",strlen(p))) *d |= IPV6_FW_IP6OPT_AH;
761 if (!strncmp(p,"nonxt",strlen(p))) *d |= IPV6_FW_IP6OPT_NONXT;
762 if (!strncmp(p,"opts",strlen(p))) *d |= IPV6_FW_IP6OPT_OPTS;
768 fill_icmptypes(types, vp, fw_flg)
777 unsigned long icmptype;
782 icmptype = strtoul(c, &c, 0);
784 if ( *c != ',' && *c != '\0' )
785 show_usage("invalid ICMP6 type");
787 if (icmptype >= IPV6_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8)
788 show_usage("ICMP6 type out of range");
790 types[icmptype / (sizeof(unsigned) * 8)] |=
791 1 << (icmptype % (sizeof(unsigned) * 8));
792 *fw_flg |= IPV6_FW_F_ICMPBIT;
805 memset(&rule, 0, sizeof rule);
810 while (ac && isdigit(**av)) {
811 rule.fw_number = atoi(*av); av++; ac--;
812 i = setsockopt(s, IPPROTO_IPV6, IPV6_FW_DEL, &rule, sizeof rule);
815 warn("rule %u: setsockopt(%s)", rule.fw_number, "IPV6_FW_DEL");
823 verify_interface(union ip6_fw_if *ifu)
828 * If a unit was specified, check for that exact interface.
829 * If a wildcard was specified, check for unit 0.
831 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d",
833 ifu->fu_via_if.unit == -1 ? 0 : ifu->fu_via_if.unit);
835 if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
836 warnx("warning: interface ``%s'' does not exist", ifr.ifr_name);
840 fill_iface(char *which, union ip6_fw_if *ifu, int *byname, int ac, char *arg)
843 show_usage("missing argument for ``%s''", which);
845 /* Parse the interface or address */
846 if (!strcmp(arg, "any")) {
847 ifu->fu_via_ip6 = in6addr_any;
849 } else if (!isdigit(*arg)) {
853 strncpy(ifu->fu_via_if.name, arg, sizeof(ifu->fu_via_if.name));
854 ifu->fu_via_if.name[sizeof(ifu->fu_via_if.name) - 1] = '\0';
855 for (q = ifu->fu_via_if.name;
856 *q && !isdigit(*q) && *q != '*'; q++)
858 ifu->fu_via_if.unit = (*q == '*') ? -1 : atoi(q);
860 verify_interface(ifu);
861 } else if (inet_pton(AF_INET6, arg, &ifu->fu_via_ip6) != 1) {
862 show_usage("bad ip6 address ``%s''", arg);
876 int saw_xmrc = 0, saw_via = 0;
878 memset(&rule, 0, sizeof rule);
883 if (ac && isdigit(**av)) {
884 rule.fw_number = atoi(*av); av++; ac--;
889 show_usage("missing action");
890 if (!strncmp(*av,"accept",strlen(*av))
891 || !strncmp(*av,"pass",strlen(*av))
892 || !strncmp(*av,"allow",strlen(*av))
893 || !strncmp(*av,"permit",strlen(*av))) {
894 rule.fw_flg |= IPV6_FW_F_ACCEPT; av++; ac--;
895 } else if (!strncmp(*av,"count",strlen(*av))) {
896 rule.fw_flg |= IPV6_FW_F_COUNT; av++; ac--;
899 else if (!strncmp(*av,"divert",strlen(*av))) {
900 rule.fw_flg |= IPV6_FW_F_DIVERT; av++; ac--;
902 show_usage("missing divert port");
903 rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
904 if (rule.fw_divert_port == 0) {
907 s = getservbyname(av[-1], "divert");
909 rule.fw_divert_port = ntohs(s->s_port);
911 show_usage("illegal divert port");
913 } else if (!strncmp(*av,"tee",strlen(*av))) {
914 rule.fw_flg |= IPV6_FW_F_TEE; av++; ac--;
916 show_usage("missing divert port");
917 rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
918 if (rule.fw_divert_port == 0) {
921 s = getservbyname(av[-1], "divert");
923 rule.fw_divert_port = ntohs(s->s_port);
925 show_usage("illegal divert port");
929 else if (!strncmp(*av,"skipto",strlen(*av))) {
930 rule.fw_flg |= IPV6_FW_F_SKIPTO; av++; ac--;
932 show_usage("missing skipto rule number");
933 rule.fw_skipto_rule = strtoul(*av, NULL, 0); av++; ac--;
934 } else if ((!strncmp(*av,"deny",strlen(*av))
935 || !strncmp(*av,"drop",strlen(*av)))) {
936 rule.fw_flg |= IPV6_FW_F_DENY; av++; ac--;
937 } else if (!strncmp(*av,"reject",strlen(*av))) {
938 rule.fw_flg |= IPV6_FW_F_REJECT; av++; ac--;
939 rule.fw_reject_code = ICMP6_DST_UNREACH_NOROUTE;
940 } else if (!strncmp(*av,"reset",strlen(*av))) {
941 rule.fw_flg |= IPV6_FW_F_REJECT; av++; ac--;
942 rule.fw_reject_code = IPV6_FW_REJECT_RST; /* check TCP later */
943 } else if (!strncmp(*av,"unreach",strlen(*av))) {
944 rule.fw_flg |= IPV6_FW_F_REJECT; av++; ac--;
945 fill_reject_code6(&rule.fw_reject_code, *av); av++; ac--;
947 show_usage("invalid action ``%s''", *av);
951 if (ac && !strncmp(*av,"log",strlen(*av))) {
952 rule.fw_flg |= IPV6_FW_F_PRN; av++; ac--;
957 show_usage("missing protocol");
958 if ((proto = atoi(*av)) > 0) {
959 rule.fw_prot = proto; av++; ac--;
960 } else if (!strncmp(*av,"all",strlen(*av))) {
961 rule.fw_prot = IPPROTO_IPV6; av++; ac--;
962 } else if ((pe = getprotobyname(*av)) != NULL) {
963 rule.fw_prot = pe->p_proto; av++; ac--;
965 show_usage("invalid protocol ``%s''", *av);
968 if (rule.fw_prot != IPPROTO_TCP
969 && (rule.fw_flg & IPV6_FW_F_COMMAND) == IPV6_FW_F_REJECT
970 && rule.fw_reject_code == IPV6_FW_REJECT_RST)
971 show_usage("``reset'' is only valid for tcp packets");
974 if (ac && !strncmp(*av,"from",strlen(*av))) { av++; ac--; }
976 show_usage("missing ``from''");
978 if (ac && !strncmp(*av,"not",strlen(*av))) {
979 rule.fw_flg |= IPV6_FW_F_INVSRC;
983 show_usage("missing arguments");
985 fill_ip6(&rule.fw_src, &rule.fw_smsk, &ac, &av);
987 if (ac && (isdigit(**av) || lookup_port(*av, 1, 1) >= 0)) {
990 if (fill_port(&nports, rule.fw_pts, 0, *av))
991 rule.fw_flg |= IPV6_FW_F_SRNG;
992 IPV6_FW_SETNSRCP(&rule, nports);
997 if (ac && !strncmp(*av,"to",strlen(*av))) { av++; ac--; }
999 show_usage("missing ``to''");
1001 if (ac && !strncmp(*av,"not",strlen(*av))) {
1002 rule.fw_flg |= IPV6_FW_F_INVDST;
1006 show_usage("missing arguments");
1008 fill_ip6(&rule.fw_dst, &rule.fw_dmsk, &ac, &av);
1010 if (ac && (isdigit(**av) || lookup_port(*av, 1, 1) >= 0)) {
1013 if (fill_port(&nports,
1014 rule.fw_pts, IPV6_FW_GETNSRCP(&rule), *av))
1015 rule.fw_flg |= IPV6_FW_F_DRNG;
1016 IPV6_FW_SETNDSTP(&rule, nports);
1020 if ((rule.fw_prot != IPPROTO_TCP) && (rule.fw_prot != IPPROTO_UDP)
1021 && (IPV6_FW_GETNSRCP(&rule) || IPV6_FW_GETNDSTP(&rule))) {
1022 show_usage("only TCP and UDP protocols are valid"
1023 " with port specifications");
1027 if (!strncmp(*av,"in",strlen(*av))) {
1028 rule.fw_flg |= IPV6_FW_F_IN;
1029 av++; ac--; continue;
1031 if (!strncmp(*av,"out",strlen(*av))) {
1032 rule.fw_flg |= IPV6_FW_F_OUT;
1033 av++; ac--; continue;
1035 if (ac && !strncmp(*av,"xmit",strlen(*av))) {
1036 union ip6_fw_if ifu;
1041 show_usage("``via'' is incompatible"
1042 " with ``xmit'' and ``recv''");
1046 fill_iface("xmit", &ifu, &byname, ac, *av);
1047 rule.fw_out_if = ifu;
1048 rule.fw_flg |= IPV6_FW_F_OIFACE;
1050 rule.fw_flg |= IPV6_FW_F_OIFNAME;
1051 av++; ac--; continue;
1053 if (ac && !strncmp(*av,"recv",strlen(*av))) {
1054 union ip6_fw_if ifu;
1061 fill_iface("recv", &ifu, &byname, ac, *av);
1062 rule.fw_in_if = ifu;
1063 rule.fw_flg |= IPV6_FW_F_IIFACE;
1065 rule.fw_flg |= IPV6_FW_F_IIFNAME;
1066 av++; ac--; continue;
1068 if (ac && !strncmp(*av,"via",strlen(*av))) {
1069 union ip6_fw_if ifu;
1076 fill_iface("via", &ifu, &byname, ac, *av);
1077 rule.fw_out_if = rule.fw_in_if = ifu;
1080 (IPV6_FW_F_IIFNAME | IPV6_FW_F_OIFNAME);
1081 av++; ac--; continue;
1083 if (!strncmp(*av,"fragment",strlen(*av))) {
1084 rule.fw_flg |= IPV6_FW_F_FRAG;
1085 av++; ac--; continue;
1087 if (!strncmp(*av,"ipv6options",strlen(*av))) {
1090 show_usage("missing argument"
1091 " for ``ipv6options''");
1092 fill_ip6opt(&rule.fw_ip6opt, &rule.fw_ip6nopt, av);
1093 av++; ac--; continue;
1095 if (rule.fw_prot == IPPROTO_TCP) {
1096 if (!strncmp(*av,"established",strlen(*av))) {
1097 rule.fw_ipflg |= IPV6_FW_IF_TCPEST;
1098 av++; ac--; continue;
1100 if (!strncmp(*av,"setup",strlen(*av))) {
1101 rule.fw_tcpf |= IPV6_FW_TCPF_SYN;
1102 rule.fw_tcpnf |= IPV6_FW_TCPF_ACK;
1103 av++; ac--; continue;
1105 if (!strncmp(*av,"tcpflags",strlen(*av))) {
1108 show_usage("missing argument"
1109 " for ``tcpflags''");
1110 fill_tcpflag(&rule.fw_tcpf, &rule.fw_tcpnf, av);
1111 av++; ac--; continue;
1114 if (rule.fw_prot == IPPROTO_ICMPV6) {
1115 if (!strncmp(*av,"icmptypes",strlen(*av))) {
1118 show_usage("missing argument"
1119 " for ``icmptypes''");
1120 fill_icmptypes(rule.fw_icmp6types,
1122 av++; ac--; continue;
1125 show_usage("unknown argument ``%s''", *av);
1128 /* No direction specified -> do both directions */
1129 if (!(rule.fw_flg & (IPV6_FW_F_OUT|IPV6_FW_F_IN)))
1130 rule.fw_flg |= (IPV6_FW_F_OUT|IPV6_FW_F_IN);
1132 /* Sanity check interface check, but handle "via" case separately */
1134 if (rule.fw_flg & IPV6_FW_F_IN)
1135 rule.fw_flg |= IPV6_FW_F_IIFACE;
1136 if (rule.fw_flg & IPV6_FW_F_OUT)
1137 rule.fw_flg |= IPV6_FW_F_OIFACE;
1138 } else if ((rule.fw_flg & IPV6_FW_F_OIFACE) && (rule.fw_flg & IPV6_FW_F_IN))
1139 show_usage("can't check xmit interface of incoming packets");
1141 /* frag may not be used in conjunction with ports or TCP flags */
1142 if (rule.fw_flg & IPV6_FW_F_FRAG) {
1143 if (rule.fw_tcpf || rule.fw_tcpnf)
1144 show_usage("can't mix 'frag' and tcpflags");
1147 show_usage("can't mix 'frag' and port specifications");
1152 i = setsockopt(s, IPPROTO_IPV6, IPV6_FW_ADD, &rule, sizeof rule);
1154 err(EX_UNAVAILABLE, "setsockopt(%s)", "IPV6_FW_ADD");
1165 /* clear all entries */
1166 if (setsockopt(s,IPPROTO_IPV6,IPV6_FW_ZERO,NULL,0)<0)
1167 err(EX_UNAVAILABLE, "setsockopt(%s)", "IPV6_FW_ZERO");
1169 printf("Accounting cleared.\n");
1174 memset(&rule, 0, sizeof rule);
1177 if (isdigit(**av)) {
1178 rule.fw_number = atoi(*av); av++; ac--;
1179 if (setsockopt(s, IPPROTO_IPV6,
1180 IPV6_FW_ZERO, &rule, sizeof rule)) {
1181 warn("rule %u: setsockopt(%s)", rule.fw_number,
1186 printf("Entry %d cleared\n",
1189 show_usage("invalid rule number ``%s''", *av);
1204 /* init optind to 1 */
1211 /* Set the force flag for non-interactive processes */
1212 do_force = !isatty(STDIN_FILENO);
1214 while ((ch = getopt(ac, av ,"afqtN")) != -1)
1236 if (*(av+=optind)==NULL) {
1237 show_usage("Bad arguments");
1240 if (!strncmp(*av, "add", strlen(*av))) {
1242 } else if (!strncmp(*av, "delete", strlen(*av))) {
1244 } else if (!strncmp(*av, "flush", strlen(*av))) {
1247 if ( do_force || do_quiet )
1253 printf("Are you sure? [yn] ");
1256 c = toupper(getc(stdin));
1257 while (c != '\n' && getc(stdin) != '\n')
1260 } while (c != 'Y' && c != 'N');
1266 if (setsockopt(s,IPPROTO_IPV6,IPV6_FW_FLUSH,NULL,0) < 0)
1267 err(EX_UNAVAILABLE, "setsockopt(%s)", "IPV6_FW_FLUSH");
1269 printf("Flushed all rules.\n");
1271 } else if (!strncmp(*av, "zero", strlen(*av))) {
1273 } else if (!strncmp(*av, "print", strlen(*av))) {
1275 } else if (!strncmp(*av, "list", strlen(*av))) {
1277 } else if (!strncmp(*av, "show", strlen(*av))) {
1281 show_usage("Bad arguments");
1292 #define WHITESP " \t\f\v\n\r"
1294 char *a, *p, *args[MAX_ARGS], *cmd = NULL;
1296 int i, c, lineno, qflag, pflag, status;
1300 s = socket( AF_INET6, SOCK_RAW, IPPROTO_RAW );
1302 err(EX_UNAVAILABLE, "socket");
1307 * Only interpret the last command line argument as a file to
1308 * be preprocessed if it is specified as an absolute pathname.
1311 if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0) {
1312 qflag = pflag = i = 0;
1315 while ((c = getopt(ac, av, "D:U:p:q")) != -1)
1319 errx(EX_USAGE, "-D requires -p");
1320 if (i > MAX_ARGS - 2)
1322 "too many -D or -U options");
1329 errx(EX_USAGE, "-U requires -p");
1330 if (i > MAX_ARGS - 2)
1332 "too many -D or -U options");
1355 show_usage("extraneous filename arguments");
1357 if ((f = fopen(av[0], "r")) == NULL)
1358 err(EX_UNAVAILABLE, "fopen: %s", av[0]);
1361 /* pipe through preprocessor (cpp or m4) */
1366 if (pipe(pipedes) == -1)
1367 err(EX_OSERR, "cannot create pipe");
1369 switch((preproc = fork())) {
1371 err(EX_OSERR, "cannot fork");
1375 if (dup2(fileno(f), 0) == -1 ||
1376 dup2(pipedes[1], 1) == -1)
1377 err(EX_OSERR, "dup2()");
1382 err(EX_OSERR, "execvp(%s) failed", cmd);
1388 if ((f = fdopen(pipedes[0], "r")) == NULL) {
1389 int savederrno = errno;
1391 (void)kill(preproc, SIGTERM);
1393 err(EX_OSERR, "fdopen()");
1398 while (fgets(buf, BUFSIZ, f)) {
1400 sprintf(linename, "Line %d", lineno);
1405 if ((p = strchr(buf, '#')) != NULL)
1408 if (qflag) args[i++]="-q";
1409 for (a = strtok(buf, WHITESP);
1410 a && i < MAX_ARGS; a = strtok(NULL, WHITESP), i++)
1412 if (i == (qflag? 2: 1))
1415 errx(EX_USAGE, "%s: too many arguments", linename);
1418 ip6fw_main(i, args);
1422 if (waitpid(preproc, &status, 0) != -1) {
1423 if (WIFEXITED(status)) {
1424 if (WEXITSTATUS(status) != EX_OK)
1425 errx(EX_UNAVAILABLE,
1426 "preprocessor exited with status %d",
1427 WEXITSTATUS(status));
1428 } else if (WIFSIGNALED(status)) {
1429 errx(EX_UNAVAILABLE,
1430 "preprocessor exited with signal %d",