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