]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/ipfw/ipfw2.c
This commit was generated by cvs2svn to compensate for changes in r163799,
[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        bzero(cmd, sizeof(*cmd));
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     if (len + 1 > F_LEN_MASK)
2863         errx(EX_DATAERR, "address list too long");
2864     cmd->o.len |= len+1;
2865 }
2866
2867
2868 /* Try to find ipv6 address by hostname */
2869 static int
2870 lookup_host6 (char *host, struct in6_addr *ip6addr)
2871 {
2872         struct hostent *he;
2873
2874         if (!inet_pton(AF_INET6, host, ip6addr)) {
2875                 if ((he = gethostbyname2(host, AF_INET6)) == NULL)
2876                         return(-1);
2877                 memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr));
2878         }
2879         return(0);
2880 }
2881
2882
2883 /* n2mask sets n bits of the mask */
2884 static void
2885 n2mask(struct in6_addr *mask, int n)
2886 {
2887         static int      minimask[9] =
2888             { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
2889         u_char          *p;
2890
2891         memset(mask, 0, sizeof(struct in6_addr));
2892         p = (u_char *) mask;
2893         for (; n > 0; p++, n -= 8) {
2894                 if (n >= 8)
2895                         *p = 0xff;
2896                 else
2897                         *p = minimask[n];
2898         }
2899         return;
2900 }
2901  
2902
2903 /*
2904  * fill the addr and mask fields in the instruction as appropriate from av.
2905  * Update length as appropriate.
2906  * The following formats are allowed:
2907  *     any     matches any IP6. Actually returns an empty instruction.
2908  *     me      returns O_IP6_*_ME
2909  *
2910  *     03f1::234:123:0342                single IP6 addres
2911  *     03f1::234:123:0342/24            address/mask
2912  *     03f1::234:123:0342/24,03f1::234:123:0343/               List of address
2913  *
2914  * Set of address (as in ipv6) not supported because ipv6 address
2915  * are typically random past the initial prefix.
2916  * Return 1 on success, 0 on failure.
2917  */
2918 static int
2919 fill_ip6(ipfw_insn_ip6 *cmd, char *av)
2920 {
2921         int len = 0;
2922         struct in6_addr *d = &(cmd->addr6);
2923         /*
2924          * Needed for multiple address.
2925          * Note d[1] points to struct in6_add r mask6 of cmd
2926          */
2927
2928        cmd->o.len &= ~F_LEN_MASK;       /* zero len */
2929
2930        if (strcmp(av, "any") == 0)
2931                return (1);
2932
2933
2934        if (strcmp(av, "me") == 0) {     /* Set the data for "me" opt*/
2935                cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2936                return (1);
2937        }
2938
2939        if (strcmp(av, "me6") == 0) {    /* Set the data for "me" opt*/
2940                cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2941                return (1);
2942        }
2943
2944        av = strdup(av);
2945        while (av) {
2946                 /*
2947                  * After the address we can have '/' indicating a mask,
2948                  * or ',' indicating another address follows.
2949                  */
2950
2951                 char *p;
2952                 int masklen;
2953                 char md = '\0';
2954
2955                 if ((p = strpbrk(av, "/,")) ) {
2956                         md = *p;        /* save the separator */
2957                         *p = '\0';      /* terminate address string */
2958                         p++;            /* and skip past it */
2959                 }
2960                 /* now p points to NULL, mask or next entry */
2961
2962                 /* lookup stores address in *d as a side effect */
2963                 if (lookup_host6(av, d) != 0) {
2964                         /* XXX: failed. Free memory and go */
2965                         errx(EX_DATAERR, "bad address \"%s\"", av);
2966                 }
2967                 /* next, look at the mask, if any */
2968                 masklen = (md == '/') ? atoi(p) : 128;
2969                 if (masklen > 128 || masklen < 0)
2970                         errx(EX_DATAERR, "bad width \"%s\''", p);
2971                 else
2972                         n2mask(&d[1], masklen);
2973
2974                 APPLY_MASK(d, &d[1])   /* mask base address with mask */
2975
2976                 /* find next separator */
2977
2978                 if (md == '/') {        /* find separator past the mask */
2979                         p = strpbrk(p, ",");
2980                         if (p != NULL)
2981                                 p++;
2982                 }
2983                 av = p;
2984
2985                 /* Check this entry */
2986                 if (masklen == 0) {
2987                         /*
2988                          * 'any' turns the entire list into a NOP.
2989                          * 'not any' never matches, so it is removed from the
2990                          * list unless it is the only item, in which case we
2991                          * report an error.
2992                          */
2993                         if (cmd->o.len & F_NOT && av == NULL && len == 0)
2994                                 errx(EX_DATAERR, "not any never matches");
2995                         continue;
2996                 }
2997
2998                 /*
2999                  * A single IP can be stored alone
3000                  */
3001                 if (masklen == 128 && av == NULL && len == 0) {
3002                         len = F_INSN_SIZE(struct in6_addr);
3003                         break;
3004                 }
3005
3006                 /* Update length and pointer to arguments */
3007                 len += F_INSN_SIZE(struct in6_addr)*2;
3008                 d += 2;
3009         } /* end while */
3010
3011         /*
3012          * Total length of the command, remember that 1 is the size of
3013          * the base command.
3014          */
3015         if (len + 1 > F_LEN_MASK)
3016                 errx(EX_DATAERR, "address list too long");
3017         cmd->o.len |= len+1;
3018         free(av);
3019         return (1);
3020 }
3021
3022 /*
3023  * fills command for ipv6 flow-id filtering
3024  * note that the 20 bit flow number is stored in a array of u_int32_t
3025  * it's supported lists of flow-id, so in the o.arg1 we store how many
3026  * additional flow-id we want to filter, the basic is 1
3027  */
3028 void
3029 fill_flow6( ipfw_insn_u32 *cmd, char *av )
3030 {
3031         u_int32_t type;  /* Current flow number */
3032         u_int16_t nflow = 0;    /* Current flow index */
3033         char *s = av;
3034         cmd->d[0] = 0;    /* Initializing the base number*/
3035
3036         while (s) {
3037                 av = strsep( &s, ",") ;
3038                 type = strtoul(av, &av, 0);
3039                 if (*av != ',' && *av != '\0')
3040                         errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
3041                 if (type > 0xfffff)
3042                         errx(EX_DATAERR, "flow number out of range %s", av);
3043                 cmd->d[nflow] |= type;
3044                 nflow++;
3045         }
3046         if( nflow > 0 ) {
3047                 cmd->o.opcode = O_FLOW6ID;
3048                 cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow;
3049                 cmd->o.arg1 = nflow;
3050         }
3051         else {
3052                 errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
3053         }
3054 }
3055
3056 static ipfw_insn *
3057 add_srcip6(ipfw_insn *cmd, char *av)
3058 {
3059
3060         fill_ip6((ipfw_insn_ip6 *)cmd, av);
3061         if (F_LEN(cmd) == 0)                            /* any */
3062                 ;
3063         if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) {     /* "me" */
3064                 cmd->opcode = O_IP6_SRC_ME;
3065         } else if (F_LEN(cmd) ==
3066             (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
3067                 /* single IP, no mask*/
3068                 cmd->opcode = O_IP6_SRC;
3069         } else {                                        /* addr/mask opt */
3070                 cmd->opcode = O_IP6_SRC_MASK;
3071         }
3072         return cmd;
3073 }
3074
3075 static ipfw_insn *
3076 add_dstip6(ipfw_insn *cmd, char *av)
3077 {
3078
3079         fill_ip6((ipfw_insn_ip6 *)cmd, av);
3080         if (F_LEN(cmd) == 0)                            /* any */
3081                 ;
3082         if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) {     /* "me" */
3083                 cmd->opcode = O_IP6_DST_ME;
3084         } else if (F_LEN(cmd) ==
3085             (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
3086                 /* single IP, no mask*/
3087                 cmd->opcode = O_IP6_DST;
3088         } else {                                        /* addr/mask opt */
3089                 cmd->opcode = O_IP6_DST_MASK;
3090         }
3091         return cmd;
3092 }
3093
3094
3095 /*
3096  * helper function to process a set of flags and set bits in the
3097  * appropriate masks.
3098  */
3099 static void
3100 fill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
3101         struct _s_x *flags, char *p)
3102 {
3103         uint8_t set=0, clear=0;
3104
3105         while (p && *p) {
3106                 char *q;        /* points to the separator */
3107                 int val;
3108                 uint8_t *which; /* mask we are working on */
3109
3110                 if (*p == '!') {
3111                         p++;
3112                         which = &clear;
3113                 } else
3114                         which = &set;
3115                 q = strchr(p, ',');
3116                 if (q)
3117                         *q++ = '\0';
3118                 val = match_token(flags, p);
3119                 if (val <= 0)
3120                         errx(EX_DATAERR, "invalid flag %s", p);
3121                 *which |= (uint8_t)val;
3122                 p = q;
3123         }
3124         cmd->opcode = opcode;
3125         cmd->len =  (cmd->len & (F_NOT | F_OR)) | 1;
3126         cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
3127 }
3128
3129
3130 static void
3131 delete(int ac, char *av[])
3132 {
3133         uint32_t rulenum;
3134         struct dn_pipe p;
3135         int i;
3136         int exitval = EX_OK;
3137         int do_set = 0;
3138
3139         memset(&p, 0, sizeof p);
3140
3141         av++; ac--;
3142         NEED1("missing rule specification");
3143         if (ac > 0 && _substrcmp(*av, "set") == 0) {
3144                 do_set = 1;     /* delete set */
3145                 ac--; av++;
3146         }
3147
3148         /* Rule number */
3149         while (ac && isdigit(**av)) {
3150                 i = atoi(*av); av++; ac--;
3151                 if (do_pipe) {
3152                         if (do_pipe == 1)
3153                                 p.pipe_nr = i;
3154                         else
3155                                 p.fs.fs_nr = i;
3156                         i = do_cmd(IP_DUMMYNET_DEL, &p, sizeof p);
3157                         if (i) {
3158                                 exitval = 1;
3159                                 warn("rule %u: setsockopt(IP_DUMMYNET_DEL)",
3160                                     do_pipe == 1 ? p.pipe_nr : p.fs.fs_nr);
3161                         }
3162                 } else {
3163                         rulenum =  (i & 0xffff) | (do_set << 24);
3164                         i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum);
3165                         if (i) {
3166                                 exitval = EX_UNAVAILABLE;
3167                                 warn("rule %u: setsockopt(IP_FW_DEL)",
3168                                     rulenum);
3169                         }
3170                 }
3171         }
3172         if (exitval != EX_OK)
3173                 exit(exitval);
3174 }
3175
3176
3177 /*
3178  * fill the interface structure. We do not check the name as we can
3179  * create interfaces dynamically, so checking them at insert time
3180  * makes relatively little sense.
3181  * Interface names containing '*', '?', or '[' are assumed to be shell 
3182  * patterns which match interfaces.
3183  */
3184 static void
3185 fill_iface(ipfw_insn_if *cmd, char *arg)
3186 {
3187         cmd->name[0] = '\0';
3188         cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
3189
3190         /* Parse the interface or address */
3191         if (strcmp(arg, "any") == 0)
3192                 cmd->o.len = 0;         /* effectively ignore this command */
3193         else if (!isdigit(*arg)) {
3194                 strlcpy(cmd->name, arg, sizeof(cmd->name));
3195                 cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
3196         } else if (!inet_aton(arg, &cmd->p.ip))
3197                 errx(EX_DATAERR, "bad ip address ``%s''", arg);
3198 }
3199
3200 static void
3201 config_pipe(int ac, char **av)
3202 {
3203         struct dn_pipe p;
3204         int i;
3205         char *end;
3206         void *par = NULL;
3207
3208         memset(&p, 0, sizeof p);
3209
3210         av++; ac--;
3211         /* Pipe number */
3212         if (ac && isdigit(**av)) {
3213                 i = atoi(*av); av++; ac--;
3214                 if (do_pipe == 1)
3215                         p.pipe_nr = i;
3216                 else
3217                         p.fs.fs_nr = i;
3218         }
3219         while (ac > 0) {
3220                 double d;
3221                 int tok = match_token(dummynet_params, *av);
3222                 ac--; av++;
3223
3224                 switch(tok) {
3225                 case TOK_NOERROR:
3226                         p.fs.flags_fs |= DN_NOERROR;
3227                         break;
3228
3229                 case TOK_PLR:
3230                         NEED1("plr needs argument 0..1\n");
3231                         d = strtod(av[0], NULL);
3232                         if (d > 1)
3233                                 d = 1;
3234                         else if (d < 0)
3235                                 d = 0;
3236                         p.fs.plr = (int)(d*0x7fffffff);
3237                         ac--; av++;
3238                         break;
3239
3240                 case TOK_QUEUE:
3241                         NEED1("queue needs queue size\n");
3242                         end = NULL;
3243                         p.fs.qsize = strtoul(av[0], &end, 0);
3244                         if (*end == 'K' || *end == 'k') {
3245                                 p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
3246                                 p.fs.qsize *= 1024;
3247                         } else if (*end == 'B' ||
3248                             _substrcmp2(end, "by", "bytes") == 0) {
3249                                 p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
3250                         }
3251                         ac--; av++;
3252                         break;
3253
3254                 case TOK_BUCKETS:
3255                         NEED1("buckets needs argument\n");
3256                         p.fs.rq_size = strtoul(av[0], NULL, 0);
3257                         ac--; av++;
3258                         break;
3259
3260                 case TOK_MASK:
3261                         NEED1("mask needs mask specifier\n");
3262                         /*
3263                          * per-flow queue, mask is dst_ip, dst_port,
3264                          * src_ip, src_port, proto measured in bits
3265                          */
3266                         par = NULL;
3267
3268                         bzero(&p.fs.flow_mask, sizeof(p.fs.flow_mask));
3269                         end = NULL;
3270
3271                         while (ac >= 1) {
3272                             uint32_t *p32 = NULL;
3273                             uint16_t *p16 = NULL;
3274                             uint32_t *p20 = NULL;
3275                             struct in6_addr *pa6 = NULL;
3276                             uint32_t a;
3277
3278                             tok = match_token(dummynet_params, *av);
3279                             ac--; av++;
3280                             switch(tok) {
3281                             case TOK_ALL:
3282                                     /*
3283                                      * special case, all bits significant
3284                                      */
3285                                     p.fs.flow_mask.dst_ip = ~0;
3286                                     p.fs.flow_mask.src_ip = ~0;
3287                                     p.fs.flow_mask.dst_port = ~0;
3288                                     p.fs.flow_mask.src_port = ~0;
3289                                     p.fs.flow_mask.proto = ~0;
3290                                     n2mask(&(p.fs.flow_mask.dst_ip6), 128);
3291                                     n2mask(&(p.fs.flow_mask.src_ip6), 128);
3292                                     p.fs.flow_mask.flow_id6 = ~0;
3293                                     p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
3294                                     goto end_mask;
3295
3296                             case TOK_DSTIP:
3297                                     p32 = &p.fs.flow_mask.dst_ip;
3298                                     break;
3299
3300                             case TOK_SRCIP:
3301                                     p32 = &p.fs.flow_mask.src_ip;
3302                                     break;
3303
3304                             case TOK_DSTIP6:
3305                                     pa6 = &(p.fs.flow_mask.dst_ip6);
3306                                     break;
3307                             
3308                             case TOK_SRCIP6:
3309                                     pa6 = &(p.fs.flow_mask.src_ip6);
3310                                     break;
3311
3312                             case TOK_FLOWID:
3313                                     p20 = &p.fs.flow_mask.flow_id6;
3314                                     break;
3315
3316                             case TOK_DSTPORT:
3317                                     p16 = &p.fs.flow_mask.dst_port;
3318                                     break;
3319
3320                             case TOK_SRCPORT:
3321                                     p16 = &p.fs.flow_mask.src_port;
3322                                     break;
3323
3324                             case TOK_PROTO:
3325                                     break;
3326
3327                             default:
3328                                     ac++; av--; /* backtrack */
3329                                     goto end_mask;
3330                             }
3331                             if (ac < 1)
3332                                     errx(EX_USAGE, "mask: value missing");
3333                             if (*av[0] == '/') {
3334                                     a = strtoul(av[0]+1, &end, 0);
3335                                     if (pa6 == NULL)
3336                                             a = (a == 32) ? ~0 : (1 << a) - 1;
3337                             } else
3338                                     a = strtoul(av[0], &end, 0);
3339                             if (p32 != NULL)
3340                                     *p32 = a;
3341                             else if (p16 != NULL) {
3342                                     if (a > 0xFFFF)
3343                                             errx(EX_DATAERR,
3344                                                 "port mask must be 16 bit");
3345                                     *p16 = (uint16_t)a;
3346                             } else if (p20 != NULL) {
3347                                     if (a > 0xfffff)
3348                                         errx(EX_DATAERR,
3349                                             "flow_id mask must be 20 bit");
3350                                     *p20 = (uint32_t)a;
3351                             } else if (pa6 != NULL) {
3352                                     if (a < 0 || a > 128)
3353                                         errx(EX_DATAERR,
3354                                             "in6addr invalid mask len");
3355                                     else
3356                                         n2mask(pa6, a);
3357                             } else {
3358                                     if (a > 0xFF)
3359                                             errx(EX_DATAERR,
3360                                                 "proto mask must be 8 bit");
3361                                     p.fs.flow_mask.proto = (uint8_t)a;
3362                             }
3363                             if (a != 0)
3364                                     p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
3365                             ac--; av++;
3366                         } /* end while, config masks */
3367 end_mask:
3368                         break;
3369
3370                 case TOK_RED:
3371                 case TOK_GRED:
3372                         NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
3373                         p.fs.flags_fs |= DN_IS_RED;
3374                         if (tok == TOK_GRED)
3375                                 p.fs.flags_fs |= DN_IS_GENTLE_RED;
3376                         /*
3377                          * the format for parameters is w_q/min_th/max_th/max_p
3378                          */
3379                         if ((end = strsep(&av[0], "/"))) {
3380                             double w_q = strtod(end, NULL);
3381                             if (w_q > 1 || w_q <= 0)
3382                                 errx(EX_DATAERR, "0 < w_q <= 1");
3383                             p.fs.w_q = (int) (w_q * (1 << SCALE_RED));
3384                         }
3385                         if ((end = strsep(&av[0], "/"))) {
3386                             p.fs.min_th = strtoul(end, &end, 0);
3387                             if (*end == 'K' || *end == 'k')
3388                                 p.fs.min_th *= 1024;
3389                         }
3390                         if ((end = strsep(&av[0], "/"))) {
3391                             p.fs.max_th = strtoul(end, &end, 0);
3392                             if (*end == 'K' || *end == 'k')
3393                                 p.fs.max_th *= 1024;
3394                         }
3395                         if ((end = strsep(&av[0], "/"))) {
3396                             double max_p = strtod(end, NULL);
3397                             if (max_p > 1 || max_p <= 0)
3398                                 errx(EX_DATAERR, "0 < max_p <= 1");
3399                             p.fs.max_p = (int)(max_p * (1 << SCALE_RED));
3400                         }
3401                         ac--; av++;
3402                         break;
3403
3404                 case TOK_DROPTAIL:
3405                         p.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
3406                         break;
3407
3408                 case TOK_BW:
3409                         NEED1("bw needs bandwidth or interface\n");
3410                         if (do_pipe != 1)
3411                             errx(EX_DATAERR, "bandwidth only valid for pipes");
3412                         /*
3413                          * set clocking interface or bandwidth value
3414                          */
3415                         if (av[0][0] >= 'a' && av[0][0] <= 'z') {
3416                             int l = sizeof(p.if_name)-1;
3417                             /* interface name */
3418                             strncpy(p.if_name, av[0], l);
3419                             p.if_name[l] = '\0';
3420                             p.bandwidth = 0;
3421                         } else {
3422                             p.if_name[0] = '\0';
3423                             p.bandwidth = strtoul(av[0], &end, 0);
3424                             if (*end == 'K' || *end == 'k') {
3425                                 end++;
3426                                 p.bandwidth *= 1000;
3427                             } else if (*end == 'M') {
3428                                 end++;
3429                                 p.bandwidth *= 1000000;
3430                             }
3431                             if ((*end == 'B' &&
3432                                   _substrcmp2(end, "Bi", "Bit/s") != 0) ||
3433                                 _substrcmp2(end, "by", "bytes") == 0)
3434                                 p.bandwidth *= 8;
3435                             if (p.bandwidth < 0)
3436                                 errx(EX_DATAERR, "bandwidth too large");
3437                         }
3438                         ac--; av++;
3439                         break;
3440
3441                 case TOK_DELAY:
3442                         if (do_pipe != 1)
3443                                 errx(EX_DATAERR, "delay only valid for pipes");
3444                         NEED1("delay needs argument 0..10000ms\n");
3445                         p.delay = strtoul(av[0], NULL, 0);
3446                         ac--; av++;
3447                         break;
3448
3449                 case TOK_WEIGHT:
3450                         if (do_pipe == 1)
3451                                 errx(EX_DATAERR,"weight only valid for queues");
3452                         NEED1("weight needs argument 0..100\n");
3453                         p.fs.weight = strtoul(av[0], &end, 0);
3454                         ac--; av++;
3455                         break;
3456
3457                 case TOK_PIPE:
3458                         if (do_pipe == 1)
3459                                 errx(EX_DATAERR,"pipe only valid for queues");
3460                         NEED1("pipe needs pipe_number\n");
3461                         p.fs.parent_nr = strtoul(av[0], &end, 0);
3462                         ac--; av++;
3463                         break;
3464
3465                 default:
3466                         errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
3467                 }
3468         }
3469         if (do_pipe == 1) {
3470                 if (p.pipe_nr == 0)
3471                         errx(EX_DATAERR, "pipe_nr must be > 0");
3472                 if (p.delay > 10000)
3473                         errx(EX_DATAERR, "delay must be < 10000");
3474         } else { /* do_pipe == 2, queue */
3475                 if (p.fs.parent_nr == 0)
3476                         errx(EX_DATAERR, "pipe must be > 0");
3477                 if (p.fs.weight >100)
3478                         errx(EX_DATAERR, "weight must be <= 100");
3479         }
3480         if (p.fs.flags_fs & DN_QSIZE_IS_BYTES) {
3481                 if (p.fs.qsize > 1024*1024)
3482                         errx(EX_DATAERR, "queue size must be < 1MB");
3483         } else {
3484                 if (p.fs.qsize > 100)
3485                         errx(EX_DATAERR, "2 <= queue size <= 100");
3486         }
3487         if (p.fs.flags_fs & DN_IS_RED) {
3488                 size_t len;
3489                 int lookup_depth, avg_pkt_size;
3490                 double s, idle, weight, w_q;
3491                 struct clockinfo ck;
3492                 int t;
3493
3494                 if (p.fs.min_th >= p.fs.max_th)
3495                     errx(EX_DATAERR, "min_th %d must be < than max_th %d",
3496                         p.fs.min_th, p.fs.max_th);
3497                 if (p.fs.max_th == 0)
3498                     errx(EX_DATAERR, "max_th must be > 0");
3499
3500                 len = sizeof(int);
3501                 if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
3502                         &lookup_depth, &len, NULL, 0) == -1)
3503
3504                     errx(1, "sysctlbyname(\"%s\")",
3505                         "net.inet.ip.dummynet.red_lookup_depth");
3506                 if (lookup_depth == 0)
3507                     errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth"
3508                         " must be greater than zero");
3509
3510                 len = sizeof(int);
3511                 if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
3512                         &avg_pkt_size, &len, NULL, 0) == -1)
3513
3514                     errx(1, "sysctlbyname(\"%s\")",
3515                         "net.inet.ip.dummynet.red_avg_pkt_size");
3516                 if (avg_pkt_size == 0)
3517                         errx(EX_DATAERR,
3518                             "net.inet.ip.dummynet.red_avg_pkt_size must"
3519                             " be greater than zero");
3520
3521                 len = sizeof(struct clockinfo);
3522                 if (sysctlbyname("kern.clockrate", &ck, &len, NULL, 0) == -1)
3523                         errx(1, "sysctlbyname(\"%s\")", "kern.clockrate");
3524
3525                 /*
3526                  * Ticks needed for sending a medium-sized packet.
3527                  * Unfortunately, when we are configuring a WF2Q+ queue, we
3528                  * do not have bandwidth information, because that is stored
3529                  * in the parent pipe, and also we have multiple queues
3530                  * competing for it. So we set s=0, which is not very
3531                  * correct. But on the other hand, why do we want RED with
3532                  * WF2Q+ ?
3533                  */
3534                 if (p.bandwidth==0) /* this is a WF2Q+ queue */
3535                         s = 0;
3536                 else
3537                         s = ck.hz * avg_pkt_size * 8 / p.bandwidth;
3538
3539                 /*
3540                  * max idle time (in ticks) before avg queue size becomes 0.
3541                  * NOTA:  (3/w_q) is approx the value x so that
3542                  * (1-w_q)^x < 10^-3.
3543                  */
3544                 w_q = ((double)p.fs.w_q) / (1 << SCALE_RED);
3545                 idle = s * 3. / w_q;
3546                 p.fs.lookup_step = (int)idle / lookup_depth;
3547                 if (!p.fs.lookup_step)
3548                         p.fs.lookup_step = 1;
3549                 weight = 1 - w_q;
3550                 for (t = p.fs.lookup_step; t > 0; --t)
3551                         weight *= weight;
3552                 p.fs.lookup_weight = (int)(weight * (1 << SCALE_RED));
3553         }
3554         i = do_cmd(IP_DUMMYNET_CONFIGURE, &p, sizeof p);
3555         if (i)
3556                 err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
3557 }
3558
3559 static void
3560 get_mac_addr_mask(char *p, uint8_t *addr, uint8_t *mask)
3561 {
3562         int i, l;
3563
3564         for (i=0; i<6; i++)
3565                 addr[i] = mask[i] = 0;
3566         if (strcmp(p, "any") == 0)
3567                 return;
3568
3569         for (i=0; *p && i<6;i++, p++) {
3570                 addr[i] = strtol(p, &p, 16);
3571                 if (*p != ':') /* we start with the mask */
3572                         break;
3573         }
3574         if (*p == '/') { /* mask len */
3575                 l = strtol(p+1, &p, 0);
3576                 for (i=0; l>0; l -=8, i++)
3577                         mask[i] = (l >=8) ? 0xff : (~0) << (8-l);
3578         } else if (*p == '&') { /* mask */
3579                 for (i=0, p++; *p && i<6;i++, p++) {
3580                         mask[i] = strtol(p, &p, 16);
3581                         if (*p != ':')
3582                                 break;
3583                 }
3584         } else if (*p == '\0') {
3585                 for (i=0; i<6; i++)
3586                         mask[i] = 0xff;
3587         }
3588         for (i=0; i<6; i++)
3589                 addr[i] &= mask[i];
3590 }
3591
3592 /*
3593  * helper function, updates the pointer to cmd with the length
3594  * of the current command, and also cleans up the first word of
3595  * the new command in case it has been clobbered before.
3596  */
3597 static ipfw_insn *
3598 next_cmd(ipfw_insn *cmd)
3599 {
3600         cmd += F_LEN(cmd);
3601         bzero(cmd, sizeof(*cmd));
3602         return cmd;
3603 }
3604
3605 /*
3606  * Takes arguments and copies them into a comment
3607  */
3608 static void
3609 fill_comment(ipfw_insn *cmd, int ac, char **av)
3610 {
3611         int i, l;
3612         char *p = (char *)(cmd + 1);
3613
3614         cmd->opcode = O_NOP;
3615         cmd->len =  (cmd->len & (F_NOT | F_OR));
3616
3617         /* Compute length of comment string. */
3618         for (i = 0, l = 0; i < ac; i++)
3619                 l += strlen(av[i]) + 1;
3620         if (l == 0)
3621                 return;
3622         if (l > 84)
3623                 errx(EX_DATAERR,
3624                     "comment too long (max 80 chars)");
3625         l = 1 + (l+3)/4;
3626         cmd->len =  (cmd->len & (F_NOT | F_OR)) | l;
3627         for (i = 0; i < ac; i++) {
3628                 strcpy(p, av[i]);
3629                 p += strlen(av[i]);
3630                 *p++ = ' ';
3631         }
3632         *(--p) = '\0';
3633 }
3634
3635 /*
3636  * A function to fill simple commands of size 1.
3637  * Existing flags are preserved.
3638  */
3639 static void
3640 fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg)
3641 {
3642         cmd->opcode = opcode;
3643         cmd->len =  ((cmd->len | flags) & (F_NOT | F_OR)) | 1;
3644         cmd->arg1 = arg;
3645 }
3646
3647 /*
3648  * Fetch and add the MAC address and type, with masks. This generates one or
3649  * two microinstructions, and returns the pointer to the last one.
3650  */
3651 static ipfw_insn *
3652 add_mac(ipfw_insn *cmd, int ac, char *av[])
3653 {
3654         ipfw_insn_mac *mac;
3655
3656         if (ac < 2)
3657                 errx(EX_DATAERR, "MAC dst src");
3658
3659         cmd->opcode = O_MACADDR2;
3660         cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
3661
3662         mac = (ipfw_insn_mac *)cmd;
3663         get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */
3664         get_mac_addr_mask(av[1], &(mac->addr[6]), &(mac->mask[6])); /* src */
3665         return cmd;
3666 }
3667
3668 static ipfw_insn *
3669 add_mactype(ipfw_insn *cmd, int ac, char *av)
3670 {
3671         if (ac < 1)
3672                 errx(EX_DATAERR, "missing MAC type");
3673         if (strcmp(av, "any") != 0) { /* we have a non-null type */
3674                 fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE);
3675                 cmd->opcode = O_MAC_TYPE;
3676                 return cmd;
3677         } else
3678                 return NULL;
3679 }
3680
3681 static ipfw_insn *
3682 add_proto0(ipfw_insn *cmd, char *av, u_char *protop)
3683 {
3684         struct protoent *pe;
3685         char *ep;
3686         int proto;
3687
3688         proto = strtol(av, &ep, 10);
3689         if (*ep != '\0' || proto <= 0) {
3690                 if ((pe = getprotobyname(av)) == NULL)
3691                         return NULL;
3692                 proto = pe->p_proto;
3693         }
3694
3695         fill_cmd(cmd, O_PROTO, 0, proto);
3696         *protop = proto;
3697         return cmd;
3698 }
3699
3700 static ipfw_insn *
3701 add_proto(ipfw_insn *cmd, char *av, u_char *protop)
3702 {
3703         u_char proto = IPPROTO_IP;
3704
3705         if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
3706                 ; /* do not set O_IP4 nor O_IP6 */
3707         else if (strcmp(av, "ip4") == 0)
3708                 /* explicit "just IPv4" rule */
3709                 fill_cmd(cmd, O_IP4, 0, 0);
3710         else if (strcmp(av, "ip6") == 0) {
3711                 /* explicit "just IPv6" rule */
3712                 proto = IPPROTO_IPV6;
3713                 fill_cmd(cmd, O_IP6, 0, 0);
3714         } else
3715                 return add_proto0(cmd, av, protop);
3716
3717         *protop = proto;
3718         return cmd;
3719 }
3720
3721 static ipfw_insn *
3722 add_proto_compat(ipfw_insn *cmd, char *av, u_char *protop)
3723 {
3724         u_char proto = IPPROTO_IP;
3725
3726         if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
3727                 ; /* do not set O_IP4 nor O_IP6 */
3728         else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0)
3729                 /* explicit "just IPv4" rule */
3730                 fill_cmd(cmd, O_IP4, 0, 0);
3731         else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) {
3732                 /* explicit "just IPv6" rule */
3733                 proto = IPPROTO_IPV6;
3734                 fill_cmd(cmd, O_IP6, 0, 0);
3735         } else
3736                 return add_proto0(cmd, av, protop);
3737
3738         *protop = proto;
3739         return cmd;
3740 }
3741
3742 static ipfw_insn *
3743 add_srcip(ipfw_insn *cmd, char *av)
3744 {
3745         fill_ip((ipfw_insn_ip *)cmd, av);
3746         if (cmd->opcode == O_IP_DST_SET)                        /* set */
3747                 cmd->opcode = O_IP_SRC_SET;
3748         else if (cmd->opcode == O_IP_DST_LOOKUP)                /* table */
3749                 cmd->opcode = O_IP_SRC_LOOKUP;
3750         else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))          /* me */
3751                 cmd->opcode = O_IP_SRC_ME;
3752         else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))      /* one IP */
3753                 cmd->opcode = O_IP_SRC;
3754         else                                                    /* addr/mask */
3755                 cmd->opcode = O_IP_SRC_MASK;
3756         return cmd;
3757 }
3758
3759 static ipfw_insn *
3760 add_dstip(ipfw_insn *cmd, char *av)
3761 {
3762         fill_ip((ipfw_insn_ip *)cmd, av);
3763         if (cmd->opcode == O_IP_DST_SET)                        /* set */
3764                 ;
3765         else if (cmd->opcode == O_IP_DST_LOOKUP)                /* table */
3766                 ;
3767         else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))          /* me */
3768                 cmd->opcode = O_IP_DST_ME;
3769         else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))      /* one IP */
3770                 cmd->opcode = O_IP_DST;
3771         else                                                    /* addr/mask */
3772                 cmd->opcode = O_IP_DST_MASK;
3773         return cmd;
3774 }
3775
3776 static ipfw_insn *
3777 add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
3778 {
3779         if (_substrcmp(av, "any") == 0) {
3780                 return NULL;
3781         } else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) {
3782                 /* XXX todo: check that we have a protocol with ports */
3783                 cmd->opcode = opcode;
3784                 return cmd;
3785         }
3786         return NULL;
3787 }
3788
3789 static ipfw_insn *
3790 add_src(ipfw_insn *cmd, char *av, u_char proto)
3791 {
3792         struct in6_addr a;
3793         char *host, *ch;
3794         ipfw_insn *ret = NULL;
3795
3796         if ((host = strdup(av)) == NULL)
3797                 return NULL;
3798         if ((ch = strrchr(host, '/')) != NULL)
3799                 *ch = '\0';
3800
3801         if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
3802             inet_pton(AF_INET6, host, &a))
3803                 ret = add_srcip6(cmd, av);
3804         /* XXX: should check for IPv4, not !IPv6 */
3805         if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
3806             !inet_pton(AF_INET6, host, &a)))
3807                 ret = add_srcip(cmd, av);
3808         if (ret == NULL && strcmp(av, "any") != 0)
3809                 ret = cmd;
3810
3811         free(host);
3812         return ret;
3813 }
3814
3815 static ipfw_insn *
3816 add_dst(ipfw_insn *cmd, char *av, u_char proto)
3817 {
3818         struct in6_addr a;
3819         char *host, *ch;
3820         ipfw_insn *ret = NULL;
3821
3822         if ((host = strdup(av)) == NULL)
3823                 return NULL;
3824         if ((ch = strrchr(host, '/')) != NULL)
3825                 *ch = '\0';
3826
3827         if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
3828             inet_pton(AF_INET6, host, &a))
3829                 ret = add_dstip6(cmd, av);
3830         /* XXX: should check for IPv4, not !IPv6 */
3831         if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
3832             !inet_pton(AF_INET6, host, &a)))
3833                 ret = add_dstip(cmd, av);
3834         if (ret == NULL && strcmp(av, "any") != 0)
3835                 ret = cmd;
3836
3837         free(host);
3838         return ret;
3839 }
3840
3841 /*
3842  * Parse arguments and assemble the microinstructions which make up a rule.
3843  * Rules are added into the 'rulebuf' and then copied in the correct order
3844  * into the actual rule.
3845  *
3846  * The syntax for a rule starts with the action, followed by
3847  * optional action parameters, and the various match patterns.
3848  * In the assembled microcode, the first opcode must be an O_PROBE_STATE
3849  * (generated if the rule includes a keep-state option), then the
3850  * various match patterns, log/altq actions, and the actual action.
3851  *
3852  */
3853 static void
3854 add(int ac, char *av[])
3855 {
3856         /*
3857          * rules are added into the 'rulebuf' and then copied in
3858          * the correct order into the actual rule.
3859          * Some things that need to go out of order (prob, action etc.)
3860          * go into actbuf[].
3861          */
3862         static uint32_t rulebuf[255], actbuf[255], cmdbuf[255];
3863
3864         ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
3865         ipfw_insn *first_cmd;   /* first match pattern */
3866
3867         struct ip_fw *rule;
3868
3869         /*
3870          * various flags used to record that we entered some fields.
3871          */
3872         ipfw_insn *have_state = NULL;   /* check-state or keep-state */
3873         ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL;
3874         size_t len;
3875
3876         int i;
3877
3878         int open_par = 0;       /* open parenthesis ( */
3879
3880         /* proto is here because it is used to fetch ports */
3881         u_char proto = IPPROTO_IP;      /* default protocol */
3882
3883         double match_prob = 1; /* match probability, default is always match */
3884
3885         bzero(actbuf, sizeof(actbuf));          /* actions go here */
3886         bzero(cmdbuf, sizeof(cmdbuf));
3887         bzero(rulebuf, sizeof(rulebuf));
3888
3889         rule = (struct ip_fw *)rulebuf;
3890         cmd = (ipfw_insn *)cmdbuf;
3891         action = (ipfw_insn *)actbuf;
3892
3893         av++; ac--;
3894
3895         /* [rule N]     -- Rule number optional */
3896         if (ac && isdigit(**av)) {
3897                 rule->rulenum = atoi(*av);
3898                 av++;
3899                 ac--;
3900         }
3901
3902         /* [set N]      -- set number (0..RESVD_SET), optional */
3903         if (ac > 1 && _substrcmp(*av, "set") == 0) {
3904                 int set = strtoul(av[1], NULL, 10);
3905                 if (set < 0 || set > RESVD_SET)
3906                         errx(EX_DATAERR, "illegal set %s", av[1]);
3907                 rule->set = set;
3908                 av += 2; ac -= 2;
3909         }
3910
3911         /* [prob D]     -- match probability, optional */
3912         if (ac > 1 && _substrcmp(*av, "prob") == 0) {
3913                 match_prob = strtod(av[1], NULL);
3914
3915                 if (match_prob <= 0 || match_prob > 1)
3916                         errx(EX_DATAERR, "illegal match prob. %s", av[1]);
3917                 av += 2; ac -= 2;
3918         }
3919
3920         /* action       -- mandatory */
3921         NEED1("missing action");
3922         i = match_token(rule_actions, *av);
3923         ac--; av++;
3924         action->len = 1;        /* default */
3925         switch(i) {
3926         case TOK_CHECKSTATE:
3927                 have_state = action;
3928                 action->opcode = O_CHECK_STATE;
3929                 break;
3930
3931         case TOK_ACCEPT:
3932                 action->opcode = O_ACCEPT;
3933                 break;
3934
3935         case TOK_DENY:
3936                 action->opcode = O_DENY;
3937                 action->arg1 = 0;
3938                 break;
3939
3940         case TOK_REJECT:
3941                 action->opcode = O_REJECT;
3942                 action->arg1 = ICMP_UNREACH_HOST;
3943                 break;
3944
3945         case TOK_RESET:
3946                 action->opcode = O_REJECT;
3947                 action->arg1 = ICMP_REJECT_RST;
3948                 break;
3949
3950         case TOK_RESET6:
3951                 action->opcode = O_UNREACH6;
3952                 action->arg1 = ICMP6_UNREACH_RST;
3953                 break;
3954
3955         case TOK_UNREACH:
3956                 action->opcode = O_REJECT;
3957                 NEED1("missing reject code");
3958                 fill_reject_code(&action->arg1, *av);
3959                 ac--; av++;
3960                 break;
3961
3962         case TOK_UNREACH6:
3963                 action->opcode = O_UNREACH6;
3964                 NEED1("missing unreach code");
3965                 fill_unreach6_code(&action->arg1, *av);
3966                 ac--; av++;
3967                 break;
3968
3969         case TOK_COUNT:
3970                 action->opcode = O_COUNT;
3971                 break;
3972
3973         case TOK_QUEUE:
3974                 action->opcode = O_QUEUE;
3975                 goto chkarg;
3976         case TOK_PIPE:
3977                 action->opcode = O_PIPE;
3978                 goto chkarg;
3979         case TOK_SKIPTO:
3980                 action->opcode = O_SKIPTO;
3981                 goto chkarg;
3982         case TOK_NETGRAPH:
3983                 action->opcode = O_NETGRAPH;
3984                 goto chkarg;
3985         case TOK_NGTEE:
3986                 action->opcode = O_NGTEE;
3987                 goto chkarg;
3988         case TOK_DIVERT:
3989                 action->opcode = O_DIVERT;
3990                 goto chkarg;
3991         case TOK_TEE:
3992                 action->opcode = O_TEE;
3993 chkarg: 
3994                 if (!ac)
3995                         errx(EX_USAGE, "missing argument for %s", *(av - 1));
3996                 if (isdigit(**av)) {
3997                         action->arg1 = strtoul(*av, NULL, 10);
3998                         if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG)
3999                                 errx(EX_DATAERR, "illegal argument for %s",
4000                                     *(av - 1));
4001                 } else if (_substrcmp(*av, TABLEARG) == 0) {
4002                         action->arg1 = IP_FW_TABLEARG;
4003                 } else if (i == TOK_DIVERT || i == TOK_TEE) {
4004                         struct servent *s;
4005                         setservent(1);
4006                         s = getservbyname(av[0], "divert");
4007                         if (s != NULL)
4008                                 action->arg1 = ntohs(s->s_port);
4009                         else
4010                                 errx(EX_DATAERR, "illegal divert/tee port");
4011                 } else
4012                         errx(EX_DATAERR, "illegal argument for %s", *(av - 1));
4013                 ac--; av++;
4014                 break;
4015
4016         case TOK_FORWARD: {
4017                 ipfw_insn_sa *p = (ipfw_insn_sa *)action;
4018                 char *s, *end;
4019
4020                 NEED1("missing forward address[:port]");
4021
4022                 action->opcode = O_FORWARD_IP;
4023                 action->len = F_INSN_SIZE(ipfw_insn_sa);
4024
4025                 p->sa.sin_len = sizeof(struct sockaddr_in);
4026                 p->sa.sin_family = AF_INET;
4027                 p->sa.sin_port = 0;
4028                 /*
4029                  * locate the address-port separator (':' or ',')
4030                  */
4031                 s = strchr(*av, ':');
4032                 if (s == NULL)
4033                         s = strchr(*av, ',');
4034                 if (s != NULL) {
4035                         *(s++) = '\0';
4036                         i = strtoport(s, &end, 0 /* base */, 0 /* proto */);
4037                         if (s == end)
4038                                 errx(EX_DATAERR,
4039                                     "illegal forwarding port ``%s''", s);
4040                         p->sa.sin_port = (u_short)i;
4041                 }
4042                 if (_substrcmp(*av, "tablearg") == 0) 
4043                         p->sa.sin_addr.s_addr = INADDR_ANY;
4044                 else
4045                         lookup_host(*av, &(p->sa.sin_addr));
4046                 ac--; av++;
4047                 break;
4048             }
4049         case TOK_COMMENT:
4050                 /* pretend it is a 'count' rule followed by the comment */
4051                 action->opcode = O_COUNT;
4052                 ac++; av--;     /* go back... */
4053                 break;
4054
4055         default:
4056                 errx(EX_DATAERR, "invalid action %s\n", av[-1]);
4057         }
4058         action = next_cmd(action);
4059
4060         /*
4061          * [altq queuename] -- altq tag, optional
4062          * [log [logamount N]]  -- log, optional
4063          *
4064          * If they exist, it go first in the cmdbuf, but then it is
4065          * skipped in the copy section to the end of the buffer.
4066          */
4067         while (ac != 0 && (i = match_token(rule_action_params, *av)) != -1) {
4068                 ac--; av++;
4069                 switch (i) {
4070                 case TOK_LOG:
4071                     {
4072                         ipfw_insn_log *c = (ipfw_insn_log *)cmd;
4073                         int l;
4074
4075                         if (have_log)
4076                                 errx(EX_DATAERR,
4077                                     "log cannot be specified more than once");
4078                         have_log = (ipfw_insn *)c;
4079                         cmd->len = F_INSN_SIZE(ipfw_insn_log);
4080                         cmd->opcode = O_LOG;
4081                         if (ac && _substrcmp(*av, "logamount") == 0) {
4082                                 ac--; av++;
4083                                 NEED1("logamount requires argument");
4084                                 l = atoi(*av);
4085                                 if (l < 0)
4086                                         errx(EX_DATAERR,
4087                                             "logamount must be positive");
4088                                 c->max_log = l;
4089                                 ac--; av++;
4090                         } else {
4091                                 len = sizeof(c->max_log);
4092                                 if (sysctlbyname("net.inet.ip.fw.verbose_limit",
4093                                     &c->max_log, &len, NULL, 0) == -1)
4094                                         errx(1, "sysctlbyname(\"%s\")",
4095                                             "net.inet.ip.fw.verbose_limit");
4096                         }
4097                     }
4098                         break;
4099
4100                 case TOK_ALTQ:
4101                     {
4102                         ipfw_insn_altq *a = (ipfw_insn_altq *)cmd;
4103
4104                         NEED1("missing altq queue name");
4105                         if (have_altq)
4106                                 errx(EX_DATAERR,
4107                                     "altq cannot be specified more than once");
4108                         have_altq = (ipfw_insn *)a;
4109                         cmd->len = F_INSN_SIZE(ipfw_insn_altq);
4110                         cmd->opcode = O_ALTQ;
4111                         fill_altq_qid(&a->qid, *av);
4112                         ac--; av++;
4113                     }
4114                         break;
4115
4116                 case TOK_TAG:
4117                 case TOK_UNTAG: {
4118                         uint16_t tag;
4119
4120                         if (have_tag)
4121                                 errx(EX_USAGE, "tag and untag cannot be "
4122                                     "specified more than once");
4123                         GET_UINT_ARG(tag, 1, 65534, i, rule_action_params);
4124                         have_tag = cmd;
4125                         fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag);
4126                         ac--; av++;
4127                         break;
4128                 }
4129
4130                 default:
4131                         abort();
4132                 }
4133                 cmd = next_cmd(cmd);
4134         }
4135
4136         if (have_state) /* must be a check-state, we are done */
4137                 goto done;
4138
4139 #define OR_START(target)                                        \
4140         if (ac && (*av[0] == '(' || *av[0] == '{')) {           \
4141                 if (open_par)                                   \
4142                         errx(EX_USAGE, "nested \"(\" not allowed\n"); \
4143                 prev = NULL;                                    \
4144                 open_par = 1;                                   \
4145                 if ( (av[0])[1] == '\0') {                      \
4146                         ac--; av++;                             \
4147                 } else                                          \
4148                         (*av)++;                                \
4149         }                                                       \
4150         target:                                                 \
4151
4152
4153 #define CLOSE_PAR                                               \
4154         if (open_par) {                                         \
4155                 if (ac && (                                     \
4156                     strcmp(*av, ")") == 0 ||                    \
4157                     strcmp(*av, "}") == 0)) {                   \
4158                         prev = NULL;                            \
4159                         open_par = 0;                           \
4160                         ac--; av++;                             \
4161                 } else                                          \
4162                         errx(EX_USAGE, "missing \")\"\n");      \
4163         }
4164
4165 #define NOT_BLOCK                                               \
4166         if (ac && _substrcmp(*av, "not") == 0) {                \
4167                 if (cmd->len & F_NOT)                           \
4168                         errx(EX_USAGE, "double \"not\" not allowed\n"); \
4169                 cmd->len |= F_NOT;                              \
4170                 ac--; av++;                                     \
4171         }
4172
4173 #define OR_BLOCK(target)                                        \
4174         if (ac && _substrcmp(*av, "or") == 0) {         \
4175                 if (prev == NULL || open_par == 0)              \
4176                         errx(EX_DATAERR, "invalid OR block");   \
4177                 prev->len |= F_OR;                              \
4178                 ac--; av++;                                     \
4179                 goto target;                                    \
4180         }                                                       \
4181         CLOSE_PAR;
4182
4183         first_cmd = cmd;
4184
4185 #if 0
4186         /*
4187          * MAC addresses, optional.
4188          * If we have this, we skip the part "proto from src to dst"
4189          * and jump straight to the option parsing.
4190          */
4191         NOT_BLOCK;
4192         NEED1("missing protocol");
4193         if (_substrcmp(*av, "MAC") == 0 ||
4194             _substrcmp(*av, "mac") == 0) {
4195                 ac--; av++;     /* the "MAC" keyword */
4196                 add_mac(cmd, ac, av); /* exits in case of errors */
4197                 cmd = next_cmd(cmd);
4198                 ac -= 2; av += 2;       /* dst-mac and src-mac */
4199                 NOT_BLOCK;
4200                 NEED1("missing mac type");
4201                 if (add_mactype(cmd, ac, av[0]))
4202                         cmd = next_cmd(cmd);
4203                 ac--; av++;     /* any or mac-type */
4204                 goto read_options;
4205         }
4206 #endif
4207
4208         /*
4209          * protocol, mandatory
4210          */
4211     OR_START(get_proto);
4212         NOT_BLOCK;
4213         NEED1("missing protocol");
4214         if (add_proto_compat(cmd, *av, &proto)) {
4215                 av++; ac--;
4216                 if (F_LEN(cmd) != 0) {
4217                         prev = cmd;
4218                         cmd = next_cmd(cmd);
4219                 }
4220         } else if (first_cmd != cmd) {
4221                 errx(EX_DATAERR, "invalid protocol ``%s''", *av);
4222         } else
4223                 goto read_options;
4224     OR_BLOCK(get_proto);
4225
4226         /*
4227          * "from", mandatory
4228          */
4229         if (!ac || _substrcmp(*av, "from") != 0)
4230                 errx(EX_USAGE, "missing ``from''");
4231         ac--; av++;
4232
4233         /*
4234          * source IP, mandatory
4235          */
4236     OR_START(source_ip);
4237         NOT_BLOCK;      /* optional "not" */
4238         NEED1("missing source address");
4239         if (add_src(cmd, *av, proto)) {
4240                 ac--; av++;
4241                 if (F_LEN(cmd) != 0) {  /* ! any */
4242                         prev = cmd;
4243                         cmd = next_cmd(cmd);
4244                 }
4245         } else
4246                 errx(EX_USAGE, "bad source address %s", *av);
4247     OR_BLOCK(source_ip);
4248
4249         /*
4250          * source ports, optional
4251          */
4252         NOT_BLOCK;      /* optional "not" */
4253         if (ac) {
4254                 if (_substrcmp(*av, "any") == 0 ||
4255                     add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
4256                         ac--; av++;
4257                         if (F_LEN(cmd) != 0)
4258                                 cmd = next_cmd(cmd);
4259                 }
4260         }
4261
4262         /*
4263          * "to", mandatory
4264          */
4265         if (!ac || _substrcmp(*av, "to") != 0)
4266                 errx(EX_USAGE, "missing ``to''");
4267         av++; ac--;
4268
4269         /*
4270          * destination, mandatory
4271          */
4272     OR_START(dest_ip);
4273         NOT_BLOCK;      /* optional "not" */
4274         NEED1("missing dst address");
4275         if (add_dst(cmd, *av, proto)) {
4276                 ac--; av++;
4277                 if (F_LEN(cmd) != 0) {  /* ! any */
4278                         prev = cmd;
4279                         cmd = next_cmd(cmd);
4280                 }
4281         } else
4282                 errx( EX_USAGE, "bad destination address %s", *av);
4283     OR_BLOCK(dest_ip);
4284
4285         /*
4286          * dest. ports, optional
4287          */
4288         NOT_BLOCK;      /* optional "not" */
4289         if (ac) {
4290                 if (_substrcmp(*av, "any") == 0 ||
4291                     add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
4292                         ac--; av++;
4293                         if (F_LEN(cmd) != 0)
4294                                 cmd = next_cmd(cmd);
4295                 }
4296         }
4297
4298 read_options:
4299         if (ac && first_cmd == cmd) {
4300                 /*
4301                  * nothing specified so far, store in the rule to ease
4302                  * printout later.
4303                  */
4304                  rule->_pad = 1;
4305         }
4306         prev = NULL;
4307         while (ac) {
4308                 char *s;
4309                 ipfw_insn_u32 *cmd32;   /* alias for cmd */
4310
4311                 s = *av;
4312                 cmd32 = (ipfw_insn_u32 *)cmd;
4313
4314                 if (*s == '!') {        /* alternate syntax for NOT */
4315                         if (cmd->len & F_NOT)
4316                                 errx(EX_USAGE, "double \"not\" not allowed\n");
4317                         cmd->len = F_NOT;
4318                         s++;
4319                 }
4320                 i = match_token(rule_options, s);
4321                 ac--; av++;
4322                 switch(i) {
4323                 case TOK_NOT:
4324                         if (cmd->len & F_NOT)
4325                                 errx(EX_USAGE, "double \"not\" not allowed\n");
4326                         cmd->len = F_NOT;
4327                         break;
4328
4329                 case TOK_OR:
4330                         if (open_par == 0 || prev == NULL)
4331                                 errx(EX_USAGE, "invalid \"or\" block\n");
4332                         prev->len |= F_OR;
4333                         break;
4334
4335                 case TOK_STARTBRACE:
4336                         if (open_par)
4337                                 errx(EX_USAGE, "+nested \"(\" not allowed\n");
4338                         open_par = 1;
4339                         break;
4340
4341                 case TOK_ENDBRACE:
4342                         if (!open_par)
4343                                 errx(EX_USAGE, "+missing \")\"\n");
4344                         open_par = 0;
4345                         prev = NULL;
4346                         break;
4347
4348                 case TOK_IN:
4349                         fill_cmd(cmd, O_IN, 0, 0);
4350                         break;
4351
4352                 case TOK_OUT:
4353                         cmd->len ^= F_NOT; /* toggle F_NOT */
4354                         fill_cmd(cmd, O_IN, 0, 0);
4355                         break;
4356
4357                 case TOK_DIVERTED:
4358                         fill_cmd(cmd, O_DIVERTED, 0, 3);
4359                         break;
4360
4361                 case TOK_DIVERTEDLOOPBACK:
4362                         fill_cmd(cmd, O_DIVERTED, 0, 1);
4363                         break;
4364
4365                 case TOK_DIVERTEDOUTPUT:
4366                         fill_cmd(cmd, O_DIVERTED, 0, 2);
4367                         break;
4368
4369                 case TOK_FRAG:
4370                         fill_cmd(cmd, O_FRAG, 0, 0);
4371                         break;
4372
4373                 case TOK_LAYER2:
4374                         fill_cmd(cmd, O_LAYER2, 0, 0);
4375                         break;
4376
4377                 case TOK_XMIT:
4378                 case TOK_RECV:
4379                 case TOK_VIA:
4380                         NEED1("recv, xmit, via require interface name"
4381                                 " or address");
4382                         fill_iface((ipfw_insn_if *)cmd, av[0]);
4383                         ac--; av++;
4384                         if (F_LEN(cmd) == 0)    /* not a valid address */
4385                                 break;
4386                         if (i == TOK_XMIT)
4387                                 cmd->opcode = O_XMIT;
4388                         else if (i == TOK_RECV)
4389                                 cmd->opcode = O_RECV;
4390                         else if (i == TOK_VIA)
4391                                 cmd->opcode = O_VIA;
4392                         break;
4393
4394                 case TOK_ICMPTYPES:
4395                         NEED1("icmptypes requires list of types");
4396                         fill_icmptypes((ipfw_insn_u32 *)cmd, *av);
4397                         av++; ac--;
4398                         break;
4399                 
4400                 case TOK_ICMP6TYPES:
4401                         NEED1("icmptypes requires list of types");
4402                         fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av);
4403                         av++; ac--;
4404                         break;
4405
4406                 case TOK_IPTTL:
4407                         NEED1("ipttl requires TTL");
4408                         if (strpbrk(*av, "-,")) {
4409                             if (!add_ports(cmd, *av, 0, O_IPTTL))
4410                                 errx(EX_DATAERR, "invalid ipttl %s", *av);
4411                         } else
4412                             fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
4413                         ac--; av++;
4414                         break;
4415
4416                 case TOK_IPID:
4417                         NEED1("ipid requires id");
4418                         if (strpbrk(*av, "-,")) {
4419                             if (!add_ports(cmd, *av, 0, O_IPID))
4420                                 errx(EX_DATAERR, "invalid ipid %s", *av);
4421                         } else
4422                             fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0));
4423                         ac--; av++;
4424                         break;
4425
4426                 case TOK_IPLEN:
4427                         NEED1("iplen requires length");
4428                         if (strpbrk(*av, "-,")) {
4429                             if (!add_ports(cmd, *av, 0, O_IPLEN))
4430                                 errx(EX_DATAERR, "invalid ip len %s", *av);
4431                         } else
4432                             fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0));
4433                         ac--; av++;
4434                         break;
4435
4436                 case TOK_IPVER:
4437                         NEED1("ipver requires version");
4438                         fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0));
4439                         ac--; av++;
4440                         break;
4441
4442                 case TOK_IPPRECEDENCE:
4443                         NEED1("ipprecedence requires value");
4444                         fill_cmd(cmd, O_IPPRECEDENCE, 0,
4445                             (strtoul(*av, NULL, 0) & 7) << 5);
4446                         ac--; av++;
4447                         break;
4448
4449                 case TOK_IPOPTS:
4450                         NEED1("missing argument for ipoptions");
4451                         fill_flags(cmd, O_IPOPT, f_ipopts, *av);
4452                         ac--; av++;
4453                         break;
4454
4455                 case TOK_IPTOS:
4456                         NEED1("missing argument for iptos");
4457                         fill_flags(cmd, O_IPTOS, f_iptos, *av);
4458                         ac--; av++;
4459                         break;
4460
4461                 case TOK_UID:
4462                         NEED1("uid requires argument");
4463                     {
4464                         char *end;
4465                         uid_t uid;
4466                         struct passwd *pwd;
4467
4468                         cmd->opcode = O_UID;
4469                         uid = strtoul(*av, &end, 0);
4470                         pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av);
4471                         if (pwd == NULL)
4472                                 errx(EX_DATAERR, "uid \"%s\" nonexistent", *av);
4473                         cmd32->d[0] = pwd->pw_uid;
4474                         cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
4475                         ac--; av++;
4476                     }
4477                         break;
4478
4479                 case TOK_GID:
4480                         NEED1("gid requires argument");
4481                     {
4482                         char *end;
4483                         gid_t gid;
4484                         struct group *grp;
4485
4486                         cmd->opcode = O_GID;
4487                         gid = strtoul(*av, &end, 0);
4488                         grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av);
4489                         if (grp == NULL)
4490                                 errx(EX_DATAERR, "gid \"%s\" nonexistent", *av);
4491                         cmd32->d[0] = grp->gr_gid;
4492                         cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
4493                         ac--; av++;
4494                     }
4495                         break;
4496
4497                 case TOK_JAIL:
4498                         NEED1("jail requires argument");
4499                     {
4500                         char *end;
4501                         int jid;
4502
4503                         cmd->opcode = O_JAIL;
4504                         jid = (int)strtol(*av, &end, 0);
4505                         if (jid < 0 || *end != '\0')
4506                                 errx(EX_DATAERR, "jail requires prison ID");
4507                         cmd32->d[0] = (uint32_t)jid;
4508                         cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
4509                         ac--; av++;
4510                     }
4511                         break;
4512
4513                 case TOK_ESTAB:
4514                         fill_cmd(cmd, O_ESTAB, 0, 0);
4515                         break;
4516
4517                 case TOK_SETUP:
4518                         fill_cmd(cmd, O_TCPFLAGS, 0,
4519                                 (TH_SYN) | ( (TH_ACK) & 0xff) <<8 );
4520                         break;
4521
4522                 case TOK_TCPDATALEN:
4523                         NEED1("tcpdatalen requires length");
4524                         if (strpbrk(*av, "-,")) {
4525                             if (!add_ports(cmd, *av, 0, O_TCPDATALEN))
4526                                 errx(EX_DATAERR, "invalid tcpdata len %s", *av);
4527                         } else
4528                             fill_cmd(cmd, O_TCPDATALEN, 0,
4529                                     strtoul(*av, NULL, 0));
4530                         ac--; av++;
4531                         break;
4532
4533                 case TOK_TCPOPTS:
4534                         NEED1("missing argument for tcpoptions");
4535                         fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av);
4536                         ac--; av++;
4537                         break;
4538
4539                 case TOK_TCPSEQ:
4540                 case TOK_TCPACK:
4541                         NEED1("tcpseq/tcpack requires argument");
4542                         cmd->len = F_INSN_SIZE(ipfw_insn_u32);
4543                         cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK;
4544                         cmd32->d[0] = htonl(strtoul(*av, NULL, 0));
4545                         ac--; av++;
4546                         break;
4547
4548                 case TOK_TCPWIN:
4549                         NEED1("tcpwin requires length");
4550                         fill_cmd(cmd, O_TCPWIN, 0,
4551                             htons(strtoul(*av, NULL, 0)));
4552                         ac--; av++;
4553                         break;
4554
4555                 case TOK_TCPFLAGS:
4556                         NEED1("missing argument for tcpflags");
4557                         cmd->opcode = O_TCPFLAGS;
4558                         fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av);
4559                         ac--; av++;
4560                         break;
4561
4562                 case TOK_KEEPSTATE:
4563                         if (open_par)
4564                                 errx(EX_USAGE, "keep-state cannot be part "
4565                                     "of an or block");
4566                         if (have_state)
4567                                 errx(EX_USAGE, "only one of keep-state "
4568                                         "and limit is allowed");
4569                         have_state = cmd;
4570                         fill_cmd(cmd, O_KEEP_STATE, 0, 0);
4571                         break;
4572
4573                 case TOK_LIMIT: {
4574                         ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
4575                         int val;
4576
4577                         if (open_par)
4578                                 errx(EX_USAGE,
4579                                     "limit cannot be part of an or block");
4580                         if (have_state)
4581                                 errx(EX_USAGE, "only one of keep-state and"
4582                                     "limit is allowed");
4583                         have_state = cmd;
4584
4585                         cmd->len = F_INSN_SIZE(ipfw_insn_limit);
4586                         cmd->opcode = O_LIMIT;
4587                         c->limit_mask = c->conn_limit = 0;
4588
4589                         while (ac > 0) {
4590                                 if ((val = match_token(limit_masks, *av)) <= 0)
4591                                         break;
4592                                 c->limit_mask |= val;
4593                                 ac--; av++;
4594                         }
4595
4596                         if (c->limit_mask == 0)
4597                                 errx(EX_USAGE, "limit: missing limit mask");
4598
4599                         GET_UINT_ARG(c->conn_limit, 1, 65534, TOK_LIMIT,
4600                             rule_options);
4601
4602                         ac--; av++;
4603                         break;
4604                 }
4605
4606                 case TOK_PROTO:
4607                         NEED1("missing protocol");
4608                         if (add_proto(cmd, *av, &proto)) {
4609                                 ac--; av++;
4610                         } else
4611                                 errx(EX_DATAERR, "invalid protocol ``%s''",
4612                                     *av);
4613                         break;
4614
4615                 case TOK_SRCIP:
4616                         NEED1("missing source IP");
4617                         if (add_srcip(cmd, *av)) {
4618                                 ac--; av++;
4619                         }
4620                         break;
4621
4622                 case TOK_DSTIP:
4623                         NEED1("missing destination IP");
4624                         if (add_dstip(cmd, *av)) {
4625                                 ac--; av++;
4626                         }
4627                         break;
4628
4629                 case TOK_SRCIP6:
4630                         NEED1("missing source IP6");
4631                         if (add_srcip6(cmd, *av)) {
4632                                 ac--; av++;
4633                         }
4634                         break;
4635                                 
4636                 case TOK_DSTIP6:
4637                         NEED1("missing destination IP6");
4638                         if (add_dstip6(cmd, *av)) {
4639                                 ac--; av++;
4640                         }
4641                         break;
4642
4643                 case TOK_SRCPORT:
4644                         NEED1("missing source port");
4645                         if (_substrcmp(*av, "any") == 0 ||
4646                             add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
4647                                 ac--; av++;
4648                         } else
4649                                 errx(EX_DATAERR, "invalid source port %s", *av);
4650                         break;
4651
4652                 case TOK_DSTPORT:
4653                         NEED1("missing destination port");
4654                         if (_substrcmp(*av, "any") == 0 ||
4655                             add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
4656                                 ac--; av++;
4657                         } else
4658                                 errx(EX_DATAERR, "invalid destination port %s",
4659                                     *av);
4660                         break;
4661
4662                 case TOK_MAC:
4663                         if (add_mac(cmd, ac, av)) {
4664                                 ac -= 2; av += 2;
4665                         }
4666                         break;
4667
4668                 case TOK_MACTYPE:
4669                         NEED1("missing mac type");
4670                         if (!add_mactype(cmd, ac, *av))
4671                                 errx(EX_DATAERR, "invalid mac type %s", *av);
4672                         ac--; av++;
4673                         break;
4674
4675                 case TOK_VERREVPATH:
4676                         fill_cmd(cmd, O_VERREVPATH, 0, 0);
4677                         break;
4678
4679                 case TOK_VERSRCREACH:
4680                         fill_cmd(cmd, O_VERSRCREACH, 0, 0);
4681                         break;
4682
4683                 case TOK_ANTISPOOF:
4684                         fill_cmd(cmd, O_ANTISPOOF, 0, 0);
4685                         break;
4686
4687                 case TOK_IPSEC:
4688                         fill_cmd(cmd, O_IPSEC, 0, 0);
4689                         break;
4690
4691                 case TOK_IPV6:
4692                         fill_cmd(cmd, O_IP6, 0, 0);
4693                         break;
4694
4695                 case TOK_IPV4:
4696                         fill_cmd(cmd, O_IP4, 0, 0);
4697                         break;
4698
4699                 case TOK_EXT6HDR:
4700                         fill_ext6hdr( cmd, *av );
4701                         ac--; av++;
4702                         break;
4703
4704                 case TOK_FLOWID:
4705                         if (proto != IPPROTO_IPV6 )
4706                                 errx( EX_USAGE, "flow-id filter is active "
4707                                     "only for ipv6 protocol\n");
4708                         fill_flow6( (ipfw_insn_u32 *) cmd, *av );
4709                         ac--; av++;
4710                         break;
4711
4712                 case TOK_COMMENT:
4713                         fill_comment(cmd, ac, av);
4714                         av += ac;
4715                         ac = 0;
4716                         break;
4717
4718                 case TOK_TAGGED:
4719                         if (ac > 0 && strpbrk(*av, "-,")) {
4720                                 if (!add_ports(cmd, *av, 0, O_TAGGED))
4721                                         errx(EX_DATAERR, "tagged: invalid tag"
4722                                             " list: %s", *av);
4723                         }
4724                         else {
4725                                 uint16_t tag;
4726
4727                                 GET_UINT_ARG(tag, 1, 65534, TOK_TAGGED,
4728                                     rule_options);
4729                                 fill_cmd(cmd, O_TAGGED, 0, tag);
4730                         }
4731                         ac--; av++;
4732                         break;
4733
4734                 default:
4735                         errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
4736                 }
4737                 if (F_LEN(cmd) > 0) {   /* prepare to advance */
4738                         prev = cmd;
4739                         cmd = next_cmd(cmd);
4740                 }
4741         }
4742
4743 done:
4744         /*
4745          * Now copy stuff into the rule.
4746          * If we have a keep-state option, the first instruction
4747          * must be a PROBE_STATE (which is generated here).
4748          * If we have a LOG option, it was stored as the first command,
4749          * and now must be moved to the top of the action part.
4750          */
4751         dst = (ipfw_insn *)rule->cmd;
4752
4753         /*
4754          * First thing to write into the command stream is the match probability.
4755          */
4756         if (match_prob != 1) { /* 1 means always match */
4757                 dst->opcode = O_PROB;
4758                 dst->len = 2;
4759                 *((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff);
4760                 dst += dst->len;
4761         }
4762
4763         /*
4764          * generate O_PROBE_STATE if necessary
4765          */
4766         if (have_state && have_state->opcode != O_CHECK_STATE) {
4767                 fill_cmd(dst, O_PROBE_STATE, 0, 0);
4768                 dst = next_cmd(dst);
4769         }
4770
4771         /* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */
4772         for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
4773                 i = F_LEN(src);
4774
4775                 switch (src->opcode) {
4776                 case O_LOG:
4777                 case O_KEEP_STATE:
4778                 case O_LIMIT:
4779                 case O_ALTQ:
4780                 case O_TAG:
4781                         break;
4782                 default:
4783                         bcopy(src, dst, i * sizeof(uint32_t));
4784                         dst += i;
4785                 }
4786         }
4787
4788         /*
4789          * put back the have_state command as last opcode
4790          */
4791         if (have_state && have_state->opcode != O_CHECK_STATE) {
4792                 i = F_LEN(have_state);
4793                 bcopy(have_state, dst, i * sizeof(uint32_t));
4794                 dst += i;
4795         }
4796         /*
4797          * start action section
4798          */
4799         rule->act_ofs = dst - rule->cmd;
4800
4801         /* put back O_LOG, O_ALTQ, O_TAG if necessary */
4802         if (have_log) {
4803                 i = F_LEN(have_log);
4804                 bcopy(have_log, dst, i * sizeof(uint32_t));
4805                 dst += i;
4806         }
4807         if (have_altq) {
4808                 i = F_LEN(have_altq);
4809                 bcopy(have_altq, dst, i * sizeof(uint32_t));
4810                 dst += i;
4811         }
4812         if (have_tag) {
4813                 i = F_LEN(have_tag);
4814                 bcopy(have_tag, dst, i * sizeof(uint32_t));
4815                 dst += i;
4816         }
4817         /*
4818          * copy all other actions
4819          */
4820         for (src = (ipfw_insn *)actbuf; src != action; src += i) {
4821                 i = F_LEN(src);
4822                 bcopy(src, dst, i * sizeof(uint32_t));
4823                 dst += i;
4824         }
4825
4826         rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd);
4827         i = (char *)dst - (char *)rule;
4828         if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1)
4829                 err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
4830         if (!do_quiet)
4831                 show_ipfw(rule, 0, 0);
4832 }
4833
4834 static void
4835 zero(int ac, char *av[], int optname /* IP_FW_ZERO or IP_FW_RESETLOG */)
4836 {
4837         int rulenum;
4838         int failed = EX_OK;
4839         char const *name = optname == IP_FW_ZERO ?  "ZERO" : "RESETLOG";
4840
4841         av++; ac--;
4842
4843         if (!ac) {
4844                 /* clear all entries */
4845                 if (do_cmd(optname, NULL, 0) < 0)
4846                         err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name);
4847                 if (!do_quiet)
4848                         printf("%s.\n", optname == IP_FW_ZERO ?
4849                             "Accounting cleared":"Logging counts reset");
4850
4851                 return;
4852         }
4853
4854         while (ac) {
4855                 /* Rule number */
4856                 if (isdigit(**av)) {
4857                         rulenum = atoi(*av);
4858                         av++;
4859                         ac--;
4860                         if (do_cmd(optname, &rulenum, sizeof rulenum)) {
4861                                 warn("rule %u: setsockopt(IP_FW_%s)",
4862                                     rulenum, name);
4863                                 failed = EX_UNAVAILABLE;
4864                         } else if (!do_quiet)
4865                                 printf("Entry %d %s.\n", rulenum,
4866                                     optname == IP_FW_ZERO ?
4867                                         "cleared" : "logging count reset");
4868                 } else {
4869                         errx(EX_USAGE, "invalid rule number ``%s''", *av);
4870                 }
4871         }
4872         if (failed != EX_OK)
4873                 exit(failed);
4874 }
4875
4876 static void
4877 flush(int force)
4878 {
4879         int cmd = do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH;
4880
4881         if (!force && !do_quiet) { /* need to ask user */
4882                 int c;
4883
4884                 printf("Are you sure? [yn] ");
4885                 fflush(stdout);
4886                 do {
4887                         c = toupper(getc(stdin));
4888                         while (c != '\n' && getc(stdin) != '\n')
4889                                 if (feof(stdin))
4890                                         return; /* and do not flush */
4891                 } while (c != 'Y' && c != 'N');
4892                 printf("\n");
4893                 if (c == 'N')   /* user said no */
4894                         return;
4895         }
4896         if (do_cmd(cmd, NULL, 0) < 0)
4897                 err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
4898                     do_pipe ? "DUMMYNET" : "FW");
4899         if (!do_quiet)
4900                 printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules");
4901 }
4902
4903 /*
4904  * Free a the (locally allocated) copy of command line arguments.
4905  */
4906 static void
4907 free_args(int ac, char **av)
4908 {
4909         int i;
4910
4911         for (i=0; i < ac; i++)
4912                 free(av[i]);
4913         free(av);
4914 }
4915
4916 /*
4917  * This one handles all table-related commands
4918  *      ipfw table N add addr[/masklen] [value]
4919  *      ipfw table N delete addr[/masklen]
4920  *      ipfw table N flush
4921  *      ipfw table N list
4922  */
4923 static void
4924 table_handler(int ac, char *av[])
4925 {
4926         ipfw_table_entry ent;
4927         ipfw_table *tbl;
4928         int do_add;
4929         char *p;
4930         socklen_t l;
4931         uint32_t a;
4932
4933         ac--; av++;
4934         if (ac && isdigit(**av)) {
4935                 ent.tbl = atoi(*av);
4936                 ac--; av++;
4937         } else
4938                 errx(EX_USAGE, "table number required");
4939         NEED1("table needs command");
4940         if (_substrcmp(*av, "add") == 0 ||
4941             _substrcmp(*av, "delete") == 0) {
4942                 do_add = **av == 'a';
4943                 ac--; av++;
4944                 if (!ac)
4945                         errx(EX_USAGE, "IP address required");
4946                 p = strchr(*av, '/');
4947                 if (p) {
4948                         *p++ = '\0';
4949                         ent.masklen = atoi(p);
4950                         if (ent.masklen > 32)
4951                                 errx(EX_DATAERR, "bad width ``%s''", p);
4952                 } else
4953                         ent.masklen = 32;
4954                 if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0)
4955                         errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
4956                 ac--; av++;
4957                 if (do_add && ac) {
4958                         unsigned int tval;
4959                         /* isdigit is a bit of a hack here.. */
4960                         if (strchr(*av, (int)'.') == NULL && isdigit(**av))  {
4961                                 ent.value = strtoul(*av, NULL, 0);
4962                         } else {
4963                                 if (lookup_host(*av, (struct in_addr *)&tval) == 0) {
4964                                         /* The value must be stored in host order        *
4965                                          * so that the values < 65k can be distinguished */
4966                                         ent.value = ntohl(tval); 
4967                                 } else {
4968                                         errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
4969                                 }
4970                         }
4971                 } else
4972                         ent.value = 0;
4973                 if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL,
4974                     &ent, sizeof(ent)) < 0) {
4975                         /* If running silent, don't bomb out on these errors. */
4976                         if (!(do_quiet && (errno == (do_add ? EEXIST : ESRCH))))
4977                                 err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
4978                                     do_add ? "ADD" : "DEL");
4979                         /* In silent mode, react to a failed add by deleting */
4980                         if (do_add) {
4981                                 do_cmd(IP_FW_TABLE_DEL, &ent, sizeof(ent));
4982                                 if (do_cmd(IP_FW_TABLE_ADD,
4983                                     &ent, sizeof(ent)) < 0)
4984                                         err(EX_OSERR,
4985                                             "setsockopt(IP_FW_TABLE_ADD)");
4986                         }
4987                 }
4988         } else if (_substrcmp(*av, "flush") == 0) {
4989                 if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl, sizeof(ent.tbl)) < 0)
4990                         err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
4991         } else if (_substrcmp(*av, "list") == 0) {
4992                 a = ent.tbl;
4993                 l = sizeof(a);
4994                 if (do_cmd(IP_FW_TABLE_GETSIZE, &a, (uintptr_t)&l) < 0)
4995                         err(EX_OSERR, "getsockopt(IP_FW_TABLE_GETSIZE)");
4996                 l = sizeof(*tbl) + a * sizeof(ipfw_table_entry);
4997                 tbl = malloc(l);
4998                 if (tbl == NULL)
4999                         err(EX_OSERR, "malloc");
5000                 tbl->tbl = ent.tbl;
5001                 if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0)
5002                         err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)");
5003                 for (a = 0; a < tbl->cnt; a++) {
5004                         /* Heuristic to print it the right way */
5005                         /* values < 64k are printed as numbers */
5006                         unsigned int tval;
5007                         tval = tbl->ent[a].value;
5008                         if (tval > 0xffff) {
5009                             char tbuf[128];
5010                             strncpy(tbuf, inet_ntoa(*(struct in_addr *)
5011                                 &tbl->ent[a].addr), 127);
5012                             /* inet_ntoa expects host order */
5013                             tval = htonl(tval);
5014                             printf("%s/%u %s\n", tbuf, tbl->ent[a].masklen,
5015                                 inet_ntoa(*(struct in_addr *)&tval));
5016                         } else {
5017                             printf("%s/%u %u\n",
5018                                 inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
5019                                 tbl->ent[a].masklen, tbl->ent[a].value);
5020                         }
5021                 }
5022         } else
5023                 errx(EX_USAGE, "invalid table command %s", *av);
5024 }
5025
5026 /*
5027  * Called with the arguments (excluding program name).
5028  * Returns 0 if successful, 1 if empty command, errx() in case of errors.
5029  */
5030 static int
5031 ipfw_main(int oldac, char **oldav)
5032 {
5033         int ch, ac, save_ac;
5034         char **av, **save_av;
5035         int do_acct = 0;                /* Show packet/byte count */
5036
5037 #define WHITESP         " \t\f\v\n\r"
5038         if (oldac == 0)
5039                 return 1;
5040         else if (oldac == 1) {
5041                 /*
5042                  * If we are called with a single string, try to split it into
5043                  * arguments for subsequent parsing.
5044                  * But first, remove spaces after a ',', by copying the string
5045                  * in-place.
5046                  */
5047                 char *arg = oldav[0];   /* The string... */
5048                 int l = strlen(arg);
5049                 int copy = 0;           /* 1 if we need to copy, 0 otherwise */
5050                 int i, j;
5051                 for (i = j = 0; i < l; i++) {
5052                         if (arg[i] == '#')      /* comment marker */
5053                                 break;
5054                         if (copy) {
5055                                 arg[j++] = arg[i];
5056                                 copy = !index("," WHITESP, arg[i]);
5057                         } else {
5058                                 copy = !index(WHITESP, arg[i]);
5059                                 if (copy)
5060                                         arg[j++] = arg[i];
5061                         }
5062                 }
5063                 if (!copy && j > 0)     /* last char was a 'blank', remove it */
5064                         j--;
5065                 l = j;                  /* the new argument length */
5066                 arg[j++] = '\0';
5067                 if (l == 0)             /* empty string! */
5068                         return 1;
5069
5070                 /*
5071                  * First, count number of arguments. Because of the previous
5072                  * processing, this is just the number of blanks plus 1.
5073                  */
5074                 for (i = 0, ac = 1; i < l; i++)
5075                         if (index(WHITESP, arg[i]) != NULL)
5076                                 ac++;
5077
5078                 av = calloc(ac, sizeof(char *));
5079
5080                 /*
5081                  * Second, copy arguments from cmd[] to av[]. For each one,
5082                  * j is the initial character, i is the one past the end.
5083                  */
5084                 for (ac = 0, i = j = 0; i < l; i++)
5085                         if (index(WHITESP, arg[i]) != NULL || i == l-1) {
5086                                 if (i == l-1)
5087                                         i++;
5088                                 av[ac] = calloc(i-j+1, 1);
5089                                 bcopy(arg+j, av[ac], i-j);
5090                                 ac++;
5091                                 j = i + 1;
5092                         }
5093         } else {
5094                 /*
5095                  * If an argument ends with ',' join with the next one.
5096                  */
5097                 int first, i, l;
5098
5099                 av = calloc(oldac, sizeof(char *));
5100                 for (first = i = ac = 0, l = 0; i < oldac; i++) {
5101                         char *arg = oldav[i];
5102                         int k = strlen(arg);
5103
5104                         l += k;
5105                         if (arg[k-1] != ',' || i == oldac-1) {
5106                                 /* Time to copy. */
5107                                 av[ac] = calloc(l+1, 1);
5108                                 for (l=0; first <= i; first++) {
5109                                         strcat(av[ac]+l, oldav[first]);
5110                                         l += strlen(oldav[first]);
5111                                 }
5112                                 ac++;
5113                                 l = 0;
5114                                 first = i+1;
5115                         }
5116                 }
5117         }
5118
5119         /* Set the force flag for non-interactive processes */
5120         if (!do_force)
5121                 do_force = !isatty(STDIN_FILENO);
5122
5123         /* Save arguments for final freeing of memory. */
5124         save_ac = ac;
5125         save_av = av;
5126
5127         optind = optreset = 0;
5128         while ((ch = getopt(ac, av, "abcdefhnNqs:STtv")) != -1)
5129                 switch (ch) {
5130                 case 'a':
5131                         do_acct = 1;
5132                         break;
5133
5134                 case 'b':
5135                         comment_only = 1;
5136                         do_compact = 1;
5137                         break;
5138
5139                 case 'c':
5140                         do_compact = 1;
5141                         break;
5142
5143                 case 'd':
5144                         do_dynamic = 1;
5145                         break;
5146
5147                 case 'e':
5148                         do_expired = 1;
5149                         break;
5150
5151                 case 'f':
5152                         do_force = 1;
5153                         break;
5154
5155                 case 'h': /* help */
5156                         free_args(save_ac, save_av);
5157                         help();
5158                         break;  /* NOTREACHED */
5159
5160                 case 'n':
5161                         test_only = 1;
5162                         break;
5163
5164                 case 'N':
5165                         do_resolv = 1;
5166                         break;
5167
5168                 case 'q':
5169                         do_quiet = 1;
5170                         break;
5171
5172                 case 's': /* sort */
5173                         do_sort = atoi(optarg);
5174                         break;
5175
5176                 case 'S':
5177                         show_sets = 1;
5178                         break;
5179
5180                 case 't':
5181                         do_time = 1;
5182                         break;
5183
5184                 case 'T':
5185                         do_time = 2;    /* numeric timestamp */
5186                         break;
5187
5188                 case 'v': /* verbose */
5189                         verbose = 1;
5190                         break;
5191
5192                 default:
5193                         free_args(save_ac, save_av);
5194                         return 1;
5195                 }
5196
5197         ac -= optind;
5198         av += optind;
5199         NEED1("bad arguments, for usage summary ``ipfw''");
5200
5201         /*
5202          * An undocumented behaviour of ipfw1 was to allow rule numbers first,
5203          * e.g. "100 add allow ..." instead of "add 100 allow ...".
5204          * In case, swap first and second argument to get the normal form.
5205          */
5206         if (ac > 1 && isdigit(*av[0])) {
5207                 char *p = av[0];
5208
5209                 av[0] = av[1];
5210                 av[1] = p;
5211         }
5212
5213         /*
5214          * optional: pipe or queue
5215          */
5216         do_pipe = 0;
5217         if (_substrcmp(*av, "pipe") == 0)
5218                 do_pipe = 1;
5219         else if (_substrcmp(*av, "queue") == 0)
5220                 do_pipe = 2;
5221         if (do_pipe) {
5222                 ac--;
5223                 av++;
5224         }
5225         NEED1("missing command");
5226
5227         /*
5228          * For pipes and queues we normally say 'pipe NN config'
5229          * but the code is easier to parse as 'pipe config NN'
5230          * so we swap the two arguments.
5231          */
5232         if (do_pipe > 0 && ac > 1 && isdigit(*av[0])) {
5233                 char *p = av[0];
5234
5235                 av[0] = av[1];
5236                 av[1] = p;
5237         }
5238
5239         if (_substrcmp(*av, "add") == 0)
5240                 add(ac, av);
5241         else if (do_pipe && _substrcmp(*av, "config") == 0)
5242                 config_pipe(ac, av);
5243         else if (_substrcmp(*av, "delete") == 0)
5244                 delete(ac, av);
5245         else if (_substrcmp(*av, "flush") == 0)
5246                 flush(do_force);
5247         else if (_substrcmp(*av, "zero") == 0)
5248                 zero(ac, av, IP_FW_ZERO);
5249         else if (_substrcmp(*av, "resetlog") == 0)
5250                 zero(ac, av, IP_FW_RESETLOG);
5251         else if (_substrcmp(*av, "print") == 0 ||
5252                  _substrcmp(*av, "list") == 0)
5253                 list(ac, av, do_acct);
5254         else if (_substrcmp(*av, "set") == 0)
5255                 sets_handler(ac, av);
5256         else if (_substrcmp(*av, "table") == 0)
5257                 table_handler(ac, av);
5258         else if (_substrcmp(*av, "enable") == 0)
5259                 sysctl_handler(ac, av, 1);
5260         else if (_substrcmp(*av, "disable") == 0)
5261                 sysctl_handler(ac, av, 0);
5262         else if (_substrcmp(*av, "show") == 0)
5263                 list(ac, av, 1 /* show counters */);
5264         else
5265                 errx(EX_USAGE, "bad command `%s'", *av);
5266
5267         /* Free memory allocated in the argument parsing. */
5268         free_args(save_ac, save_av);
5269         return 0;
5270 }
5271
5272
5273 static void
5274 ipfw_readfile(int ac, char *av[])
5275 {
5276 #define MAX_ARGS        32
5277         char    buf[BUFSIZ];
5278         char    *cmd = NULL, *filename = av[ac-1];
5279         int     c, lineno=0;
5280         FILE    *f = NULL;
5281         pid_t   preproc = 0;
5282
5283         filename = av[ac-1];
5284
5285         while ((c = getopt(ac, av, "cfNnp:qS")) != -1) {
5286                 switch(c) {
5287                 case 'c':
5288                         do_compact = 1;
5289                         break;
5290
5291                 case 'f':
5292                         do_force = 1;
5293                         break;
5294
5295                 case 'N':
5296                         do_resolv = 1;
5297                         break;
5298
5299                 case 'n':
5300                         test_only = 1;
5301                         break;
5302
5303                 case 'p':
5304                         cmd = optarg;
5305                         /*
5306                          * Skip previous args and delete last one, so we
5307                          * pass all but the last argument to the preprocessor
5308                          * via av[optind-1]
5309                          */
5310                         av += optind - 1;
5311                         ac -= optind - 1;
5312                         if (ac < 2)
5313                                 errx(EX_USAGE, "no filename argument");
5314                         av[ac-1] = NULL;
5315                         fprintf(stderr, "command is %s\n", av[0]);
5316                         break;
5317
5318                 case 'q':
5319                         do_quiet = 1;
5320                         break;
5321
5322                 case 'S':
5323                         show_sets = 1;
5324                         break;
5325
5326                 default:
5327                         errx(EX_USAGE, "bad arguments, for usage"
5328                              " summary ``ipfw''");
5329                 }
5330
5331                 if (cmd != NULL)
5332                         break;
5333         }
5334
5335         if (cmd == NULL && ac != optind + 1) {
5336                 fprintf(stderr, "ac %d, optind %d\n", ac, optind);
5337                 errx(EX_USAGE, "extraneous filename arguments");
5338         }
5339
5340         if ((f = fopen(filename, "r")) == NULL)
5341                 err(EX_UNAVAILABLE, "fopen: %s", filename);
5342
5343         if (cmd != NULL) {                      /* pipe through preprocessor */
5344                 int pipedes[2];
5345
5346                 if (pipe(pipedes) == -1)
5347                         err(EX_OSERR, "cannot create pipe");
5348
5349                 preproc = fork();
5350                 if (preproc == -1)
5351                         err(EX_OSERR, "cannot fork");
5352
5353                 if (preproc == 0) {
5354                         /*
5355                          * Child, will run the preprocessor with the
5356                          * file on stdin and the pipe on stdout.
5357                          */
5358                         if (dup2(fileno(f), 0) == -1
5359                             || dup2(pipedes[1], 1) == -1)
5360                                 err(EX_OSERR, "dup2()");
5361                         fclose(f);
5362                         close(pipedes[1]);
5363                         close(pipedes[0]);
5364                         execvp(cmd, av);
5365                         err(EX_OSERR, "execvp(%s) failed", cmd);
5366                 } else { /* parent, will reopen f as the pipe */
5367                         fclose(f);
5368                         close(pipedes[1]);
5369                         if ((f = fdopen(pipedes[0], "r")) == NULL) {
5370                                 int savederrno = errno;
5371
5372                                 (void)kill(preproc, SIGTERM);
5373                                 errno = savederrno;
5374                                 err(EX_OSERR, "fdopen()");
5375                         }
5376                 }
5377         }
5378
5379         while (fgets(buf, BUFSIZ, f)) {         /* read commands */
5380                 char linename[10];
5381                 char *args[1];
5382
5383                 lineno++;
5384                 sprintf(linename, "Line %d", lineno);
5385                 setprogname(linename); /* XXX */
5386                 args[0] = buf;
5387                 ipfw_main(1, args);
5388         }
5389         fclose(f);
5390         if (cmd != NULL) {
5391                 int status;
5392
5393                 if (waitpid(preproc, &status, 0) == -1)
5394                         errx(EX_OSERR, "waitpid()");
5395                 if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
5396                         errx(EX_UNAVAILABLE,
5397                             "preprocessor exited with status %d",
5398                             WEXITSTATUS(status));
5399                 else if (WIFSIGNALED(status))
5400                         errx(EX_UNAVAILABLE,
5401                             "preprocessor exited with signal %d",
5402                             WTERMSIG(status));
5403         }
5404 }
5405
5406 int
5407 main(int ac, char *av[])
5408 {
5409         /*
5410          * If the last argument is an absolute pathname, interpret it
5411          * as a file to be preprocessed.
5412          */
5413
5414         if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0)
5415                 ipfw_readfile(ac, av);
5416         else {
5417                 if (ipfw_main(ac-1, av+1))
5418                         show_usage();
5419         }
5420         return EX_OK;
5421 }