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