]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/ipfw/ipfw2.c
This commit was generated by cvs2svn to compensate for changes in r162079,
[FreeBSD/FreeBSD.git] / sbin / ipfw / ipfw2.c
1 /*
2  * Copyright (c) 2002-2003 Luigi Rizzo
3  * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
4  * Copyright (c) 1994 Ugen J.S.Antsilevich
5  *
6  * Idea and grammar partially left from:
7  * Copyright (c) 1993 Daniel Boulet
8  *
9  * Redistribution and use in source forms, with and without modification,
10  * are permitted provided that this entire comment appears intact.
11  *
12  * Redistribution in binary form may occur without any restrictions.
13  * Obviously, it would be nice if you gave credit where credit is due
14  * but requiring it would be too onerous.
15  *
16  * This software is provided ``AS IS'' without any warranties of any kind.
17  *
18  * NEW command line interface for IP firewall facility
19  *
20  * $FreeBSD$
21  */
22
23 #include <sys/param.h>
24 #include <sys/mbuf.h>
25 #include <sys/socket.h>
26 #include <sys/sockio.h>
27 #include <sys/sysctl.h>
28 #include <sys/time.h>
29 #include <sys/wait.h>
30 #include <sys/queue.h>
31
32 #include <ctype.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <grp.h>
36 #include <limits.h>
37 #include <netdb.h>
38 #include <pwd.h>
39 #include <signal.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <stdarg.h>
43 #include <string.h>
44 #include <timeconv.h>   /* XXX do we need this ? */
45 #include <unistd.h>
46 #include <sysexits.h>
47 #include <unistd.h>
48 #include <fcntl.h>
49
50 #include <net/if.h>
51 #include <net/pfvar.h>
52 #include <net/route.h> /* def. of struct route */
53 #include <netinet/in.h>
54 #include <netinet/in_systm.h>
55 #include <netinet/ip.h>
56 #include <netinet/ip_icmp.h>
57 #include <netinet/icmp6.h>
58 #include <netinet/ip_fw.h>
59 #include <netinet/ip_dummynet.h>
60 #include <netinet/tcp.h>
61 #include <arpa/inet.h>
62
63 int
64                 do_resolv,              /* Would try to resolve all */
65                 do_time,                /* Show time stamps */
66                 do_quiet,               /* Be quiet in add and flush */
67                 do_pipe,                /* this cmd refers to a pipe */
68                 do_sort,                /* field to sort results (0 = no) */
69                 do_dynamic,             /* display dynamic rules */
70                 do_expired,             /* display expired dynamic rules */
71                 do_compact,             /* show rules in compact mode */
72                 do_force,               /* do not ask for confirmation */
73                 show_sets,              /* display rule sets */
74                 test_only,              /* only check syntax */
75                 comment_only,           /* only print action and comment */
76                 verbose;
77
78 #define IP_MASK_ALL     0xffffffff
79 /*
80  * the following macro returns an error message if we run out of
81  * arguments.
82  */
83 #define NEED1(msg)      {if (!ac) errx(EX_USAGE, msg);}
84
85 #define GET_UINT_ARG(arg, min, max, tok, s_x) do {                      \
86         if (!ac)                                                        \
87                 errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \
88         if (_substrcmp(*av, "tablearg") == 0) {                         \
89                 arg = IP_FW_TABLEARG;                                   \
90                 break;                                                  \
91         }                                                               \
92                                                                         \
93         {                                                               \
94         long val;                                                       \
95         char *end;                                                      \
96                                                                         \
97         val = strtol(*av, &end, 10);                                    \
98                                                                         \
99         if (!isdigit(**av) || *end != '\0' || (val == 0 && errno == EINVAL)) \
100                 errx(EX_DATAERR, "%s: invalid argument: %s",            \
101                     match_value(s_x, tok), *av);                        \
102                                                                         \
103         if (errno == ERANGE || val < min || val > max)                  \
104                 errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \
105                     match_value(s_x, tok), min, max, *av);              \
106                                                                         \
107         if (val == IP_FW_TABLEARG)                                      \
108                 errx(EX_DATAERR, "%s: illegal argument value: %s",      \
109                     match_value(s_x, tok), *av);                        \
110         arg = val;                                                      \
111         }                                                               \
112 } while (0)
113
114 #define PRINT_UINT_ARG(str, arg) do {                                   \
115         if (str != NULL)                                                \
116                 printf("%s",str);                                       \
117         if (arg == IP_FW_TABLEARG)                                      \
118                 printf("tablearg");                                     \
119         else                                                            \
120                 printf("%u", (uint32_t)arg);                            \
121 } while (0)
122
123 /*
124  * _s_x is a structure that stores a string <-> token pairs, used in
125  * various places in the parser. Entries are stored in arrays,
126  * with an entry with s=NULL as terminator.
127  * The search routines are match_token() and match_value().
128  * Often, an element with x=0 contains an error string.
129  *
130  */
131 struct _s_x {
132         char const *s;
133         int x;
134 };
135
136 static struct _s_x f_tcpflags[] = {
137         { "syn", TH_SYN },
138         { "fin", TH_FIN },
139         { "ack", TH_ACK },
140         { "psh", TH_PUSH },
141         { "rst", TH_RST },
142         { "urg", TH_URG },
143         { "tcp flag", 0 },
144         { NULL, 0 }
145 };
146
147 static struct _s_x f_tcpopts[] = {
148         { "mss",        IP_FW_TCPOPT_MSS },
149         { "maxseg",     IP_FW_TCPOPT_MSS },
150         { "window",     IP_FW_TCPOPT_WINDOW },
151         { "sack",       IP_FW_TCPOPT_SACK },
152         { "ts",         IP_FW_TCPOPT_TS },
153         { "timestamp",  IP_FW_TCPOPT_TS },
154         { "cc",         IP_FW_TCPOPT_CC },
155         { "tcp option", 0 },
156         { NULL, 0 }
157 };
158
159 /*
160  * IP options span the range 0 to 255 so we need to remap them
161  * (though in fact only the low 5 bits are significant).
162  */
163 static struct _s_x f_ipopts[] = {
164         { "ssrr",       IP_FW_IPOPT_SSRR},
165         { "lsrr",       IP_FW_IPOPT_LSRR},
166         { "rr",         IP_FW_IPOPT_RR},
167         { "ts",         IP_FW_IPOPT_TS},
168         { "ip option",  0 },
169         { NULL, 0 }
170 };
171
172 static struct _s_x f_iptos[] = {
173         { "lowdelay",   IPTOS_LOWDELAY},
174         { "throughput", IPTOS_THROUGHPUT},
175         { "reliability", IPTOS_RELIABILITY},
176         { "mincost",    IPTOS_MINCOST},
177         { "congestion", IPTOS_CE},
178         { "ecntransport", IPTOS_ECT},
179         { "ip tos option", 0},
180         { NULL, 0 }
181 };
182
183 static struct _s_x limit_masks[] = {
184         {"all",         DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
185         {"src-addr",    DYN_SRC_ADDR},
186         {"src-port",    DYN_SRC_PORT},
187         {"dst-addr",    DYN_DST_ADDR},
188         {"dst-port",    DYN_DST_PORT},
189         {NULL,          0}
190 };
191
192 /*
193  * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines
194  * This is only used in this code.
195  */
196 #define IPPROTO_ETHERTYPE       0x1000
197 static struct _s_x ether_types[] = {
198     /*
199      * Note, we cannot use "-:&/" in the names because they are field
200      * separators in the type specifications. Also, we use s = NULL as
201      * end-delimiter, because a type of 0 can be legal.
202      */
203         { "ip",         0x0800 },
204         { "ipv4",       0x0800 },
205         { "ipv6",       0x86dd },
206         { "arp",        0x0806 },
207         { "rarp",       0x8035 },
208         { "vlan",       0x8100 },
209         { "loop",       0x9000 },
210         { "trail",      0x1000 },
211         { "at",         0x809b },
212         { "atalk",      0x809b },
213         { "aarp",       0x80f3 },
214         { "pppoe_disc", 0x8863 },
215         { "pppoe_sess", 0x8864 },
216         { "ipx_8022",   0x00E0 },
217         { "ipx_8023",   0x0000 },
218         { "ipx_ii",     0x8137 },
219         { "ipx_snap",   0x8137 },
220         { "ipx",        0x8137 },
221         { "ns",         0x0600 },
222         { NULL,         0 }
223 };
224
225 static void show_usage(void);
226
227 enum tokens {
228         TOK_NULL=0,
229
230         TOK_OR,
231         TOK_NOT,
232         TOK_STARTBRACE,
233         TOK_ENDBRACE,
234
235         TOK_ACCEPT,
236         TOK_COUNT,
237         TOK_PIPE,
238         TOK_QUEUE,
239         TOK_DIVERT,
240         TOK_TEE,
241         TOK_NETGRAPH,
242         TOK_NGTEE,
243         TOK_FORWARD,
244         TOK_SKIPTO,
245         TOK_DENY,
246         TOK_REJECT,
247         TOK_RESET,
248         TOK_UNREACH,
249         TOK_CHECKSTATE,
250
251         TOK_ALTQ,
252         TOK_LOG,
253         TOK_TAG,
254         TOK_UNTAG,
255
256         TOK_TAGGED,
257         TOK_UID,
258         TOK_GID,
259         TOK_JAIL,
260         TOK_IN,
261         TOK_LIMIT,
262         TOK_KEEPSTATE,
263         TOK_LAYER2,
264         TOK_OUT,
265         TOK_DIVERTED,
266         TOK_DIVERTEDLOOPBACK,
267         TOK_DIVERTEDOUTPUT,
268         TOK_XMIT,
269         TOK_RECV,
270         TOK_VIA,
271         TOK_FRAG,
272         TOK_IPOPTS,
273         TOK_IPLEN,
274         TOK_IPID,
275         TOK_IPPRECEDENCE,
276         TOK_IPTOS,
277         TOK_IPTTL,
278         TOK_IPVER,
279         TOK_ESTAB,
280         TOK_SETUP,
281         TOK_TCPDATALEN,
282         TOK_TCPFLAGS,
283         TOK_TCPOPTS,
284         TOK_TCPSEQ,
285         TOK_TCPACK,
286         TOK_TCPWIN,
287         TOK_ICMPTYPES,
288         TOK_MAC,
289         TOK_MACTYPE,
290         TOK_VERREVPATH,
291         TOK_VERSRCREACH,
292         TOK_ANTISPOOF,
293         TOK_IPSEC,
294         TOK_COMMENT,
295
296         TOK_PLR,
297         TOK_NOERROR,
298         TOK_BUCKETS,
299         TOK_DSTIP,
300         TOK_SRCIP,
301         TOK_DSTPORT,
302         TOK_SRCPORT,
303         TOK_ALL,
304         TOK_MASK,
305         TOK_BW,
306         TOK_DELAY,
307         TOK_RED,
308         TOK_GRED,
309         TOK_DROPTAIL,
310         TOK_PROTO,
311         TOK_WEIGHT,
312
313         TOK_IPV6,
314         TOK_FLOWID,
315         TOK_ICMP6TYPES,
316         TOK_EXT6HDR,
317         TOK_DSTIP6,
318         TOK_SRCIP6,
319
320         TOK_IPV4,
321         TOK_UNREACH6,
322         TOK_RESET6,
323 };
324
325 struct _s_x dummynet_params[] = {
326         { "plr",                TOK_PLR },
327         { "noerror",            TOK_NOERROR },
328         { "buckets",            TOK_BUCKETS },
329         { "dst-ip",             TOK_DSTIP },
330         { "src-ip",             TOK_SRCIP },
331         { "dst-port",           TOK_DSTPORT },
332         { "src-port",           TOK_SRCPORT },
333         { "proto",              TOK_PROTO },
334         { "weight",             TOK_WEIGHT },
335         { "all",                TOK_ALL },
336         { "mask",               TOK_MASK },
337         { "droptail",           TOK_DROPTAIL },
338         { "red",                TOK_RED },
339         { "gred",               TOK_GRED },
340         { "bw",                 TOK_BW },
341         { "bandwidth",          TOK_BW },
342         { "delay",              TOK_DELAY },
343         { "pipe",               TOK_PIPE },
344         { "queue",              TOK_QUEUE },
345         { "flow-id",            TOK_FLOWID},
346         { "dst-ipv6",           TOK_DSTIP6},
347         { "dst-ip6",            TOK_DSTIP6},
348         { "src-ipv6",           TOK_SRCIP6},
349         { "src-ip6",            TOK_SRCIP6},
350         { "dummynet-params",    TOK_NULL },
351         { NULL, 0 }     /* terminator */
352 };
353
354 struct _s_x rule_actions[] = {
355         { "accept",             TOK_ACCEPT },
356         { "pass",               TOK_ACCEPT },
357         { "allow",              TOK_ACCEPT },
358         { "permit",             TOK_ACCEPT },
359         { "count",              TOK_COUNT },
360         { "pipe",               TOK_PIPE },
361         { "queue",              TOK_QUEUE },
362         { "divert",             TOK_DIVERT },
363         { "tee",                TOK_TEE },
364         { "netgraph",           TOK_NETGRAPH },
365         { "ngtee",              TOK_NGTEE },
366         { "fwd",                TOK_FORWARD },
367         { "forward",            TOK_FORWARD },
368         { "skipto",             TOK_SKIPTO },
369         { "deny",               TOK_DENY },
370         { "drop",               TOK_DENY },
371         { "reject",             TOK_REJECT },
372         { "reset6",             TOK_RESET6 },
373         { "reset",              TOK_RESET },
374         { "unreach6",           TOK_UNREACH6 },
375         { "unreach",            TOK_UNREACH },
376         { "check-state",        TOK_CHECKSTATE },
377         { "//",                 TOK_COMMENT },
378         { NULL, 0 }     /* terminator */
379 };
380
381 struct _s_x rule_action_params[] = {
382         { "altq",               TOK_ALTQ },
383         { "log",                TOK_LOG },
384         { "tag",                TOK_TAG },
385         { "untag",              TOK_UNTAG },
386         { NULL, 0 }     /* terminator */
387 };
388
389 struct _s_x rule_options[] = {
390         { "tagged",             TOK_TAGGED },
391         { "uid",                TOK_UID },
392         { "gid",                TOK_GID },
393         { "jail",               TOK_JAIL },
394         { "in",                 TOK_IN },
395         { "limit",              TOK_LIMIT },
396         { "keep-state",         TOK_KEEPSTATE },
397         { "bridged",            TOK_LAYER2 },
398         { "layer2",             TOK_LAYER2 },
399         { "out",                TOK_OUT },
400         { "diverted",           TOK_DIVERTED },
401         { "diverted-loopback",  TOK_DIVERTEDLOOPBACK },
402         { "diverted-output",    TOK_DIVERTEDOUTPUT },
403         { "xmit",               TOK_XMIT },
404         { "recv",               TOK_RECV },
405         { "via",                TOK_VIA },
406         { "fragment",           TOK_FRAG },
407         { "frag",               TOK_FRAG },
408         { "ipoptions",          TOK_IPOPTS },
409         { "ipopts",             TOK_IPOPTS },
410         { "iplen",              TOK_IPLEN },
411         { "ipid",               TOK_IPID },
412         { "ipprecedence",       TOK_IPPRECEDENCE },
413         { "iptos",              TOK_IPTOS },
414         { "ipttl",              TOK_IPTTL },
415         { "ipversion",          TOK_IPVER },
416         { "ipver",              TOK_IPVER },
417         { "estab",              TOK_ESTAB },
418         { "established",        TOK_ESTAB },
419         { "setup",              TOK_SETUP },
420         { "tcpdatalen",         TOK_TCPDATALEN },
421         { "tcpflags",           TOK_TCPFLAGS },
422         { "tcpflgs",            TOK_TCPFLAGS },
423         { "tcpoptions",         TOK_TCPOPTS },
424         { "tcpopts",            TOK_TCPOPTS },
425         { "tcpseq",             TOK_TCPSEQ },
426         { "tcpack",             TOK_TCPACK },
427         { "tcpwin",             TOK_TCPWIN },
428         { "icmptype",           TOK_ICMPTYPES },
429         { "icmptypes",          TOK_ICMPTYPES },
430         { "dst-ip",             TOK_DSTIP },
431         { "src-ip",             TOK_SRCIP },
432         { "dst-port",           TOK_DSTPORT },
433         { "src-port",           TOK_SRCPORT },
434         { "proto",              TOK_PROTO },
435         { "MAC",                TOK_MAC },
436         { "mac",                TOK_MAC },
437         { "mac-type",           TOK_MACTYPE },
438         { "verrevpath",         TOK_VERREVPATH },
439         { "versrcreach",        TOK_VERSRCREACH },
440         { "antispoof",          TOK_ANTISPOOF },
441         { "ipsec",              TOK_IPSEC },
442         { "icmp6type",          TOK_ICMP6TYPES },
443         { "icmp6types",         TOK_ICMP6TYPES },
444         { "ext6hdr",            TOK_EXT6HDR},
445         { "flow-id",            TOK_FLOWID},
446         { "ipv6",               TOK_IPV6},
447         { "ip6",                TOK_IPV6},
448         { "ipv4",               TOK_IPV4},
449         { "ip4",                TOK_IPV4},
450         { "dst-ipv6",           TOK_DSTIP6},
451         { "dst-ip6",            TOK_DSTIP6},
452         { "src-ipv6",           TOK_SRCIP6},
453         { "src-ip6",            TOK_SRCIP6},
454         { "//",                 TOK_COMMENT },
455
456         { "not",                TOK_NOT },              /* pseudo option */
457         { "!", /* escape ? */   TOK_NOT },              /* pseudo option */
458         { "or",                 TOK_OR },               /* pseudo option */
459         { "|", /* escape */     TOK_OR },               /* pseudo option */
460         { "{",                  TOK_STARTBRACE },       /* pseudo option */
461         { "(",                  TOK_STARTBRACE },       /* pseudo option */
462         { "}",                  TOK_ENDBRACE },         /* pseudo option */
463         { ")",                  TOK_ENDBRACE },         /* pseudo option */
464         { NULL, 0 }     /* terminator */
465 };
466
467 #define TABLEARG        "tablearg"
468
469 static __inline uint64_t
470 align_uint64(uint64_t *pll) {
471         uint64_t ret;
472
473         bcopy (pll, &ret, sizeof(ret));
474         return ret;
475 }
476
477 /*
478  * conditionally runs the command.
479  */
480 static int
481 do_cmd(int optname, void *optval, uintptr_t optlen)
482 {
483         static int s = -1;      /* the socket */
484         int i;
485
486         if (test_only)
487                 return 0;
488
489         if (s == -1)
490                 s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
491         if (s < 0)
492                 err(EX_UNAVAILABLE, "socket");
493
494         if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
495             optname == IP_FW_ADD || optname == IP_FW_TABLE_LIST ||
496             optname == IP_FW_TABLE_GETSIZE)
497                 i = getsockopt(s, IPPROTO_IP, optname, optval,
498                         (socklen_t *)optlen);
499         else
500                 i = setsockopt(s, IPPROTO_IP, optname, optval, optlen);
501         return i;
502 }
503
504 /**
505  * match_token takes a table and a string, returns the value associated
506  * with the string (-1 in case of failure).
507  */
508 static int
509 match_token(struct _s_x *table, char *string)
510 {
511         struct _s_x *pt;
512         uint i = strlen(string);
513
514         for (pt = table ; i && pt->s != NULL ; pt++)
515                 if (strlen(pt->s) == i && !bcmp(string, pt->s, i))
516                         return pt->x;
517         return -1;
518 }
519
520 /**
521  * match_value takes a table and a value, returns the string associated
522  * with the value (NULL in case of failure).
523  */
524 static char const *
525 match_value(struct _s_x *p, int value)
526 {
527         for (; p->s != NULL; p++)
528                 if (p->x == value)
529                         return p->s;
530         return NULL;
531 }
532
533 /*
534  * _substrcmp takes two strings and returns 1 if they do not match,
535  * and 0 if they match exactly or the first string is a sub-string
536  * of the second.  A warning is printed to stderr in the case that the
537  * first string is a sub-string of the second.
538  *
539  * This function will be removed in the future through the usual
540  * deprecation process.
541  */
542 static int
543 _substrcmp(const char *str1, const char* str2)
544 {
545         
546         if (strncmp(str1, str2, strlen(str1)) != 0)
547                 return 1;
548
549         if (strlen(str1) != strlen(str2))
550                 warnx("DEPRECATED: '%s' matched '%s' as a sub-string",
551                     str1, str2);
552         return 0;
553 }
554
555 /*
556  * _substrcmp2 takes three strings and returns 1 if the first two do not match,
557  * and 0 if they match exactly or the second string is a sub-string
558  * of the first.  A warning is printed to stderr in the case that the
559  * first string does not match the third.
560  *
561  * This function exists to warn about the bizzare construction
562  * strncmp(str, "by", 2) which is used to allow people to use a shotcut
563  * for "bytes".  The problem is that in addition to accepting "by",
564  * "byt", "byte", and "bytes", it also excepts "by_rabid_dogs" and any
565  * other string beginning with "by".
566  *
567  * This function will be removed in the future through the usual
568  * deprecation process.
569  */
570 static int
571 _substrcmp2(const char *str1, const char* str2, const char* str3)
572 {
573         
574         if (strncmp(str1, str2, strlen(str2)) != 0)
575                 return 1;
576
577         if (strcmp(str1, str3) != 0)
578                 warnx("DEPRECATED: '%s' matched '%s'",
579                     str1, str3);
580         return 0;
581 }
582
583 /*
584  * prints one port, symbolic or numeric
585  */
586 static void
587 print_port(int proto, uint16_t port)
588 {
589
590         if (proto == IPPROTO_ETHERTYPE) {
591                 char const *s;
592
593                 if (do_resolv && (s = match_value(ether_types, port)) )
594                         printf("%s", s);
595                 else
596                         printf("0x%04x", port);
597         } else {
598                 struct servent *se = NULL;
599                 if (do_resolv) {
600                         struct protoent *pe = getprotobynumber(proto);
601
602                         se = getservbyport(htons(port), pe ? pe->p_name : NULL);
603                 }
604                 if (se)
605                         printf("%s", se->s_name);
606                 else
607                         printf("%d", port);
608         }
609 }
610
611 struct _s_x _port_name[] = {
612         {"dst-port",    O_IP_DSTPORT},
613         {"src-port",    O_IP_SRCPORT},
614         {"ipid",        O_IPID},
615         {"iplen",       O_IPLEN},
616         {"ipttl",       O_IPTTL},
617         {"mac-type",    O_MAC_TYPE},
618         {"tcpdatalen",  O_TCPDATALEN},
619         {"tagged",      O_TAGGED},
620         {NULL,          0}
621 };
622
623 /*
624  * Print the values in a list 16-bit items of the types above.
625  * XXX todo: add support for mask.
626  */
627 static void
628 print_newports(ipfw_insn_u16 *cmd, int proto, int opcode)
629 {
630         uint16_t *p = cmd->ports;
631         int i;
632         char const *sep;
633
634         if (cmd->o.len & F_NOT)
635                 printf(" not");
636         if (opcode != 0) {
637                 sep = match_value(_port_name, opcode);
638                 if (sep == NULL)
639                         sep = "???";
640                 printf (" %s", sep);
641         }
642         sep = " ";
643         for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
644                 printf(sep);
645                 print_port(proto, p[0]);
646                 if (p[0] != p[1]) {
647                         printf("-");
648                         print_port(proto, p[1]);
649                 }
650                 sep = ",";
651         }
652 }
653
654 /*
655  * Like strtol, but also translates service names into port numbers
656  * for some protocols.
657  * In particular:
658  *      proto == -1 disables the protocol check;
659  *      proto == IPPROTO_ETHERTYPE looks up an internal table
660  *      proto == <some value in /etc/protocols> matches the values there.
661  * Returns *end == s in case the parameter is not found.
662  */
663 static int
664 strtoport(char *s, char **end, int base, int proto)
665 {
666         char *p, *buf;
667         char *s1;
668         int i;
669
670         *end = s;               /* default - not found */
671         if (*s == '\0')
672                 return 0;       /* not found */
673
674         if (isdigit(*s))
675                 return strtol(s, end, base);
676
677         /*
678          * find separator. '\\' escapes the next char.
679          */
680         for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++)
681                 if (*s1 == '\\' && s1[1] != '\0')
682                         s1++;
683
684         buf = malloc(s1 - s + 1);
685         if (buf == NULL)
686                 return 0;
687
688         /*
689          * copy into a buffer skipping backslashes
690          */
691         for (p = s, i = 0; p != s1 ; p++)
692                 if (*p != '\\')
693                         buf[i++] = *p;
694         buf[i++] = '\0';
695
696         if (proto == IPPROTO_ETHERTYPE) {
697                 i = match_token(ether_types, buf);
698                 free(buf);
699                 if (i != -1) {  /* found */
700                         *end = s1;
701                         return i;
702                 }
703         } else {
704                 struct protoent *pe = NULL;
705                 struct servent *se;
706
707                 if (proto != 0)
708                         pe = getprotobynumber(proto);
709                 setservent(1);
710                 se = getservbyname(buf, pe ? pe->p_name : NULL);
711                 free(buf);
712                 if (se != NULL) {
713                         *end = s1;
714                         return ntohs(se->s_port);
715                 }
716         }
717         return 0;       /* not found */
718 }
719
720 /*
721  * Map between current altq queue id numbers and names.
722  */
723 static int altq_fetched = 0;
724 static TAILQ_HEAD(, pf_altq) altq_entries = 
725         TAILQ_HEAD_INITIALIZER(altq_entries);
726
727 static void
728 altq_set_enabled(int enabled)
729 {
730         int pffd;
731
732         pffd = open("/dev/pf", O_RDWR);
733         if (pffd == -1)
734                 err(EX_UNAVAILABLE,
735                     "altq support opening pf(4) control device");
736         if (enabled) {
737                 if (ioctl(pffd, DIOCSTARTALTQ) != 0 && errno != EEXIST)
738                         err(EX_UNAVAILABLE, "enabling altq");
739         } else {
740                 if (ioctl(pffd, DIOCSTOPALTQ) != 0 && errno != ENOENT)
741                         err(EX_UNAVAILABLE, "disabling altq");
742         }
743         close(pffd);
744 }
745
746 static void
747 altq_fetch()
748 {
749         struct pfioc_altq pfioc;
750         struct pf_altq *altq;
751         int pffd, mnr;
752
753         if (altq_fetched)
754                 return;
755         altq_fetched = 1;
756         pffd = open("/dev/pf", O_RDONLY);
757         if (pffd == -1) {
758                 warn("altq support opening pf(4) control device");
759                 return;
760         }
761         bzero(&pfioc, sizeof(pfioc));
762         if (ioctl(pffd, DIOCGETALTQS, &pfioc) != 0) {
763                 warn("altq support getting queue list");
764                 close(pffd);
765                 return;
766         }
767         mnr = pfioc.nr;
768         for (pfioc.nr = 0; pfioc.nr < mnr; pfioc.nr++) {
769                 if (ioctl(pffd, DIOCGETALTQ, &pfioc) != 0) {
770                         if (errno == EBUSY)
771                                 break;
772                         warn("altq support getting queue list");
773                         close(pffd);
774                         return;
775                 }
776                 if (pfioc.altq.qid == 0)
777                         continue;
778                 altq = malloc(sizeof(*altq));
779                 if (altq == NULL)
780                         err(EX_OSERR, "malloc");
781                 *altq = pfioc.altq;
782                 TAILQ_INSERT_TAIL(&altq_entries, altq, entries);
783         }
784         close(pffd);
785 }
786
787 static u_int32_t
788 altq_name_to_qid(const char *name)
789 {
790         struct pf_altq *altq;
791
792         altq_fetch();
793         TAILQ_FOREACH(altq, &altq_entries, entries)
794                 if (strcmp(name, altq->qname) == 0)
795                         break;
796         if (altq == NULL)
797                 errx(EX_DATAERR, "altq has no queue named `%s'", name);
798         return altq->qid;
799 }
800
801 static const char *
802 altq_qid_to_name(u_int32_t qid)
803 {
804         struct pf_altq *altq;
805
806         altq_fetch();
807         TAILQ_FOREACH(altq, &altq_entries, entries)
808                 if (qid == altq->qid)
809                         break;
810         if (altq == NULL)
811                 return NULL;
812         return altq->qname;
813 }
814
815 static void
816 fill_altq_qid(u_int32_t *qid, const char *av)
817 {
818         *qid = altq_name_to_qid(av);
819 }
820
821 /*
822  * Fill the body of the command with the list of port ranges.
823  */
824 static int
825 fill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
826 {
827         uint16_t a, b, *p = cmd->ports;
828         int i = 0;
829         char *s = av;
830
831         while (*s) {
832                 a = strtoport(av, &s, 0, proto);
833                 if (s == av)                    /* empty or invalid argument */
834                         return (0);
835
836                 switch (*s) {
837                 case '-':                       /* a range */
838                         av = s + 1;
839                         b = strtoport(av, &s, 0, proto);
840                         /* Reject expressions like '1-abc' or '1-2-3'. */
841                         if (s == av || (*s != ',' && *s != '\0'))
842                                 return (0);
843                         p[0] = a;
844                         p[1] = b;
845                         break;
846                 case ',':                       /* comma separated list */
847                 case '\0':
848                         p[0] = p[1] = a;
849                         break;
850                 default:
851                         warnx("port list: invalid separator <%c> in <%s>",
852                                 *s, av);
853                         return (0);
854                 }
855
856                 i++;
857                 p += 2;
858                 av = s + 1;
859         }
860         if (i > 0) {
861                 if (i + 1 > F_LEN_MASK)
862                         errx(EX_DATAERR, "too many ports/ranges\n");
863                 cmd->o.len |= i + 1;    /* leave F_NOT and F_OR untouched */
864         }
865         return (i);
866 }
867
868 static struct _s_x icmpcodes[] = {
869       { "net",                  ICMP_UNREACH_NET },
870       { "host",                 ICMP_UNREACH_HOST },
871       { "protocol",             ICMP_UNREACH_PROTOCOL },
872       { "port",                 ICMP_UNREACH_PORT },
873       { "needfrag",             ICMP_UNREACH_NEEDFRAG },
874       { "srcfail",              ICMP_UNREACH_SRCFAIL },
875       { "net-unknown",          ICMP_UNREACH_NET_UNKNOWN },
876       { "host-unknown",         ICMP_UNREACH_HOST_UNKNOWN },
877       { "isolated",             ICMP_UNREACH_ISOLATED },
878       { "net-prohib",           ICMP_UNREACH_NET_PROHIB },
879       { "host-prohib",          ICMP_UNREACH_HOST_PROHIB },
880       { "tosnet",               ICMP_UNREACH_TOSNET },
881       { "toshost",              ICMP_UNREACH_TOSHOST },
882       { "filter-prohib",        ICMP_UNREACH_FILTER_PROHIB },
883       { "host-precedence",      ICMP_UNREACH_HOST_PRECEDENCE },
884       { "precedence-cutoff",    ICMP_UNREACH_PRECEDENCE_CUTOFF },
885       { NULL, 0 }
886 };
887
888 static void
889 fill_reject_code(u_short *codep, char *str)
890 {
891         int val;
892         char *s;
893
894         val = strtoul(str, &s, 0);
895         if (s == str || *s != '\0' || val >= 0x100)
896                 val = match_token(icmpcodes, str);
897         if (val < 0)
898                 errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
899         *codep = val;
900         return;
901 }
902
903 static void
904 print_reject_code(uint16_t code)
905 {
906         char const *s = match_value(icmpcodes, code);
907
908         if (s != NULL)
909                 printf("unreach %s", s);
910         else
911                 printf("unreach %u", code);
912 }
913
914 static struct _s_x icmp6codes[] = {
915       { "no-route",             ICMP6_DST_UNREACH_NOROUTE },
916       { "admin-prohib",         ICMP6_DST_UNREACH_ADMIN },
917       { "address",              ICMP6_DST_UNREACH_ADDR },
918       { "port",                 ICMP6_DST_UNREACH_NOPORT },
919       { NULL, 0 }
920 };
921
922 static void
923 fill_unreach6_code(u_short *codep, char *str)
924 {
925         int val;
926         char *s;
927
928         val = strtoul(str, &s, 0);
929         if (s == str || *s != '\0' || val >= 0x100)
930                 val = match_token(icmp6codes, str);
931         if (val < 0)
932                 errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str);
933         *codep = val;
934         return;
935 }
936
937 static void
938 print_unreach6_code(uint16_t code)
939 {
940         char const *s = match_value(icmp6codes, code);
941
942         if (s != NULL)
943                 printf("unreach6 %s", s);
944         else
945                 printf("unreach6 %u", code);
946 }
947
948 /*
949  * Returns the number of bits set (from left) in a contiguous bitmask,
950  * or -1 if the mask is not contiguous.
951  * XXX this needs a proper fix.
952  * This effectively works on masks in big-endian (network) format.
953  * when compiled on little endian architectures.
954  *
955  * First bit is bit 7 of the first byte -- note, for MAC addresses,
956  * the first bit on the wire is bit 0 of the first byte.
957  * len is the max length in bits.
958  */
959 static int
960 contigmask(uint8_t *p, int len)
961 {
962         int i, n;
963
964         for (i=0; i<len ; i++)
965                 if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
966                         break;
967         for (n=i+1; n < len; n++)
968                 if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
969                         return -1; /* mask not contiguous */
970         return i;
971 }
972
973 /*
974  * print flags set/clear in the two bitmasks passed as parameters.
975  * There is a specialized check for f_tcpflags.
976  */
977 static void
978 print_flags(char const *name, ipfw_insn *cmd, struct _s_x *list)
979 {
980         char const *comma = "";
981         int i;
982         uint8_t set = cmd->arg1 & 0xff;
983         uint8_t clear = (cmd->arg1 >> 8) & 0xff;
984
985         if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) {
986                 printf(" setup");
987                 return;
988         }
989
990         printf(" %s ", name);
991         for (i=0; list[i].x != 0; i++) {
992                 if (set & list[i].x) {
993                         set &= ~list[i].x;
994                         printf("%s%s", comma, list[i].s);
995                         comma = ",";
996                 }
997                 if (clear & list[i].x) {
998                         clear &= ~list[i].x;
999                         printf("%s!%s", comma, list[i].s);
1000                         comma = ",";
1001                 }
1002         }
1003 }
1004
1005 /*
1006  * Print the ip address contained in a command.
1007  */
1008 static void
1009 print_ip(ipfw_insn_ip *cmd, char const *s)
1010 {
1011         struct hostent *he = NULL;
1012         int len = F_LEN((ipfw_insn *)cmd);
1013         uint32_t *a = ((ipfw_insn_u32 *)cmd)->d;
1014
1015         printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
1016
1017         if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) {
1018                 printf("me");
1019                 return;
1020         }
1021         if (cmd->o.opcode == O_IP_SRC_LOOKUP ||
1022             cmd->o.opcode == O_IP_DST_LOOKUP) {
1023                 printf("table(%u", ((ipfw_insn *)cmd)->arg1);
1024                 if (len == F_INSN_SIZE(ipfw_insn_u32))
1025                         printf(",%u", *a);
1026                 printf(")");
1027                 return;
1028         }
1029         if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) {
1030                 uint32_t x, *map = (uint32_t *)&(cmd->mask);
1031                 int i, j;
1032                 char comma = '{';
1033
1034                 x = cmd->o.arg1 - 1;
1035                 x = htonl( ~x );
1036                 cmd->addr.s_addr = htonl(cmd->addr.s_addr);
1037                 printf("%s/%d", inet_ntoa(cmd->addr),
1038                         contigmask((uint8_t *)&x, 32));
1039                 x = cmd->addr.s_addr = htonl(cmd->addr.s_addr);
1040                 x &= 0xff; /* base */
1041                 /*
1042                  * Print bits and ranges.
1043                  * Locate first bit set (i), then locate first bit unset (j).
1044                  * If we have 3+ consecutive bits set, then print them as a
1045                  * range, otherwise only print the initial bit and rescan.
1046                  */
1047                 for (i=0; i < cmd->o.arg1; i++)
1048                         if (map[i/32] & (1<<(i & 31))) {
1049                                 for (j=i+1; j < cmd->o.arg1; j++)
1050                                         if (!(map[ j/32] & (1<<(j & 31))))
1051                                                 break;
1052                                 printf("%c%d", comma, i+x);
1053                                 if (j>i+2) { /* range has at least 3 elements */
1054                                         printf("-%d", j-1+x);
1055                                         i = j-1;
1056                                 }
1057                                 comma = ',';
1058                         }
1059                 printf("}");
1060                 return;
1061         }
1062         /*
1063          * len == 2 indicates a single IP, whereas lists of 1 or more
1064          * addr/mask pairs have len = (2n+1). We convert len to n so we
1065          * use that to count the number of entries.
1066          */
1067     for (len = len / 2; len > 0; len--, a += 2) {
1068         int mb =        /* mask length */
1069             (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ?
1070                 32 : contigmask((uint8_t *)&(a[1]), 32);
1071         if (mb == 32 && do_resolv)
1072                 he = gethostbyaddr((char *)&(a[0]), sizeof(u_long), AF_INET);
1073         if (he != NULL)         /* resolved to name */
1074                 printf("%s", he->h_name);
1075         else if (mb == 0)       /* any */
1076                 printf("any");
1077         else {          /* numeric IP followed by some kind of mask */
1078                 printf("%s", inet_ntoa( *((struct in_addr *)&a[0]) ) );
1079                 if (mb < 0)
1080                         printf(":%s", inet_ntoa( *((struct in_addr *)&a[1]) ) );
1081                 else if (mb < 32)
1082                         printf("/%d", mb);
1083         }
1084         if (len > 1)
1085                 printf(",");
1086     }
1087 }
1088
1089 /*
1090  * prints a MAC address/mask pair
1091  */
1092 static void
1093 print_mac(uint8_t *addr, uint8_t *mask)
1094 {
1095         int l = contigmask(mask, 48);
1096
1097         if (l == 0)
1098                 printf(" any");
1099         else {
1100                 printf(" %02x:%02x:%02x:%02x:%02x:%02x",
1101                     addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
1102                 if (l == -1)
1103                         printf("&%02x:%02x:%02x:%02x:%02x:%02x",
1104                             mask[0], mask[1], mask[2],
1105                             mask[3], mask[4], mask[5]);
1106                 else if (l < 48)
1107                         printf("/%d", l);
1108         }
1109 }
1110
1111 static void
1112 fill_icmptypes(ipfw_insn_u32 *cmd, char *av)
1113 {
1114         uint8_t type;
1115
1116         cmd->d[0] = 0;
1117         while (*av) {
1118                 if (*av == ',')
1119                         av++;
1120
1121                 type = strtoul(av, &av, 0);
1122
1123                 if (*av != ',' && *av != '\0')
1124                         errx(EX_DATAERR, "invalid ICMP type");
1125
1126                 if (type > 31)
1127                         errx(EX_DATAERR, "ICMP type out of range");
1128
1129                 cmd->d[0] |= 1 << type;
1130         }
1131         cmd->o.opcode = O_ICMPTYPE;
1132         cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
1133 }
1134
1135 static void
1136 print_icmptypes(ipfw_insn_u32 *cmd)
1137 {
1138         int i;
1139         char sep= ' ';
1140
1141         printf(" icmptypes");
1142         for (i = 0; i < 32; i++) {
1143                 if ( (cmd->d[0] & (1 << (i))) == 0)
1144                         continue;
1145                 printf("%c%d", sep, i);
1146                 sep = ',';
1147         }
1148 }
1149
1150 /* 
1151  * Print the ip address contained in a command.
1152  */
1153 static void
1154 print_ip6(ipfw_insn_ip6 *cmd, char const *s)
1155 {
1156        struct hostent *he = NULL;
1157        int len = F_LEN((ipfw_insn *) cmd) - 1;
1158        struct in6_addr *a = &(cmd->addr6);
1159        char trad[255];
1160
1161        printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
1162
1163        if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) {
1164                printf("me6");
1165                return;
1166        }
1167        if (cmd->o.opcode == O_IP6) {
1168                printf(" ip6");
1169                return;
1170        }
1171
1172        /*
1173         * len == 4 indicates a single IP, whereas lists of 1 or more
1174         * addr/mask pairs have len = (2n+1). We convert len to n so we
1175         * use that to count the number of entries.
1176         */
1177
1178        for (len = len / 4; len > 0; len -= 2, a += 2) {
1179            int mb =        /* mask length */
1180                (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ?
1181                128 : contigmask((uint8_t *)&(a[1]), 128);
1182
1183            if (mb == 128 && do_resolv)
1184                he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6);
1185            if (he != NULL)             /* resolved to name */
1186                printf("%s", he->h_name);
1187            else if (mb == 0)           /* any */
1188                printf("any");
1189            else {          /* numeric IP followed by some kind of mask */
1190                if (inet_ntop(AF_INET6,  a, trad, sizeof( trad ) ) == NULL)
1191                    printf("Error ntop in print_ip6\n");
1192                printf("%s",  trad );
1193                if (mb < 0)     /* XXX not really legal... */
1194                    printf(":%s",
1195                        inet_ntop(AF_INET6, &a[1], trad, sizeof(trad)));
1196                else if (mb < 128)
1197                    printf("/%d", mb);
1198            }
1199            if (len > 2)
1200                printf(",");
1201        }
1202 }
1203
1204 static void
1205 fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av)
1206 {
1207        uint8_t type;
1208
1209        cmd->d[0] = 0;
1210        while (*av) {
1211            if (*av == ',')
1212                av++;
1213            type = strtoul(av, &av, 0);
1214            if (*av != ',' && *av != '\0')
1215                errx(EX_DATAERR, "invalid ICMP6 type");
1216            /*
1217             * XXX: shouldn't this be 0xFF?  I can't see any reason why
1218             * we shouldn't be able to filter all possiable values
1219             * regardless of the ability of the rest of the kernel to do
1220             * anything useful with them.
1221             */
1222            if (type > ICMP6_MAXTYPE)
1223                errx(EX_DATAERR, "ICMP6 type out of range");
1224            cmd->d[type / 32] |= ( 1 << (type % 32));
1225        }
1226        cmd->o.opcode = O_ICMP6TYPE;
1227        cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6);
1228 }
1229
1230
1231 static void
1232 print_icmp6types(ipfw_insn_u32 *cmd)
1233 {
1234        int i, j;
1235        char sep= ' ';
1236
1237        printf(" ip6 icmp6types");
1238        for (i = 0; i < 7; i++)
1239                for (j=0; j < 32; ++j) {
1240                        if ( (cmd->d[i] & (1 << (j))) == 0)
1241                                continue;
1242                        printf("%c%d", sep, (i*32 + j));
1243                        sep = ',';
1244                }
1245 }
1246
1247 static void
1248 print_flow6id( ipfw_insn_u32 *cmd)
1249 {
1250        uint16_t i, limit = cmd->o.arg1;
1251        char sep = ',';
1252
1253        printf(" flow-id ");
1254        for( i=0; i < limit; ++i) {
1255                if (i == limit - 1)
1256                        sep = ' ';
1257                printf("%d%c", cmd->d[i], sep);
1258        }
1259 }
1260
1261 /* structure and define for the extension header in ipv6 */
1262 static struct _s_x ext6hdrcodes[] = {
1263        { "frag",       EXT_FRAGMENT },
1264        { "hopopt",     EXT_HOPOPTS },
1265        { "route",      EXT_ROUTING },
1266        { "dstopt",     EXT_DSTOPTS },
1267        { "ah",         EXT_AH },
1268        { "esp",        EXT_ESP },
1269        { NULL,         0 }
1270 };
1271
1272 /* fills command for the extension header filtering */
1273 int
1274 fill_ext6hdr( ipfw_insn *cmd, char *av)
1275 {
1276        int tok;
1277        char *s = av;
1278
1279        cmd->arg1 = 0;
1280
1281        while(s) {
1282            av = strsep( &s, ",") ;
1283            tok = match_token(ext6hdrcodes, av);
1284            switch (tok) {
1285            case EXT_FRAGMENT:
1286                cmd->arg1 |= EXT_FRAGMENT;
1287                break;
1288
1289            case EXT_HOPOPTS:
1290                cmd->arg1 |= EXT_HOPOPTS;
1291                break;
1292
1293            case EXT_ROUTING:
1294                cmd->arg1 |= EXT_ROUTING;
1295                break;
1296
1297            case EXT_DSTOPTS:
1298                cmd->arg1 |= EXT_DSTOPTS;
1299                break;
1300
1301            case EXT_AH:
1302                cmd->arg1 |= EXT_AH;
1303                break;
1304
1305            case EXT_ESP:
1306                cmd->arg1 |= EXT_ESP;
1307                break;
1308
1309            default:
1310                errx( EX_DATAERR, "invalid option for ipv6 exten header" );
1311                break;
1312            }
1313        }
1314        if (cmd->arg1 == 0 )
1315            return 0;
1316        cmd->opcode = O_EXT_HDR;
1317        cmd->len |= F_INSN_SIZE( ipfw_insn );
1318        return 1;
1319 }
1320
1321 void
1322 print_ext6hdr( ipfw_insn *cmd )
1323 {
1324        char sep = ' ';
1325
1326        printf(" extension header:");
1327        if (cmd->arg1 & EXT_FRAGMENT ) {
1328            printf("%cfragmentation", sep);
1329            sep = ',';
1330        }
1331        if (cmd->arg1 & EXT_HOPOPTS ) {
1332            printf("%chop options", sep);
1333            sep = ',';
1334        }
1335        if (cmd->arg1 & EXT_ROUTING ) {
1336            printf("%crouting options", sep);
1337            sep = ',';
1338        }
1339        if (cmd->arg1 & EXT_DSTOPTS ) {
1340            printf("%cdestination options", sep);
1341            sep = ',';
1342        }
1343        if (cmd->arg1 & EXT_AH ) {
1344            printf("%cauthentication header", sep);
1345            sep = ',';
1346        }
1347        if (cmd->arg1 & EXT_ESP ) {
1348            printf("%cencapsulated security payload", sep);
1349        }
1350 }
1351
1352 /*
1353  * show_ipfw() prints the body of an ipfw rule.
1354  * Because the standard rule has at least proto src_ip dst_ip, we use
1355  * a helper function to produce these entries if not provided explicitly.
1356  * The first argument is the list of fields we have, the second is
1357  * the list of fields we want to be printed.
1358  *
1359  * Special cases if we have provided a MAC header:
1360  *   + if the rule does not contain IP addresses/ports, do not print them;
1361  *   + if the rule does not contain an IP proto, print "all" instead of "ip";
1362  *
1363  * Once we have 'have_options', IP header fields are printed as options.
1364  */
1365 #define HAVE_PROTO      0x0001
1366 #define HAVE_SRCIP      0x0002
1367 #define HAVE_DSTIP      0x0004
1368 #define HAVE_MAC        0x0008
1369 #define HAVE_MACTYPE    0x0010
1370 #define HAVE_PROTO4     0x0040
1371 #define HAVE_PROTO6     0x0080
1372 #define HAVE_OPTIONS    0x8000
1373
1374 #define HAVE_IP         (HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP)
1375 static void
1376 show_prerequisites(int *flags, int want, int cmd)
1377 {
1378         if (comment_only)
1379                 return;
1380         if ( (*flags & HAVE_IP) == HAVE_IP)
1381                 *flags |= HAVE_OPTIONS;
1382
1383         if ( (*flags & (HAVE_MAC|HAVE_MACTYPE|HAVE_OPTIONS)) == HAVE_MAC &&
1384              cmd != O_MAC_TYPE) {
1385                 /*
1386                  * mac-type was optimized out by the compiler,
1387                  * restore it
1388                  */
1389                 printf(" any");
1390                 *flags |= HAVE_MACTYPE | HAVE_OPTIONS;
1391                 return;
1392         }
1393         if ( !(*flags & HAVE_OPTIONS)) {
1394                 if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO))
1395                         if ( (*flags & HAVE_PROTO4))
1396                                 printf(" ip4");
1397                         else if ( (*flags & HAVE_PROTO6))
1398                                 printf(" ip6");
1399                         else
1400                                 printf(" ip");
1401
1402                 if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
1403                         printf(" from any");
1404                 if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP))
1405                         printf(" to any");
1406         }
1407         *flags |= want;
1408 }
1409
1410 static void
1411 show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
1412 {
1413         static int twidth = 0;
1414         int l;
1415         ipfw_insn *cmd, *tagptr = NULL;
1416         char *comment = NULL;   /* ptr to comment if we have one */
1417         int proto = 0;          /* default */
1418         int flags = 0;  /* prerequisites */
1419         ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */
1420         ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */
1421         int or_block = 0;       /* we are in an or block */
1422         uint32_t set_disable;
1423
1424         bcopy(&rule->next_rule, &set_disable, sizeof(set_disable));
1425
1426         if (set_disable & (1 << rule->set)) { /* disabled */
1427                 if (!show_sets)
1428                         return;
1429                 else
1430                         printf("# DISABLED ");
1431         }
1432         printf("%05u ", rule->rulenum);
1433
1434         if (pcwidth>0 || bcwidth>0)
1435                 printf("%*llu %*llu ", pcwidth, align_uint64(&rule->pcnt),
1436                     bcwidth, align_uint64(&rule->bcnt));
1437
1438         if (do_time == 2)
1439                 printf("%10u ", rule->timestamp);
1440         else if (do_time == 1) {
1441                 char timestr[30];
1442                 time_t t = (time_t)0;
1443
1444                 if (twidth == 0) {
1445                         strcpy(timestr, ctime(&t));
1446                         *strchr(timestr, '\n') = '\0';
1447                         twidth = strlen(timestr);
1448                 }
1449                 if (rule->timestamp) {
1450                         t = _long_to_time(rule->timestamp);
1451
1452                         strcpy(timestr, ctime(&t));
1453                         *strchr(timestr, '\n') = '\0';
1454                         printf("%s ", timestr);
1455                 } else {
1456                         printf("%*s", twidth, " ");
1457                 }
1458         }
1459
1460         if (show_sets)
1461                 printf("set %d ", rule->set);
1462
1463         /*
1464          * print the optional "match probability"
1465          */
1466         if (rule->cmd_len > 0) {
1467                 cmd = rule->cmd ;
1468                 if (cmd->opcode == O_PROB) {
1469                         ipfw_insn_u32 *p = (ipfw_insn_u32 *)cmd;
1470                         double d = 1.0 * p->d[0];
1471
1472                         d = (d / 0x7fffffff);
1473                         printf("prob %f ", d);
1474                 }
1475         }
1476
1477         /*
1478          * first print actions
1479          */
1480         for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule);
1481                         l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
1482                 switch(cmd->opcode) {
1483                 case O_CHECK_STATE:
1484                         printf("check-state");
1485                         flags = HAVE_IP; /* avoid printing anything else */
1486                         break;
1487
1488                 case O_ACCEPT:
1489                         printf("allow");
1490                         break;
1491
1492                 case O_COUNT:
1493                         printf("count");
1494                         break;
1495
1496                 case O_DENY:
1497                         printf("deny");
1498                         break;
1499
1500                 case O_REJECT:
1501                         if (cmd->arg1 == ICMP_REJECT_RST)
1502                                 printf("reset");
1503                         else if (cmd->arg1 == ICMP_UNREACH_HOST)
1504                                 printf("reject");
1505                         else
1506                                 print_reject_code(cmd->arg1);
1507                         break;
1508
1509                 case O_UNREACH6:
1510                         if (cmd->arg1 == ICMP6_UNREACH_RST)
1511                                 printf("reset6");
1512                         else
1513                                 print_unreach6_code(cmd->arg1);
1514                         break;
1515
1516                 case O_SKIPTO:
1517                         PRINT_UINT_ARG("skipto ", cmd->arg1);
1518                         break;
1519
1520                 case O_PIPE:
1521                         PRINT_UINT_ARG("pipe ", cmd->arg1);
1522                         break;
1523
1524                 case O_QUEUE:
1525                         PRINT_UINT_ARG("queue ", cmd->arg1);
1526                         break;
1527
1528                 case O_DIVERT:
1529                         PRINT_UINT_ARG("divert ", cmd->arg1);
1530                         break;
1531
1532                 case O_TEE:
1533                         PRINT_UINT_ARG("tee ", cmd->arg1);
1534                         break;
1535
1536                 case O_NETGRAPH:
1537                         PRINT_UINT_ARG("netgraph ", cmd->arg1);
1538                         break;
1539
1540                 case O_NGTEE:
1541                         PRINT_UINT_ARG("ngtee ", cmd->arg1);
1542                         break;
1543
1544                 case O_FORWARD_IP:
1545                     {
1546                         ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
1547
1548                         if (s->sa.sin_addr.s_addr == INADDR_ANY) {
1549                                 printf("fwd tablearg");
1550                         } else {
1551                                 printf("fwd %s", inet_ntoa(s->sa.sin_addr));
1552                         }
1553                         if (s->sa.sin_port)
1554                                 printf(",%d", s->sa.sin_port);
1555                     }
1556                         break;
1557
1558                 case O_LOG: /* O_LOG is printed last */
1559                         logptr = (ipfw_insn_log *)cmd;
1560                         break;
1561
1562                 case O_ALTQ: /* O_ALTQ is printed after O_LOG */
1563                         altqptr = (ipfw_insn_altq *)cmd;
1564                         break;
1565
1566                 case O_TAG:
1567                         tagptr = cmd;
1568                         break;
1569
1570                 default:
1571                         printf("** unrecognized action %d len %d ",
1572                                 cmd->opcode, cmd->len);
1573                 }
1574         }
1575         if (logptr) {
1576                 if (logptr->max_log > 0)
1577                         printf(" log logamount %d", logptr->max_log);
1578                 else
1579                         printf(" log");
1580         }
1581         if (altqptr) {
1582                 const char *qname;
1583
1584                 qname = altq_qid_to_name(altqptr->qid);
1585                 if (qname == NULL)
1586                         printf(" altq ?<%u>", altqptr->qid);
1587                 else
1588                         printf(" altq %s", qname);
1589         }
1590         if (tagptr) {
1591                 if (tagptr->len & F_NOT)
1592                         PRINT_UINT_ARG(" untag ", tagptr->arg1);
1593                 else
1594                         PRINT_UINT_ARG(" tag ", tagptr->arg1);
1595         }
1596
1597         /*
1598          * then print the body.
1599          */
1600         for (l = rule->act_ofs, cmd = rule->cmd ;
1601                         l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
1602                 if ((cmd->len & F_OR) || (cmd->len & F_NOT))
1603                         continue;
1604                 if (cmd->opcode == O_IP4) {
1605                         flags |= HAVE_PROTO4;
1606                         break;
1607                 } else if (cmd->opcode == O_IP6) {
1608                         flags |= HAVE_PROTO6;
1609                         break;
1610                 }                       
1611         }
1612         if (rule->_pad & 1) {   /* empty rules before options */
1613                 if (!do_compact) {
1614                         show_prerequisites(&flags, HAVE_PROTO, 0);
1615                         printf(" from any to any");
1616                 }
1617                 flags |= HAVE_IP | HAVE_OPTIONS;
1618         }
1619
1620         if (comment_only)
1621                 comment = "...";
1622
1623         for (l = rule->act_ofs, cmd = rule->cmd ;
1624                         l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
1625                 /* useful alias */
1626                 ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
1627
1628                 if (comment_only) {
1629                         if (cmd->opcode != O_NOP)
1630                                 continue;
1631                         printf(" // %s\n", (char *)(cmd + 1));
1632                         return;
1633                 }
1634
1635                 show_prerequisites(&flags, 0, cmd->opcode);
1636
1637                 switch(cmd->opcode) {
1638                 case O_PROB:
1639                         break;  /* done already */
1640
1641                 case O_PROBE_STATE:
1642                         break; /* no need to print anything here */
1643
1644                 case O_MACADDR2: {
1645                         ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
1646
1647                         if ((cmd->len & F_OR) && !or_block)
1648                                 printf(" {");
1649                         if (cmd->len & F_NOT)
1650                                 printf(" not");
1651                         printf(" MAC");
1652                         flags |= HAVE_MAC;
1653                         print_mac(m->addr, m->mask);
1654                         print_mac(m->addr + 6, m->mask + 6);
1655                         }
1656                         break;
1657
1658                 case O_MAC_TYPE:
1659                         if ((cmd->len & F_OR) && !or_block)
1660                                 printf(" {");
1661                         print_newports((ipfw_insn_u16 *)cmd, IPPROTO_ETHERTYPE,
1662                                 (flags & HAVE_OPTIONS) ? cmd->opcode : 0);
1663                         flags |= HAVE_MAC | HAVE_MACTYPE | HAVE_OPTIONS;
1664                         break;
1665
1666                 case O_IP_SRC:
1667                 case O_IP_SRC_LOOKUP:
1668                 case O_IP_SRC_MASK:
1669                 case O_IP_SRC_ME:
1670                 case O_IP_SRC_SET:
1671                         show_prerequisites(&flags, HAVE_PROTO, 0);
1672                         if (!(flags & HAVE_SRCIP))
1673                                 printf(" from");
1674                         if ((cmd->len & F_OR) && !or_block)
1675                                 printf(" {");
1676                         print_ip((ipfw_insn_ip *)cmd,
1677                                 (flags & HAVE_OPTIONS) ? " src-ip" : "");
1678                         flags |= HAVE_SRCIP;
1679                         break;
1680
1681                 case O_IP_DST:
1682                 case O_IP_DST_LOOKUP:
1683                 case O_IP_DST_MASK:
1684                 case O_IP_DST_ME:
1685                 case O_IP_DST_SET:
1686                         show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1687                         if (!(flags & HAVE_DSTIP))
1688                                 printf(" to");
1689                         if ((cmd->len & F_OR) && !or_block)
1690                                 printf(" {");
1691                         print_ip((ipfw_insn_ip *)cmd,
1692                                 (flags & HAVE_OPTIONS) ? " dst-ip" : "");
1693                         flags |= HAVE_DSTIP;
1694                         break;
1695
1696                 case O_IP6_SRC:
1697                 case O_IP6_SRC_MASK:
1698                 case O_IP6_SRC_ME:
1699                         show_prerequisites(&flags, HAVE_PROTO, 0);
1700                         if (!(flags & HAVE_SRCIP))
1701                                 printf(" from");
1702                         if ((cmd->len & F_OR) && !or_block)
1703                                 printf(" {");
1704                         print_ip6((ipfw_insn_ip6 *)cmd,
1705                             (flags & HAVE_OPTIONS) ? " src-ip6" : "");
1706                         flags |= HAVE_SRCIP | HAVE_PROTO;
1707                         break;
1708
1709                 case O_IP6_DST:
1710                 case O_IP6_DST_MASK:
1711                 case O_IP6_DST_ME:
1712                         show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1713                         if (!(flags & HAVE_DSTIP))
1714                                 printf(" to");
1715                         if ((cmd->len & F_OR) && !or_block)
1716                                 printf(" {");
1717                         print_ip6((ipfw_insn_ip6 *)cmd,
1718                             (flags & HAVE_OPTIONS) ? " dst-ip6" : "");
1719                         flags |= HAVE_DSTIP;
1720                         break;
1721
1722                 case O_FLOW6ID:
1723                 print_flow6id( (ipfw_insn_u32 *) cmd );
1724                 flags |= HAVE_OPTIONS;
1725                 break;
1726
1727                 case O_IP_DSTPORT:
1728                         show_prerequisites(&flags, HAVE_IP, 0);
1729                 case O_IP_SRCPORT:
1730                         show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1731                         if ((cmd->len & F_OR) && !or_block)
1732                                 printf(" {");
1733                         print_newports((ipfw_insn_u16 *)cmd, proto,
1734                                 (flags & HAVE_OPTIONS) ? cmd->opcode : 0);
1735                         break;
1736
1737                 case O_PROTO: {
1738                         struct protoent *pe = NULL;
1739
1740                         if ((cmd->len & F_OR) && !or_block)
1741                                 printf(" {");
1742                         if (cmd->len & F_NOT)
1743                                 printf(" not");
1744                         proto = cmd->arg1;
1745                         pe = getprotobynumber(cmd->arg1);
1746                         if ((flags & (HAVE_PROTO4 | HAVE_PROTO6)) &&
1747                             !(flags & HAVE_PROTO))
1748                                 show_prerequisites(&flags,
1749                                     HAVE_IP | HAVE_OPTIONS, 0);
1750                         if (flags & HAVE_OPTIONS)
1751                                 printf(" proto");
1752                         if (pe)
1753                                 printf(" %s", pe->p_name);
1754                         else
1755                                 printf(" %u", cmd->arg1);
1756                         }
1757                         flags |= HAVE_PROTO;
1758                         break;
1759
1760                 default: /*options ... */
1761                         if (!(cmd->len & (F_OR|F_NOT)))
1762                                 if (((cmd->opcode == O_IP6) &&
1763                                     (flags & HAVE_PROTO6)) ||
1764                                     ((cmd->opcode == O_IP4) &&
1765                                     (flags & HAVE_PROTO4)))
1766                                         break;
1767                         show_prerequisites(&flags, HAVE_IP | HAVE_OPTIONS, 0);
1768                         if ((cmd->len & F_OR) && !or_block)
1769                                 printf(" {");
1770                         if (cmd->len & F_NOT && cmd->opcode != O_IN)
1771                                 printf(" not");
1772                         switch(cmd->opcode) {
1773                         case O_FRAG:
1774                                 printf(" frag");
1775                                 break;
1776
1777                         case O_IN:
1778                                 printf(cmd->len & F_NOT ? " out" : " in");
1779                                 break;
1780
1781                         case O_DIVERTED:
1782                                 switch (cmd->arg1) {
1783                                 case 3:
1784                                         printf(" diverted");
1785                                         break;
1786                                 case 1:
1787                                         printf(" diverted-loopback");
1788                                         break;
1789                                 case 2:
1790                                         printf(" diverted-output");
1791                                         break;
1792                                 default:
1793                                         printf(" diverted-?<%u>", cmd->arg1);
1794                                         break;
1795                                 }
1796                                 break;
1797
1798                         case O_LAYER2:
1799                                 printf(" layer2");
1800                                 break;
1801                         case O_XMIT:
1802                         case O_RECV:
1803                         case O_VIA:
1804                             {
1805                                 char const *s;
1806                                 ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd;
1807
1808                                 if (cmd->opcode == O_XMIT)
1809                                         s = "xmit";
1810                                 else if (cmd->opcode == O_RECV)
1811                                         s = "recv";
1812                                 else /* if (cmd->opcode == O_VIA) */
1813                                         s = "via";
1814                                 if (cmdif->name[0] == '\0')
1815                                         printf(" %s %s", s,
1816                                             inet_ntoa(cmdif->p.ip));
1817                                 else
1818                                         printf(" %s %s", s, cmdif->name);
1819
1820                                 break;
1821                             }
1822                         case O_IPID:
1823                                 if (F_LEN(cmd) == 1)
1824                                     printf(" ipid %u", cmd->arg1 );
1825                                 else
1826                                     print_newports((ipfw_insn_u16 *)cmd, 0,
1827                                         O_IPID);
1828                                 break;
1829
1830                         case O_IPTTL:
1831                                 if (F_LEN(cmd) == 1)
1832                                     printf(" ipttl %u", cmd->arg1 );
1833                                 else
1834                                     print_newports((ipfw_insn_u16 *)cmd, 0,
1835                                         O_IPTTL);
1836                                 break;
1837
1838                         case O_IPVER:
1839                                 printf(" ipver %u", cmd->arg1 );
1840                                 break;
1841
1842                         case O_IPPRECEDENCE:
1843                                 printf(" ipprecedence %u", (cmd->arg1) >> 5 );
1844                                 break;
1845
1846                         case O_IPLEN:
1847                                 if (F_LEN(cmd) == 1)
1848                                     printf(" iplen %u", cmd->arg1 );
1849                                 else
1850                                     print_newports((ipfw_insn_u16 *)cmd, 0,
1851                                         O_IPLEN);
1852                                 break;
1853
1854                         case O_IPOPT:
1855                                 print_flags("ipoptions", cmd, f_ipopts);
1856                                 break;
1857
1858                         case O_IPTOS:
1859                                 print_flags("iptos", cmd, f_iptos);
1860                                 break;
1861
1862                         case O_ICMPTYPE:
1863                                 print_icmptypes((ipfw_insn_u32 *)cmd);
1864                                 break;
1865
1866                         case O_ESTAB:
1867                                 printf(" established");
1868                                 break;
1869
1870                         case O_TCPDATALEN:
1871                                 if (F_LEN(cmd) == 1)
1872                                     printf(" tcpdatalen %u", cmd->arg1 );
1873                                 else
1874                                     print_newports((ipfw_insn_u16 *)cmd, 0,
1875                                         O_TCPDATALEN);
1876                                 break;
1877
1878                         case O_TCPFLAGS:
1879                                 print_flags("tcpflags", cmd, f_tcpflags);
1880                                 break;
1881
1882                         case O_TCPOPTS:
1883                                 print_flags("tcpoptions", cmd, f_tcpopts);
1884                                 break;
1885
1886                         case O_TCPWIN:
1887                                 printf(" tcpwin %d", ntohs(cmd->arg1));
1888                                 break;
1889
1890                         case O_TCPACK:
1891                                 printf(" tcpack %d", ntohl(cmd32->d[0]));
1892                                 break;
1893
1894                         case O_TCPSEQ:
1895                                 printf(" tcpseq %d", ntohl(cmd32->d[0]));
1896                                 break;
1897
1898                         case O_UID:
1899                             {
1900                                 struct passwd *pwd = getpwuid(cmd32->d[0]);
1901
1902                                 if (pwd)
1903                                         printf(" uid %s", pwd->pw_name);
1904                                 else
1905                                         printf(" uid %u", cmd32->d[0]);
1906                             }
1907                                 break;
1908
1909                         case O_GID:
1910                             {
1911                                 struct group *grp = getgrgid(cmd32->d[0]);
1912
1913                                 if (grp)
1914                                         printf(" gid %s", grp->gr_name);
1915                                 else
1916                                         printf(" gid %u", cmd32->d[0]);
1917                             }
1918                                 break;
1919
1920                         case O_JAIL:
1921                                 printf(" jail %d", cmd32->d[0]);
1922                                 break;
1923
1924                         case O_VERREVPATH:
1925                                 printf(" verrevpath");
1926                                 break;
1927
1928                         case O_VERSRCREACH:
1929                                 printf(" versrcreach");
1930                                 break;
1931
1932                         case O_ANTISPOOF:
1933                                 printf(" antispoof");
1934                                 break;
1935
1936                         case O_IPSEC:
1937                                 printf(" ipsec");
1938                                 break;
1939
1940                         case O_NOP:
1941                                 comment = (char *)(cmd + 1);
1942                                 break;
1943
1944                         case O_KEEP_STATE:
1945                                 printf(" keep-state");
1946                                 break;
1947
1948                         case O_LIMIT: {
1949                                 struct _s_x *p = limit_masks;
1950                                 ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
1951                                 uint8_t x = c->limit_mask;
1952                                 char const *comma = " ";
1953
1954                                 printf(" limit");
1955                                 for (; p->x != 0 ; p++)
1956                                         if ((x & p->x) == p->x) {
1957                                                 x &= ~p->x;
1958                                                 printf("%s%s", comma, p->s);
1959                                                 comma = ",";
1960                                         }
1961                                 PRINT_UINT_ARG(" ", c->conn_limit);
1962                                 break;
1963                         }
1964
1965                         case O_IP6:
1966                                 printf(" ip6");
1967                                 break;
1968
1969                         case O_IP4:
1970                                 printf(" ip4");
1971                                 break;
1972
1973                         case O_ICMP6TYPE:
1974                                 print_icmp6types((ipfw_insn_u32 *)cmd);
1975                                 break;
1976
1977                         case O_EXT_HDR:
1978                                 print_ext6hdr( (ipfw_insn *) cmd );
1979                                 break;
1980
1981                         case O_TAGGED:
1982                                 if (F_LEN(cmd) == 1)
1983                                         PRINT_UINT_ARG(" tagged ", cmd->arg1);
1984                                 else
1985                                         print_newports((ipfw_insn_u16 *)cmd, 0,
1986                                             O_TAGGED);
1987                                 break;
1988
1989                         default:
1990                                 printf(" [opcode %d len %d]",
1991                                     cmd->opcode, cmd->len);
1992                         }
1993                 }
1994                 if (cmd->len & F_OR) {
1995                         printf(" or");
1996                         or_block = 1;
1997                 } else if (or_block) {
1998                         printf(" }");
1999                         or_block = 0;
2000                 }
2001         }
2002         show_prerequisites(&flags, HAVE_IP, 0);
2003         if (comment)
2004                 printf(" // %s", comment);
2005         printf("\n");
2006 }
2007
2008 static void
2009 show_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth)
2010 {
2011         struct protoent *pe;
2012         struct in_addr a;
2013         uint16_t rulenum;
2014         char buf[INET6_ADDRSTRLEN];
2015
2016         if (!do_expired) {
2017                 if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT))
2018                         return;
2019         }
2020         bcopy(&d->rule, &rulenum, sizeof(rulenum));
2021         printf("%05d", rulenum);
2022         if (pcwidth>0 || bcwidth>0)
2023             printf(" %*llu %*llu (%ds)", pcwidth,
2024                 align_uint64(&d->pcnt), bcwidth,
2025                 align_uint64(&d->bcnt), d->expire);
2026         switch (d->dyn_type) {
2027         case O_LIMIT_PARENT:
2028                 printf(" PARENT %d", d->count);
2029                 break;
2030         case O_LIMIT:
2031                 printf(" LIMIT");
2032                 break;
2033         case O_KEEP_STATE: /* bidir, no mask */
2034                 printf(" STATE");
2035                 break;
2036         }
2037
2038         if ((pe = getprotobynumber(d->id.proto)) != NULL)
2039                 printf(" %s", pe->p_name);
2040         else
2041                 printf(" proto %u", d->id.proto);
2042
2043         if (d->id.addr_type == 4) {
2044                 a.s_addr = htonl(d->id.src_ip);
2045                 printf(" %s %d", inet_ntoa(a), d->id.src_port);
2046
2047                 a.s_addr = htonl(d->id.dst_ip);
2048                 printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port);
2049         } else if (d->id.addr_type == 6) {
2050                 printf(" %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf,
2051                     sizeof(buf)), d->id.src_port);
2052                 printf(" <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf,
2053                     sizeof(buf)), d->id.dst_port);
2054         } else
2055                 printf(" UNKNOWN <-> UNKNOWN\n");
2056         
2057         printf("\n");
2058 }
2059
2060 static int
2061 sort_q(const void *pa, const void *pb)
2062 {
2063         int rev = (do_sort < 0);
2064         int field = rev ? -do_sort : do_sort;
2065         long long res = 0;
2066         const struct dn_flow_queue *a = pa;
2067         const struct dn_flow_queue *b = pb;
2068
2069         switch (field) {
2070         case 1: /* pkts */
2071                 res = a->len - b->len;
2072                 break;
2073         case 2: /* bytes */
2074                 res = a->len_bytes - b->len_bytes;
2075                 break;
2076
2077         case 3: /* tot pkts */
2078                 res = a->tot_pkts - b->tot_pkts;
2079                 break;
2080
2081         case 4: /* tot bytes */
2082                 res = a->tot_bytes - b->tot_bytes;
2083                 break;
2084         }
2085         if (res < 0)
2086                 res = -1;
2087         if (res > 0)
2088                 res = 1;
2089         return (int)(rev ? res : -res);
2090 }
2091
2092 static void
2093 list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
2094 {
2095         int l;
2096         int index_printed, indexes = 0;
2097         char buff[255];
2098         struct protoent *pe;
2099
2100         if (fs->rq_elements == 0)
2101                 return;
2102
2103         if (do_sort != 0)
2104                 heapsort(q, fs->rq_elements, sizeof *q, sort_q);
2105
2106         /* Print IPv4 flows */
2107         index_printed = 0;
2108         for (l = 0; l < fs->rq_elements; l++) {
2109                 struct in_addr ina;
2110
2111                 /* XXX: Should check for IPv4 flows */
2112                 if (IS_IP6_FLOW_ID(&(q[l].id)))
2113                         continue;
2114
2115                 if (!index_printed) {
2116                         index_printed = 1;
2117                         if (indexes > 0)        /* currently a no-op */
2118                                 printf("\n");
2119                         indexes++;
2120                         printf("    "
2121                             "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
2122                             fs->flow_mask.proto,
2123                             fs->flow_mask.src_ip, fs->flow_mask.src_port,
2124                             fs->flow_mask.dst_ip, fs->flow_mask.dst_port);
2125
2126                         printf("BKT Prot ___Source IP/port____ "
2127                             "____Dest. IP/port____ "
2128                             "Tot_pkt/bytes Pkt/Byte Drp\n");
2129                 }
2130
2131                 printf("%3d ", q[l].hash_slot);
2132                 pe = getprotobynumber(q[l].id.proto);
2133                 if (pe)
2134                         printf("%-4s ", pe->p_name);
2135                 else
2136                         printf("%4u ", q[l].id.proto);
2137                 ina.s_addr = htonl(q[l].id.src_ip);
2138                 printf("%15s/%-5d ",
2139                     inet_ntoa(ina), q[l].id.src_port);
2140                 ina.s_addr = htonl(q[l].id.dst_ip);
2141                 printf("%15s/%-5d ",
2142                     inet_ntoa(ina), q[l].id.dst_port);
2143                 printf("%4qu %8qu %2u %4u %3u\n",
2144                     q[l].tot_pkts, q[l].tot_bytes,
2145                     q[l].len, q[l].len_bytes, q[l].drops);
2146                 if (verbose)
2147                         printf("   S %20qd  F %20qd\n",
2148                             q[l].S, q[l].F);
2149         }
2150
2151         /* Print IPv6 flows */
2152         index_printed = 0;
2153         for (l = 0; l < fs->rq_elements; l++) {
2154                 if (!IS_IP6_FLOW_ID(&(q[l].id)))
2155                         continue;
2156
2157                 if (!index_printed) {
2158                         index_printed = 1;
2159                         if (indexes > 0)
2160                                 printf("\n");
2161                         indexes++;
2162                         printf("\n        mask: proto: 0x%02x, flow_id: 0x%08x,  ",
2163                             fs->flow_mask.proto, fs->flow_mask.flow_id6);
2164                         inet_ntop(AF_INET6, &(fs->flow_mask.src_ip6),
2165                             buff, sizeof(buff));
2166                         printf("%s/0x%04x -> ", buff, fs->flow_mask.src_port);
2167                         inet_ntop( AF_INET6, &(fs->flow_mask.dst_ip6),
2168                             buff, sizeof(buff) );
2169                         printf("%s/0x%04x\n", buff, fs->flow_mask.dst_port);
2170
2171                         printf("BKT ___Prot___ _flow-id_ "
2172                             "______________Source IPv6/port_______________ "
2173                             "_______________Dest. IPv6/port_______________ "
2174                             "Tot_pkt/bytes Pkt/Byte Drp\n");
2175                 }
2176                 printf("%3d ", q[l].hash_slot);
2177                 pe = getprotobynumber(q[l].id.proto);
2178                 if (pe != NULL)
2179                         printf("%9s ", pe->p_name);
2180                 else
2181                         printf("%9u ", q[l].id.proto);
2182                 printf("%7d  %39s/%-5d ", q[l].id.flow_id6,
2183                     inet_ntop(AF_INET6, &(q[l].id.src_ip6), buff, sizeof(buff)),
2184                     q[l].id.src_port);
2185                 printf(" %39s/%-5d ",
2186                     inet_ntop(AF_INET6, &(q[l].id.dst_ip6), buff, sizeof(buff)),
2187                     q[l].id.dst_port);
2188                 printf(" %4qu %8qu %2u %4u %3u\n",
2189                     q[l].tot_pkts, q[l].tot_bytes,
2190                     q[l].len, q[l].len_bytes, q[l].drops);
2191                 if (verbose)
2192                         printf("   S %20qd  F %20qd\n", q[l].S, q[l].F);
2193         }
2194 }
2195
2196 static void
2197 print_flowset_parms(struct dn_flow_set *fs, char *prefix)
2198 {
2199         int l;
2200         char qs[30];
2201         char plr[30];
2202         char red[90];   /* Display RED parameters */
2203
2204         l = fs->qsize;
2205         if (fs->flags_fs & DN_QSIZE_IS_BYTES) {
2206                 if (l >= 8192)
2207                         sprintf(qs, "%d KB", l / 1024);
2208                 else
2209                         sprintf(qs, "%d B", l);
2210         } else
2211                 sprintf(qs, "%3d sl.", l);
2212         if (fs->plr)
2213                 sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
2214         else
2215                 plr[0] = '\0';
2216         if (fs->flags_fs & DN_IS_RED)   /* RED parameters */
2217                 sprintf(red,
2218                     "\n\t  %cRED w_q %f min_th %d max_th %d max_p %f",
2219                     (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ',
2220                     1.0 * fs->w_q / (double)(1 << SCALE_RED),
2221                     SCALE_VAL(fs->min_th),
2222                     SCALE_VAL(fs->max_th),
2223                     1.0 * fs->max_p / (double)(1 << SCALE_RED));
2224         else
2225                 sprintf(red, "droptail");
2226
2227         printf("%s %s%s %d queues (%d buckets) %s\n",
2228             prefix, qs, plr, fs->rq_elements, fs->rq_size, red);
2229 }
2230
2231 static void
2232 list_pipes(void *data, uint nbytes, int ac, char *av[])
2233 {
2234         int rulenum;
2235         void *next = data;
2236         struct dn_pipe *p = (struct dn_pipe *) data;
2237         struct dn_flow_set *fs;
2238         struct dn_flow_queue *q;
2239         int l;
2240
2241         if (ac > 0)
2242                 rulenum = strtoul(*av++, NULL, 10);
2243         else
2244                 rulenum = 0;
2245         for (; nbytes >= sizeof *p; p = (struct dn_pipe *)next) {
2246                 double b = p->bandwidth;
2247                 char buf[30];
2248                 char prefix[80];
2249
2250                 if (SLIST_NEXT(p, next) != (struct dn_pipe *)DN_IS_PIPE)
2251                         break;  /* done with pipes, now queues */
2252
2253                 /*
2254                  * compute length, as pipe have variable size
2255                  */
2256                 l = sizeof(*p) + p->fs.rq_elements * sizeof(*q);
2257                 next = (char *)p + l;
2258                 nbytes -= l;
2259
2260                 if ((rulenum != 0 && rulenum != p->pipe_nr) || do_pipe == 2)
2261                         continue;
2262
2263                 /*
2264                  * Print rate (or clocking interface)
2265                  */
2266                 if (p->if_name[0] != '\0')
2267                         sprintf(buf, "%s", p->if_name);
2268                 else if (b == 0)
2269                         sprintf(buf, "unlimited");
2270                 else if (b >= 1000000)
2271                         sprintf(buf, "%7.3f Mbit/s", b/1000000);
2272                 else if (b >= 1000)
2273                         sprintf(buf, "%7.3f Kbit/s", b/1000);
2274                 else
2275                         sprintf(buf, "%7.3f bit/s ", b);
2276
2277                 sprintf(prefix, "%05d: %s %4d ms ",
2278                     p->pipe_nr, buf, p->delay);
2279                 print_flowset_parms(&(p->fs), prefix);
2280                 if (verbose)
2281                         printf("   V %20qd\n", p->V >> MY_M);
2282
2283                 q = (struct dn_flow_queue *)(p+1);
2284                 list_queues(&(p->fs), q);
2285         }
2286         for (fs = next; nbytes >= sizeof *fs; fs = next) {
2287                 char prefix[80];
2288
2289                 if (SLIST_NEXT(fs, next) != (struct dn_flow_set *)DN_IS_QUEUE)
2290                         break;
2291                 l = sizeof(*fs) + fs->rq_elements * sizeof(*q);
2292                 next = (char *)fs + l;
2293                 nbytes -= l;
2294
2295                 if (rulenum != 0 && ((rulenum != fs->fs_nr && do_pipe == 2) ||
2296                     (rulenum != fs->parent_nr && do_pipe == 1))) {
2297                         continue;
2298                 }
2299
2300                 q = (struct dn_flow_queue *)(fs+1);
2301                 sprintf(prefix, "q%05d: weight %d pipe %d ",
2302                     fs->fs_nr, fs->weight, fs->parent_nr);
2303                 print_flowset_parms(fs, prefix);
2304                 list_queues(fs, q);
2305         }
2306 }
2307
2308 /*
2309  * This one handles all set-related commands
2310  *      ipfw set { show | enable | disable }
2311  *      ipfw set swap X Y
2312  *      ipfw set move X to Y
2313  *      ipfw set move rule X to Y
2314  */
2315 static void
2316 sets_handler(int ac, char *av[])
2317 {
2318         uint32_t set_disable, masks[2];
2319         int i, nbytes;
2320         uint16_t rulenum;
2321         uint8_t cmd, new_set;
2322
2323         ac--;
2324         av++;
2325
2326         if (!ac)
2327                 errx(EX_USAGE, "set needs command");
2328         if (_substrcmp(*av, "show") == 0) {
2329                 void *data;
2330                 char const *msg;
2331
2332                 nbytes = sizeof(struct ip_fw);
2333                 if ((data = calloc(1, nbytes)) == NULL)
2334                         err(EX_OSERR, "calloc");
2335                 if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0)
2336                         err(EX_OSERR, "getsockopt(IP_FW_GET)");
2337                 bcopy(&((struct ip_fw *)data)->next_rule,
2338                         &set_disable, sizeof(set_disable));
2339
2340                 for (i = 0, msg = "disable" ; i < RESVD_SET; i++)
2341                         if ((set_disable & (1<<i))) {
2342                                 printf("%s %d", msg, i);
2343                                 msg = "";
2344                         }
2345                 msg = (set_disable) ? " enable" : "enable";
2346                 for (i = 0; i < RESVD_SET; i++)
2347                         if (!(set_disable & (1<<i))) {
2348                                 printf("%s %d", msg, i);
2349                                 msg = "";
2350                         }
2351                 printf("\n");
2352         } else if (_substrcmp(*av, "swap") == 0) {
2353                 ac--; av++;
2354                 if (ac != 2)
2355                         errx(EX_USAGE, "set swap needs 2 set numbers\n");
2356                 rulenum = atoi(av[0]);
2357                 new_set = atoi(av[1]);
2358                 if (!isdigit(*(av[0])) || rulenum > RESVD_SET)
2359                         errx(EX_DATAERR, "invalid set number %s\n", av[0]);
2360                 if (!isdigit(*(av[1])) || new_set > RESVD_SET)
2361                         errx(EX_DATAERR, "invalid set number %s\n", av[1]);
2362                 masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
2363                 i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
2364         } else if (_substrcmp(*av, "move") == 0) {
2365                 ac--; av++;
2366                 if (ac && _substrcmp(*av, "rule") == 0) {
2367                         cmd = 2;
2368                         ac--; av++;
2369                 } else
2370                         cmd = 3;
2371                 if (ac != 3 || _substrcmp(av[1], "to") != 0)
2372                         errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
2373                 rulenum = atoi(av[0]);
2374                 new_set = atoi(av[2]);
2375                 if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) ||
2376                         (cmd == 2 && rulenum == 65535) )
2377                         errx(EX_DATAERR, "invalid source number %s\n", av[0]);
2378                 if (!isdigit(*(av[2])) || new_set > RESVD_SET)
2379                         errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
2380                 masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
2381                 i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
2382         } else if (_substrcmp(*av, "disable") == 0 ||
2383                    _substrcmp(*av, "enable") == 0 ) {
2384                 int which = _substrcmp(*av, "enable") == 0 ? 1 : 0;
2385
2386                 ac--; av++;
2387                 masks[0] = masks[1] = 0;
2388
2389                 while (ac) {
2390                         if (isdigit(**av)) {
2391                                 i = atoi(*av);
2392                                 if (i < 0 || i > RESVD_SET)
2393                                         errx(EX_DATAERR,
2394                                             "invalid set number %d\n", i);
2395                                 masks[which] |= (1<<i);
2396                         } else if (_substrcmp(*av, "disable") == 0)
2397                                 which = 0;
2398                         else if (_substrcmp(*av, "enable") == 0)
2399                                 which = 1;
2400                         else
2401                                 errx(EX_DATAERR,
2402                                         "invalid set command %s\n", *av);
2403                         av++; ac--;
2404                 }
2405                 if ( (masks[0] & masks[1]) != 0 )
2406                         errx(EX_DATAERR,
2407                             "cannot enable and disable the same set\n");
2408
2409                 i = do_cmd(IP_FW_DEL, masks, sizeof(masks));
2410                 if (i)
2411                         warn("set enable/disable: setsockopt(IP_FW_DEL)");
2412         } else
2413                 errx(EX_USAGE, "invalid set command %s\n", *av);
2414 }
2415
2416 static void
2417 sysctl_handler(int ac, char *av[], int which)
2418 {
2419         ac--;
2420         av++;
2421
2422         if (ac == 0) {
2423                 warnx("missing keyword to enable/disable\n");
2424         } else if (_substrcmp(*av, "firewall") == 0) {
2425                 sysctlbyname("net.inet.ip.fw.enable", NULL, 0,
2426                     &which, sizeof(which));
2427         } else if (_substrcmp(*av, "one_pass") == 0) {
2428                 sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0,
2429                     &which, sizeof(which));
2430         } else if (_substrcmp(*av, "debug") == 0) {
2431                 sysctlbyname("net.inet.ip.fw.debug", NULL, 0,
2432                     &which, sizeof(which));
2433         } else if (_substrcmp(*av, "verbose") == 0) {
2434                 sysctlbyname("net.inet.ip.fw.verbose", NULL, 0,
2435                     &which, sizeof(which));
2436         } else if (_substrcmp(*av, "dyn_keepalive") == 0) {
2437                 sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0,
2438                     &which, sizeof(which));
2439         } else if (_substrcmp(*av, "altq") == 0) {
2440                 altq_set_enabled(which);
2441         } else {
2442                 warnx("unrecognize enable/disable keyword: %s\n", *av);
2443         }
2444 }
2445
2446 static void
2447 list(int ac, char *av[], int show_counters)
2448 {
2449         struct ip_fw *r;
2450         ipfw_dyn_rule *dynrules, *d;
2451
2452 #define NEXT(r) ((struct ip_fw *)((char *)r + RULESIZE(r)))
2453         char *lim;
2454         void *data = NULL;
2455         int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width;
2456         int exitval = EX_OK;
2457         int lac;
2458         char **lav;
2459         u_long rnum, last;
2460         char *endptr;
2461         int seen = 0;
2462
2463         const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
2464         int nalloc = 1024;      /* start somewhere... */
2465
2466         last = 0;
2467
2468         if (test_only) {
2469                 fprintf(stderr, "Testing only, list disabled\n");
2470                 return;
2471         }
2472
2473         ac--;
2474         av++;
2475
2476         /* get rules or pipes from kernel, resizing array as necessary */
2477         nbytes = nalloc;
2478
2479         while (nbytes >= nalloc) {
2480                 nalloc = nalloc * 2 + 200;
2481                 nbytes = nalloc;
2482                 if ((data = realloc(data, nbytes)) == NULL)
2483                         err(EX_OSERR, "realloc");
2484                 if (do_cmd(ocmd, data, (uintptr_t)&nbytes) < 0)
2485                         err(EX_OSERR, "getsockopt(IP_%s_GET)",
2486                                 do_pipe ? "DUMMYNET" : "FW");
2487         }
2488
2489         if (do_pipe) {
2490                 list_pipes(data, nbytes, ac, av);
2491                 goto done;
2492         }
2493
2494         /*
2495          * Count static rules. They have variable size so we
2496          * need to scan the list to count them.
2497          */
2498         for (nstat = 1, r = data, lim = (char *)data + nbytes;
2499                     r->rulenum < 65535 && (char *)r < lim;
2500                     ++nstat, r = NEXT(r) )
2501                 ; /* nothing */
2502
2503         /*
2504          * Count dynamic rules. This is easier as they have
2505          * fixed size.
2506          */
2507         r = NEXT(r);
2508         dynrules = (ipfw_dyn_rule *)r ;
2509         n = (char *)r - (char *)data;
2510         ndyn = (nbytes - n) / sizeof *dynrules;
2511
2512         /* if showing stats, figure out column widths ahead of time */
2513         bcwidth = pcwidth = 0;
2514         if (show_counters) {
2515                 for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
2516                         /* packet counter */
2517                         width = snprintf(NULL, 0, "%llu",
2518                             align_uint64(&r->pcnt));
2519                         if (width > pcwidth)
2520                                 pcwidth = width;
2521
2522                         /* byte counter */
2523                         width = snprintf(NULL, 0, "%llu",
2524                             align_uint64(&r->bcnt));
2525                         if (width > bcwidth)
2526                                 bcwidth = width;
2527                 }
2528         }
2529         if (do_dynamic && ndyn) {
2530                 for (n = 0, d = dynrules; n < ndyn; n++, d++) {
2531                         width = snprintf(NULL, 0, "%llu",
2532                             align_uint64(&d->pcnt));
2533                         if (width > pcwidth)
2534                                 pcwidth = width;
2535
2536                         width = snprintf(NULL, 0, "%llu",
2537                             align_uint64(&d->bcnt));
2538                         if (width > bcwidth)
2539                                 bcwidth = width;
2540                 }
2541         }
2542         /* if no rule numbers were specified, list all rules */
2543         if (ac == 0) {
2544                 for (n = 0, r = data; n < nstat; n++, r = NEXT(r) )
2545                         show_ipfw(r, pcwidth, bcwidth);
2546
2547                 if (do_dynamic && ndyn) {
2548                         printf("## Dynamic rules (%d):\n", ndyn);
2549                         for (n = 0, d = dynrules; n < ndyn; n++, d++)
2550                                 show_dyn_ipfw(d, pcwidth, bcwidth);
2551                 }
2552                 goto done;
2553         }
2554
2555         /* display specific rules requested on command line */
2556
2557         for (lac = ac, lav = av; lac != 0; lac--) {
2558                 /* convert command line rule # */
2559                 last = rnum = strtoul(*lav++, &endptr, 10);
2560                 if (*endptr == '-')
2561                         last = strtoul(endptr+1, &endptr, 10);
2562                 if (*endptr) {
2563                         exitval = EX_USAGE;
2564                         warnx("invalid rule number: %s", *(lav - 1));
2565                         continue;
2566                 }
2567                 for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) {
2568                         if (r->rulenum > last)
2569                                 break;
2570                         if (r->rulenum >= rnum && r->rulenum <= last) {
2571                                 show_ipfw(r, pcwidth, bcwidth);
2572                                 seen = 1;
2573                         }
2574                 }
2575                 if (!seen) {
2576                         /* give precedence to other error(s) */
2577                         if (exitval == EX_OK)
2578                                 exitval = EX_UNAVAILABLE;
2579                         warnx("rule %lu does not exist", rnum);
2580                 }
2581         }
2582
2583         if (do_dynamic && ndyn) {
2584                 printf("## Dynamic rules:\n");
2585                 for (lac = ac, lav = av; lac != 0; lac--) {
2586                         last = rnum = strtoul(*lav++, &endptr, 10);
2587                         if (*endptr == '-')
2588                                 last = strtoul(endptr+1, &endptr, 10);
2589                         if (*endptr)
2590                                 /* already warned */
2591                                 continue;
2592                         for (n = 0, d = dynrules; n < ndyn; n++, d++) {
2593                                 uint16_t rulenum;
2594
2595                                 bcopy(&d->rule, &rulenum, sizeof(rulenum));
2596                                 if (rulenum > rnum)
2597                                         break;
2598                                 if (r->rulenum >= rnum && r->rulenum <= last)
2599                                         show_dyn_ipfw(d, pcwidth, bcwidth);
2600                         }
2601                 }
2602         }
2603
2604         ac = 0;
2605
2606 done:
2607         free(data);
2608
2609         if (exitval != EX_OK)
2610                 exit(exitval);
2611 #undef NEXT
2612 }
2613
2614 static void
2615 show_usage(void)
2616 {
2617         fprintf(stderr, "usage: ipfw [options]\n"
2618 "do \"ipfw -h\" or see ipfw manpage for details\n"
2619 );
2620         exit(EX_USAGE);
2621 }
2622
2623 static void
2624 help(void)
2625 {
2626         fprintf(stderr,
2627 "ipfw syntax summary (but please do read the ipfw(8) manpage):\n"
2628 "ipfw [-abcdefhnNqStTv] <command> where <command> is one of:\n"
2629 "add [num] [set N] [prob x] RULE-BODY\n"
2630 "{pipe|queue} N config PIPE-BODY\n"
2631 "[pipe|queue] {zero|delete|show} [N{,N}]\n"
2632 "set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n"
2633 "table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n"
2634 "\n"
2635 "RULE-BODY:     check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n"
2636 "ACTION:        check-state | allow | count | deny | unreach{,6} CODE |\n"
2637 "               skipto N | {divert|tee} PORT | forward ADDR |\n"
2638 "               pipe N | queue N\n"
2639 "PARAMS:        [log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n"
2640 "ADDR:          [ MAC dst src ether_type ] \n"
2641 "               [ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
2642 "               [ ipv6|ip6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n"
2643 "IPADDR:        [not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n"
2644 "IP6ADDR:       [not] { any | me | me6 | ip6/bits | IP6LIST }\n"
2645 "IP6LIST:       { ip6 | ip6/bits }[,IP6LIST]\n"
2646 "IPLIST:        { ip | ip/bits | ip:mask }[,IPLIST]\n"
2647 "OPTION_LIST:   OPTION [OPTION_LIST]\n"
2648 "OPTION:        bridged | diverted | diverted-loopback | diverted-output |\n"
2649 "       {dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n"
2650 "       {dst-port|src-port} LIST |\n"
2651 "       estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n"
2652 "       iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n"
2653 "       ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n"
2654 "       icmp6types LIST | ext6hdr LIST | flow-id N[,N] |\n"
2655 "       mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n"
2656 "       setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n"
2657 "       tcpdatalen LIST | verrevpath | versrcreach | antispoof\n"
2658 );
2659 exit(0);
2660 }
2661
2662
2663 static int
2664 lookup_host (char *host, struct in_addr *ipaddr)
2665 {
2666         struct hostent *he;
2667
2668         if (!inet_aton(host, ipaddr)) {
2669                 if ((he = gethostbyname(host)) == NULL)
2670                         return(-1);
2671                 *ipaddr = *(struct in_addr *)he->h_addr_list[0];
2672         }
2673         return(0);
2674 }
2675
2676 /*
2677  * fills the addr and mask fields in the instruction as appropriate from av.
2678  * Update length as appropriate.
2679  * The following formats are allowed:
2680  *      me      returns O_IP_*_ME
2681  *      1.2.3.4         single IP address
2682  *      1.2.3.4:5.6.7.8 address:mask
2683  *      1.2.3.4/24      address/mask
2684  *      1.2.3.4/26{1,6,5,4,23}  set of addresses in a subnet
2685  * We can have multiple comma-separated address/mask entries.
2686  */
2687 static void
2688 fill_ip(ipfw_insn_ip *cmd, char *av)
2689 {
2690         int len = 0;
2691         uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
2692
2693         cmd->o.len &= ~F_LEN_MASK;      /* zero len */
2694
2695         if (_substrcmp(av, "any") == 0)
2696                 return;
2697
2698         if (_substrcmp(av, "me") == 0) {
2699                 cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2700                 return;
2701         }
2702
2703         if (strncmp(av, "table(", 6) == 0) {
2704                 char *p = strchr(av + 6, ',');
2705
2706                 if (p)
2707                         *p++ = '\0';
2708                 cmd->o.opcode = O_IP_DST_LOOKUP;
2709                 cmd->o.arg1 = strtoul(av + 6, NULL, 0);
2710                 if (p) {
2711                         cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2712                         d[0] = strtoul(p, NULL, 0);
2713                 } else
2714                         cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2715                 return;
2716         }
2717
2718     while (av) {
2719         /*
2720          * After the address we can have '/' or ':' indicating a mask,
2721          * ',' indicating another address follows, '{' indicating a
2722          * set of addresses of unspecified size.
2723          */
2724         char *p = strpbrk(av, "/:,{");
2725         int masklen;
2726         char md;
2727
2728         if (p) {
2729                 md = *p;
2730                 *p++ = '\0';
2731         } else
2732                 md = '\0';
2733
2734         if (lookup_host(av, (struct in_addr *)&d[0]) != 0)
2735                 errx(EX_NOHOST, "hostname ``%s'' unknown", av);
2736         switch (md) {
2737         case ':':
2738                 if (!inet_aton(p, (struct in_addr *)&d[1]))
2739                         errx(EX_DATAERR, "bad netmask ``%s''", p);
2740                 break;
2741         case '/':
2742                 masklen = atoi(p);
2743                 if (masklen == 0)
2744                         d[1] = htonl(0);        /* mask */
2745                 else if (masklen > 32)
2746                         errx(EX_DATAERR, "bad width ``%s''", p);
2747                 else
2748                         d[1] = htonl(~0 << (32 - masklen));
2749                 break;
2750         case '{':       /* no mask, assume /24 and put back the '{' */
2751                 d[1] = htonl(~0 << (32 - 24));
2752                 *(--p) = md;
2753                 break;
2754
2755         case ',':       /* single address plus continuation */
2756                 *(--p) = md;
2757                 /* FALLTHROUGH */
2758         case 0:         /* initialization value */
2759         default:
2760                 d[1] = htonl(~0);       /* force /32 */
2761                 break;
2762         }
2763         d[0] &= d[1];           /* mask base address with mask */
2764         /* find next separator */
2765         if (p)
2766                 p = strpbrk(p, ",{");
2767         if (p && *p == '{') {
2768                 /*
2769                  * We have a set of addresses. They are stored as follows:
2770                  *   arg1       is the set size (powers of 2, 2..256)
2771                  *   addr       is the base address IN HOST FORMAT
2772                  *   mask..     is an array of arg1 bits (rounded up to
2773                  *              the next multiple of 32) with bits set
2774                  *              for each host in the map.
2775                  */
2776                 uint32_t *map = (uint32_t *)&cmd->mask;
2777                 int low, high;
2778                 int i = contigmask((uint8_t *)&(d[1]), 32);
2779
2780                 if (len > 0)
2781                         errx(EX_DATAERR, "address set cannot be in a list");
2782                 if (i < 24 || i > 31)
2783                         errx(EX_DATAERR, "invalid set with mask %d\n", i);
2784                 cmd->o.arg1 = 1<<(32-i);        /* map length           */
2785                 d[0] = ntohl(d[0]);             /* base addr in host format */
2786                 cmd->o.opcode = O_IP_DST_SET;   /* default */
2787                 cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32;
2788                 for (i = 0; i < (cmd->o.arg1+31)/32 ; i++)
2789                         map[i] = 0;     /* clear map */
2790
2791                 av = p + 1;
2792                 low = d[0] & 0xff;
2793                 high = low + cmd->o.arg1 - 1;
2794                 /*
2795                  * Here, i stores the previous value when we specify a range
2796                  * of addresses within a mask, e.g. 45-63. i = -1 means we
2797                  * have no previous value.
2798                  */
2799                 i = -1; /* previous value in a range */
2800                 while (isdigit(*av)) {
2801                         char *s;
2802                         int a = strtol(av, &s, 0);
2803
2804                         if (s == av) { /* no parameter */
2805                             if (*av != '}')
2806                                 errx(EX_DATAERR, "set not closed\n");
2807                             if (i != -1)
2808                                 errx(EX_DATAERR, "incomplete range %d-", i);
2809                             break;
2810                         }
2811                         if (a < low || a > high)
2812                             errx(EX_DATAERR, "addr %d out of range [%d-%d]\n",
2813                                 a, low, high);
2814                         a -= low;
2815                         if (i == -1)    /* no previous in range */
2816                             i = a;
2817                         else {          /* check that range is valid */
2818                             if (i > a)
2819                                 errx(EX_DATAERR, "invalid range %d-%d",
2820                                         i+low, a+low);
2821                             if (*s == '-')
2822                                 errx(EX_DATAERR, "double '-' in range");
2823                         }
2824                         for (; i <= a; i++)
2825                             map[i/32] |= 1<<(i & 31);
2826                         i = -1;
2827                         if (*s == '-')
2828                             i = a;
2829                         else if (*s == '}')
2830                             break;
2831                         av = s+1;
2832                 }
2833                 return;
2834         }
2835         av = p;
2836         if (av)                 /* then *av must be a ',' */
2837                 av++;
2838
2839         /* Check this entry */
2840         if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */
2841                 /*
2842                  * 'any' turns the entire list into a NOP.
2843                  * 'not any' never matches, so it is removed from the
2844                  * list unless it is the only item, in which case we
2845                  * report an error.
2846                  */
2847                 if (cmd->o.len & F_NOT) {       /* "not any" never matches */
2848                         if (av == NULL && len == 0) /* only this entry */
2849                                 errx(EX_DATAERR, "not any never matches");
2850                 }
2851                 /* else do nothing and skip this entry */
2852                 return;
2853         }
2854         /* A single IP can be stored in an optimized format */
2855         if (d[1] == IP_MASK_ALL && av == NULL && len == 0) {
2856                 cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2857                 return;
2858         }
2859         len += 2;       /* two words... */
2860         d += 2;
2861     } /* end while */
2862     cmd->o.len |= len+1;
2863 }
2864
2865
2866 /* Try to find ipv6 address by hostname */
2867 static int
2868 lookup_host6 (char *host, struct in6_addr *ip6addr)
2869 {
2870         struct hostent *he;
2871
2872         if (!inet_pton(AF_INET6, host, ip6addr)) {
2873                 if ((he = gethostbyname2(host, AF_INET6)) == NULL)
2874                         return(-1);
2875                 memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr));
2876         }
2877         return(0);
2878 }
2879
2880
2881 /* n2mask sets n bits of the mask */
2882 static void
2883 n2mask(struct in6_addr *mask, int n)
2884 {
2885         static int      minimask[9] =
2886             { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
2887         u_char          *p;
2888
2889         memset(mask, 0, sizeof(struct in6_addr));
2890         p = (u_char *) mask;
2891         for (; n > 0; p++, n -= 8) {
2892                 if (n >= 8)
2893                         *p = 0xff;
2894                 else
2895                         *p = minimask[n];
2896         }
2897         return;
2898 }
2899  
2900
2901 /*
2902  * fill the addr and mask fields in the instruction as appropriate from av.
2903  * Update length as appropriate.
2904  * The following formats are allowed:
2905  *     any     matches any IP6. Actually returns an empty instruction.
2906  *     me      returns O_IP6_*_ME
2907  *
2908  *     03f1::234:123:0342                single IP6 addres
2909  *     03f1::234:123:0342/24            address/mask
2910  *     03f1::234:123:0342/24,03f1::234:123:0343/               List of address
2911  *
2912  * Set of address (as in ipv6) not supported because ipv6 address
2913  * are typically random past the initial prefix.
2914  * Return 1 on success, 0 on failure.
2915  */
2916 static int
2917 fill_ip6(ipfw_insn_ip6 *cmd, char *av)
2918 {
2919         int len = 0;
2920         struct in6_addr *d = &(cmd->addr6);
2921         /*
2922          * Needed for multiple address.
2923          * Note d[1] points to struct in6_add r mask6 of cmd
2924          */
2925
2926        cmd->o.len &= ~F_LEN_MASK;       /* zero len */
2927
2928        if (strcmp(av, "any") == 0)
2929                return (1);
2930
2931
2932        if (strcmp(av, "me") == 0) {     /* Set the data for "me" opt*/
2933                cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2934                return (1);
2935        }
2936
2937        if (strcmp(av, "me6") == 0) {    /* Set the data for "me" opt*/
2938                cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2939                return (1);
2940        }
2941
2942        av = strdup(av);
2943        while (av) {
2944                 /*
2945                  * After the address we can have '/' indicating a mask,
2946                  * or ',' indicating another address follows.
2947                  */
2948
2949                 char *p;
2950                 int masklen;
2951                 char md = '\0';
2952
2953                 if ((p = strpbrk(av, "/,")) ) {
2954                         md = *p;        /* save the separator */
2955                         *p = '\0';      /* terminate address string */
2956                         p++;            /* and skip past it */
2957                 }
2958                 /* now p points to NULL, mask or next entry */
2959
2960                 /* lookup stores address in *d as a side effect */
2961                 if (lookup_host6(av, d) != 0) {
2962                         /* XXX: failed. Free memory and go */
2963                         errx(EX_DATAERR, "bad address \"%s\"", av);
2964                 }
2965                 /* next, look at the mask, if any */
2966                 masklen = (md == '/') ? atoi(p) : 128;
2967                 if (masklen > 128 || masklen < 0)
2968                         errx(EX_DATAERR, "bad width \"%s\''", p);
2969                 else
2970                         n2mask(&d[1], masklen);
2971
2972                 APPLY_MASK(d, &d[1])   /* mask base address with mask */
2973
2974                 /* find next separator */
2975
2976                 if (md == '/') {        /* find separator past the mask */
2977                         p = strpbrk(p, ",");
2978                         if (p != NULL)
2979                                 p++;
2980                 }
2981                 av = p;
2982
2983                 /* Check this entry */
2984                 if (masklen == 0) {
2985                         /*
2986                          * 'any' turns the entire list into a NOP.
2987                          * 'not any' never matches, so it is removed from the
2988                          * list unless it is the only item, in which case we
2989                          * report an error.
2990                          */
2991                         if (cmd->o.len & F_NOT && av == NULL && len == 0)
2992                                 errx(EX_DATAERR, "not any never matches");
2993                         continue;
2994                 }
2995
2996                 /*
2997                  * A single IP can be stored alone
2998                  */
2999                 if (masklen == 128 && av == NULL && len == 0) {
3000                         len = F_INSN_SIZE(struct in6_addr);
3001                         break;
3002                 }
3003
3004                 /* Update length and pointer to arguments */
3005                 len += F_INSN_SIZE(struct in6_addr)*2;
3006                 d += 2;
3007         } /* end while */
3008
3009         /*
3010          * Total length of the command, remember that 1 is the size of
3011          * the base command.
3012          */
3013         cmd->o.len |= len+1;
3014         free(av);
3015         return (1);
3016 }
3017
3018 /*
3019  * fills command for ipv6 flow-id filtering
3020  * note that the 20 bit flow number is stored in a array of u_int32_t
3021  * it's supported lists of flow-id, so in the o.arg1 we store how many
3022  * additional flow-id we want to filter, the basic is 1
3023  */
3024 void
3025 fill_flow6( ipfw_insn_u32 *cmd, char *av )
3026 {
3027         u_int32_t type;  /* Current flow number */
3028         u_int16_t nflow = 0;    /* Current flow index */
3029         char *s = av;
3030         cmd->d[0] = 0;    /* Initializing the base number*/
3031
3032         while (s) {
3033                 av = strsep( &s, ",") ;
3034                 type = strtoul(av, &av, 0);
3035                 if (*av != ',' && *av != '\0')
3036                         errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
3037                 if (type > 0xfffff)
3038                         errx(EX_DATAERR, "flow number out of range %s", av);
3039                 cmd->d[nflow] |= type;
3040                 nflow++;
3041         }
3042         if( nflow > 0 ) {
3043                 cmd->o.opcode = O_FLOW6ID;
3044                 cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow;
3045                 cmd->o.arg1 = nflow;
3046         }
3047         else {
3048                 errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
3049         }
3050 }
3051
3052 static ipfw_insn *
3053 add_srcip6(ipfw_insn *cmd, char *av)
3054 {
3055
3056         fill_ip6((ipfw_insn_ip6 *)cmd, av);
3057         if (F_LEN(cmd) == 0)                            /* any */
3058                 ;
3059         if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) {     /* "me" */
3060                 cmd->opcode = O_IP6_SRC_ME;
3061         } else if (F_LEN(cmd) ==
3062             (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
3063                 /* single IP, no mask*/
3064                 cmd->opcode = O_IP6_SRC;
3065         } else {                                        /* addr/mask opt */
3066                 cmd->opcode = O_IP6_SRC_MASK;
3067         }
3068         return cmd;
3069 }
3070
3071 static ipfw_insn *
3072 add_dstip6(ipfw_insn *cmd, char *av)
3073 {
3074
3075         fill_ip6((ipfw_insn_ip6 *)cmd, av);
3076         if (F_LEN(cmd) == 0)                            /* any */
3077                 ;
3078         if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) {     /* "me" */
3079                 cmd->opcode = O_IP6_DST_ME;
3080         } else if (F_LEN(cmd) ==
3081             (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
3082                 /* single IP, no mask*/
3083                 cmd->opcode = O_IP6_DST;
3084         } else {                                        /* addr/mask opt */
3085                 cmd->opcode = O_IP6_DST_MASK;
3086         }
3087         return cmd;
3088 }
3089
3090
3091 /*
3092  * helper function to process a set of flags and set bits in the
3093  * appropriate masks.
3094  */
3095 static void
3096 fill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
3097         struct _s_x *flags, char *p)
3098 {
3099         uint8_t set=0, clear=0;
3100
3101         while (p && *p) {
3102                 char *q;        /* points to the separator */
3103                 int val;
3104                 uint8_t *which; /* mask we are working on */
3105
3106                 if (*p == '!') {
3107                         p++;
3108                         which = &clear;
3109                 } else
3110                         which = &set;
3111                 q = strchr(p, ',');
3112                 if (q)
3113                         *q++ = '\0';
3114                 val = match_token(flags, p);
3115                 if (val <= 0)
3116                         errx(EX_DATAERR, "invalid flag %s", p);
3117                 *which |= (uint8_t)val;
3118                 p = q;
3119         }
3120         cmd->opcode = opcode;
3121         cmd->len =  (cmd->len & (F_NOT | F_OR)) | 1;
3122         cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
3123 }
3124
3125
3126 static void
3127 delete(int ac, char *av[])
3128 {
3129         uint32_t rulenum;
3130         struct dn_pipe p;
3131         int i;
3132         int exitval = EX_OK;
3133         int do_set = 0;
3134
3135         memset(&p, 0, sizeof p);
3136
3137         av++; ac--;
3138         NEED1("missing rule specification");
3139         if (ac > 0 && _substrcmp(*av, "set") == 0) {
3140                 do_set = 1;     /* delete set */
3141                 ac--; av++;
3142         }
3143
3144         /* Rule number */
3145         while (ac && isdigit(**av)) {
3146                 i = atoi(*av); av++; ac--;
3147                 if (do_pipe) {
3148                         if (do_pipe == 1)
3149                                 p.pipe_nr = i;
3150                         else
3151                                 p.fs.fs_nr = i;
3152                         i = do_cmd(IP_DUMMYNET_DEL, &p, sizeof p);
3153                         if (i) {
3154                                 exitval = 1;
3155                                 warn("rule %u: setsockopt(IP_DUMMYNET_DEL)",
3156                                     do_pipe == 1 ? p.pipe_nr : p.fs.fs_nr);
3157                         }
3158                 } else {
3159                         rulenum =  (i & 0xffff) | (do_set << 24);
3160                         i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum);
3161                         if (i) {
3162                                 exitval = EX_UNAVAILABLE;
3163                                 warn("rule %u: setsockopt(IP_FW_DEL)",
3164                                     rulenum);
3165                         }
3166                 }
3167         }
3168         if (exitval != EX_OK)
3169                 exit(exitval);
3170 }
3171
3172
3173 /*
3174  * fill the interface structure. We do not check the name as we can
3175  * create interfaces dynamically, so checking them at insert time
3176  * makes relatively little sense.
3177  * Interface names containing '*', '?', or '[' are assumed to be shell 
3178  * patterns which match interfaces.
3179  */
3180 static void
3181 fill_iface(ipfw_insn_if *cmd, char *arg)
3182 {
3183         cmd->name[0] = '\0';
3184         cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
3185
3186         /* Parse the interface or address */
3187         if (strcmp(arg, "any") == 0)
3188                 cmd->o.len = 0;         /* effectively ignore this command */
3189         else if (!isdigit(*arg)) {
3190                 strlcpy(cmd->name, arg, sizeof(cmd->name));
3191                 cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
3192         } else if (!inet_aton(arg, &cmd->p.ip))
3193                 errx(EX_DATAERR, "bad ip address ``%s''", arg);
3194 }
3195
3196 static void
3197 config_pipe(int ac, char **av)
3198 {
3199         struct dn_pipe p;
3200         int i;
3201         char *end;
3202         void *par = NULL;
3203
3204         memset(&p, 0, sizeof p);
3205
3206         av++; ac--;
3207         /* Pipe number */
3208         if (ac && isdigit(**av)) {
3209                 i = atoi(*av); av++; ac--;
3210                 if (do_pipe == 1)
3211                         p.pipe_nr = i;
3212                 else
3213                         p.fs.fs_nr = i;
3214         }
3215         while (ac > 0) {
3216                 double d;
3217                 int tok = match_token(dummynet_params, *av);
3218                 ac--; av++;
3219
3220                 switch(tok) {
3221                 case TOK_NOERROR:
3222                         p.fs.flags_fs |= DN_NOERROR;
3223                         break;
3224
3225                 case TOK_PLR:
3226                         NEED1("plr needs argument 0..1\n");
3227                         d = strtod(av[0], NULL);
3228                         if (d > 1)
3229                                 d = 1;
3230                         else if (d < 0)
3231                                 d = 0;
3232                         p.fs.plr = (int)(d*0x7fffffff);
3233                         ac--; av++;
3234                         break;
3235
3236                 case TOK_QUEUE:
3237                         NEED1("queue needs queue size\n");
3238                         end = NULL;
3239                         p.fs.qsize = strtoul(av[0], &end, 0);
3240                         if (*end == 'K' || *end == 'k') {
3241                                 p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
3242                                 p.fs.qsize *= 1024;
3243                         } else if (*end == 'B' ||
3244                             _substrcmp2(end, "by", "bytes") == 0) {
3245                                 p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
3246                         }
3247                         ac--; av++;
3248                         break;
3249
3250                 case TOK_BUCKETS:
3251                         NEED1("buckets needs argument\n");
3252                         p.fs.rq_size = strtoul(av[0], NULL, 0);
3253                         ac--; av++;
3254                         break;
3255
3256                 case TOK_MASK:
3257                         NEED1("mask needs mask specifier\n");
3258                         /*
3259                          * per-flow queue, mask is dst_ip, dst_port,
3260                          * src_ip, src_port, proto measured in bits
3261                          */
3262                         par = NULL;
3263
3264                         bzero(&p.fs.flow_mask, sizeof(p.fs.flow_mask));
3265                         end = NULL;
3266
3267                         while (ac >= 1) {
3268                             uint32_t *p32 = NULL;
3269                             uint16_t *p16 = NULL;
3270                             uint32_t *p20 = NULL;
3271                             struct in6_addr *pa6 = NULL;
3272                             uint32_t a;
3273
3274                             tok = match_token(dummynet_params, *av);
3275                             ac--; av++;
3276                             switch(tok) {
3277                             case TOK_ALL:
3278                                     /*
3279                                      * special case, all bits significant
3280                                      */
3281                                     p.fs.flow_mask.dst_ip = ~0;
3282                                     p.fs.flow_mask.src_ip = ~0;
3283                                     p.fs.flow_mask.dst_port = ~0;
3284                                     p.fs.flow_mask.src_port = ~0;
3285                                     p.fs.flow_mask.proto = ~0;
3286                                     n2mask(&(p.fs.flow_mask.dst_ip6), 128);
3287                                     n2mask(&(p.fs.flow_mask.src_ip6), 128);
3288                                     p.fs.flow_mask.flow_id6 = ~0;
3289                                     p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
3290                                     goto end_mask;
3291
3292                             case TOK_DSTIP:
3293                                     p32 = &p.fs.flow_mask.dst_ip;
3294                                     break;
3295
3296                             case TOK_SRCIP:
3297                                     p32 = &p.fs.flow_mask.src_ip;
3298                                     break;
3299
3300                             case TOK_DSTIP6:
3301                                     pa6 = &(p.fs.flow_mask.dst_ip6);
3302                                     break;
3303                             
3304                             case TOK_SRCIP6:
3305                                     pa6 = &(p.fs.flow_mask.src_ip6);
3306                                     break;
3307
3308                             case TOK_FLOWID:
3309                                     p20 = &p.fs.flow_mask.flow_id6;
3310                                     break;
3311
3312                             case TOK_DSTPORT:
3313                                     p16 = &p.fs.flow_mask.dst_port;
3314                                     break;
3315
3316                             case TOK_SRCPORT:
3317                                     p16 = &p.fs.flow_mask.src_port;
3318                                     break;
3319
3320                             case TOK_PROTO:
3321                                     break;
3322
3323                             default:
3324                                     ac++; av--; /* backtrack */
3325                                     goto end_mask;
3326                             }
3327                             if (ac < 1)
3328                                     errx(EX_USAGE, "mask: value missing");
3329                             if (*av[0] == '/') {
3330                                     a = strtoul(av[0]+1, &end, 0);
3331                                     if (pa6 == NULL)
3332                                             a = (a == 32) ? ~0 : (1 << a) - 1;
3333                             } else
3334                                     a = strtoul(av[0], &end, 0);
3335                             if (p32 != NULL)
3336                                     *p32 = a;
3337                             else if (p16 != NULL) {
3338                                     if (a > 0xFFFF)
3339                                             errx(EX_DATAERR,
3340                                                 "port mask must be 16 bit");
3341                                     *p16 = (uint16_t)a;
3342                             } else if (p20 != NULL) {
3343                                     if (a > 0xfffff)
3344                                         errx(EX_DATAERR,
3345                                             "flow_id mask must be 20 bit");
3346                                     *p20 = (uint32_t)a;
3347                             } else if (pa6 != NULL) {
3348                                     if (a < 0 || a > 128)
3349                                         errx(EX_DATAERR,
3350                                             "in6addr invalid mask len");
3351                                     else
3352                                         n2mask(pa6, a);
3353                             } else {
3354                                     if (a > 0xFF)
3355                                             errx(EX_DATAERR,
3356                                                 "proto mask must be 8 bit");
3357                                     p.fs.flow_mask.proto = (uint8_t)a;
3358                             }
3359                             if (a != 0)
3360                                     p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
3361                             ac--; av++;
3362                         } /* end while, config masks */
3363 end_mask:
3364                         break;
3365
3366                 case TOK_RED:
3367                 case TOK_GRED:
3368                         NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
3369                         p.fs.flags_fs |= DN_IS_RED;
3370                         if (tok == TOK_GRED)
3371                                 p.fs.flags_fs |= DN_IS_GENTLE_RED;
3372                         /*
3373                          * the format for parameters is w_q/min_th/max_th/max_p
3374                          */
3375                         if ((end = strsep(&av[0], "/"))) {
3376                             double w_q = strtod(end, NULL);
3377                             if (w_q > 1 || w_q <= 0)
3378                                 errx(EX_DATAERR, "0 < w_q <= 1");
3379                             p.fs.w_q = (int) (w_q * (1 << SCALE_RED));
3380                         }
3381                         if ((end = strsep(&av[0], "/"))) {
3382                             p.fs.min_th = strtoul(end, &end, 0);
3383                             if (*end == 'K' || *end == 'k')
3384                                 p.fs.min_th *= 1024;
3385                         }
3386                         if ((end = strsep(&av[0], "/"))) {
3387                             p.fs.max_th = strtoul(end, &end, 0);
3388                             if (*end == 'K' || *end == 'k')
3389                                 p.fs.max_th *= 1024;
3390                         }
3391                         if ((end = strsep(&av[0], "/"))) {
3392                             double max_p = strtod(end, NULL);
3393                             if (max_p > 1 || max_p <= 0)
3394                                 errx(EX_DATAERR, "0 < max_p <= 1");
3395                             p.fs.max_p = (int)(max_p * (1 << SCALE_RED));
3396                         }
3397                         ac--; av++;
3398                         break;
3399
3400                 case TOK_DROPTAIL:
3401                         p.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
3402                         break;
3403
3404                 case TOK_BW:
3405                         NEED1("bw needs bandwidth or interface\n");
3406                         if (do_pipe != 1)
3407                             errx(EX_DATAERR, "bandwidth only valid for pipes");
3408                         /*
3409                          * set clocking interface or bandwidth value
3410                          */
3411                         if (av[0][0] >= 'a' && av[0][0] <= 'z') {
3412                             int l = sizeof(p.if_name)-1;
3413                             /* interface name */
3414                             strncpy(p.if_name, av[0], l);
3415                             p.if_name[l] = '\0';
3416                             p.bandwidth = 0;
3417                         } else {
3418                             p.if_name[0] = '\0';
3419                             p.bandwidth = strtoul(av[0], &end, 0);
3420                             if (*end == 'K' || *end == 'k') {
3421                                 end++;
3422                                 p.bandwidth *= 1000;
3423                             } else if (*end == 'M') {
3424                                 end++;
3425                                 p.bandwidth *= 1000000;
3426                             }
3427                             if ((*end == 'B' &&
3428                                   _substrcmp2(end, "Bi", "Bit/s") != 0) ||
3429                                 _substrcmp2(end, "by", "bytes") == 0)
3430                                 p.bandwidth *= 8;
3431                             if (p.bandwidth < 0)
3432                                 errx(EX_DATAERR, "bandwidth too large");
3433                         }
3434                         ac--; av++;
3435                         break;
3436
3437                 case TOK_DELAY:
3438                         if (do_pipe != 1)
3439                                 errx(EX_DATAERR, "delay only valid for pipes");
3440                         NEED1("delay needs argument 0..10000ms\n");
3441                         p.delay = strtoul(av[0], NULL, 0);
3442                         ac--; av++;
3443                         break;
3444
3445                 case TOK_WEIGHT:
3446                         if (do_pipe == 1)
3447                                 errx(EX_DATAERR,"weight only valid for queues");
3448                         NEED1("weight needs argument 0..100\n");
3449                         p.fs.weight = strtoul(av[0], &end, 0);
3450                         ac--; av++;
3451                         break;
3452
3453                 case TOK_PIPE:
3454                         if (do_pipe == 1)
3455                                 errx(EX_DATAERR,"pipe only valid for queues");
3456                         NEED1("pipe needs pipe_number\n");
3457                         p.fs.parent_nr = strtoul(av[0], &end, 0);
3458                         ac--; av++;
3459                         break;
3460
3461                 default:
3462                         errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
3463                 }
3464         }
3465         if (do_pipe == 1) {
3466                 if (p.pipe_nr == 0)
3467                         errx(EX_DATAERR, "pipe_nr must be > 0");
3468                 if (p.delay > 10000)
3469                         errx(EX_DATAERR, "delay must be < 10000");
3470         } else { /* do_pipe == 2, queue */
3471                 if (p.fs.parent_nr == 0)
3472                         errx(EX_DATAERR, "pipe must be > 0");
3473                 if (p.fs.weight >100)
3474                         errx(EX_DATAERR, "weight must be <= 100");
3475         }
3476         if (p.fs.flags_fs & DN_QSIZE_IS_BYTES) {
3477                 if (p.fs.qsize > 1024*1024)
3478                         errx(EX_DATAERR, "queue size must be < 1MB");
3479         } else {
3480                 if (p.fs.qsize > 100)
3481                         errx(EX_DATAERR, "2 <= queue size <= 100");
3482         }
3483         if (p.fs.flags_fs & DN_IS_RED) {
3484                 size_t len;
3485                 int lookup_depth, avg_pkt_size;
3486                 double s, idle, weight, w_q;
3487                 struct clockinfo ck;
3488                 int t;
3489
3490                 if (p.fs.min_th >= p.fs.max_th)
3491                     errx(EX_DATAERR, "min_th %d must be < than max_th %d",
3492                         p.fs.min_th, p.fs.max_th);
3493                 if (p.fs.max_th == 0)
3494                     errx(EX_DATAERR, "max_th must be > 0");
3495
3496                 len = sizeof(int);
3497                 if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
3498                         &lookup_depth, &len, NULL, 0) == -1)
3499
3500                     errx(1, "sysctlbyname(\"%s\")",
3501                         "net.inet.ip.dummynet.red_lookup_depth");
3502                 if (lookup_depth == 0)
3503                     errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth"
3504                         " must be greater than zero");
3505
3506                 len = sizeof(int);
3507                 if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
3508                         &avg_pkt_size, &len, NULL, 0) == -1)
3509
3510                     errx(1, "sysctlbyname(\"%s\")",
3511                         "net.inet.ip.dummynet.red_avg_pkt_size");
3512                 if (avg_pkt_size == 0)
3513                         errx(EX_DATAERR,
3514                             "net.inet.ip.dummynet.red_avg_pkt_size must"
3515                             " be greater than zero");
3516
3517                 len = sizeof(struct clockinfo);
3518                 if (sysctlbyname("kern.clockrate", &ck, &len, NULL, 0) == -1)
3519                         errx(1, "sysctlbyname(\"%s\")", "kern.clockrate");
3520
3521                 /*
3522                  * Ticks needed for sending a medium-sized packet.
3523                  * Unfortunately, when we are configuring a WF2Q+ queue, we
3524                  * do not have bandwidth information, because that is stored
3525                  * in the parent pipe, and also we have multiple queues
3526                  * competing for it. So we set s=0, which is not very
3527                  * correct. But on the other hand, why do we want RED with
3528                  * WF2Q+ ?
3529                  */
3530                 if (p.bandwidth==0) /* this is a WF2Q+ queue */
3531                         s = 0;
3532                 else
3533                         s = ck.hz * avg_pkt_size * 8 / p.bandwidth;
3534
3535                 /*
3536                  * max idle time (in ticks) before avg queue size becomes 0.
3537                  * NOTA:  (3/w_q) is approx the value x so that
3538                  * (1-w_q)^x < 10^-3.
3539                  */
3540                 w_q = ((double)p.fs.w_q) / (1 << SCALE_RED);
3541                 idle = s * 3. / w_q;
3542                 p.fs.lookup_step = (int)idle / lookup_depth;
3543                 if (!p.fs.lookup_step)
3544                         p.fs.lookup_step = 1;
3545                 weight = 1 - w_q;
3546                 for (t = p.fs.lookup_step; t > 0; --t)
3547                         weight *= weight;
3548                 p.fs.lookup_weight = (int)(weight * (1 << SCALE_RED));
3549         }
3550         i = do_cmd(IP_DUMMYNET_CONFIGURE, &p, sizeof p);
3551         if (i)
3552                 err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
3553 }
3554
3555 static void
3556 get_mac_addr_mask(char *p, uint8_t *addr, uint8_t *mask)
3557 {
3558         int i, l;
3559
3560         for (i=0; i<6; i++)
3561                 addr[i] = mask[i] = 0;
3562         if (strcmp(p, "any") == 0)
3563                 return;
3564
3565         for (i=0; *p && i<6;i++, p++) {
3566                 addr[i] = strtol(p, &p, 16);
3567                 if (*p != ':') /* we start with the mask */
3568                         break;
3569         }
3570         if (*p == '/') { /* mask len */
3571                 l = strtol(p+1, &p, 0);
3572                 for (i=0; l>0; l -=8, i++)
3573                         mask[i] = (l >=8) ? 0xff : (~0) << (8-l);
3574         } else if (*p == '&') { /* mask */
3575                 for (i=0, p++; *p && i<6;i++, p++) {
3576                         mask[i] = strtol(p, &p, 16);
3577                         if (*p != ':')
3578                                 break;
3579                 }
3580         } else if (*p == '\0') {
3581                 for (i=0; i<6; i++)
3582                         mask[i] = 0xff;
3583         }
3584         for (i=0; i<6; i++)
3585                 addr[i] &= mask[i];
3586 }
3587
3588 /*
3589  * helper function, updates the pointer to cmd with the length
3590  * of the current command, and also cleans up the first word of
3591  * the new command in case it has been clobbered before.
3592  */
3593 static ipfw_insn *
3594 next_cmd(ipfw_insn *cmd)
3595 {
3596         cmd += F_LEN(cmd);
3597         bzero(cmd, sizeof(*cmd));
3598         return cmd;
3599 }
3600
3601 /*
3602  * Takes arguments and copies them into a comment
3603  */
3604 static void
3605 fill_comment(ipfw_insn *cmd, int ac, char **av)
3606 {
3607         int i, l;
3608         char *p = (char *)(cmd + 1);
3609
3610         cmd->opcode = O_NOP;
3611         cmd->len =  (cmd->len & (F_NOT | F_OR));
3612
3613         /* Compute length of comment string. */
3614         for (i = 0, l = 0; i < ac; i++)
3615                 l += strlen(av[i]) + 1;
3616         if (l == 0)
3617                 return;
3618         if (l > 84)
3619                 errx(EX_DATAERR,
3620                     "comment too long (max 80 chars)");
3621         l = 1 + (l+3)/4;
3622         cmd->len =  (cmd->len & (F_NOT | F_OR)) | l;
3623         for (i = 0; i < ac; i++) {
3624                 strcpy(p, av[i]);
3625                 p += strlen(av[i]);
3626                 *p++ = ' ';
3627         }
3628         *(--p) = '\0';
3629 }
3630
3631 /*
3632  * A function to fill simple commands of size 1.
3633  * Existing flags are preserved.
3634  */
3635 static void
3636 fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg)
3637 {
3638         cmd->opcode = opcode;
3639         cmd->len =  ((cmd->len | flags) & (F_NOT | F_OR)) | 1;
3640         cmd->arg1 = arg;
3641 }
3642
3643 /*
3644  * Fetch and add the MAC address and type, with masks. This generates one or
3645  * two microinstructions, and returns the pointer to the last one.
3646  */
3647 static ipfw_insn *
3648 add_mac(ipfw_insn *cmd, int ac, char *av[])
3649 {
3650         ipfw_insn_mac *mac;
3651
3652         if (ac < 2)
3653                 errx(EX_DATAERR, "MAC dst src");
3654
3655         cmd->opcode = O_MACADDR2;
3656         cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
3657
3658         mac = (ipfw_insn_mac *)cmd;
3659         get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */
3660         get_mac_addr_mask(av[1], &(mac->addr[6]), &(mac->mask[6])); /* src */
3661         return cmd;
3662 }
3663
3664 static ipfw_insn *
3665 add_mactype(ipfw_insn *cmd, int ac, char *av)
3666 {
3667         if (ac < 1)
3668                 errx(EX_DATAERR, "missing MAC type");
3669         if (strcmp(av, "any") != 0) { /* we have a non-null type */
3670                 fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE);
3671                 cmd->opcode = O_MAC_TYPE;
3672                 return cmd;
3673         } else
3674                 return NULL;
3675 }
3676
3677 static ipfw_insn *
3678 add_proto0(ipfw_insn *cmd, char *av, u_char *protop)
3679 {
3680         struct protoent *pe;
3681         char *ep;
3682         int proto;
3683
3684         proto = strtol(av, &ep, 10);
3685         if (*ep != '\0' || proto <= 0) {
3686                 if ((pe = getprotobyname(av)) == NULL)
3687                         return NULL;
3688                 proto = pe->p_proto;
3689         }
3690
3691         fill_cmd(cmd, O_PROTO, 0, proto);
3692         *protop = proto;
3693         return cmd;
3694 }
3695
3696 static ipfw_insn *
3697 add_proto(ipfw_insn *cmd, char *av, u_char *protop)
3698 {
3699         u_char proto = IPPROTO_IP;
3700
3701         if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
3702                 ; /* do not set O_IP4 nor O_IP6 */
3703         else if (strcmp(av, "ip4") == 0)
3704                 /* explicit "just IPv4" rule */
3705                 fill_cmd(cmd, O_IP4, 0, 0);
3706         else if (strcmp(av, "ip6") == 0) {
3707                 /* explicit "just IPv6" rule */
3708                 proto = IPPROTO_IPV6;
3709                 fill_cmd(cmd, O_IP6, 0, 0);
3710         } else
3711                 return add_proto0(cmd, av, protop);
3712
3713         *protop = proto;
3714         return cmd;
3715 }
3716
3717 static ipfw_insn *
3718 add_proto_compat(ipfw_insn *cmd, char *av, u_char *protop)
3719 {
3720         u_char proto = IPPROTO_IP;
3721
3722         if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
3723                 ; /* do not set O_IP4 nor O_IP6 */
3724         else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0)
3725                 /* explicit "just IPv4" rule */
3726                 fill_cmd(cmd, O_IP4, 0, 0);
3727         else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) {
3728                 /* explicit "just IPv6" rule */
3729                 proto = IPPROTO_IPV6;
3730                 fill_cmd(cmd, O_IP6, 0, 0);
3731         } else
3732                 return add_proto0(cmd, av, protop);
3733
3734         *protop = proto;
3735         return cmd;
3736 }
3737
3738 static ipfw_insn *
3739 add_srcip(ipfw_insn *cmd, char *av)
3740 {
3741         fill_ip((ipfw_insn_ip *)cmd, av);
3742         if (cmd->opcode == O_IP_DST_SET)                        /* set */
3743                 cmd->opcode = O_IP_SRC_SET;
3744         else if (cmd->opcode == O_IP_DST_LOOKUP)                /* table */
3745                 cmd->opcode = O_IP_SRC_LOOKUP;
3746         else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))          /* me */
3747                 cmd->opcode = O_IP_SRC_ME;
3748         else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))      /* one IP */
3749                 cmd->opcode = O_IP_SRC;
3750         else                                                    /* addr/mask */
3751                 cmd->opcode = O_IP_SRC_MASK;
3752         return cmd;
3753 }
3754
3755 static ipfw_insn *
3756 add_dstip(ipfw_insn *cmd, char *av)
3757 {
3758         fill_ip((ipfw_insn_ip *)cmd, av);
3759         if (cmd->opcode == O_IP_DST_SET)                        /* set */
3760                 ;
3761         else if (cmd->opcode == O_IP_DST_LOOKUP)                /* table */
3762                 ;
3763         else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))          /* me */
3764                 cmd->opcode = O_IP_DST_ME;
3765         else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))      /* one IP */
3766                 cmd->opcode = O_IP_DST;
3767         else                                                    /* addr/mask */
3768                 cmd->opcode = O_IP_DST_MASK;
3769         return cmd;
3770 }
3771
3772 static ipfw_insn *
3773 add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
3774 {
3775         if (_substrcmp(av, "any") == 0) {
3776                 return NULL;
3777         } else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) {
3778                 /* XXX todo: check that we have a protocol with ports */
3779                 cmd->opcode = opcode;
3780                 return cmd;
3781         }
3782         return NULL;
3783 }
3784
3785 static ipfw_insn *
3786 add_src(ipfw_insn *cmd, char *av, u_char proto)
3787 {
3788         struct in6_addr a;
3789         char *host, *ch;
3790         ipfw_insn *ret = NULL;
3791
3792         if ((host = strdup(av)) == NULL)
3793                 return NULL;
3794         if ((ch = strrchr(host, '/')) != NULL)
3795                 *ch = '\0';
3796
3797         if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
3798             inet_pton(AF_INET6, host, &a))
3799                 ret = add_srcip6(cmd, av);
3800         /* XXX: should check for IPv4, not !IPv6 */
3801         if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
3802             !inet_pton(AF_INET6, host, &a)))
3803                 ret = add_srcip(cmd, av);
3804         if (ret == NULL && strcmp(av, "any") != 0)
3805                 ret = cmd;
3806
3807         free(host);
3808         return ret;
3809 }
3810
3811 static ipfw_insn *
3812 add_dst(ipfw_insn *cmd, char *av, u_char proto)
3813 {
3814         struct in6_addr a;
3815         char *host, *ch;
3816         ipfw_insn *ret = NULL;
3817
3818         if ((host = strdup(av)) == NULL)
3819                 return NULL;
3820         if ((ch = strrchr(host, '/')) != NULL)
3821                 *ch = '\0';
3822
3823         if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
3824             inet_pton(AF_INET6, host, &a))
3825                 ret = add_dstip6(cmd, av);
3826         /* XXX: should check for IPv4, not !IPv6 */
3827         if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
3828             !inet_pton(AF_INET6, host, &a)))
3829                 ret = add_dstip(cmd, av);
3830         if (ret == NULL && strcmp(av, "any") != 0)
3831                 ret = cmd;
3832
3833         free(host);
3834         return ret;
3835 }
3836
3837 /*
3838  * Parse arguments and assemble the microinstructions which make up a rule.
3839  * Rules are added into the 'rulebuf' and then copied in the correct order
3840  * into the actual rule.
3841  *
3842  * The syntax for a rule starts with the action, followed by
3843  * optional action parameters, and the various match patterns.
3844  * In the assembled microcode, the first opcode must be an O_PROBE_STATE
3845  * (generated if the rule includes a keep-state option), then the
3846  * various match patterns, log/altq actions, and the actual action.
3847  *
3848  */
3849 static void
3850 add(int ac, char *av[])
3851 {
3852         /*
3853          * rules are added into the 'rulebuf' and then copied in
3854          * the correct order into the actual rule.
3855          * Some things that need to go out of order (prob, action etc.)
3856          * go into actbuf[].
3857          */
3858         static uint32_t rulebuf[255], actbuf[255], cmdbuf[255];
3859
3860         ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
3861         ipfw_insn *first_cmd;   /* first match pattern */
3862
3863         struct ip_fw *rule;
3864
3865         /*
3866          * various flags used to record that we entered some fields.
3867          */
3868         ipfw_insn *have_state = NULL;   /* check-state or keep-state */
3869         ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL;
3870         size_t len;
3871
3872         int i;
3873
3874         int open_par = 0;       /* open parenthesis ( */
3875
3876         /* proto is here because it is used to fetch ports */
3877         u_char proto = IPPROTO_IP;      /* default protocol */
3878
3879         double match_prob = 1; /* match probability, default is always match */
3880
3881         bzero(actbuf, sizeof(actbuf));          /* actions go here */
3882         bzero(cmdbuf, sizeof(cmdbuf));
3883         bzero(rulebuf, sizeof(rulebuf));
3884
3885         rule = (struct ip_fw *)rulebuf;
3886         cmd = (ipfw_insn *)cmdbuf;
3887         action = (ipfw_insn *)actbuf;
3888
3889         av++; ac--;
3890
3891         /* [rule N]     -- Rule number optional */
3892         if (ac && isdigit(**av)) {
3893                 rule->rulenum = atoi(*av);
3894                 av++;
3895                 ac--;
3896         }
3897
3898         /* [set N]      -- set number (0..RESVD_SET), optional */
3899         if (ac > 1 && _substrcmp(*av, "set") == 0) {
3900                 int set = strtoul(av[1], NULL, 10);
3901                 if (set < 0 || set > RESVD_SET)
3902                         errx(EX_DATAERR, "illegal set %s", av[1]);
3903                 rule->set = set;
3904                 av += 2; ac -= 2;
3905         }
3906
3907         /* [prob D]     -- match probability, optional */
3908         if (ac > 1 && _substrcmp(*av, "prob") == 0) {
3909                 match_prob = strtod(av[1], NULL);
3910
3911                 if (match_prob <= 0 || match_prob > 1)
3912                         errx(EX_DATAERR, "illegal match prob. %s", av[1]);
3913                 av += 2; ac -= 2;
3914         }
3915
3916         /* action       -- mandatory */
3917         NEED1("missing action");
3918         i = match_token(rule_actions, *av);
3919         ac--; av++;
3920         action->len = 1;        /* default */
3921         switch(i) {
3922         case TOK_CHECKSTATE:
3923                 have_state = action;
3924                 action->opcode = O_CHECK_STATE;
3925                 break;
3926
3927         case TOK_ACCEPT:
3928                 action->opcode = O_ACCEPT;
3929                 break;
3930
3931         case TOK_DENY:
3932                 action->opcode = O_DENY;
3933                 action->arg1 = 0;
3934                 break;
3935
3936         case TOK_REJECT:
3937                 action->opcode = O_REJECT;
3938                 action->arg1 = ICMP_UNREACH_HOST;
3939                 break;
3940
3941         case TOK_RESET:
3942                 action->opcode = O_REJECT;
3943                 action->arg1 = ICMP_REJECT_RST;
3944                 break;
3945
3946         case TOK_RESET6:
3947                 action->opcode = O_UNREACH6;
3948                 action->arg1 = ICMP6_UNREACH_RST;
3949                 break;
3950
3951         case TOK_UNREACH:
3952                 action->opcode = O_REJECT;
3953                 NEED1("missing reject code");
3954                 fill_reject_code(&action->arg1, *av);
3955                 ac--; av++;
3956                 break;
3957
3958         case TOK_UNREACH6:
3959                 action->opcode = O_UNREACH6;
3960                 NEED1("missing unreach code");
3961                 fill_unreach6_code(&action->arg1, *av);
3962                 ac--; av++;
3963                 break;
3964
3965         case TOK_COUNT:
3966                 action->opcode = O_COUNT;
3967                 break;
3968
3969         case TOK_QUEUE:
3970                 action->opcode = O_QUEUE;
3971                 goto chkarg;
3972         case TOK_PIPE:
3973                 action->opcode = O_PIPE;
3974                 goto chkarg;
3975         case TOK_SKIPTO:
3976                 action->opcode = O_SKIPTO;
3977                 goto chkarg;
3978         case TOK_NETGRAPH:
3979                 action->opcode = O_NETGRAPH;
3980                 goto chkarg;
3981         case TOK_NGTEE:
3982                 action->opcode = O_NGTEE;
3983                 goto chkarg;
3984         case TOK_DIVERT:
3985                 action->opcode = O_DIVERT;
3986                 goto chkarg;
3987         case TOK_TEE:
3988                 action->opcode = O_TEE;
3989 chkarg: 
3990                 if (!ac)
3991                         errx(EX_USAGE, "missing argument for %s", *(av - 1));
3992                 if (isdigit(**av)) {
3993                         action->arg1 = strtoul(*av, NULL, 10);
3994                         if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG)
3995                                 errx(EX_DATAERR, "illegal argument for %s",
3996                                     *(av - 1));
3997                 } else if (_substrcmp(*av, TABLEARG) == 0) {
3998                         action->arg1 = IP_FW_TABLEARG;
3999                 } else if (i == TOK_DIVERT || i == TOK_TEE) {
4000                         struct servent *s;
4001                         setservent(1);
4002                         s = getservbyname(av[0], "divert");
4003                         if (s != NULL)
4004                                 action->arg1 = ntohs(s->s_port);
4005                         else
4006                                 errx(EX_DATAERR, "illegal divert/tee port");
4007                 } else
4008                         errx(EX_DATAERR, "illegal argument for %s", *(av - 1));
4009                 ac--; av++;
4010                 break;
4011
4012         case TOK_FORWARD: {
4013                 ipfw_insn_sa *p = (ipfw_insn_sa *)action;
4014                 char *s, *end;
4015
4016                 NEED1("missing forward address[:port]");
4017
4018                 action->opcode = O_FORWARD_IP;
4019                 action->len = F_INSN_SIZE(ipfw_insn_sa);
4020
4021                 p->sa.sin_len = sizeof(struct sockaddr_in);
4022                 p->sa.sin_family = AF_INET;
4023                 p->sa.sin_port = 0;
4024                 /*
4025                  * locate the address-port separator (':' or ',')
4026                  */
4027                 s = strchr(*av, ':');
4028                 if (s == NULL)
4029                         s = strchr(*av, ',');
4030                 if (s != NULL) {
4031                         *(s++) = '\0';
4032                         i = strtoport(s, &end, 0 /* base */, 0 /* proto */);
4033                         if (s == end)
4034                                 errx(EX_DATAERR,
4035                                     "illegal forwarding port ``%s''", s);
4036                         p->sa.sin_port = (u_short)i;
4037                 }
4038                 if (_substrcmp(*av, "tablearg") == 0) 
4039                         p->sa.sin_addr.s_addr = INADDR_ANY;
4040                 else
4041                         lookup_host(*av, &(p->sa.sin_addr));
4042                 ac--; av++;
4043                 break;
4044             }
4045         case TOK_COMMENT:
4046                 /* pretend it is a 'count' rule followed by the comment */
4047                 action->opcode = O_COUNT;
4048                 ac++; av--;     /* go back... */
4049                 break;
4050
4051         default:
4052                 errx(EX_DATAERR, "invalid action %s\n", av[-1]);
4053         }
4054         action = next_cmd(action);
4055
4056         /*
4057          * [altq queuename] -- altq tag, optional
4058          * [log [logamount N]]  -- log, optional
4059          *
4060          * If they exist, it go first in the cmdbuf, but then it is
4061          * skipped in the copy section to the end of the buffer.
4062          */
4063         while (ac != 0 && (i = match_token(rule_action_params, *av)) != -1) {
4064                 ac--; av++;
4065                 switch (i) {
4066                 case TOK_LOG:
4067                     {
4068                         ipfw_insn_log *c = (ipfw_insn_log *)cmd;
4069                         int l;
4070
4071                         if (have_log)
4072                                 errx(EX_DATAERR,
4073                                     "log cannot be specified more than once");
4074                         have_log = (ipfw_insn *)c;
4075                         cmd->len = F_INSN_SIZE(ipfw_insn_log);
4076                         cmd->opcode = O_LOG;
4077                         if (ac && _substrcmp(*av, "logamount") == 0) {
4078                                 ac--; av++;
4079                                 NEED1("logamount requires argument");
4080                                 l = atoi(*av);
4081                                 if (l < 0)
4082                                         errx(EX_DATAERR,
4083                                             "logamount must be positive");
4084                                 c->max_log = l;
4085                                 ac--; av++;
4086                         } else {
4087                                 len = sizeof(c->max_log);
4088                                 if (sysctlbyname("net.inet.ip.fw.verbose_limit",
4089                                     &c->max_log, &len, NULL, 0) == -1)
4090                                         errx(1, "sysctlbyname(\"%s\")",
4091                                             "net.inet.ip.fw.verbose_limit");
4092                         }
4093                     }
4094                         break;
4095
4096                 case TOK_ALTQ:
4097                     {
4098                         ipfw_insn_altq *a = (ipfw_insn_altq *)cmd;
4099
4100                         NEED1("missing altq queue name");
4101                         if (have_altq)
4102                                 errx(EX_DATAERR,
4103                                     "altq cannot be specified more than once");
4104                         have_altq = (ipfw_insn *)a;
4105                         cmd->len = F_INSN_SIZE(ipfw_insn_altq);
4106                         cmd->opcode = O_ALTQ;
4107                         fill_altq_qid(&a->qid, *av);
4108                         ac--; av++;
4109                     }
4110                         break;
4111
4112                 case TOK_TAG:
4113                 case TOK_UNTAG: {
4114                         uint16_t tag;
4115
4116                         if (have_tag)
4117                                 errx(EX_USAGE, "tag and untag cannot be "
4118                                     "specified more than once");
4119                         GET_UINT_ARG(tag, 1, 65534, i, rule_action_params);
4120                         have_tag = cmd;
4121                         fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag);
4122                         ac--; av++;
4123                         break;
4124                 }
4125
4126                 default:
4127                         abort();
4128                 }
4129                 cmd = next_cmd(cmd);
4130         }
4131
4132         if (have_state) /* must be a check-state, we are done */
4133                 goto done;
4134
4135 #define OR_START(target)                                        \
4136         if (ac && (*av[0] == '(' || *av[0] == '{')) {           \
4137                 if (open_par)                                   \
4138                         errx(EX_USAGE, "nested \"(\" not allowed\n"); \
4139                 prev = NULL;                                    \
4140                 open_par = 1;                                   \
4141                 if ( (av[0])[1] == '\0') {                      \
4142                         ac--; av++;                             \
4143                 } else                                          \
4144                         (*av)++;                                \
4145         }                                                       \
4146         target:                                                 \
4147
4148
4149 #define CLOSE_PAR                                               \
4150         if (open_par) {                                         \
4151                 if (ac && (                                     \
4152                     strcmp(*av, ")") == 0 ||                    \
4153                     strcmp(*av, "}") == 0)) {                   \
4154                         prev = NULL;                            \
4155                         open_par = 0;                           \
4156                         ac--; av++;                             \
4157                 } else                                          \
4158                         errx(EX_USAGE, "missing \")\"\n");      \
4159         }
4160
4161 #define NOT_BLOCK                                               \
4162         if (ac && _substrcmp(*av, "not") == 0) {                \
4163                 if (cmd->len & F_NOT)                           \
4164                         errx(EX_USAGE, "double \"not\" not allowed\n"); \
4165                 cmd->len |= F_NOT;                              \
4166                 ac--; av++;                                     \
4167         }
4168
4169 #define OR_BLOCK(target)                                        \
4170         if (ac && _substrcmp(*av, "or") == 0) {         \
4171                 if (prev == NULL || open_par == 0)              \
4172                         errx(EX_DATAERR, "invalid OR block");   \
4173                 prev->len |= F_OR;                              \
4174                 ac--; av++;                                     \
4175                 goto target;                                    \
4176         }                                                       \
4177         CLOSE_PAR;
4178
4179         first_cmd = cmd;
4180
4181 #if 0
4182         /*
4183          * MAC addresses, optional.
4184          * If we have this, we skip the part "proto from src to dst"
4185          * and jump straight to the option parsing.
4186          */
4187         NOT_BLOCK;
4188         NEED1("missing protocol");
4189         if (_substrcmp(*av, "MAC") == 0 ||
4190             _substrcmp(*av, "mac") == 0) {
4191                 ac--; av++;     /* the "MAC" keyword */
4192                 add_mac(cmd, ac, av); /* exits in case of errors */
4193                 cmd = next_cmd(cmd);
4194                 ac -= 2; av += 2;       /* dst-mac and src-mac */
4195                 NOT_BLOCK;
4196                 NEED1("missing mac type");
4197                 if (add_mactype(cmd, ac, av[0]))
4198                         cmd = next_cmd(cmd);
4199                 ac--; av++;     /* any or mac-type */
4200                 goto read_options;
4201         }
4202 #endif
4203
4204         /*
4205          * protocol, mandatory
4206          */
4207     OR_START(get_proto);
4208         NOT_BLOCK;
4209         NEED1("missing protocol");
4210         if (add_proto_compat(cmd, *av, &proto)) {
4211                 av++; ac--;
4212                 if (F_LEN(cmd) != 0) {
4213                         prev = cmd;
4214                         cmd = next_cmd(cmd);
4215                 }
4216         } else if (first_cmd != cmd) {
4217                 errx(EX_DATAERR, "invalid protocol ``%s''", *av);
4218         } else
4219                 goto read_options;
4220     OR_BLOCK(get_proto);
4221
4222         /*
4223          * "from", mandatory
4224          */
4225         if (!ac || _substrcmp(*av, "from") != 0)
4226                 errx(EX_USAGE, "missing ``from''");
4227         ac--; av++;
4228
4229         /*
4230          * source IP, mandatory
4231          */
4232     OR_START(source_ip);
4233         NOT_BLOCK;      /* optional "not" */
4234         NEED1("missing source address");
4235         if (add_src(cmd, *av, proto)) {
4236                 ac--; av++;
4237                 if (F_LEN(cmd) != 0) {  /* ! any */
4238                         prev = cmd;
4239                         cmd = next_cmd(cmd);
4240                 }
4241         } else
4242                 errx(EX_USAGE, "bad source address %s", *av);
4243     OR_BLOCK(source_ip);
4244
4245         /*
4246          * source ports, optional
4247          */
4248         NOT_BLOCK;      /* optional "not" */
4249         if (ac) {
4250                 if (_substrcmp(*av, "any") == 0 ||
4251                     add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
4252                         ac--; av++;
4253                         if (F_LEN(cmd) != 0)
4254                                 cmd = next_cmd(cmd);
4255                 }
4256         }
4257
4258         /*
4259          * "to", mandatory
4260          */
4261         if (!ac || _substrcmp(*av, "to") != 0)
4262                 errx(EX_USAGE, "missing ``to''");
4263         av++; ac--;
4264
4265         /*
4266          * destination, mandatory
4267          */
4268     OR_START(dest_ip);
4269         NOT_BLOCK;      /* optional "not" */
4270         NEED1("missing dst address");
4271         if (add_dst(cmd, *av, proto)) {
4272                 ac--; av++;
4273                 if (F_LEN(cmd) != 0) {  /* ! any */
4274                         prev = cmd;
4275                         cmd = next_cmd(cmd);
4276                 }
4277         } else
4278                 errx( EX_USAGE, "bad destination address %s", *av);
4279     OR_BLOCK(dest_ip);
4280
4281         /*
4282          * dest. ports, optional
4283          */
4284         NOT_BLOCK;      /* optional "not" */
4285         if (ac) {
4286                 if (_substrcmp(*av, "any") == 0 ||
4287                     add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
4288                         ac--; av++;
4289                         if (F_LEN(cmd) != 0)
4290                                 cmd = next_cmd(cmd);
4291                 }
4292         }
4293
4294 read_options:
4295         if (ac && first_cmd == cmd) {
4296                 /*
4297                  * nothing specified so far, store in the rule to ease
4298                  * printout later.
4299                  */
4300                  rule->_pad = 1;
4301         }
4302         prev = NULL;
4303         while (ac) {
4304                 char *s;
4305                 ipfw_insn_u32 *cmd32;   /* alias for cmd */
4306
4307                 s = *av;
4308                 cmd32 = (ipfw_insn_u32 *)cmd;
4309
4310                 if (*s == '!') {        /* alternate syntax for NOT */
4311                         if (cmd->len & F_NOT)
4312                                 errx(EX_USAGE, "double \"not\" not allowed\n");
4313                         cmd->len = F_NOT;
4314                         s++;
4315                 }
4316                 i = match_token(rule_options, s);
4317                 ac--; av++;
4318                 switch(i) {
4319                 case TOK_NOT:
4320                         if (cmd->len & F_NOT)
4321                                 errx(EX_USAGE, "double \"not\" not allowed\n");
4322                         cmd->len = F_NOT;
4323                         break;
4324
4325                 case TOK_OR:
4326                         if (open_par == 0 || prev == NULL)
4327                                 errx(EX_USAGE, "invalid \"or\" block\n");
4328                         prev->len |= F_OR;
4329                         break;
4330
4331                 case TOK_STARTBRACE:
4332                         if (open_par)
4333                                 errx(EX_USAGE, "+nested \"(\" not allowed\n");
4334                         open_par = 1;
4335                         break;
4336
4337                 case TOK_ENDBRACE:
4338                         if (!open_par)
4339                                 errx(EX_USAGE, "+missing \")\"\n");
4340                         open_par = 0;
4341                         prev = NULL;
4342                         break;
4343
4344                 case TOK_IN:
4345                         fill_cmd(cmd, O_IN, 0, 0);
4346                         break;
4347
4348                 case TOK_OUT:
4349                         cmd->len ^= F_NOT; /* toggle F_NOT */
4350                         fill_cmd(cmd, O_IN, 0, 0);
4351                         break;
4352
4353                 case TOK_DIVERTED:
4354                         fill_cmd(cmd, O_DIVERTED, 0, 3);
4355                         break;
4356
4357                 case TOK_DIVERTEDLOOPBACK:
4358                         fill_cmd(cmd, O_DIVERTED, 0, 1);
4359                         break;
4360
4361                 case TOK_DIVERTEDOUTPUT:
4362                         fill_cmd(cmd, O_DIVERTED, 0, 2);
4363                         break;
4364
4365                 case TOK_FRAG:
4366                         fill_cmd(cmd, O_FRAG, 0, 0);
4367                         break;
4368
4369                 case TOK_LAYER2:
4370                         fill_cmd(cmd, O_LAYER2, 0, 0);
4371                         break;
4372
4373                 case TOK_XMIT:
4374                 case TOK_RECV:
4375                 case TOK_VIA:
4376                         NEED1("recv, xmit, via require interface name"
4377                                 " or address");
4378                         fill_iface((ipfw_insn_if *)cmd, av[0]);
4379                         ac--; av++;
4380                         if (F_LEN(cmd) == 0)    /* not a valid address */
4381                                 break;
4382                         if (i == TOK_XMIT)
4383                                 cmd->opcode = O_XMIT;
4384                         else if (i == TOK_RECV)
4385                                 cmd->opcode = O_RECV;
4386                         else if (i == TOK_VIA)
4387                                 cmd->opcode = O_VIA;
4388                         break;
4389
4390                 case TOK_ICMPTYPES:
4391                         NEED1("icmptypes requires list of types");
4392                         fill_icmptypes((ipfw_insn_u32 *)cmd, *av);
4393                         av++; ac--;
4394                         break;
4395                 
4396                 case TOK_ICMP6TYPES:
4397                         NEED1("icmptypes requires list of types");
4398                         fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av);
4399                         av++; ac--;
4400                         break;
4401
4402                 case TOK_IPTTL:
4403                         NEED1("ipttl requires TTL");
4404                         if (strpbrk(*av, "-,")) {
4405                             if (!add_ports(cmd, *av, 0, O_IPTTL))
4406                                 errx(EX_DATAERR, "invalid ipttl %s", *av);
4407                         } else
4408                             fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
4409                         ac--; av++;
4410                         break;
4411
4412                 case TOK_IPID:
4413                         NEED1("ipid requires id");
4414                         if (strpbrk(*av, "-,")) {
4415                             if (!add_ports(cmd, *av, 0, O_IPID))
4416                                 errx(EX_DATAERR, "invalid ipid %s", *av);
4417                         } else
4418                             fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0));
4419                         ac--; av++;
4420                         break;
4421
4422                 case TOK_IPLEN:
4423                         NEED1("iplen requires length");
4424                         if (strpbrk(*av, "-,")) {
4425                             if (!add_ports(cmd, *av, 0, O_IPLEN))
4426                                 errx(EX_DATAERR, "invalid ip len %s", *av);
4427                         } else
4428                             fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0));
4429                         ac--; av++;
4430                         break;
4431
4432                 case TOK_IPVER:
4433                         NEED1("ipver requires version");
4434                         fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0));
4435                         ac--; av++;
4436                         break;
4437
4438                 case TOK_IPPRECEDENCE:
4439                         NEED1("ipprecedence requires value");
4440                         fill_cmd(cmd, O_IPPRECEDENCE, 0,
4441                             (strtoul(*av, NULL, 0) & 7) << 5);
4442                         ac--; av++;
4443                         break;
4444
4445                 case TOK_IPOPTS:
4446                         NEED1("missing argument for ipoptions");
4447                         fill_flags(cmd, O_IPOPT, f_ipopts, *av);
4448                         ac--; av++;
4449                         break;
4450
4451                 case TOK_IPTOS:
4452                         NEED1("missing argument for iptos");
4453                         fill_flags(cmd, O_IPTOS, f_iptos, *av);
4454                         ac--; av++;
4455                         break;
4456
4457                 case TOK_UID:
4458                         NEED1("uid requires argument");
4459                     {
4460                         char *end;
4461                         uid_t uid;
4462                         struct passwd *pwd;
4463
4464                         cmd->opcode = O_UID;
4465                         uid = strtoul(*av, &end, 0);
4466                         pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av);
4467                         if (pwd == NULL)
4468                                 errx(EX_DATAERR, "uid \"%s\" nonexistent", *av);
4469                         cmd32->d[0] = pwd->pw_uid;
4470                         cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
4471                         ac--; av++;
4472                     }
4473                         break;
4474
4475                 case TOK_GID:
4476                         NEED1("gid requires argument");
4477                     {
4478                         char *end;
4479                         gid_t gid;
4480                         struct group *grp;
4481
4482                         cmd->opcode = O_GID;
4483                         gid = strtoul(*av, &end, 0);
4484                         grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av);
4485                         if (grp == NULL)
4486                                 errx(EX_DATAERR, "gid \"%s\" nonexistent", *av);
4487                         cmd32->d[0] = grp->gr_gid;
4488                         cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
4489                         ac--; av++;
4490                     }
4491                         break;
4492
4493                 case TOK_JAIL:
4494                         NEED1("jail requires argument");
4495                     {
4496                         char *end;
4497                         int jid;
4498
4499                         cmd->opcode = O_JAIL;
4500                         jid = (int)strtol(*av, &end, 0);
4501                         if (jid < 0 || *end != '\0')
4502                                 errx(EX_DATAERR, "jail requires prison ID");
4503                         cmd32->d[0] = (uint32_t)jid;
4504                         cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
4505                         ac--; av++;
4506                     }
4507                         break;
4508
4509                 case TOK_ESTAB:
4510                         fill_cmd(cmd, O_ESTAB, 0, 0);
4511                         break;
4512
4513                 case TOK_SETUP:
4514                         fill_cmd(cmd, O_TCPFLAGS, 0,
4515                                 (TH_SYN) | ( (TH_ACK) & 0xff) <<8 );
4516                         break;
4517
4518                 case TOK_TCPDATALEN:
4519                         NEED1("tcpdatalen requires length");
4520                         if (strpbrk(*av, "-,")) {
4521                             if (!add_ports(cmd, *av, 0, O_TCPDATALEN))
4522                                 errx(EX_DATAERR, "invalid tcpdata len %s", *av);
4523                         } else
4524                             fill_cmd(cmd, O_TCPDATALEN, 0,
4525                                     strtoul(*av, NULL, 0));
4526                         ac--; av++;
4527                         break;
4528
4529                 case TOK_TCPOPTS:
4530                         NEED1("missing argument for tcpoptions");
4531                         fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av);
4532                         ac--; av++;
4533                         break;
4534
4535                 case TOK_TCPSEQ:
4536                 case TOK_TCPACK:
4537                         NEED1("tcpseq/tcpack requires argument");
4538                         cmd->len = F_INSN_SIZE(ipfw_insn_u32);
4539                         cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK;
4540                         cmd32->d[0] = htonl(strtoul(*av, NULL, 0));
4541                         ac--; av++;
4542                         break;
4543
4544                 case TOK_TCPWIN:
4545                         NEED1("tcpwin requires length");
4546                         fill_cmd(cmd, O_TCPWIN, 0,
4547                             htons(strtoul(*av, NULL, 0)));
4548                         ac--; av++;
4549                         break;
4550
4551                 case TOK_TCPFLAGS:
4552                         NEED1("missing argument for tcpflags");
4553                         cmd->opcode = O_TCPFLAGS;
4554                         fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av);
4555                         ac--; av++;
4556                         break;
4557
4558                 case TOK_KEEPSTATE:
4559                         if (open_par)
4560                                 errx(EX_USAGE, "keep-state cannot be part "
4561                                     "of an or block");
4562                         if (have_state)
4563                                 errx(EX_USAGE, "only one of keep-state "
4564                                         "and limit is allowed");
4565                         have_state = cmd;
4566                         fill_cmd(cmd, O_KEEP_STATE, 0, 0);
4567                         break;
4568
4569                 case TOK_LIMIT: {
4570                         ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
4571                         int val;
4572
4573                         if (open_par)
4574                                 errx(EX_USAGE,
4575                                     "limit cannot be part of an or block");
4576                         if (have_state)
4577                                 errx(EX_USAGE, "only one of keep-state and"
4578                                     "limit is allowed");
4579                         have_state = cmd;
4580
4581                         cmd->len = F_INSN_SIZE(ipfw_insn_limit);
4582                         cmd->opcode = O_LIMIT;
4583                         c->limit_mask = c->conn_limit = 0;
4584
4585                         while (ac > 0) {
4586                                 if ((val = match_token(limit_masks, *av)) <= 0)
4587                                         break;
4588                                 c->limit_mask |= val;
4589                                 ac--; av++;
4590                         }
4591
4592                         if (c->limit_mask == 0)
4593                                 errx(EX_USAGE, "limit: missing limit mask");
4594
4595                         GET_UINT_ARG(c->conn_limit, 1, 65534, TOK_LIMIT,
4596                             rule_options);
4597
4598                         ac--; av++;
4599                         break;
4600                 }
4601
4602                 case TOK_PROTO:
4603                         NEED1("missing protocol");
4604                         if (add_proto(cmd, *av, &proto)) {
4605                                 ac--; av++;
4606                         } else
4607                                 errx(EX_DATAERR, "invalid protocol ``%s''",
4608                                     *av);
4609                         break;
4610
4611                 case TOK_SRCIP:
4612                         NEED1("missing source IP");
4613                         if (add_srcip(cmd, *av)) {
4614                                 ac--; av++;
4615                         }
4616                         break;
4617
4618                 case TOK_DSTIP:
4619                         NEED1("missing destination IP");
4620                         if (add_dstip(cmd, *av)) {
4621                                 ac--; av++;
4622                         }
4623                         break;
4624
4625                 case TOK_SRCIP6:
4626                         NEED1("missing source IP6");
4627                         if (add_srcip6(cmd, *av)) {
4628                                 ac--; av++;
4629                         }
4630                         break;
4631                                 
4632                 case TOK_DSTIP6:
4633                         NEED1("missing destination IP6");
4634                         if (add_dstip6(cmd, *av)) {
4635                                 ac--; av++;
4636                         }
4637                         break;
4638
4639                 case TOK_SRCPORT:
4640                         NEED1("missing source port");
4641                         if (_substrcmp(*av, "any") == 0 ||
4642                             add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
4643                                 ac--; av++;
4644                         } else
4645                                 errx(EX_DATAERR, "invalid source port %s", *av);
4646                         break;
4647
4648                 case TOK_DSTPORT:
4649                         NEED1("missing destination port");
4650                         if (_substrcmp(*av, "any") == 0 ||
4651                             add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
4652                                 ac--; av++;
4653                         } else
4654                                 errx(EX_DATAERR, "invalid destination port %s",
4655                                     *av);
4656                         break;
4657
4658                 case TOK_MAC:
4659                         if (add_mac(cmd, ac, av)) {
4660                                 ac -= 2; av += 2;
4661                         }
4662                         break;
4663
4664                 case TOK_MACTYPE:
4665                         NEED1("missing mac type");
4666                         if (!add_mactype(cmd, ac, *av))
4667                                 errx(EX_DATAERR, "invalid mac type %s", *av);
4668                         ac--; av++;
4669                         break;
4670
4671                 case TOK_VERREVPATH:
4672                         fill_cmd(cmd, O_VERREVPATH, 0, 0);
4673                         break;
4674
4675                 case TOK_VERSRCREACH:
4676                         fill_cmd(cmd, O_VERSRCREACH, 0, 0);
4677                         break;
4678
4679                 case TOK_ANTISPOOF:
4680                         fill_cmd(cmd, O_ANTISPOOF, 0, 0);
4681                         break;
4682
4683                 case TOK_IPSEC:
4684                         fill_cmd(cmd, O_IPSEC, 0, 0);
4685                         break;
4686
4687                 case TOK_IPV6:
4688                         fill_cmd(cmd, O_IP6, 0, 0);
4689                         break;
4690
4691                 case TOK_IPV4:
4692                         fill_cmd(cmd, O_IP4, 0, 0);
4693                         break;
4694
4695                 case TOK_EXT6HDR:
4696                         fill_ext6hdr( cmd, *av );
4697                         ac--; av++;
4698                         break;
4699
4700                 case TOK_FLOWID:
4701                         if (proto != IPPROTO_IPV6 )
4702                                 errx( EX_USAGE, "flow-id filter is active "
4703                                     "only for ipv6 protocol\n");
4704                         fill_flow6( (ipfw_insn_u32 *) cmd, *av );
4705                         ac--; av++;
4706                         break;
4707
4708                 case TOK_COMMENT:
4709                         fill_comment(cmd, ac, av);
4710                         av += ac;
4711                         ac = 0;
4712                         break;
4713
4714                 case TOK_TAGGED:
4715                         if (ac > 0 && strpbrk(*av, "-,")) {
4716                                 if (!add_ports(cmd, *av, 0, O_TAGGED))
4717                                         errx(EX_DATAERR, "tagged: invalid tag"
4718                                             " list: %s", *av);
4719                         }
4720                         else {
4721                                 uint16_t tag;
4722
4723                                 GET_UINT_ARG(tag, 1, 65534, TOK_TAGGED,
4724                                     rule_options);
4725                                 fill_cmd(cmd, O_TAGGED, 0, tag);
4726                         }
4727                         ac--; av++;
4728                         break;
4729
4730                 default:
4731                         errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
4732                 }
4733                 if (F_LEN(cmd) > 0) {   /* prepare to advance */
4734                         prev = cmd;
4735                         cmd = next_cmd(cmd);
4736                 }
4737         }
4738
4739 done:
4740         /*
4741          * Now copy stuff into the rule.
4742          * If we have a keep-state option, the first instruction
4743          * must be a PROBE_STATE (which is generated here).
4744          * If we have a LOG option, it was stored as the first command,
4745          * and now must be moved to the top of the action part.
4746          */
4747         dst = (ipfw_insn *)rule->cmd;
4748
4749         /*
4750          * First thing to write into the command stream is the match probability.
4751          */
4752         if (match_prob != 1) { /* 1 means always match */
4753                 dst->opcode = O_PROB;
4754                 dst->len = 2;
4755                 *((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff);
4756                 dst += dst->len;
4757         }
4758
4759         /*
4760          * generate O_PROBE_STATE if necessary
4761          */
4762         if (have_state && have_state->opcode != O_CHECK_STATE) {
4763                 fill_cmd(dst, O_PROBE_STATE, 0, 0);
4764                 dst = next_cmd(dst);
4765         }
4766
4767         /* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */
4768         for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
4769                 i = F_LEN(src);
4770
4771                 switch (src->opcode) {
4772                 case O_LOG:
4773                 case O_KEEP_STATE:
4774                 case O_LIMIT:
4775                 case O_ALTQ:
4776                 case O_TAG:
4777                         break;
4778                 default:
4779                         bcopy(src, dst, i * sizeof(uint32_t));
4780                         dst += i;
4781                 }
4782         }
4783
4784         /*
4785          * put back the have_state command as last opcode
4786          */
4787         if (have_state && have_state->opcode != O_CHECK_STATE) {
4788                 i = F_LEN(have_state);
4789                 bcopy(have_state, dst, i * sizeof(uint32_t));
4790                 dst += i;
4791         }
4792         /*
4793          * start action section
4794          */
4795         rule->act_ofs = dst - rule->cmd;
4796
4797         /* put back O_LOG, O_ALTQ, O_TAG if necessary */
4798         if (have_log) {
4799                 i = F_LEN(have_log);
4800                 bcopy(have_log, dst, i * sizeof(uint32_t));
4801                 dst += i;
4802         }
4803         if (have_altq) {
4804                 i = F_LEN(have_altq);
4805                 bcopy(have_altq, dst, i * sizeof(uint32_t));
4806                 dst += i;
4807         }
4808         if (have_tag) {
4809                 i = F_LEN(have_tag);
4810                 bcopy(have_tag, dst, i * sizeof(uint32_t));
4811                 dst += i;
4812         }
4813         /*
4814          * copy all other actions
4815          */
4816         for (src = (ipfw_insn *)actbuf; src != action; src += i) {
4817                 i = F_LEN(src);
4818                 bcopy(src, dst, i * sizeof(uint32_t));
4819                 dst += i;
4820         }
4821
4822         rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd);
4823         i = (char *)dst - (char *)rule;
4824         if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1)
4825                 err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
4826         if (!do_quiet)
4827                 show_ipfw(rule, 0, 0);
4828 }
4829
4830 static void
4831 zero(int ac, char *av[], int optname /* IP_FW_ZERO or IP_FW_RESETLOG */)
4832 {
4833         int rulenum;
4834         int failed = EX_OK;
4835         char const *name = optname == IP_FW_ZERO ?  "ZERO" : "RESETLOG";
4836
4837         av++; ac--;
4838
4839         if (!ac) {
4840                 /* clear all entries */
4841                 if (do_cmd(optname, NULL, 0) < 0)
4842                         err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name);
4843                 if (!do_quiet)
4844                         printf("%s.\n", optname == IP_FW_ZERO ?
4845                             "Accounting cleared":"Logging counts reset");
4846
4847                 return;
4848         }
4849
4850         while (ac) {
4851                 /* Rule number */
4852                 if (isdigit(**av)) {
4853                         rulenum = atoi(*av);
4854                         av++;
4855                         ac--;
4856                         if (do_cmd(optname, &rulenum, sizeof rulenum)) {
4857                                 warn("rule %u: setsockopt(IP_FW_%s)",
4858                                     rulenum, name);
4859                                 failed = EX_UNAVAILABLE;
4860                         } else if (!do_quiet)
4861                                 printf("Entry %d %s.\n", rulenum,
4862                                     optname == IP_FW_ZERO ?
4863                                         "cleared" : "logging count reset");
4864                 } else {
4865                         errx(EX_USAGE, "invalid rule number ``%s''", *av);
4866                 }
4867         }
4868         if (failed != EX_OK)
4869                 exit(failed);
4870 }
4871
4872 static void
4873 flush(int force)
4874 {
4875         int cmd = do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH;
4876
4877         if (!force && !do_quiet) { /* need to ask user */
4878                 int c;
4879
4880                 printf("Are you sure? [yn] ");
4881                 fflush(stdout);
4882                 do {
4883                         c = toupper(getc(stdin));
4884                         while (c != '\n' && getc(stdin) != '\n')
4885                                 if (feof(stdin))
4886                                         return; /* and do not flush */
4887                 } while (c != 'Y' && c != 'N');
4888                 printf("\n");
4889                 if (c == 'N')   /* user said no */
4890                         return;
4891         }
4892         if (do_cmd(cmd, NULL, 0) < 0)
4893                 err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
4894                     do_pipe ? "DUMMYNET" : "FW");
4895         if (!do_quiet)
4896                 printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules");
4897 }
4898
4899 /*
4900  * Free a the (locally allocated) copy of command line arguments.
4901  */
4902 static void
4903 free_args(int ac, char **av)
4904 {
4905         int i;
4906
4907         for (i=0; i < ac; i++)
4908                 free(av[i]);
4909         free(av);
4910 }
4911
4912 /*
4913  * This one handles all table-related commands
4914  *      ipfw table N add addr[/masklen] [value]
4915  *      ipfw table N delete addr[/masklen]
4916  *      ipfw table N flush
4917  *      ipfw table N list
4918  */
4919 static void
4920 table_handler(int ac, char *av[])
4921 {
4922         ipfw_table_entry ent;
4923         ipfw_table *tbl;
4924         int do_add;
4925         char *p;
4926         socklen_t l;
4927         uint32_t a;
4928
4929         ac--; av++;
4930         if (ac && isdigit(**av)) {
4931                 ent.tbl = atoi(*av);
4932                 ac--; av++;
4933         } else
4934                 errx(EX_USAGE, "table number required");
4935         NEED1("table needs command");
4936         if (_substrcmp(*av, "add") == 0 ||
4937             _substrcmp(*av, "delete") == 0) {
4938                 do_add = **av == 'a';
4939                 ac--; av++;
4940                 if (!ac)
4941                         errx(EX_USAGE, "IP address required");
4942                 p = strchr(*av, '/');
4943                 if (p) {
4944                         *p++ = '\0';
4945                         ent.masklen = atoi(p);
4946                         if (ent.masklen > 32)
4947                                 errx(EX_DATAERR, "bad width ``%s''", p);
4948                 } else
4949                         ent.masklen = 32;
4950                 if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0)
4951                         errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
4952                 ac--; av++;
4953                 if (do_add && ac) {
4954                         unsigned int tval;
4955                         /* isdigit is a bit of a hack here.. */
4956                         if (strchr(*av, (int)'.') == NULL && isdigit(**av))  {
4957                                 ent.value = strtoul(*av, NULL, 0);
4958                         } else {
4959                                 if (lookup_host(*av, (struct in_addr *)&tval) == 0) {
4960                                         /* The value must be stored in host order        *
4961                                          * so that the values < 65k can be distinguished */
4962                                         ent.value = ntohl(tval); 
4963                                 } else {
4964                                         errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
4965                                 }
4966                         }
4967                 } else
4968                         ent.value = 0;
4969                 if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL,
4970                     &ent, sizeof(ent)) < 0) {
4971                         /* If running silent, don't bomb out on these errors. */
4972                         if (!(do_quiet && (errno == (do_add ? EEXIST : ESRCH))))
4973                                 err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
4974                                     do_add ? "ADD" : "DEL");
4975                         /* In silent mode, react to a failed add by deleting */
4976                         if (do_add) {
4977                                 do_cmd(IP_FW_TABLE_DEL, &ent, sizeof(ent));
4978                                 if (do_cmd(IP_FW_TABLE_ADD,
4979                                     &ent, sizeof(ent)) < 0)
4980                                         err(EX_OSERR,
4981                                             "setsockopt(IP_FW_TABLE_ADD)");
4982                         }
4983                 }
4984         } else if (_substrcmp(*av, "flush") == 0) {
4985                 if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl, sizeof(ent.tbl)) < 0)
4986                         err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
4987         } else if (_substrcmp(*av, "list") == 0) {
4988                 a = ent.tbl;
4989                 l = sizeof(a);
4990                 if (do_cmd(IP_FW_TABLE_GETSIZE, &a, (uintptr_t)&l) < 0)
4991                         err(EX_OSERR, "getsockopt(IP_FW_TABLE_GETSIZE)");
4992                 l = sizeof(*tbl) + a * sizeof(ipfw_table_entry);
4993                 tbl = malloc(l);
4994                 if (tbl == NULL)
4995                         err(EX_OSERR, "malloc");
4996                 tbl->tbl = ent.tbl;
4997                 if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0)
4998                         err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)");
4999                 for (a = 0; a < tbl->cnt; a++) {
5000                         /* Heuristic to print it the right way */
5001                         /* values < 64k are printed as numbers */
5002                         unsigned int tval;
5003                         tval = tbl->ent[a].value;
5004                         if (tval > 0xffff) {
5005                             char tbuf[128];
5006                             strncpy(tbuf, inet_ntoa(*(struct in_addr *)
5007                                 &tbl->ent[a].addr), 127);
5008                             /* inet_ntoa expects host order */
5009                             tval = htonl(tval);
5010                             printf("%s/%u %s\n", tbuf, tbl->ent[a].masklen,
5011                                 inet_ntoa(*(struct in_addr *)&tval));
5012                         } else {
5013                             printf("%s/%u %u\n",
5014                                 inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
5015                                 tbl->ent[a].masklen, tbl->ent[a].value);
5016                         }
5017                 }
5018         } else
5019                 errx(EX_USAGE, "invalid table command %s", *av);
5020 }
5021
5022 /*
5023  * Called with the arguments (excluding program name).
5024  * Returns 0 if successful, 1 if empty command, errx() in case of errors.
5025  */
5026 static int
5027 ipfw_main(int oldac, char **oldav)
5028 {
5029         int ch, ac, save_ac;
5030         char **av, **save_av;
5031         int do_acct = 0;                /* Show packet/byte count */
5032
5033 #define WHITESP         " \t\f\v\n\r"
5034         if (oldac == 0)
5035                 return 1;
5036         else if (oldac == 1) {
5037                 /*
5038                  * If we are called with a single string, try to split it into
5039                  * arguments for subsequent parsing.
5040                  * But first, remove spaces after a ',', by copying the string
5041                  * in-place.
5042                  */
5043                 char *arg = oldav[0];   /* The string... */
5044                 int l = strlen(arg);
5045                 int copy = 0;           /* 1 if we need to copy, 0 otherwise */
5046                 int i, j;
5047                 for (i = j = 0; i < l; i++) {
5048                         if (arg[i] == '#')      /* comment marker */
5049                                 break;
5050                         if (copy) {
5051                                 arg[j++] = arg[i];
5052                                 copy = !index("," WHITESP, arg[i]);
5053                         } else {
5054                                 copy = !index(WHITESP, arg[i]);
5055                                 if (copy)
5056                                         arg[j++] = arg[i];
5057                         }
5058                 }
5059                 if (!copy && j > 0)     /* last char was a 'blank', remove it */
5060                         j--;
5061                 l = j;                  /* the new argument length */
5062                 arg[j++] = '\0';
5063                 if (l == 0)             /* empty string! */
5064                         return 1;
5065
5066                 /*
5067                  * First, count number of arguments. Because of the previous
5068                  * processing, this is just the number of blanks plus 1.
5069                  */
5070                 for (i = 0, ac = 1; i < l; i++)
5071                         if (index(WHITESP, arg[i]) != NULL)
5072                                 ac++;
5073
5074                 av = calloc(ac, sizeof(char *));
5075
5076                 /*
5077                  * Second, copy arguments from cmd[] to av[]. For each one,
5078                  * j is the initial character, i is the one past the end.
5079                  */
5080                 for (ac = 0, i = j = 0; i < l; i++)
5081                         if (index(WHITESP, arg[i]) != NULL || i == l-1) {
5082                                 if (i == l-1)
5083                                         i++;
5084                                 av[ac] = calloc(i-j+1, 1);
5085                                 bcopy(arg+j, av[ac], i-j);
5086                                 ac++;
5087                                 j = i + 1;
5088                         }
5089         } else {
5090                 /*
5091                  * If an argument ends with ',' join with the next one.
5092                  */
5093                 int first, i, l;
5094
5095                 av = calloc(oldac, sizeof(char *));
5096                 for (first = i = ac = 0, l = 0; i < oldac; i++) {
5097                         char *arg = oldav[i];
5098                         int k = strlen(arg);
5099
5100                         l += k;
5101                         if (arg[k-1] != ',' || i == oldac-1) {
5102                                 /* Time to copy. */
5103                                 av[ac] = calloc(l+1, 1);
5104                                 for (l=0; first <= i; first++) {
5105                                         strcat(av[ac]+l, oldav[first]);
5106                                         l += strlen(oldav[first]);
5107                                 }
5108                                 ac++;
5109                                 l = 0;
5110                                 first = i+1;
5111                         }
5112                 }
5113         }
5114
5115         /* Set the force flag for non-interactive processes */
5116         if (!do_force)
5117                 do_force = !isatty(STDIN_FILENO);
5118
5119         /* Save arguments for final freeing of memory. */
5120         save_ac = ac;
5121         save_av = av;
5122
5123         optind = optreset = 0;
5124         while ((ch = getopt(ac, av, "abcdefhnNqs:STtv")) != -1)
5125                 switch (ch) {
5126                 case 'a':
5127                         do_acct = 1;
5128                         break;
5129
5130                 case 'b':
5131                         comment_only = 1;
5132                         do_compact = 1;
5133                         break;
5134
5135                 case 'c':
5136                         do_compact = 1;
5137                         break;
5138
5139                 case 'd':
5140                         do_dynamic = 1;
5141                         break;
5142
5143                 case 'e':
5144                         do_expired = 1;
5145                         break;
5146
5147                 case 'f':
5148                         do_force = 1;
5149                         break;
5150
5151                 case 'h': /* help */
5152                         free_args(save_ac, save_av);
5153                         help();
5154                         break;  /* NOTREACHED */
5155
5156                 case 'n':
5157                         test_only = 1;
5158                         break;
5159
5160                 case 'N':
5161                         do_resolv = 1;
5162                         break;
5163
5164                 case 'q':
5165                         do_quiet = 1;
5166                         break;
5167
5168                 case 's': /* sort */
5169                         do_sort = atoi(optarg);
5170                         break;
5171
5172                 case 'S':
5173                         show_sets = 1;
5174                         break;
5175
5176                 case 't':
5177                         do_time = 1;
5178                         break;
5179
5180                 case 'T':
5181                         do_time = 2;    /* numeric timestamp */
5182                         break;
5183
5184                 case 'v': /* verbose */
5185                         verbose = 1;
5186                         break;
5187
5188                 default:
5189                         free_args(save_ac, save_av);
5190                         return 1;
5191                 }
5192
5193         ac -= optind;
5194         av += optind;
5195         NEED1("bad arguments, for usage summary ``ipfw''");
5196
5197         /*
5198          * An undocumented behaviour of ipfw1 was to allow rule numbers first,
5199          * e.g. "100 add allow ..." instead of "add 100 allow ...".
5200          * In case, swap first and second argument to get the normal form.
5201          */
5202         if (ac > 1 && isdigit(*av[0])) {
5203                 char *p = av[0];
5204
5205                 av[0] = av[1];
5206                 av[1] = p;
5207         }
5208
5209         /*
5210          * optional: pipe or queue
5211          */
5212         do_pipe = 0;
5213         if (_substrcmp(*av, "pipe") == 0)
5214                 do_pipe = 1;
5215         else if (_substrcmp(*av, "queue") == 0)
5216                 do_pipe = 2;
5217         if (do_pipe) {
5218                 ac--;
5219                 av++;
5220         }
5221         NEED1("missing command");
5222
5223         /*
5224          * For pipes and queues we normally say 'pipe NN config'
5225          * but the code is easier to parse as 'pipe config NN'
5226          * so we swap the two arguments.
5227          */
5228         if (do_pipe > 0 && ac > 1 && isdigit(*av[0])) {
5229                 char *p = av[0];
5230
5231                 av[0] = av[1];
5232                 av[1] = p;
5233         }
5234
5235         if (_substrcmp(*av, "add") == 0)
5236                 add(ac, av);
5237         else if (do_pipe && _substrcmp(*av, "config") == 0)
5238                 config_pipe(ac, av);
5239         else if (_substrcmp(*av, "delete") == 0)
5240                 delete(ac, av);
5241         else if (_substrcmp(*av, "flush") == 0)
5242                 flush(do_force);
5243         else if (_substrcmp(*av, "zero") == 0)
5244                 zero(ac, av, IP_FW_ZERO);
5245         else if (_substrcmp(*av, "resetlog") == 0)
5246                 zero(ac, av, IP_FW_RESETLOG);
5247         else if (_substrcmp(*av, "print") == 0 ||
5248                  _substrcmp(*av, "list") == 0)
5249                 list(ac, av, do_acct);
5250         else if (_substrcmp(*av, "set") == 0)
5251                 sets_handler(ac, av);
5252         else if (_substrcmp(*av, "table") == 0)
5253                 table_handler(ac, av);
5254         else if (_substrcmp(*av, "enable") == 0)
5255                 sysctl_handler(ac, av, 1);
5256         else if (_substrcmp(*av, "disable") == 0)
5257                 sysctl_handler(ac, av, 0);
5258         else if (_substrcmp(*av, "show") == 0)
5259                 list(ac, av, 1 /* show counters */);
5260         else
5261                 errx(EX_USAGE, "bad command `%s'", *av);
5262
5263         /* Free memory allocated in the argument parsing. */
5264         free_args(save_ac, save_av);
5265         return 0;
5266 }
5267
5268
5269 static void
5270 ipfw_readfile(int ac, char *av[])
5271 {
5272 #define MAX_ARGS        32
5273         char    buf[BUFSIZ];
5274         char    *cmd = NULL, *filename = av[ac-1];
5275         int     c, lineno=0;
5276         FILE    *f = NULL;
5277         pid_t   preproc = 0;
5278
5279         filename = av[ac-1];
5280
5281         while ((c = getopt(ac, av, "cfNnp:qS")) != -1) {
5282                 switch(c) {
5283                 case 'c':
5284                         do_compact = 1;
5285                         break;
5286
5287                 case 'f':
5288                         do_force = 1;
5289                         break;
5290
5291                 case 'N':
5292                         do_resolv = 1;
5293                         break;
5294
5295                 case 'n':
5296                         test_only = 1;
5297                         break;
5298
5299                 case 'p':
5300                         cmd = optarg;
5301                         /*
5302                          * Skip previous args and delete last one, so we
5303                          * pass all but the last argument to the preprocessor
5304                          * via av[optind-1]
5305                          */
5306                         av += optind - 1;
5307                         ac -= optind - 1;
5308                         av[ac-1] = NULL;
5309                         fprintf(stderr, "command is %s\n", av[0]);
5310                         break;
5311
5312                 case 'q':
5313                         do_quiet = 1;
5314                         break;
5315
5316                 case 'S':
5317                         show_sets = 1;
5318                         break;
5319
5320                 default:
5321                         errx(EX_USAGE, "bad arguments, for usage"
5322                              " summary ``ipfw''");
5323                 }
5324
5325                 if (cmd != NULL)
5326                         break;
5327         }
5328
5329         if (cmd == NULL && ac != optind + 1) {
5330                 fprintf(stderr, "ac %d, optind %d\n", ac, optind);
5331                 errx(EX_USAGE, "extraneous filename arguments");
5332         }
5333
5334         if ((f = fopen(filename, "r")) == NULL)
5335                 err(EX_UNAVAILABLE, "fopen: %s", filename);
5336
5337         if (cmd != NULL) {                      /* pipe through preprocessor */
5338                 int pipedes[2];
5339
5340                 if (pipe(pipedes) == -1)
5341                         err(EX_OSERR, "cannot create pipe");
5342
5343                 preproc = fork();
5344                 if (preproc == -1)
5345                         err(EX_OSERR, "cannot fork");
5346
5347                 if (preproc == 0) {
5348                         /*
5349                          * Child, will run the preprocessor with the
5350                          * file on stdin and the pipe on stdout.
5351                          */
5352                         if (dup2(fileno(f), 0) == -1
5353                             || dup2(pipedes[1], 1) == -1)
5354                                 err(EX_OSERR, "dup2()");
5355                         fclose(f);
5356                         close(pipedes[1]);
5357                         close(pipedes[0]);
5358                         execvp(cmd, av);
5359                         err(EX_OSERR, "execvp(%s) failed", cmd);
5360                 } else { /* parent, will reopen f as the pipe */
5361                         fclose(f);
5362                         close(pipedes[1]);
5363                         if ((f = fdopen(pipedes[0], "r")) == NULL) {
5364                                 int savederrno = errno;
5365
5366                                 (void)kill(preproc, SIGTERM);
5367                                 errno = savederrno;
5368                                 err(EX_OSERR, "fdopen()");
5369                         }
5370                 }
5371         }
5372
5373         while (fgets(buf, BUFSIZ, f)) {         /* read commands */
5374                 char linename[10];
5375                 char *args[1];
5376
5377                 lineno++;
5378                 sprintf(linename, "Line %d", lineno);
5379                 setprogname(linename); /* XXX */
5380                 args[0] = buf;
5381                 ipfw_main(1, args);
5382         }
5383         fclose(f);
5384         if (cmd != NULL) {
5385                 int status;
5386
5387                 if (waitpid(preproc, &status, 0) == -1)
5388                         errx(EX_OSERR, "waitpid()");
5389                 if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
5390                         errx(EX_UNAVAILABLE,
5391                             "preprocessor exited with status %d",
5392                             WEXITSTATUS(status));
5393                 else if (WIFSIGNALED(status))
5394                         errx(EX_UNAVAILABLE,
5395                             "preprocessor exited with signal %d",
5396                             WTERMSIG(status));
5397         }
5398 }
5399
5400 int
5401 main(int ac, char *av[])
5402 {
5403         /*
5404          * If the last argument is an absolute pathname, interpret it
5405          * as a file to be preprocessed.
5406          */
5407
5408         if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0)
5409                 ipfw_readfile(ac, av);
5410         else {
5411                 if (ipfw_main(ac-1, av+1))
5412                         show_usage();
5413         }
5414         return EX_OK;
5415 }