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