ipfw2.c revision 205179
1212420Sken/*
2212420Sken * Copyright (c) 2002-2003 Luigi Rizzo
3237683Sken * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
4212420Sken * Copyright (c) 1994 Ugen J.S.Antsilevich
5212420Sken *
6212420Sken * Idea and grammar partially left from:
7212420Sken * Copyright (c) 1993 Daniel Boulet
8212420Sken *
9212420Sken * Redistribution and use in source forms, with and without modification,
10212420Sken * are permitted provided that this entire comment appears intact.
11212420Sken *
12212420Sken * Redistribution in binary form may occur without any restrictions.
13212420Sken * Obviously, it would be nice if you gave credit where credit is due
14212420Sken * but requiring it would be too onerous.
15212420Sken *
16212420Sken * This software is provided ``AS IS'' without any warranties of any kind.
17212420Sken *
18212420Sken * NEW command line interface for IP firewall facility
19212420Sken *
20212420Sken * $FreeBSD: head/sbin/ipfw/ipfw2.c 205179 2010-03-15 18:20:51Z luigi $
21212420Sken */
22212420Sken
23212420Sken#include <sys/types.h>
24212420Sken#include <sys/socket.h>
25212420Sken#include <sys/sockio.h>
26230592Sken#include <sys/sysctl.h>
27230592Sken
28230592Sken#include "ipfw2.h"
29230592Sken
30230592Sken#include <ctype.h>
31212420Sken#include <err.h>
32212420Sken#include <errno.h>
33212420Sken#include <grp.h>
34212420Sken#include <netdb.h>
35212420Sken#include <pwd.h>
36212420Sken#include <stdio.h>
37230592Sken#include <stdlib.h>
38212420Sken#include <string.h>
39212420Sken#include <sysexits.h>
40212420Sken#include <time.h>	/* ctime */
41212420Sken#include <timeconv.h>	/* _long_to_time */
42212420Sken#include <unistd.h>
43212420Sken#include <fcntl.h>
44212420Sken
45212420Sken#include <net/ethernet.h>
46212420Sken#include <net/if.h>		/* only IFNAMSIZ */
47212420Sken#include <netinet/in.h>
48212420Sken#include <netinet/in_systm.h>	/* only n_short, n_long */
49212420Sken#include <netinet/ip.h>
50216088Sken#include <netinet/ip_icmp.h>
51230592Sken#include <netinet/ip_fw.h>
52230592Sken#include <netinet/tcp.h>
53230592Sken#include <arpa/inet.h>
54230592Sken
55212420Skenstruct cmdline_opts co;	/* global options */
56212420Sken
57212420Skenint resvd_set_number = RESVD_SET;
58212420Sken
59212420Sken#define GET_UINT_ARG(arg, min, max, tok, s_x) do {			\
60230592Sken	if (!av[0])							\
61230592Sken		errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \
62212420Sken	if (_substrcmp(*av, "tablearg") == 0) {				\
63212420Sken		arg = IP_FW_TABLEARG;					\
64230592Sken		break;							\
65212420Sken	}								\
66212420Sken									\
67212420Sken	{								\
68212420Sken	long _xval;							\
69212420Sken	char *end;							\
70212420Sken									\
71212420Sken	_xval = strtol(*av, &end, 10);					\
72216088Sken									\
73216088Sken	if (!isdigit(**av) || *end != '\0' || (_xval == 0 && errno == EINVAL)) \
74216088Sken		errx(EX_DATAERR, "%s: invalid argument: %s",		\
75212420Sken		    match_value(s_x, tok), *av);			\
76212420Sken									\
77212420Sken	if (errno == ERANGE || _xval < min || _xval > max)		\
78212420Sken		errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \
79212420Sken		    match_value(s_x, tok), min, max, *av);		\
80212420Sken									\
81212420Sken	if (_xval == IP_FW_TABLEARG)					\
82230592Sken		errx(EX_DATAERR, "%s: illegal argument value: %s",	\
83230592Sken		    match_value(s_x, tok), *av);			\
84212420Sken	arg = _xval;							\
85212420Sken	}								\
86230592Sken} while (0)
87212420Sken
88230592Skenstatic void
89230592SkenPRINT_UINT_ARG(const char *str, uint32_t arg)
90212420Sken{
91230592Sken	if (str != NULL)
92230592Sken		printf("%s",str);
93230592Sken	if (arg == IP_FW_TABLEARG)
94230592Sken		printf("tablearg");
95230592Sken	else
96230592Sken		printf("%u", arg);
97230592Sken}
98230592Sken
99230592Skenstatic struct _s_x f_tcpflags[] = {
100230592Sken	{ "syn", TH_SYN },
101230592Sken	{ "fin", TH_FIN },
102230592Sken	{ "ack", TH_ACK },
103230592Sken	{ "psh", TH_PUSH },
104230592Sken	{ "rst", TH_RST },
105230592Sken	{ "urg", TH_URG },
106230592Sken	{ "tcp flag", 0 },
107230592Sken	{ NULL,	0 }
108230592Sken};
109230592Sken
110230592Skenstatic struct _s_x f_tcpopts[] = {
111230592Sken	{ "mss",	IP_FW_TCPOPT_MSS },
112230592Sken	{ "maxseg",	IP_FW_TCPOPT_MSS },
113230592Sken	{ "window",	IP_FW_TCPOPT_WINDOW },
114212420Sken	{ "sack",	IP_FW_TCPOPT_SACK },
115212420Sken	{ "ts",		IP_FW_TCPOPT_TS },
116230592Sken	{ "timestamp",	IP_FW_TCPOPT_TS },
117212420Sken	{ "cc",		IP_FW_TCPOPT_CC },
118212420Sken	{ "tcp option",	0 },
119212420Sken	{ NULL,	0 }
120212420Sken};
121212420Sken
122212420Sken/*
123212420Sken * IP options span the range 0 to 255 so we need to remap them
124230592Sken * (though in fact only the low 5 bits are significant).
125230592Sken */
126212420Skenstatic struct _s_x f_ipopts[] = {
127212420Sken	{ "ssrr",	IP_FW_IPOPT_SSRR},
128230592Sken	{ "lsrr",	IP_FW_IPOPT_LSRR},
129216088Sken	{ "rr",		IP_FW_IPOPT_RR},
130216088Sken	{ "ts",		IP_FW_IPOPT_TS},
131216088Sken	{ "ip option",	0 },
132216088Sken	{ NULL,	0 }
133216088Sken};
134230592Sken
135212420Skenstatic struct _s_x f_iptos[] = {
136230592Sken	{ "lowdelay",	IPTOS_LOWDELAY},
137230592Sken	{ "throughput",	IPTOS_THROUGHPUT},
138230592Sken	{ "reliability", IPTOS_RELIABILITY},
139230592Sken	{ "mincost",	IPTOS_MINCOST},
140253549Sken	{ "congestion",	IPTOS_ECN_CE},
141253549Sken	{ "ecntransport", IPTOS_ECN_ECT0},
142253549Sken	{ "ip tos option", 0},
143253549Sken	{ NULL,	0 }
144230592Sken};
145230592Sken
146230592Skenstatic struct _s_x limit_masks[] = {
147230592Sken	{"all",		DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
148230592Sken	{"src-addr",	DYN_SRC_ADDR},
149212420Sken	{"src-port",	DYN_SRC_PORT},
150231240Sken	{"dst-addr",	DYN_DST_ADDR},
151230592Sken	{"dst-port",	DYN_DST_PORT},
152216368Sken	{NULL,		0}
153230592Sken};
154230592Sken
155216368Sken/*
156264492Sscottl * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines
157230592Sken * This is only used in this code.
158230592Sken */
159230592Sken#define IPPROTO_ETHERTYPE	0x1000
160216368Skenstatic struct _s_x ether_types[] = {
161216368Sken    /*
162230592Sken     * Note, we cannot use "-:&/" in the names because they are field
163216368Sken     * separators in the type specifications. Also, we use s = NULL as
164216368Sken     * end-delimiter, because a type of 0 can be legal.
165230592Sken     */
166230592Sken	{ "ip",		0x0800 },
167230592Sken	{ "ipv4",	0x0800 },
168230592Sken	{ "ipv6",	0x86dd },
169230592Sken	{ "arp",	0x0806 },
170230592Sken	{ "rarp",	0x8035 },
171230592Sken	{ "vlan",	0x8100 },
172230592Sken	{ "loop",	0x9000 },
173230592Sken	{ "trail",	0x1000 },
174212420Sken	{ "at",		0x809b },
175253460Sscottl	{ "atalk",	0x809b },
176253460Sscottl	{ "aarp",	0x80f3 },
177230592Sken	{ "pppoe_disc",	0x8863 },
178230592Sken	{ "pppoe_sess",	0x8864 },
179230592Sken	{ "ipx_8022",	0x00E0 },
180253460Sscottl	{ "ipx_8023",	0x0000 },
181230592Sken	{ "ipx_ii",	0x8137 },
182262853Smav	{ "ipx_snap",	0x8137 },
183262853Smav	{ "ipx",	0x8137 },
184262853Smav	{ "ns",		0x0600 },
185230592Sken	{ NULL,		0 }
186230592Sken};
187253460Sscottl
188230592Sken
189230592Skenstatic struct _s_x rule_actions[] = {
190230592Sken	{ "accept",		TOK_ACCEPT },
191212420Sken	{ "pass",		TOK_ACCEPT },
192230592Sken	{ "allow",		TOK_ACCEPT },
193270250Sslm	{ "permit",		TOK_ACCEPT },
194270250Sslm	{ "count",		TOK_COUNT },
195270250Sslm	{ "pipe",		TOK_PIPE },
196270250Sslm	{ "queue",		TOK_QUEUE },
197270250Sslm	{ "divert",		TOK_DIVERT },
198270250Sslm	{ "tee",		TOK_TEE },
199270250Sslm	{ "netgraph",		TOK_NETGRAPH },
200270250Sslm	{ "ngtee",		TOK_NGTEE },
201270250Sslm	{ "fwd",		TOK_FORWARD },
202270250Sslm	{ "forward",		TOK_FORWARD },
203230592Sken	{ "skipto",		TOK_SKIPTO },
204230592Sken	{ "deny",		TOK_DENY },
205253460Sscottl	{ "drop",		TOK_DENY },
206253460Sscottl	{ "reject",		TOK_REJECT },
207230592Sken	{ "reset6",		TOK_RESET6 },
208230592Sken	{ "reset",		TOK_RESET },
209230592Sken	{ "unreach6",		TOK_UNREACH6 },
210230592Sken	{ "unreach",		TOK_UNREACH },
211230592Sken	{ "check-state",	TOK_CHECKSTATE },
212253460Sscottl	{ "//",			TOK_COMMENT },
213230592Sken	{ "nat",                TOK_NAT },
214230592Sken	{ "reass",		TOK_REASS },
215262853Smav	{ "setfib",		TOK_SETFIB },
216253549Sken	{ NULL, 0 }	/* terminator */
217253549Sken};
218253549Sken
219230592Skenstatic struct _s_x rule_action_params[] = {
220253549Sken	{ "altq",		TOK_ALTQ },
221230592Sken	{ "log",		TOK_LOG },
222253460Sscottl	{ "tag",		TOK_TAG },
223230592Sken	{ "untag",		TOK_UNTAG },
224212420Sken	{ NULL, 0 }	/* terminator */
225230592Sken};
226212420Sken
227230592Sken/*
228230592Sken * The 'lookup' instruction accepts one of the following arguments.
229230592Sken * -1 is a terminator for the list.
230230592Sken * Arguments are passed as v[1] in O_DST_LOOKUP options.
231230592Sken */
232230592Skenstatic int lookup_key[] = {
233230592Sken	TOK_DSTIP, TOK_SRCIP, TOK_DSTPORT, TOK_SRCPORT,
234230592Sken	TOK_UID, TOK_JAIL, TOK_DSCP, -1 };
235212420Sken
236253460Sscottlstatic struct _s_x rule_options[] = {
237230592Sken	{ "tagged",		TOK_TAGGED },
238230592Sken	{ "uid",		TOK_UID },
239230592Sken	{ "gid",		TOK_GID },
240253460Sscottl	{ "jail",		TOK_JAIL },
241253460Sscottl	{ "in",			TOK_IN },
242230592Sken	{ "limit",		TOK_LIMIT },
243230592Sken	{ "keep-state",		TOK_KEEPSTATE },
244253460Sscottl	{ "bridged",		TOK_LAYER2 },
245230592Sken	{ "layer2",		TOK_LAYER2 },
246230592Sken	{ "out",		TOK_OUT },
247230592Sken	{ "diverted",		TOK_DIVERTED },
248212420Sken	{ "diverted-loopback",	TOK_DIVERTEDLOOPBACK },
249212420Sken	{ "diverted-output",	TOK_DIVERTEDOUTPUT },
250230592Sken	{ "xmit",		TOK_XMIT },
251230592Sken	{ "recv",		TOK_RECV },
252212420Sken	{ "via",		TOK_VIA },
253253460Sscottl	{ "fragment",		TOK_FRAG },
254230592Sken	{ "frag",		TOK_FRAG },
255230592Sken	{ "fib",		TOK_FIB },
256212420Sken	{ "ipoptions",		TOK_IPOPTS },
257230592Sken	{ "ipopts",		TOK_IPOPTS },
258230592Sken	{ "iplen",		TOK_IPLEN },
259230592Sken	{ "ipid",		TOK_IPID },
260230592Sken	{ "ipprecedence",	TOK_IPPRECEDENCE },
261230592Sken	{ "dscp",		TOK_DSCP },
262253460Sscottl	{ "iptos",		TOK_IPTOS },
263230592Sken	{ "ipttl",		TOK_IPTTL },
264212420Sken	{ "ipversion",		TOK_IPVER },
265253460Sscottl	{ "ipver",		TOK_IPVER },
266230592Sken	{ "estab",		TOK_ESTAB },
267212420Sken	{ "established",	TOK_ESTAB },
268230592Sken	{ "setup",		TOK_SETUP },
269212420Sken	{ "tcpdatalen",		TOK_TCPDATALEN },
270212420Sken	{ "tcpflags",		TOK_TCPFLAGS },
271230592Sken	{ "tcpflgs",		TOK_TCPFLAGS },
272230592Sken	{ "tcpoptions",		TOK_TCPOPTS },
273212420Sken	{ "tcpopts",		TOK_TCPOPTS },
274230592Sken	{ "tcpseq",		TOK_TCPSEQ },
275230592Sken	{ "tcpack",		TOK_TCPACK },
276230592Sken	{ "tcpwin",		TOK_TCPWIN },
277230592Sken	{ "icmptype",		TOK_ICMPTYPES },
278212420Sken	{ "icmptypes",		TOK_ICMPTYPES },
279253460Sscottl	{ "dst-ip",		TOK_DSTIP },
280230592Sken	{ "src-ip",		TOK_SRCIP },
281230592Sken	{ "dst-port",		TOK_DSTPORT },
282230592Sken	{ "src-port",		TOK_SRCPORT },
283230592Sken	{ "proto",		TOK_PROTO },
284230592Sken	{ "MAC",		TOK_MAC },
285212420Sken	{ "mac",		TOK_MAC },
286230592Sken	{ "mac-type",		TOK_MACTYPE },
287230592Sken	{ "verrevpath",		TOK_VERREVPATH },
288230592Sken	{ "versrcreach",	TOK_VERSRCREACH },
289230592Sken	{ "antispoof",		TOK_ANTISPOOF },
290230592Sken	{ "ipsec",		TOK_IPSEC },
291253460Sscottl	{ "icmp6type",		TOK_ICMP6TYPES },
292212420Sken	{ "icmp6types",		TOK_ICMP6TYPES },
293212420Sken	{ "ext6hdr",		TOK_EXT6HDR},
294212420Sken	{ "flow-id",		TOK_FLOWID},
295249468Smav	{ "ipv6",		TOK_IPV6},
296253550Sken	{ "ip6",		TOK_IPV6},
297253460Sscottl	{ "ipv4",		TOK_IPV4},
298230592Sken	{ "ip4",		TOK_IPV4},
299212420Sken	{ "dst-ipv6",		TOK_DSTIP6},
300212420Sken	{ "dst-ip6",		TOK_DSTIP6},
301230592Sken	{ "src-ipv6",		TOK_SRCIP6},
302237800Sken	{ "src-ip6",		TOK_SRCIP6},
303237800Sken	{ "lookup",		TOK_LOOKUP},
304237800Sken	{ "//",			TOK_COMMENT },
305237800Sken
306237800Sken	{ "not",		TOK_NOT },		/* pseudo option */
307230592Sken	{ "!", /* escape ? */	TOK_NOT },		/* pseudo option */
308253549Sken	{ "or",			TOK_OR },		/* pseudo option */
309212420Sken	{ "|", /* escape */	TOK_OR },		/* pseudo option */
310212420Sken	{ "{",			TOK_STARTBRACE },	/* pseudo option */
311212420Sken	{ "(",			TOK_STARTBRACE },	/* pseudo option */
312253460Sscottl	{ "}",			TOK_ENDBRACE },		/* pseudo option */
313212420Sken	{ ")",			TOK_ENDBRACE },		/* pseudo option */
314230592Sken	{ NULL, 0 }	/* terminator */
315230592Sken};
316230592Sken
317230592Sken/*
318212420Sken * The following is used to generate a printable argument for
319230592Sken * 64-bit numbers, irrespective of platform alignment and bit size.
320230592Sken * Because all the printf in this program use %llu as a format,
321212420Sken * we just return an unsigned long long, which is larger than
322254253Sscottl * we need in certain cases, but saves the hassle of using
323254257Smav * PRIu64 as a format specifier.
324254253Sscottl * We don't care about inlining, this is not performance critical code.
325254253Sscottl */
326230592Skenunsigned long long
327212420Skenalign_uint64(const uint64_t *pll)
328230592Sken{
329212420Sken	uint64_t ret;
330230592Sken
331230592Sken	bcopy (pll, &ret, sizeof(ret));
332230592Sken	return ret;
333230592Sken}
334230592Sken
335230592Skenvoid *
336230592Skensafe_calloc(size_t number, size_t size)
337230592Sken{
338212420Sken	void *ret = calloc(number, size);
339212420Sken
340230592Sken	if (ret == NULL)
341230592Sken		err(EX_OSERR, "calloc");
342230592Sken	return ret;
343230592Sken}
344230592Sken
345230592Skenvoid *
346230592Skensafe_realloc(void *ptr, size_t size)
347230592Sken{
348212420Sken	void *ret = realloc(ptr, size);
349230592Sken
350230592Sken	if (ret == NULL)
351230592Sken		err(EX_OSERR, "realloc");
352253460Sscottl	return ret;
353212420Sken}
354230592Sken
355230592Sken/*
356212420Sken * conditionally runs the command.
357231240Sken * Selected options or negative -> getsockopt
358230592Sken */
359231240Skenint
360230592Skendo_cmd(int optname, void *optval, uintptr_t optlen)
361231240Sken{
362231240Sken	static int s = -1;	/* the socket */
363231240Sken	int i;
364212420Sken
365253460Sscottl	if (co.test_only)
366231240Sken		return 0;
367231240Sken
368231240Sken	if (s == -1)
369231240Sken		s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
370231240Sken	if (s < 0)
371231240Sken		err(EX_UNAVAILABLE, "socket");
372231240Sken
373253460Sscottl	if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
374253460Sscottl	    optname == IP_FW_ADD || optname == IP_FW_TABLE_LIST ||
375231240Sken	    optname == IP_FW_TABLE_GETSIZE ||
376230592Sken	    optname == IP_FW_NAT_GET_CONFIG ||
377230592Sken	    optname < 0 ||
378212420Sken	    optname == IP_FW_NAT_GET_LOG) {
379231240Sken		if (optname < 0)
380253460Sscottl			optname = -optname;
381253460Sscottl		i = getsockopt(s, IPPROTO_IP, optname, optval,
382231240Sken			(socklen_t *)optlen);
383231240Sken	} else {
384231240Sken		i = setsockopt(s, IPPROTO_IP, optname, optval, optlen);
385231240Sken	}
386231240Sken	return i;
387253460Sscottl}
388253460Sscottl
389231240Sken/**
390240518Seadler * match_token takes a table and a string, returns the value associated
391231240Sken * with the string (-1 in case of failure).
392253460Sscottl */
393253460Sscottlint
394231240Skenmatch_token(struct _s_x *table, char *string)
395231240Sken{
396231240Sken	struct _s_x *pt;
397231240Sken	uint i = strlen(string);
398231240Sken
399231240Sken	for (pt = table ; i && pt->s != NULL ; pt++)
400231240Sken		if (strlen(pt->s) == i && !bcmp(string, pt->s, i))
401231240Sken			return pt->x;
402231240Sken	return -1;
403231240Sken}
404231240Sken
405231240Sken/**
406231240Sken * match_value takes a table and a value, returns the string associated
407231240Sken * with the value (NULL in case of failure).
408231240Sken */
409231240Skenchar const *
410231240Skenmatch_value(struct _s_x *p, int value)
411231240Sken{
412231240Sken	for (; p->s != NULL; p++)
413231240Sken		if (p->x == value)
414212420Sken			return p->s;
415212420Sken	return NULL;
416231240Sken}
417212420Sken
418231240Sken/*
419231240Sken * _substrcmp takes two strings and returns 1 if they do not match,
420231240Sken * and 0 if they match exactly or the first string is a sub-string
421231240Sken * of the second.  A warning is printed to stderr in the case that the
422231240Sken * first string is a sub-string of the second.
423231240Sken *
424231240Sken * This function will be removed in the future through the usual
425231240Sken * deprecation process.
426231240Sken */
427231240Skenint
428231240Sken_substrcmp(const char *str1, const char* str2)
429253460Sscottl{
430231240Sken
431231240Sken	if (strncmp(str1, str2, strlen(str1)) != 0)
432231240Sken		return 1;
433231240Sken
434231240Sken	if (strlen(str1) != strlen(str2))
435231240Sken		warnx("DEPRECATED: '%s' matched '%s' as a sub-string",
436231240Sken		    str1, str2);
437231240Sken	return 0;
438231240Sken}
439231240Sken
440231240Sken/*
441231240Sken * _substrcmp2 takes three strings and returns 1 if the first two do not match,
442231240Sken * and 0 if they match exactly or the second string is a sub-string
443231240Sken * of the first.  A warning is printed to stderr in the case that the
444231240Sken * first string does not match the third.
445231240Sken *
446231240Sken * This function exists to warn about the bizzare construction
447231240Sken * strncmp(str, "by", 2) which is used to allow people to use a shotcut
448253460Sscottl * for "bytes".  The problem is that in addition to accepting "by",
449253460Sscottl * "byt", "byte", and "bytes", it also excepts "by_rabid_dogs" and any
450231240Sken * other string beginning with "by".
451231240Sken *
452231240Sken * This function will be removed in the future through the usual
453231240Sken * deprecation process.
454231240Sken */
455231240Skenint
456231240Sken_substrcmp2(const char *str1, const char* str2, const char* str3)
457253460Sscottl{
458253460Sscottl
459231240Sken	if (strncmp(str1, str2, strlen(str2)) != 0)
460231240Sken		return 1;
461231240Sken
462231240Sken	if (strcmp(str1, str3) != 0)
463231240Sken		warnx("DEPRECATED: '%s' matched '%s'",
464231240Sken		    str1, str3);
465231240Sken	return 0;
466231240Sken}
467231240Sken
468231240Sken/*
469231240Sken * prints one port, symbolic or numeric
470231240Sken */
471231240Skenstatic void
472231240Skenprint_port(int proto, uint16_t port)
473231240Sken{
474231240Sken
475231240Sken	if (proto == IPPROTO_ETHERTYPE) {
476231240Sken		char const *s;
477231240Sken
478231240Sken		if (co.do_resolv && (s = match_value(ether_types, port)) )
479231240Sken			printf("%s", s);
480231240Sken		else
481231240Sken			printf("0x%04x", port);
482230592Sken	} else {
483230592Sken		struct servent *se = NULL;
484230592Sken		if (co.do_resolv) {
485230592Sken			struct protoent *pe = getprotobynumber(proto);
486230592Sken
487230592Sken			se = getservbyport(htons(port), pe ? pe->p_name : NULL);
488230592Sken		}
489212420Sken		if (se)
490230592Sken			printf("%s", se->s_name);
491230592Sken		else
492212420Sken			printf("%d", port);
493212420Sken	}
494212420Sken}
495212420Sken
496212420Skenstatic struct _s_x _port_name[] = {
497212420Sken	{"dst-port",	O_IP_DSTPORT},
498253460Sscottl	{"src-port",	O_IP_SRCPORT},
499212420Sken	{"ipid",	O_IPID},
500230592Sken	{"iplen",	O_IPLEN},
501230592Sken	{"ipttl",	O_IPTTL},
502230592Sken	{"mac-type",	O_MAC_TYPE},
503230592Sken	{"tcpdatalen",	O_TCPDATALEN},
504230592Sken	{"tagged",	O_TAGGED},
505212420Sken	{NULL,		0}
506253460Sscottl};
507253460Sscottl
508212420Sken/*
509230592Sken * Print the values in a list 16-bit items of the types above.
510212420Sken * XXX todo: add support for mask.
511230592Sken */
512230592Skenstatic void
513230592Skenprint_newports(ipfw_insn_u16 *cmd, int proto, int opcode)
514212420Sken{
515253460Sscottl	uint16_t *p = cmd->ports;
516253460Sscottl	int i;
517212420Sken	char const *sep;
518212420Sken
519212420Sken	if (opcode != 0) {
520231240Sken		sep = match_value(_port_name, opcode);
521218811Sken		if (sep == NULL)
522212420Sken			sep = "???";
523218811Sken		printf (" %s", sep);
524237683Sken	}
525212420Sken	sep = " ";
526212420Sken	for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
527212420Sken		printf("%s", sep);
528212420Sken		print_port(proto, p[0]);
529212420Sken		if (p[0] != p[1]) {
530212420Sken			printf("-");
531230592Sken			print_port(proto, p[1]);
532212420Sken		}
533230592Sken		sep = ",";
534212420Sken	}
535230592Sken}
536230592Sken
537212420Sken/*
538212420Sken * Like strtol, but also translates service names into port numbers
539212420Sken * for some protocols.
540230592Sken * In particular:
541212420Sken *	proto == -1 disables the protocol check;
542212420Sken *	proto == IPPROTO_ETHERTYPE looks up an internal table
543212420Sken *	proto == <some value in /etc/protocols> matches the values there.
544212420Sken * Returns *end == s in case the parameter is not found.
545218811Sken */
546212420Skenstatic int
547212420Skenstrtoport(char *s, char **end, int base, int proto)
548253460Sscottl{
549212420Sken	char *p, *buf;
550230592Sken	char *s1;
551230592Sken	int i;
552230592Sken
553213535Sken	*end = s;		/* default - not found */
554218812Sken	if (*s == '\0')
555218812Sken		return 0;	/* not found */
556218812Sken
557218812Sken	if (isdigit(*s))
558218812Sken		return strtol(s, end, base);
559230592Sken
560253460Sscottl	/*
561253460Sscottl	 * find separator. '\\' escapes the next char.
562253460Sscottl	 */
563253460Sscottl	for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++)
564230592Sken		if (*s1 == '\\' && s1[1] != '\0')
565218812Sken			s1++;
566218812Sken
567218812Sken	buf = safe_calloc(s1 - s + 1, 1);
568230592Sken
569230592Sken	/*
570253460Sscottl	 * copy into a buffer skipping backslashes
571253460Sscottl	 */
572230592Sken	for (p = s, i = 0; p != s1 ; p++)
573230592Sken		if (*p != '\\')
574230592Sken			buf[i++] = *p;
575230592Sken	buf[i++] = '\0';
576237683Sken
577253460Sscottl	if (proto == IPPROTO_ETHERTYPE) {
578253460Sscottl		i = match_token(ether_types, buf);
579237683Sken		free(buf);
580230592Sken		if (i != -1) {	/* found */
581212420Sken			*end = s1;
582212420Sken			return i;
583212420Sken		}
584253460Sscottl	} else {
585237683Sken		struct protoent *pe = NULL;
586230592Sken		struct servent *se;
587240518Seadler
588212420Sken		if (proto != 0)
589212420Sken			pe = getprotobynumber(proto);
590230592Sken		setservent(1);
591218811Sken		se = getservbyname(buf, pe ? pe->p_name : NULL);
592212420Sken		free(buf);
593212420Sken		if (se != NULL) {
594237683Sken			*end = s1;
595230592Sken			return ntohs(se->s_port);
596230592Sken		}
597230592Sken	}
598230592Sken	return 0;	/* not found */
599212420Sken}
600230592Sken
601212420Sken/*
602253460Sscottl * Fill the body of the command with the list of port ranges.
603230592Sken */
604230592Skenstatic int
605218811Skenfill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
606218811Sken{
607253460Sscottl	uint16_t a, b, *p = cmd->ports;
608230592Sken	int i = 0;
609268197Sscottl	char *s = av;
610230592Sken
611218811Sken	while (*s) {
612212420Sken		a = strtoport(av, &s, 0, proto);
613212420Sken		if (s == av) 			/* empty or invalid argument */
614212420Sken			return (0);
615230592Sken
616212420Sken		switch (*s) {
617212420Sken		case '-':			/* a range */
618230592Sken			av = s + 1;
619230592Sken			b = strtoport(av, &s, 0, proto);
620237683Sken			/* Reject expressions like '1-abc' or '1-2-3'. */
621212420Sken			if (s == av || (*s != ',' && *s != '\0'))
622253460Sscottl				return (0);
623212420Sken			p[0] = a;
624230592Sken			p[1] = b;
625230592Sken			break;
626212420Sken		case ',':			/* comma separated list */
627230592Sken		case '\0':
628230592Sken			p[0] = p[1] = a;
629230592Sken			break;
630230592Sken		default:
631230592Sken			warnx("port list: invalid separator <%c> in <%s>",
632230592Sken				*s, av);
633253460Sscottl			return (0);
634253460Sscottl		}
635230592Sken
636230592Sken		i++;
637230592Sken		p += 2;
638230592Sken		av = s + 1;
639230592Sken	}
640212420Sken	if (i > 0) {
641230592Sken		if (i + 1 > F_LEN_MASK)
642230592Sken			errx(EX_DATAERR, "too many ports/ranges\n");
643253460Sscottl		cmd->o.len |= i + 1;	/* leave F_NOT and F_OR untouched */
644253460Sscottl	}
645230592Sken	return (i);
646230592Sken}
647212420Sken
648212420Skenstatic struct _s_x icmpcodes[] = {
649253460Sscottl      { "net",			ICMP_UNREACH_NET },
650253460Sscottl      { "host",			ICMP_UNREACH_HOST },
651237683Sken      { "protocol",		ICMP_UNREACH_PROTOCOL },
652212420Sken      { "port",			ICMP_UNREACH_PORT },
653230592Sken      { "needfrag",		ICMP_UNREACH_NEEDFRAG },
654230592Sken      { "srcfail",		ICMP_UNREACH_SRCFAIL },
655230592Sken      { "net-unknown",		ICMP_UNREACH_NET_UNKNOWN },
656230592Sken      { "host-unknown",		ICMP_UNREACH_HOST_UNKNOWN },
657230592Sken      { "isolated",		ICMP_UNREACH_ISOLATED },
658230592Sken      { "net-prohib",		ICMP_UNREACH_NET_PROHIB },
659237683Sken      { "host-prohib",		ICMP_UNREACH_HOST_PROHIB },
660230592Sken      { "tosnet",		ICMP_UNREACH_TOSNET },
661230592Sken      { "toshost",		ICMP_UNREACH_TOSHOST },
662230592Sken      { "filter-prohib",	ICMP_UNREACH_FILTER_PROHIB },
663230592Sken      { "host-precedence",	ICMP_UNREACH_HOST_PRECEDENCE },
664230592Sken      { "precedence-cutoff",	ICMP_UNREACH_PRECEDENCE_CUTOFF },
665230592Sken      { NULL, 0 }
666230592Sken};
667230592Sken
668231240Skenstatic void
669237683Skenfill_reject_code(u_short *codep, char *str)
670237683Sken{
671237683Sken	int val;
672237683Sken	char *s;
673237683Sken
674237683Sken	val = strtoul(str, &s, 0);
675212420Sken	if (s == str || *s != '\0' || val >= 0x100)
676237683Sken		val = match_token(icmpcodes, str);
677212420Sken	if (val < 0)
678230592Sken		errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
679212420Sken	*codep = val;
680212420Sken	return;
681212420Sken}
682212420Sken
683212420Skenstatic void
684237683Skenprint_reject_code(uint16_t code)
685212420Sken{
686212420Sken	char const *s = match_value(icmpcodes, code);
687212420Sken
688212420Sken	if (s != NULL)
689212420Sken		printf("unreach %s", s);
690212420Sken	else
691212420Sken		printf("unreach %u", code);
692212420Sken}
693212420Sken
694230592Sken/*
695230592Sken * Returns the number of bits set (from left) in a contiguous bitmask,
696230592Sken * or -1 if the mask is not contiguous.
697230592Sken * XXX this needs a proper fix.
698230592Sken * This effectively works on masks in big-endian (network) format.
699212420Sken * when compiled on little endian architectures.
700212420Sken *
701212420Sken * First bit is bit 7 of the first byte -- note, for MAC addresses,
702212420Sken * the first bit on the wire is bit 0 of the first byte.
703212420Sken * len is the max length in bits.
704212420Sken */
705212420Skenint
706212420Skencontigmask(uint8_t *p, int len)
707212420Sken{
708212420Sken	int i, n;
709212420Sken
710230592Sken	for (i=0; i<len ; i++)
711230592Sken		if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
712212420Sken			break;
713253460Sscottl	for (n=i+1; n < len; n++)
714212420Sken		if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
715212420Sken			return -1; /* mask not contiguous */
716237683Sken	return i;
717237683Sken}
718237683Sken
719237683Sken/*
720237683Sken * print flags set/clear in the two bitmasks passed as parameters.
721264492Sscottl * There is a specialized check for f_tcpflags.
722264492Sscottl */
723264492Sscottlstatic void
724264492Sscottlprint_flags(char const *name, ipfw_insn *cmd, struct _s_x *list)
725264492Sscottl{
726264492Sscottl	char const *comma = "";
727264492Sscottl	int i;
728264492Sscottl	uint8_t set = cmd->arg1 & 0xff;
729212420Sken	uint8_t clear = (cmd->arg1 >> 8) & 0xff;
730264492Sscottl
731237683Sken	if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) {
732237683Sken		printf(" setup");
733237683Sken		return;
734237683Sken	}
735237683Sken
736237683Sken	printf(" %s ", name);
737212420Sken	for (i=0; list[i].x != 0; i++) {
738212420Sken		if (set & list[i].x) {
739212420Sken			set &= ~list[i].x;
740230592Sken			printf("%s%s", comma, list[i].s);
741253460Sscottl			comma = ",";
742212420Sken		}
743212420Sken		if (clear & list[i].x) {
744212420Sken			clear &= ~list[i].x;
745212420Sken			printf("%s!%s", comma, list[i].s);
746230592Sken			comma = ",";
747212420Sken		}
748230592Sken	}
749212420Sken}
750253460Sscottl
751212420Sken/*
752212420Sken * Print the ip address contained in a command.
753212420Sken */
754212420Skenstatic void
755230592Skenprint_ip(ipfw_insn_ip *cmd, char const *s)
756230592Sken{
757230592Sken	struct hostent *he = NULL;
758230592Sken	uint32_t len = F_LEN((ipfw_insn *)cmd);
759230592Sken	uint32_t *a = ((ipfw_insn_u32 *)cmd)->d;
760230592Sken
761230592Sken	if (cmd->o.opcode == O_IP_DST_LOOKUP && len > F_INSN_SIZE(ipfw_insn_u32)) {
762230592Sken		uint32_t d = a[1];
763230592Sken		const char *arg = "<invalid>";
764230592Sken
765230592Sken		if (d < sizeof(lookup_key)/sizeof(lookup_key[0]))
766230592Sken			arg = match_value(rule_options, lookup_key[d]);
767230592Sken		printf("%s lookup %s %d", cmd->o.len & F_NOT ? " not": "",
768212420Sken			arg, cmd->o.arg1);
769212420Sken		return;
770212420Sken	}
771212420Sken	printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
772212420Sken
773212420Sken	if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) {
774253460Sscottl		printf("me");
775212420Sken		return;
776212420Sken	}
777212420Sken	if (cmd->o.opcode == O_IP_SRC_LOOKUP ||
778212420Sken	    cmd->o.opcode == O_IP_DST_LOOKUP) {
779212420Sken		printf("table(%u", ((ipfw_insn *)cmd)->arg1);
780212420Sken		if (len == F_INSN_SIZE(ipfw_insn_u32))
781253549Sken			printf(",%u", *a);
782253549Sken		printf(")");
783253549Sken		return;
784212420Sken	}
785230592Sken	if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) {
786230592Sken		uint32_t x, *map = (uint32_t *)&(cmd->mask);
787262853Smav		int i, j;
788212420Sken		char comma = '{';
789212420Sken
790212420Sken		x = cmd->o.arg1 - 1;
791212420Sken		x = htonl( ~x );
792230592Sken		cmd->addr.s_addr = htonl(cmd->addr.s_addr);
793230592Sken		printf("%s/%d", inet_ntoa(cmd->addr),
794253549Sken			contigmask((uint8_t *)&x, 32));
795253549Sken		x = cmd->addr.s_addr = htonl(cmd->addr.s_addr);
796253549Sken		x &= 0xff; /* base */
797253549Sken		/*
798253549Sken		 * Print bits and ranges.
799253549Sken		 * Locate first bit set (i), then locate first bit unset (j).
800253549Sken		 * If we have 3+ consecutive bits set, then print them as a
801230592Sken		 * range, otherwise only print the initial bit and rescan.
802253549Sken		 */
803253549Sken		for (i=0; i < cmd->o.arg1; i++)
804253549Sken			if (map[i/32] & (1<<(i & 31))) {
805253549Sken				for (j=i+1; j < cmd->o.arg1; j++)
806253549Sken					if (!(map[ j/32] & (1<<(j & 31))))
807253549Sken						break;
808253549Sken				printf("%c%d", comma, i+x);
809253549Sken				if (j>i+2) { /* range has at least 3 elements */
810253549Sken					printf("-%d", j-1+x);
811253549Sken					i = j-1;
812253549Sken				}
813253549Sken				comma = ',';
814253549Sken			}
815253549Sken		printf("}");
816253549Sken		return;
817253549Sken	}
818253549Sken	/*
819253549Sken	 * len == 2 indicates a single IP, whereas lists of 1 or more
820253549Sken	 * addr/mask pairs have len = (2n+1). We convert len to n so we
821253549Sken	 * use that to count the number of entries.
822230592Sken	 */
823253549Sken    for (len = len / 2; len > 0; len--, a += 2) {
824253549Sken	int mb =	/* mask length */
825253549Sken	    (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ?
826253549Sken		32 : contigmask((uint8_t *)&(a[1]), 32);
827253549Sken	if (mb == 32 && co.do_resolv)
828253549Sken		he = gethostbyaddr((char *)&(a[0]), sizeof(u_long), AF_INET);
829253549Sken	if (he != NULL)		/* resolved to name */
830230592Sken		printf("%s", he->h_name);
831230592Sken	else if (mb == 0)	/* any */
832230592Sken		printf("any");
833212420Sken	else {		/* numeric IP followed by some kind of mask */
834212420Sken		printf("%s", inet_ntoa( *((struct in_addr *)&a[0]) ) );
835212420Sken		if (mb < 0)
836212420Sken			printf(":%s", inet_ntoa( *((struct in_addr *)&a[1]) ) );
837212420Sken		else if (mb < 32)
838212420Sken			printf("/%d", mb);
839212420Sken	}
840212420Sken	if (len > 1)
841212420Sken		printf(",");
842212420Sken    }
843212420Sken}
844237683Sken
845237683Sken/*
846237683Sken * prints a MAC address/mask pair
847212420Sken */
848253460Sscottlstatic void
849212420Skenprint_mac(uint8_t *addr, uint8_t *mask)
850212420Sken{
851212420Sken	int l = contigmask(mask, 48);
852212420Sken
853212420Sken	if (l == 0)
854230592Sken		printf(" any");
855212420Sken	else {
856230592Sken		printf(" %02x:%02x:%02x:%02x:%02x:%02x",
857230592Sken		    addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
858230592Sken		if (l == -1)
859230592Sken			printf("&%02x:%02x:%02x:%02x:%02x:%02x",
860230592Sken			    mask[0], mask[1], mask[2],
861230592Sken			    mask[3], mask[4], mask[5]);
862230592Sken		else if (l < 48)
863230592Sken			printf("/%d", l);
864212420Sken	}
865212420Sken}
866230592Sken
867230592Skenstatic void
868253549Skenfill_icmptypes(ipfw_insn_u32 *cmd, char *av)
869253549Sken{
870253549Sken	uint8_t type;
871253549Sken
872253549Sken	cmd->d[0] = 0;
873230592Sken	while (*av) {
874212420Sken		if (*av == ',')
875212420Sken			av++;
876212420Sken
877212420Sken		type = strtoul(av, &av, 0);
878212420Sken
879212420Sken		if (*av != ',' && *av != '\0')
880212420Sken			errx(EX_DATAERR, "invalid ICMP type");
881230592Sken
882253549Sken		if (type > 31)
883212420Sken			errx(EX_DATAERR, "ICMP type out of range");
884212420Sken
885212420Sken		cmd->d[0] |= 1 << type;
886212420Sken	}
887212420Sken	cmd->o.opcode = O_ICMPTYPE;
888264492Sscottl	cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
889237683Sken}
890237683Sken
891237683Skenstatic void
892237683Skenprint_icmptypes(ipfw_insn_u32 *cmd)
893237683Sken{
894212420Sken	int i;
895212420Sken	char sep= ' ';
896212420Sken
897212420Sken	printf(" icmptypes");
898212420Sken	for (i = 0; i < 32; i++) {
899212420Sken		if ( (cmd->d[0] & (1 << (i))) == 0)
900212420Sken			continue;
901230592Sken		printf("%c%d", sep, i);
902212420Sken		sep = ',';
903212420Sken	}
904212420Sken}
905212420Sken
906253460Sscottl/*
907212420Sken * show_ipfw() prints the body of an ipfw rule.
908212420Sken * Because the standard rule has at least proto src_ip dst_ip, we use
909212420Sken * a helper function to produce these entries if not provided explicitly.
910212420Sken * The first argument is the list of fields we have, the second is
911212420Sken * the list of fields we want to be printed.
912212420Sken *
913212420Sken * Special cases if we have provided a MAC header:
914212420Sken *   + if the rule does not contain IP addresses/ports, do not print them;
915212420Sken *   + if the rule does not contain an IP proto, print "all" instead of "ip";
916212420Sken *
917212420Sken * Once we have 'have_options', IP header fields are printed as options.
918212420Sken */
919212420Sken#define	HAVE_PROTO	0x0001
920253460Sscottl#define	HAVE_SRCIP	0x0002
921253460Sscottl#define	HAVE_DSTIP	0x0004
922212420Sken#define	HAVE_PROTO4	0x0008
923230592Sken#define	HAVE_PROTO6	0x0010
924212420Sken#define	HAVE_IP		0x0100
925212420Sken#define	HAVE_OPTIONS	0x8000
926212420Sken
927212420Skenstatic void
928212420Skenshow_prerequisites(int *flags, int want, int cmd __unused)
929212420Sken{
930212420Sken	if (co.comment_only)
931212420Sken		return;
932212420Sken	if ( (*flags & HAVE_IP) == HAVE_IP)
933253549Sken		*flags |= HAVE_OPTIONS;
934253549Sken
935253549Sken	if ( !(*flags & HAVE_OPTIONS)) {
936248825Smav		if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO)) {
937253549Sken			if ( (*flags & HAVE_PROTO4))
938212420Sken				printf(" ip4");
939264492Sscottl			else if ( (*flags & HAVE_PROTO6))
940237683Sken				printf(" ip6");
941264492Sscottl			else
942212420Sken				printf(" ip");
943212420Sken		}
944212420Sken		if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
945212420Sken			printf(" from any");
946212420Sken		if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP))
947212420Sken			printf(" to any");
948212420Sken	}
949212420Sken	*flags |= want;
950212420Sken}
951212420Sken
952230592Skenstatic void
953230592Skenshow_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
954230592Sken{
955230592Sken	static int twidth = 0;
956230592Sken	int l;
957230592Sken	ipfw_insn *cmd, *tagptr = NULL;
958268197Sscottl	const char *comment = NULL;	/* ptr to comment if we have one */
959212420Sken	int proto = 0;		/* default */
960212420Sken	int flags = 0;	/* prerequisites */
961212420Sken	ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */
962212420Sken	ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */
963212420Sken	int or_block = 0;	/* we are in an or block */
964212420Sken	uint32_t set_disable;
965212420Sken
966212420Sken	bcopy(&rule->next_rule, &set_disable, sizeof(set_disable));
967212420Sken
968212420Sken	if (set_disable & (1 << rule->set)) { /* disabled */
969212420Sken		if (!co.show_sets)
970212420Sken			return;
971212420Sken		else
972264492Sscottl			printf("# DISABLED ");
973264492Sscottl	}
974264492Sscottl	printf("%05u ", rule->rulenum);
975212420Sken
976212420Sken	if (pcwidth>0 || bcwidth>0)
977270250Sslm		printf("%*llu %*llu ", pcwidth, align_uint64(&rule->pcnt),
978212420Sken		    bcwidth, align_uint64(&rule->bcnt));
979212420Sken
980212420Sken	if (co.do_time == 2)
981212420Sken		printf("%10u ", rule->timestamp);
982212420Sken	else if (co.do_time == 1) {
983212420Sken		char timestr[30];
984212420Sken		time_t t = (time_t)0;
985212420Sken
986212420Sken		if (twidth == 0) {
987212420Sken			strcpy(timestr, ctime(&t));
988212420Sken			*strchr(timestr, '\n') = '\0';
989212420Sken			twidth = strlen(timestr);
990212420Sken		}
991212420Sken		if (rule->timestamp) {
992212420Sken			t = _long_to_time(rule->timestamp);
993212420Sken
994212420Sken			strcpy(timestr, ctime(&t));
995212420Sken			*strchr(timestr, '\n') = '\0';
996212420Sken			printf("%s ", timestr);
997212420Sken		} else {
998212420Sken			printf("%*s", twidth, " ");
999212420Sken		}
1000212420Sken	}
1001212420Sken
1002212420Sken	if (co.show_sets)
1003212420Sken		printf("set %d ", rule->set);
1004268197Sscottl
1005212420Sken	/*
1006212420Sken	 * print the optional "match probability"
1007212420Sken	 */
1008212420Sken	if (rule->cmd_len > 0) {
1009268197Sscottl		cmd = rule->cmd ;
1010212420Sken		if (cmd->opcode == O_PROB) {
1011212420Sken			ipfw_insn_u32 *p = (ipfw_insn_u32 *)cmd;
1012253460Sscottl			double d = 1.0 * p->d[0];
1013212420Sken
1014212420Sken			d = (d / 0x7fffffff);
1015212420Sken			printf("prob %f ", d);
1016212420Sken		}
1017212420Sken	}
1018253460Sscottl
1019253460Sscottl	/*
1020268197Sscottl	 * first print actions
1021212420Sken	 */
1022212420Sken        for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule);
1023212420Sken			l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
1024212420Sken		switch(cmd->opcode) {
1025216088Sken		case O_CHECK_STATE:
1026216088Sken			printf("check-state");
1027216088Sken			/* avoid printing anything else */
1028216088Sken			flags = HAVE_PROTO | HAVE_SRCIP |
1029230592Sken				HAVE_DSTIP | HAVE_IP;
1030212420Sken			break;
1031268197Sscottl
1032212420Sken		case O_ACCEPT:
1033212420Sken			printf("allow");
1034212420Sken			break;
1035212420Sken
1036212420Sken		case O_COUNT:
1037212420Sken			printf("count");
1038212420Sken			break;
1039230592Sken
1040230592Sken		case O_DENY:
1041212420Sken			printf("deny");
1042230592Sken			break;
1043230592Sken
1044212420Sken		case O_REJECT:
1045253460Sscottl			if (cmd->arg1 == ICMP_REJECT_RST)
1046230592Sken				printf("reset");
1047212420Sken			else if (cmd->arg1 == ICMP_UNREACH_HOST)
1048230592Sken				printf("reject");
1049230592Sken			else
1050253460Sscottl				print_reject_code(cmd->arg1);
1051230592Sken			break;
1052230592Sken
1053212420Sken		case O_UNREACH6:
1054212420Sken			if (cmd->arg1 == ICMP6_UNREACH_RST)
1055230592Sken				printf("reset6");
1056230592Sken			else
1057212420Sken				print_unreach6_code(cmd->arg1);
1058212420Sken			break;
1059230592Sken
1060230592Sken		case O_SKIPTO:
1061212420Sken			PRINT_UINT_ARG("skipto ", cmd->arg1);
1062212420Sken			break;
1063230592Sken
1064230592Sken		case O_PIPE:
1065212420Sken			PRINT_UINT_ARG("pipe ", cmd->arg1);
1066253460Sscottl			break;
1067230592Sken
1068212420Sken		case O_QUEUE:
1069230592Sken			PRINT_UINT_ARG("queue ", cmd->arg1);
1070230592Sken			break;
1071230592Sken
1072230592Sken		case O_DIVERT:
1073230592Sken			PRINT_UINT_ARG("divert ", cmd->arg1);
1074230592Sken			break;
1075230592Sken
1076230592Sken		case O_TEE:
1077230592Sken			PRINT_UINT_ARG("tee ", cmd->arg1);
1078230592Sken			break;
1079253460Sscottl
1080230592Sken		case O_NETGRAPH:
1081230592Sken			PRINT_UINT_ARG("netgraph ", cmd->arg1);
1082230592Sken			break;
1083230592Sken
1084230592Sken		case O_NGTEE:
1085230592Sken			PRINT_UINT_ARG("ngtee ", cmd->arg1);
1086230592Sken			break;
1087230592Sken
1088253460Sscottl		case O_FORWARD_IP:
1089230592Sken		    {
1090230592Sken			ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
1091230592Sken
1092230592Sken			if (s->sa.sin_addr.s_addr == INADDR_ANY) {
1093230592Sken				printf("fwd tablearg");
1094270250Sslm			} else {
1095270250Sslm				printf("fwd %s", inet_ntoa(s->sa.sin_addr));
1096270250Sslm			}
1097270250Sslm			if (s->sa.sin_port)
1098270250Sslm				printf(",%d", s->sa.sin_port);
1099270250Sslm		    }
1100270250Sslm			break;
1101270250Sslm
1102230592Sken		case O_LOG: /* O_LOG is printed last */
1103230592Sken			logptr = (ipfw_insn_log *)cmd;
1104230592Sken			break;
1105253460Sscottl
1106230592Sken		case O_ALTQ: /* O_ALTQ is printed after O_LOG */
1107230592Sken			altqptr = (ipfw_insn_altq *)cmd;
1108230592Sken			break;
1109230592Sken
1110212420Sken		case O_TAG:
1111230592Sken			tagptr = cmd;
1112212420Sken			break;
1113230592Sken
1114230592Sken		case O_NAT:
1115230592Sken			PRINT_UINT_ARG("nat ", cmd->arg1);
1116230592Sken 			break;
1117212420Sken
1118230592Sken		case O_SETFIB:
1119230592Sken			PRINT_UINT_ARG("setfib ", cmd->arg1);
1120230592Sken 			break;
1121230592Sken
1122230592Sken		case O_REASS:
1123230592Sken			printf("reass");
1124230592Sken			break;
1125212802Sken
1126253460Sscottl		default:
1127230592Sken			printf("** unrecognized action %d len %d ",
1128230592Sken				cmd->opcode, cmd->len);
1129262853Smav		}
1130212420Sken	}
1131230592Sken	if (logptr) {
1132230592Sken		if (logptr->max_log > 0)
1133230592Sken			printf(" log logamount %d", logptr->max_log);
1134212420Sken		else
1135230592Sken			printf(" log");
1136230592Sken	}
1137230592Sken#ifndef NO_ALTQ
1138253460Sscottl	if (altqptr) {
1139253460Sscottl		print_altq_cmd(altqptr);
1140230592Sken	}
1141230592Sken#endif
1142230592Sken	if (tagptr) {
1143230592Sken		if (tagptr->len & F_NOT)
1144230592Sken			PRINT_UINT_ARG(" untag ", tagptr->arg1);
1145230592Sken		else
1146264492Sscottl			PRINT_UINT_ARG(" tag ", tagptr->arg1);
1147230592Sken	}
1148253460Sscottl
1149230592Sken	/*
1150230592Sken	 * then print the body.
1151230592Sken	 */
1152230592Sken        for (l = rule->act_ofs, cmd = rule->cmd ;
1153230592Sken			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
1154230592Sken		if ((cmd->len & F_OR) || (cmd->len & F_NOT))
1155212420Sken			continue;
1156253460Sscottl		if (cmd->opcode == O_IP4) {
1157230592Sken			flags |= HAVE_PROTO4;
1158230592Sken			break;
1159230592Sken		} else if (cmd->opcode == O_IP6) {
1160230592Sken			flags |= HAVE_PROTO6;
1161230592Sken			break;
1162212420Sken		}
1163230592Sken	}
1164230592Sken	if (rule->_pad & 1) {	/* empty rules before options */
1165253460Sscottl		if (!co.do_compact) {
1166253460Sscottl			show_prerequisites(&flags, HAVE_PROTO, 0);
1167230592Sken			printf(" from any to any");
1168230592Sken		}
1169230592Sken		flags |= HAVE_IP | HAVE_OPTIONS | HAVE_PROTO |
1170212420Sken			 HAVE_SRCIP | HAVE_DSTIP;
1171230592Sken	}
1172212420Sken
1173230592Sken	if (co.comment_only)
1174212420Sken		comment = "...";
1175230592Sken
1176230592Sken        for (l = rule->act_ofs, cmd = rule->cmd ;
1177230592Sken			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
1178212420Sken		/* useful alias */
1179230592Sken		ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
1180212420Sken
1181230592Sken		if (co.comment_only) {
1182230592Sken			if (cmd->opcode != O_NOP)
1183230592Sken				continue;
1184230592Sken			printf(" // %s\n", (char *)(cmd + 1));
1185218812Sken			return;
1186218812Sken		}
1187218812Sken
1188218812Sken		show_prerequisites(&flags, 0, cmd->opcode);
1189253460Sscottl
1190218812Sken		switch(cmd->opcode) {
1191230592Sken		case O_PROB:
1192253460Sscottl			break;	/* done already */
1193230592Sken
1194230592Sken		case O_PROBE_STATE:
1195230592Sken			break; /* no need to print anything here */
1196218812Sken
1197218812Sken		case O_IP_SRC:
1198230592Sken		case O_IP_SRC_LOOKUP:
1199253460Sscottl		case O_IP_SRC_MASK:
1200253460Sscottl		case O_IP_SRC_ME:
1201230592Sken		case O_IP_SRC_SET:
1202230592Sken			show_prerequisites(&flags, HAVE_PROTO, 0);
1203230592Sken			if (!(flags & HAVE_SRCIP))
1204230592Sken				printf(" from");
1205230592Sken			if ((cmd->len & F_OR) && !or_block)
1206230592Sken				printf(" {");
1207230592Sken			print_ip((ipfw_insn_ip *)cmd,
1208230592Sken				(flags & HAVE_OPTIONS) ? " src-ip" : "");
1209230592Sken			flags |= HAVE_SRCIP;
1210230592Sken			break;
1211230592Sken
1212230592Sken		case O_IP_DST:
1213212420Sken		case O_IP_DST_LOOKUP:
1214253460Sscottl		case O_IP_DST_MASK:
1215230592Sken		case O_IP_DST_ME:
1216237683Sken		case O_IP_DST_SET:
1217237683Sken			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1218230592Sken			if (!(flags & HAVE_DSTIP))
1219230592Sken				printf(" to");
1220230592Sken			if ((cmd->len & F_OR) && !or_block)
1221230592Sken				printf(" {");
1222230592Sken			print_ip((ipfw_insn_ip *)cmd,
1223230592Sken				(flags & HAVE_OPTIONS) ? " dst-ip" : "");
1224230592Sken			flags |= HAVE_DSTIP;
1225230592Sken			break;
1226230592Sken
1227230592Sken		case O_IP6_SRC:
1228230592Sken		case O_IP6_SRC_MASK:
1229253460Sscottl		case O_IP6_SRC_ME:
1230230592Sken			show_prerequisites(&flags, HAVE_PROTO, 0);
1231230592Sken			if (!(flags & HAVE_SRCIP))
1232230592Sken				printf(" from");
1233230592Sken			if ((cmd->len & F_OR) && !or_block)
1234230592Sken				printf(" {");
1235230592Sken			print_ip6((ipfw_insn_ip6 *)cmd,
1236230592Sken			    (flags & HAVE_OPTIONS) ? " src-ip6" : "");
1237230592Sken			flags |= HAVE_SRCIP | HAVE_PROTO;
1238230592Sken			break;
1239230592Sken
1240230592Sken		case O_IP6_DST:
1241230592Sken		case O_IP6_DST_MASK:
1242230592Sken		case O_IP6_DST_ME:
1243230592Sken			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1244230592Sken			if (!(flags & HAVE_DSTIP))
1245230592Sken				printf(" to");
1246230592Sken			if ((cmd->len & F_OR) && !or_block)
1247230592Sken				printf(" {");
1248230592Sken			print_ip6((ipfw_insn_ip6 *)cmd,
1249230592Sken			    (flags & HAVE_OPTIONS) ? " dst-ip6" : "");
1250230592Sken			flags |= HAVE_DSTIP;
1251230592Sken			break;
1252230592Sken
1253230592Sken		case O_FLOW6ID:
1254253460Sscottl		print_flow6id( (ipfw_insn_u32 *) cmd );
1255230592Sken		flags |= HAVE_OPTIONS;
1256230592Sken		break;
1257230592Sken
1258230592Sken		case O_IP_DSTPORT:
1259230592Sken			show_prerequisites(&flags,
1260212420Sken				HAVE_PROTO | HAVE_SRCIP |
1261212420Sken				HAVE_DSTIP | HAVE_IP, 0);
1262212420Sken		case O_IP_SRCPORT:
1263230592Sken			show_prerequisites(&flags,
1264212420Sken				HAVE_PROTO | HAVE_SRCIP, 0);
1265230592Sken			if ((cmd->len & F_OR) && !or_block)
1266230592Sken				printf(" {");
1267230592Sken			if (cmd->len & F_NOT)
1268212420Sken				printf(" not");
1269230592Sken			print_newports((ipfw_insn_u16 *)cmd, proto,
1270230592Sken				(flags & HAVE_OPTIONS) ? cmd->opcode : 0);
1271230592Sken			break;
1272230592Sken
1273230592Sken		case O_PROTO: {
1274230592Sken			struct protoent *pe = NULL;
1275230592Sken
1276230592Sken			if ((cmd->len & F_OR) && !or_block)
1277230592Sken				printf(" {");
1278230592Sken			if (cmd->len & F_NOT)
1279230592Sken				printf(" not");
1280230592Sken			proto = cmd->arg1;
1281253460Sscottl			pe = getprotobynumber(cmd->arg1);
1282230592Sken			if ((flags & (HAVE_PROTO4 | HAVE_PROTO6)) &&
1283230592Sken			    !(flags & HAVE_PROTO))
1284212420Sken				show_prerequisites(&flags,
1285212420Sken				    HAVE_PROTO | HAVE_IP | HAVE_SRCIP |
1286212420Sken				    HAVE_DSTIP | HAVE_OPTIONS, 0);
1287230592Sken			if (flags & HAVE_OPTIONS)
1288253460Sscottl				printf(" proto");
1289253460Sscottl			if (pe)
1290230592Sken				printf(" %s", pe->p_name);
1291230592Sken			else
1292230592Sken				printf(" %u", cmd->arg1);
1293230592Sken			}
1294230592Sken			flags |= HAVE_PROTO;
1295230592Sken			break;
1296230592Sken
1297230592Sken		default: /*options ... */
1298230592Sken			if (!(cmd->len & (F_OR|F_NOT)))
1299230592Sken				if (((cmd->opcode == O_IP6) &&
1300230592Sken				    (flags & HAVE_PROTO6)) ||
1301230592Sken				    ((cmd->opcode == O_IP4) &&
1302212420Sken				    (flags & HAVE_PROTO4)))
1303253460Sscottl					break;
1304230592Sken			show_prerequisites(&flags, HAVE_PROTO | HAVE_SRCIP |
1305237683Sken				    HAVE_DSTIP | HAVE_IP | HAVE_OPTIONS, 0);
1306237683Sken			if ((cmd->len & F_OR) && !or_block)
1307212420Sken				printf(" {");
1308230592Sken			if (cmd->len & F_NOT && cmd->opcode != O_IN)
1309212420Sken				printf(" not");
1310230592Sken			switch(cmd->opcode) {
1311230592Sken			case O_MACADDR2: {
1312230592Sken				ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
1313230592Sken
1314253460Sscottl				printf(" MAC");
1315230592Sken				print_mac(m->addr, m->mask);
1316213535Sken				print_mac(m->addr + 6, m->mask + 6);
1317230592Sken				}
1318230592Sken				break;
1319230592Sken
1320230592Sken			case O_MAC_TYPE:
1321230592Sken				print_newports((ipfw_insn_u16 *)cmd,
1322230592Sken						IPPROTO_ETHERTYPE, cmd->opcode);
1323230592Sken				break;
1324230592Sken
1325230592Sken
1326230592Sken			case O_FRAG:
1327230592Sken				printf(" frag");
1328253460Sscottl				break;
1329230592Sken
1330230592Sken			case O_FIB:
1331230592Sken				printf(" fib %u", cmd->arg1 );
1332230592Sken				break;
1333213535Sken
1334213535Sken			case O_IN:
1335230592Sken				printf(cmd->len & F_NOT ? " out" : " in");
1336230592Sken				break;
1337213535Sken
1338230592Sken			case O_DIVERTED:
1339213535Sken				switch (cmd->arg1) {
1340230592Sken				case 3:
1341230592Sken					printf(" diverted");
1342230592Sken					break;
1343213535Sken				case 1:
1344230592Sken					printf(" diverted-loopback");
1345230592Sken					break;
1346253460Sscottl				case 2:
1347230592Sken					printf(" diverted-output");
1348230592Sken					break;
1349230592Sken				default:
1350213535Sken					printf(" diverted-?<%u>", cmd->arg1);
1351230592Sken					break;
1352237683Sken				}
1353230592Sken				break;
1354230592Sken
1355213535Sken			case O_LAYER2:
1356230592Sken				printf(" layer2");
1357230592Sken				break;
1358230592Sken			case O_XMIT:
1359230592Sken			case O_RECV:
1360253460Sscottl			case O_VIA:
1361253460Sscottl			    {
1362230592Sken				char const *s;
1363230592Sken				ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd;
1364230592Sken
1365230592Sken				if (cmd->opcode == O_XMIT)
1366230592Sken					s = "xmit";
1367230592Sken				else if (cmd->opcode == O_RECV)
1368230592Sken					s = "recv";
1369253460Sscottl				else /* if (cmd->opcode == O_VIA) */
1370253460Sscottl					s = "via";
1371230592Sken				if (cmdif->name[0] == '\0')
1372230592Sken					printf(" %s %s", s,
1373230592Sken					    inet_ntoa(cmdif->p.ip));
1374253460Sscottl				else
1375230592Sken					printf(" %s %s", s, cmdif->name);
1376230592Sken
1377230592Sken				break;
1378230592Sken			    }
1379230592Sken			case O_IPID:
1380230592Sken				if (F_LEN(cmd) == 1)
1381230592Sken				    printf(" ipid %u", cmd->arg1 );
1382230592Sken				else
1383230592Sken				    print_newports((ipfw_insn_u16 *)cmd, 0,
1384230592Sken					O_IPID);
1385230592Sken				break;
1386230592Sken
1387253460Sscottl			case O_IPTTL:
1388230592Sken				if (F_LEN(cmd) == 1)
1389230592Sken				    printf(" ipttl %u", cmd->arg1 );
1390230592Sken				else
1391230592Sken				    print_newports((ipfw_insn_u16 *)cmd, 0,
1392213535Sken					O_IPTTL);
1393213535Sken				break;
1394230592Sken
1395213535Sken			case O_IPVER:
1396230592Sken				printf(" ipver %u", cmd->arg1 );
1397213535Sken				break;
1398230592Sken
1399230592Sken			case O_IPPRECEDENCE:
1400230592Sken				printf(" ipprecedence %u", (cmd->arg1) >> 5 );
1401230592Sken				break;
1402213535Sken
1403230592Sken			case O_IPLEN:
1404213535Sken				if (F_LEN(cmd) == 1)
1405230592Sken				    printf(" iplen %u", cmd->arg1 );
1406230592Sken				else
1407230592Sken				    print_newports((ipfw_insn_u16 *)cmd, 0,
1408213535Sken					O_IPLEN);
1409212772Sken				break;
1410230592Sken
1411230592Sken			case O_IPOPT:
1412230592Sken				print_flags("ipoptions", cmd, f_ipopts);
1413212772Sken				break;
1414230592Sken
1415253460Sscottl			case O_IPTOS:
1416230592Sken				print_flags("iptos", cmd, f_iptos);
1417237683Sken				break;
1418230592Sken
1419230592Sken			case O_ICMPTYPE:
1420230592Sken				print_icmptypes((ipfw_insn_u32 *)cmd);
1421212772Sken				break;
1422230592Sken
1423253460Sscottl			case O_ESTAB:
1424230592Sken				printf(" established");
1425237683Sken				break;
1426230592Sken
1427230592Sken			case O_TCPDATALEN:
1428230592Sken				if (F_LEN(cmd) == 1)
1429230592Sken				    printf(" tcpdatalen %u", cmd->arg1 );
1430230592Sken				else
1431230592Sken				    print_newports((ipfw_insn_u16 *)cmd, 0,
1432230592Sken					O_TCPDATALEN);
1433230592Sken				break;
1434230592Sken
1435230592Sken			case O_TCPFLAGS:
1436230592Sken				print_flags("tcpflags", cmd, f_tcpflags);
1437212420Sken				break;
1438253460Sscottl
1439230592Sken			case O_TCPOPTS:
1440237683Sken				print_flags("tcpoptions", cmd, f_tcpopts);
1441237683Sken				break;
1442237683Sken
1443213535Sken			case O_TCPWIN:
1444230592Sken				printf(" tcpwin %d", ntohs(cmd->arg1));
1445230592Sken				break;
1446230592Sken
1447230592Sken			case O_TCPACK:
1448213535Sken				printf(" tcpack %d", ntohl(cmd32->d[0]));
1449253460Sscottl				break;
1450230592Sken
1451237683Sken			case O_TCPSEQ:
1452230592Sken				printf(" tcpseq %d", ntohl(cmd32->d[0]));
1453230592Sken				break;
1454230592Sken
1455212420Sken			case O_UID:
1456237683Sken			    {
1457230592Sken				struct passwd *pwd = getpwuid(cmd32->d[0]);
1458253460Sscottl
1459230592Sken				if (pwd)
1460237683Sken					printf(" uid %s", pwd->pw_name);
1461230592Sken				else
1462230592Sken					printf(" uid %u", cmd32->d[0]);
1463230592Sken			    }
1464230592Sken				break;
1465230592Sken
1466230592Sken			case O_GID:
1467230592Sken			    {
1468253460Sscottl				struct group *grp = getgrgid(cmd32->d[0]);
1469230592Sken
1470237683Sken				if (grp)
1471230592Sken					printf(" gid %s", grp->gr_name);
1472230592Sken				else
1473230592Sken					printf(" gid %u", cmd32->d[0]);
1474230592Sken			    }
1475213535Sken				break;
1476212420Sken
1477230592Sken			case O_JAIL:
1478230592Sken				printf(" jail %d", cmd32->d[0]);
1479230592Sken				break;
1480230592Sken
1481213535Sken			case O_VERREVPATH:
1482230592Sken				printf(" verrevpath");
1483230592Sken				break;
1484230592Sken
1485212420Sken			case O_VERSRCREACH:
1486230592Sken				printf(" versrcreach");
1487230592Sken				break;
1488253460Sscottl
1489230592Sken			case O_ANTISPOOF:
1490230592Sken				printf(" antispoof");
1491230592Sken				break;
1492213535Sken
1493253460Sscottl			case O_IPSEC:
1494253460Sscottl				printf(" ipsec");
1495253460Sscottl				break;
1496230592Sken
1497237683Sken			case O_NOP:
1498230592Sken				comment = (char *)(cmd + 1);
1499230592Sken				break;
1500213535Sken
1501230592Sken			case O_KEEP_STATE:
1502230592Sken				printf(" keep-state");
1503212420Sken				break;
1504237683Sken
1505213535Sken			case O_LIMIT: {
1506230592Sken				struct _s_x *p = limit_masks;
1507230592Sken				ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
1508230592Sken				uint8_t x = c->limit_mask;
1509230592Sken				char const *comma = " ";
1510230592Sken
1511230592Sken				printf(" limit");
1512213535Sken				for (; p->x != 0 ; p++)
1513230592Sken					if ((x & p->x) == p->x) {
1514230592Sken						x &= ~p->x;
1515213535Sken						printf("%s%s", comma, p->s);
1516230592Sken						comma = ",";
1517213535Sken					}
1518230592Sken				PRINT_UINT_ARG(" ", c->conn_limit);
1519230592Sken				break;
1520253460Sscottl			}
1521230592Sken
1522230592Sken			case O_IP6:
1523230592Sken				printf(" ip6");
1524230592Sken				break;
1525213535Sken
1526213535Sken			case O_IP4:
1527230592Sken				printf(" ip4");
1528230592Sken				break;
1529230592Sken
1530230592Sken			case O_ICMP6TYPE:
1531230592Sken				print_icmp6types((ipfw_insn_u32 *)cmd);
1532230592Sken				break;
1533213535Sken
1534230592Sken			case O_EXT_HDR:
1535230592Sken				print_ext6hdr( (ipfw_insn *) cmd );
1536213535Sken				break;
1537253460Sscottl
1538230592Sken			case O_TAGGED:
1539213535Sken				if (F_LEN(cmd) == 1)
1540253460Sscottl					PRINT_UINT_ARG(" tagged ", cmd->arg1);
1541213535Sken				else
1542230592Sken					print_newports((ipfw_insn_u16 *)cmd, 0,
1543230592Sken					    O_TAGGED);
1544230592Sken				break;
1545230592Sken
1546230592Sken			default:
1547230592Sken				printf(" [opcode %d len %d]",
1548230592Sken				    cmd->opcode, cmd->len);
1549253460Sscottl			}
1550253460Sscottl		}
1551230592Sken		if (cmd->len & F_OR) {
1552230592Sken			printf(" or");
1553213535Sken			or_block = 1;
1554230592Sken		} else if (or_block) {
1555253460Sscottl			printf(" }");
1556230592Sken			or_block = 0;
1557230592Sken		}
1558213535Sken	}
1559253460Sscottl	show_prerequisites(&flags, HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP
1560230592Sken				              | HAVE_IP, 0);
1561213535Sken	if (comment)
1562230592Sken		printf(" // %s", comment);
1563230592Sken	printf("\n");
1564213535Sken}
1565230592Sken
1566230592Skenstatic void
1567230592Skenshow_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth)
1568268197Sscottl{
1569230592Sken	struct protoent *pe;
1570230592Sken	struct in_addr a;
1571230592Sken	uint16_t rulenum;
1572230592Sken	char buf[INET6_ADDRSTRLEN];
1573230592Sken
1574230592Sken	if (!co.do_expired) {
1575230592Sken		if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT))
1576253460Sscottl			return;
1577253460Sscottl	}
1578230592Sken	bcopy(&d->rule, &rulenum, sizeof(rulenum));
1579213535Sken	printf("%05d", rulenum);
1580230592Sken	if (pcwidth>0 || bcwidth>0)
1581253460Sscottl	    printf(" %*llu %*llu (%ds)", pcwidth,
1582230592Sken		align_uint64(&d->pcnt), bcwidth,
1583213535Sken		align_uint64(&d->bcnt), d->expire);
1584230592Sken	switch (d->dyn_type) {
1585230592Sken	case O_LIMIT_PARENT:
1586230592Sken		printf(" PARENT %d", d->count);
1587230592Sken		break;
1588230592Sken	case O_LIMIT:
1589230592Sken		printf(" LIMIT");
1590230592Sken		break;
1591230592Sken	case O_KEEP_STATE: /* bidir, no mask */
1592230592Sken		printf(" STATE");
1593230592Sken		break;
1594230592Sken	}
1595230592Sken
1596230592Sken	if ((pe = getprotobynumber(d->id.proto)) != NULL)
1597253460Sscottl		printf(" %s", pe->p_name);
1598253460Sscottl	else
1599230592Sken		printf(" proto %u", d->id.proto);
1600230592Sken
1601213535Sken	if (d->id.addr_type == 4) {
1602213535Sken		a.s_addr = htonl(d->id.src_ip);
1603212420Sken		printf(" %s %d", inet_ntoa(a), d->id.src_port);
1604212420Sken
1605212420Sken		a.s_addr = htonl(d->id.dst_ip);
1606212420Sken		printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port);
1607212420Sken	} else if (d->id.addr_type == 6) {
1608212420Sken		printf(" %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf,
1609212420Sken		    sizeof(buf)), d->id.src_port);
1610230592Sken		printf(" <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf,
1611212420Sken		    sizeof(buf)), d->id.dst_port);
1612230592Sken	} else
1613230592Sken		printf(" UNKNOWN <-> UNKNOWN\n");
1614237683Sken
1615212420Sken	printf("\n");
1616212420Sken}
1617253460Sscottl
1618230592Sken/*
1619212420Sken * This one handles all set-related commands
1620212420Sken * 	ipfw set { show | enable | disable }
1621264492Sscottl * 	ipfw set swap X Y
1622264492Sscottl * 	ipfw set move X to Y
1623264492Sscottl * 	ipfw set move rule X to Y
1624212420Sken */
1625253460Sscottlvoid
1626212420Skenipfw_sets_handler(char *av[])
1627253460Sscottl{
1628230592Sken	uint32_t set_disable, masks[2];
1629270250Sslm	int i, nbytes;
1630212420Sken	uint16_t rulenum;
1631212420Sken	uint8_t cmd, new_set;
1632212420Sken
1633231240Sken	av++;
1634253550Sken
1635253550Sken	if (av[0] == NULL)
1636270250Sslm		errx(EX_USAGE, "set needs command");
1637231240Sken	if (_substrcmp(*av, "show") == 0) {
1638231240Sken		void *data = NULL;
1639231240Sken		char const *msg;
1640230592Sken		int nalloc;
1641253550Sken
1642253550Sken		nalloc = nbytes = sizeof(struct ip_fw);
1643253550Sken		while (nbytes >= nalloc) {
1644253550Sken			if (data)
1645268197Sscottl				free(data);
1646253550Sken			nalloc = nalloc * 2 + 200;
1647253550Sken			nbytes = nalloc;
1648253550Sken		data = safe_calloc(1, nbytes);
1649253550Sken		if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0)
1650253550Sken			err(EX_OSERR, "getsockopt(IP_FW_GET)");
1651253550Sken		}
1652230592Sken
1653230592Sken		bcopy(&((struct ip_fw *)data)->next_rule,
1654230592Sken			&set_disable, sizeof(set_disable));
1655230592Sken
1656230592Sken		for (i = 0, msg = "disable" ; i < RESVD_SET; i++)
1657230592Sken			if ((set_disable & (1<<i))) {
1658268197Sscottl				printf("%s %d", msg, i);
1659230592Sken				msg = "";
1660268197Sscottl			}
1661230592Sken		msg = (set_disable) ? " enable" : "enable";
1662230592Sken		for (i = 0; i < RESVD_SET; i++)
1663230592Sken			if (!(set_disable & (1<<i))) {
1664212420Sken				printf("%s %d", msg, i);
1665230592Sken				msg = "";
1666253460Sscottl			}
1667270250Sslm		printf("\n");
1668230592Sken	} else if (_substrcmp(*av, "swap") == 0) {
1669230592Sken		av++;
1670230592Sken		if ( av[0] == NULL || av[1] == NULL )
1671230592Sken			errx(EX_USAGE, "set swap needs 2 set numbers\n");
1672212420Sken		rulenum = atoi(av[0]);
1673270250Sslm		new_set = atoi(av[1]);
1674270250Sslm		if (!isdigit(*(av[0])) || rulenum > RESVD_SET)
1675270250Sslm			errx(EX_DATAERR, "invalid set number %s\n", av[0]);
1676270250Sslm		if (!isdigit(*(av[1])) || new_set > RESVD_SET)
1677212420Sken			errx(EX_DATAERR, "invalid set number %s\n", av[1]);
1678212420Sken		masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
1679212420Sken		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
1680212420Sken	} else if (_substrcmp(*av, "move") == 0) {
1681212420Sken		av++;
1682212420Sken		if (av[0] && _substrcmp(*av, "rule") == 0) {
1683212420Sken			cmd = 2;
1684212420Sken			av++;
1685212420Sken		} else
1686212420Sken			cmd = 3;
1687212420Sken		if (av[0] == NULL || av[1] == NULL || av[2] == NULL ||
1688218812Sken				av[3] != NULL ||  _substrcmp(av[1], "to") != 0)
1689237683Sken			errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
1690212420Sken		rulenum = atoi(av[0]);
1691212420Sken		new_set = atoi(av[2]);
1692237683Sken		if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) ||
1693212420Sken			(cmd == 2 && rulenum == IPFW_DEFAULT_RULE) )
1694212420Sken			errx(EX_DATAERR, "invalid source number %s\n", av[0]);
1695212420Sken		if (!isdigit(*(av[2])) || new_set > RESVD_SET)
1696212420Sken			errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
1697212420Sken		masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
1698212420Sken		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
1699212420Sken	} else if (_substrcmp(*av, "disable") == 0 ||
1700212420Sken		   _substrcmp(*av, "enable") == 0 ) {
1701237683Sken		int which = _substrcmp(*av, "enable") == 0 ? 1 : 0;
1702212420Sken
1703237683Sken		av++;
1704212420Sken		masks[0] = masks[1] = 0;
1705212420Sken
1706212420Sken		while (av[0]) {
1707212420Sken			if (isdigit(**av)) {
1708212420Sken				i = atoi(*av);
1709237683Sken				if (i < 0 || i > RESVD_SET)
1710212420Sken					errx(EX_DATAERR,
1711212420Sken					    "invalid set number %d\n", i);
1712212420Sken				masks[which] |= (1<<i);
1713237683Sken			} else if (_substrcmp(*av, "disable") == 0)
1714212420Sken				which = 0;
1715212420Sken			else if (_substrcmp(*av, "enable") == 0)
1716212420Sken				which = 1;
1717212420Sken			else
1718237683Sken				errx(EX_DATAERR,
1719212420Sken					"invalid set command %s\n", *av);
1720212420Sken			av++;
1721237683Sken		}
1722253550Sken		if ( (masks[0] & masks[1]) != 0 )
1723237683Sken			errx(EX_DATAERR,
1724212420Sken			    "cannot enable and disable the same set\n");
1725212420Sken
1726212420Sken		i = do_cmd(IP_FW_DEL, masks, sizeof(masks));
1727212420Sken		if (i)
1728212420Sken			warn("set enable/disable: setsockopt(IP_FW_DEL)");
1729212420Sken	} else
1730212420Sken		errx(EX_USAGE, "invalid set command %s\n", *av);
1731237683Sken}
1732212420Sken
1733212420Skenvoid
1734237683Skenipfw_sysctl_handler(char *av[], int which)
1735212420Sken{
1736212420Sken	av++;
1737237683Sken
1738212420Sken	if (av[0] == NULL) {
1739212420Sken		warnx("missing keyword to enable/disable\n");
1740212420Sken	} else if (_substrcmp(*av, "firewall") == 0) {
1741212420Sken		sysctlbyname("net.inet.ip.fw.enable", NULL, 0,
1742237683Sken		    &which, sizeof(which));
1743212420Sken	} else if (_substrcmp(*av, "one_pass") == 0) {
1744212420Sken		sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0,
1745237683Sken		    &which, sizeof(which));
1746237683Sken	} else if (_substrcmp(*av, "debug") == 0) {
1747216368Sken		sysctlbyname("net.inet.ip.fw.debug", NULL, 0,
1748212420Sken		    &which, sizeof(which));
1749268197Sscottl	} else if (_substrcmp(*av, "verbose") == 0) {
1750212420Sken		sysctlbyname("net.inet.ip.fw.verbose", NULL, 0,
1751212420Sken		    &which, sizeof(which));
1752212420Sken	} else if (_substrcmp(*av, "dyn_keepalive") == 0) {
1753212420Sken		sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0,
1754212420Sken		    &which, sizeof(which));
1755212420Sken#ifndef NO_ALTQ
1756212420Sken	} else if (_substrcmp(*av, "altq") == 0) {
1757212420Sken		altq_set_enabled(which);
1758237683Sken#endif
1759212420Sken	} else {
1760216088Sken		warnx("unrecognize enable/disable keyword: %s\n", *av);
1761230592Sken	}
1762230592Sken}
1763230592Sken
1764230592Skenvoid
1765216088Skenipfw_list(int ac, char *av[], int show_counters)
1766230592Sken{
1767230592Sken	struct ip_fw *r;
1768230592Sken	ipfw_dyn_rule *dynrules, *d;
1769230592Sken
1770230592Sken#define NEXT(r)	((struct ip_fw *)((char *)r + RULESIZE(r)))
1771230592Sken	char *lim;
1772230592Sken	void *data = NULL;
1773230592Sken	int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width;
1774230592Sken	int exitval = EX_OK;
1775237683Sken	int lac;
1776230592Sken	char **lav;
1777230592Sken	u_long rnum, last;
1778230592Sken	char *endptr;
1779237683Sken	int seen = 0;
1780230592Sken	uint8_t set;
1781230592Sken
1782230592Sken	const int ocmd = co.do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
1783230592Sken	int nalloc = 1024;	/* start somewhere... */
1784230592Sken
1785230592Sken	last = 0;
1786230592Sken
1787230592Sken	if (co.test_only) {
1788230592Sken		fprintf(stderr, "Testing only, list disabled\n");
1789230592Sken		return;
1790230592Sken	}
1791230592Sken	if (co.do_pipe) {
1792230592Sken		dummynet_list(ac, av, show_counters);
1793230592Sken		return;
1794230592Sken	}
1795230592Sken
1796230592Sken	ac--;
1797230592Sken	av++;
1798237683Sken
1799237683Sken	/* get rules or pipes from kernel, resizing array as necessary */
1800230592Sken	nbytes = nalloc;
1801230592Sken
1802230592Sken	while (nbytes >= nalloc) {
1803230592Sken		nalloc = nalloc * 2 + 200;
1804230592Sken		nbytes = nalloc;
1805230592Sken		data = safe_realloc(data, nbytes);
1806230592Sken		if (do_cmd(ocmd, data, (uintptr_t)&nbytes) < 0)
1807237683Sken			err(EX_OSERR, "getsockopt(IP_%s_GET)",
1808230592Sken				co.do_pipe ? "DUMMYNET" : "FW");
1809230592Sken	}
1810230592Sken
1811230592Sken	/*
1812230592Sken	 * Count static rules. They have variable size so we
1813230592Sken	 * need to scan the list to count them.
1814212420Sken	 */
1815246713Skib	for (nstat = 1, r = data, lim = (char *)data + nbytes;
1816246713Skib		    r->rulenum < IPFW_DEFAULT_RULE && (char *)r < lim;
1817246713Skib		    ++nstat, r = NEXT(r) )
1818246713Skib		; /* nothing */
1819246713Skib
1820246713Skib	/*
1821212420Sken	 * Count dynamic rules. This is easier as they have
1822212420Sken	 * fixed size.
1823212420Sken	 */
1824237683Sken	r = NEXT(r);
1825212420Sken	dynrules = (ipfw_dyn_rule *)r ;
1826212420Sken	n = (char *)r - (char *)data;
1827212420Sken	ndyn = (nbytes - n) / sizeof *dynrules;
1828230592Sken
1829230592Sken	/* if showing stats, figure out column widths ahead of time */
1830212420Sken	bcwidth = pcwidth = 0;
1831230592Sken	if (show_counters) {
1832230592Sken		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
1833230592Sken			/* skip rules from another set */
1834230592Sken			if (co.use_set && r->set != co.use_set - 1)
1835230592Sken				continue;
1836230592Sken
1837268197Sscottl			/* packet counter */
1838230592Sken			width = snprintf(NULL, 0, "%llu",
1839230592Sken			    align_uint64(&r->pcnt));
1840268197Sscottl			if (width > pcwidth)
1841230592Sken				pcwidth = width;
1842230592Sken
1843218812Sken			/* byte counter */
1844212420Sken			width = snprintf(NULL, 0, "%llu",
1845212420Sken			    align_uint64(&r->bcnt));
1846212420Sken			if (width > bcwidth)
1847230592Sken				bcwidth = width;
1848230592Sken		}
1849230592Sken	}
1850253550Sken	if (co.do_dynamic && ndyn) {
1851230592Sken		for (n = 0, d = dynrules; n < ndyn; n++, d++) {
1852253460Sscottl			if (co.use_set) {
1853253460Sscottl				/* skip rules from another set */
1854230592Sken				bcopy((char *)&d->rule + sizeof(uint16_t),
1855212420Sken				      &set, sizeof(uint8_t));
1856212420Sken				if (set != co.use_set - 1)
1857212420Sken					continue;
1858212420Sken			}
1859212420Sken			width = snprintf(NULL, 0, "%llu",
1860231240Sken			    align_uint64(&d->pcnt));
1861231240Sken			if (width > pcwidth)
1862231240Sken				pcwidth = width;
1863231240Sken
1864231240Sken			width = snprintf(NULL, 0, "%llu",
1865231240Sken			    align_uint64(&d->bcnt));
1866231240Sken			if (width > bcwidth)
1867231240Sken				bcwidth = width;
1868231240Sken		}
1869231240Sken	}
1870231240Sken	/* if no rule numbers were specified, list all rules */
1871231240Sken	if (ac == 0) {
1872231240Sken		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
1873231240Sken			if (co.use_set && r->set != co.use_set - 1)
1874231240Sken				continue;
1875231240Sken			show_ipfw(r, pcwidth, bcwidth);
1876231240Sken		}
1877231240Sken
1878231240Sken		if (co.do_dynamic && ndyn) {
1879231240Sken			printf("## Dynamic rules (%d):\n", ndyn);
1880231240Sken			for (n = 0, d = dynrules; n < ndyn; n++, d++) {
1881231240Sken				if (co.use_set) {
1882231240Sken					bcopy((char *)&d->rule + sizeof(uint16_t),
1883231240Sken					      &set, sizeof(uint8_t));
1884231240Sken					if (set != co.use_set - 1)
1885231240Sken						continue;
1886231240Sken				}
1887231240Sken				show_dyn_ipfw(d, pcwidth, bcwidth);
1888231240Sken		}
1889231240Sken		}
1890231240Sken		goto done;
1891231240Sken	}
1892231240Sken
1893253460Sscottl	/* display specific rules requested on command line */
1894231240Sken
1895231240Sken	for (lac = ac, lav = av; lac != 0; lac--) {
1896231240Sken		/* convert command line rule # */
1897231240Sken		last = rnum = strtoul(*lav++, &endptr, 10);
1898231240Sken		if (*endptr == '-')
1899231240Sken			last = strtoul(endptr+1, &endptr, 10);
1900231240Sken		if (*endptr) {
1901231240Sken			exitval = EX_USAGE;
1902231240Sken			warnx("invalid rule number: %s", *(lav - 1));
1903231240Sken			continue;
1904231240Sken		}
1905231240Sken		for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) {
1906231240Sken			if (r->rulenum > last)
1907231240Sken				break;
1908231240Sken			if (co.use_set && r->set != co.use_set - 1)
1909231240Sken				continue;
1910231240Sken			if (r->rulenum >= rnum && r->rulenum <= last) {
1911231240Sken				show_ipfw(r, pcwidth, bcwidth);
1912231240Sken				seen = 1;
1913231240Sken			}
1914231240Sken		}
1915231240Sken		if (!seen) {
1916231240Sken			/* give precedence to other error(s) */
1917231240Sken			if (exitval == EX_OK)
1918231240Sken				exitval = EX_UNAVAILABLE;
1919231240Sken			warnx("rule %lu does not exist", rnum);
1920231240Sken		}
1921231240Sken	}
1922231240Sken
1923231240Sken	if (co.do_dynamic && ndyn) {
1924231240Sken		printf("## Dynamic rules:\n");
1925231240Sken		for (lac = ac, lav = av; lac != 0; lac--) {
1926231240Sken			last = rnum = strtoul(*lav++, &endptr, 10);
1927231240Sken			if (*endptr == '-')
1928231240Sken				last = strtoul(endptr+1, &endptr, 10);
1929231240Sken			if (*endptr)
1930231240Sken				/* already warned */
1931231240Sken				continue;
1932231240Sken			for (n = 0, d = dynrules; n < ndyn; n++, d++) {
1933231240Sken				uint16_t rulenum;
1934231240Sken
1935231240Sken				bcopy(&d->rule, &rulenum, sizeof(rulenum));
1936231240Sken				if (rulenum > rnum)
1937231240Sken					break;
1938231240Sken				if (co.use_set) {
1939231240Sken					bcopy((char *)&d->rule + sizeof(uint16_t),
1940231240Sken					      &set, sizeof(uint8_t));
1941231240Sken					if (set != co.use_set - 1)
1942231240Sken						continue;
1943231240Sken				}
1944231240Sken				if (r->rulenum >= rnum && r->rulenum <= last)
1945231240Sken					show_dyn_ipfw(d, pcwidth, bcwidth);
1946231240Sken			}
1947231240Sken		}
1948231240Sken	}
1949231240Sken
1950231240Sken	ac = 0;
1951231240Sken
1952231240Skendone:
1953231240Sken	free(data);
1954231240Sken
1955231240Sken	if (exitval != EX_OK)
1956231240Sken		exit(exitval);
1957231240Sken#undef NEXT
1958231240Sken}
1959231240Sken
1960231240Skenstatic int
1961231240Skenlookup_host (char *host, struct in_addr *ipaddr)
1962231240Sken{
1963231240Sken	struct hostent *he;
1964231240Sken
1965231240Sken	if (!inet_aton(host, ipaddr)) {
1966231240Sken		if ((he = gethostbyname(host)) == NULL)
1967231240Sken			return(-1);
1968231240Sken		*ipaddr = *(struct in_addr *)he->h_addr_list[0];
1969231240Sken	}
1970231240Sken	return(0);
1971231240Sken}
1972231240Sken
1973231240Sken/*
1974231240Sken * fills the addr and mask fields in the instruction as appropriate from av.
1975231240Sken * Update length as appropriate.
1976231240Sken * The following formats are allowed:
1977231240Sken *	me	returns O_IP_*_ME
1978231240Sken *	1.2.3.4		single IP address
1979231240Sken *	1.2.3.4:5.6.7.8	address:mask
1980231240Sken *	1.2.3.4/24	address/mask
1981231240Sken *	1.2.3.4/26{1,6,5,4,23}	set of addresses in a subnet
1982231240Sken * We can have multiple comma-separated address/mask entries.
1983231240Sken */
1984231240Skenstatic void
1985231240Skenfill_ip(ipfw_insn_ip *cmd, char *av)
1986231240Sken{
1987231240Sken	int len = 0;
1988231240Sken	uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
1989231240Sken
1990231240Sken	cmd->o.len &= ~F_LEN_MASK;	/* zero len */
1991231240Sken
1992231240Sken	if (_substrcmp(av, "any") == 0)
1993231240Sken		return;
1994231240Sken
1995231240Sken	if (_substrcmp(av, "me") == 0) {
1996231240Sken		cmd->o.len |= F_INSN_SIZE(ipfw_insn);
1997231240Sken		return;
1998231240Sken	}
1999231240Sken
2000231240Sken	if (strncmp(av, "table(", 6) == 0) {
2001231240Sken		char *p = strchr(av + 6, ',');
2002231240Sken
2003231240Sken		if (p)
2004231240Sken			*p++ = '\0';
2005231240Sken		cmd->o.opcode = O_IP_DST_LOOKUP;
2006231240Sken		cmd->o.arg1 = strtoul(av + 6, NULL, 0);
2007231240Sken		if (p) {
2008231240Sken			cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2009231240Sken			d[0] = strtoul(p, NULL, 0);
2010231240Sken		} else
2011231240Sken			cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2012231240Sken		return;
2013231240Sken	}
2014231240Sken
2015231240Sken    while (av) {
2016231240Sken	/*
2017231240Sken	 * After the address we can have '/' or ':' indicating a mask,
2018231240Sken	 * ',' indicating another address follows, '{' indicating a
2019231240Sken	 * set of addresses of unspecified size.
2020231240Sken	 */
2021231240Sken	char *t = NULL, *p = strpbrk(av, "/:,{");
2022231240Sken	int masklen;
2023231240Sken	char md, nd = '\0';
2024231240Sken
2025231240Sken	if (p) {
2026231240Sken		md = *p;
2027253460Sscottl		*p++ = '\0';
2028253460Sscottl		if ((t = strpbrk(p, ",{")) != NULL) {
2029231240Sken			nd = *t;
2030231240Sken			*t = '\0';
2031231240Sken		}
2032253460Sscottl	} else
2033253550Sken		md = '\0';
2034253550Sken
2035231240Sken	if (lookup_host(av, (struct in_addr *)&d[0]) != 0)
2036253460Sscottl		errx(EX_NOHOST, "hostname ``%s'' unknown", av);
2037231240Sken	switch (md) {
2038253460Sscottl	case ':':
2039231240Sken		if (!inet_aton(p, (struct in_addr *)&d[1]))
2040253460Sscottl			errx(EX_DATAERR, "bad netmask ``%s''", p);
2041231240Sken		break;
2042231240Sken	case '/':
2043231240Sken		masklen = atoi(p);
2044231240Sken		if (masklen == 0)
2045231240Sken			d[1] = htonl(0);	/* mask */
2046231240Sken		else if (masklen > 32)
2047231240Sken			errx(EX_DATAERR, "bad width ``%s''", p);
2048231240Sken		else
2049231240Sken			d[1] = htonl(~0 << (32 - masklen));
2050231240Sken		break;
2051212420Sken	case '{':	/* no mask, assume /24 and put back the '{' */
2052212420Sken		d[1] = htonl(~0 << (32 - 24));
2053212420Sken		*(--p) = md;
2054212420Sken		break;
2055230592Sken
2056212420Sken	case ',':	/* single address plus continuation */
2057230592Sken		*(--p) = md;
2058230592Sken		/* FALLTHROUGH */
2059230592Sken	case 0:		/* initialization value */
2060230592Sken	default:
2061212420Sken		d[1] = htonl(~0);	/* force /32 */
2062253460Sscottl		break;
2063230592Sken	}
2064253460Sscottl	d[0] &= d[1];		/* mask base address with mask */
2065253460Sscottl	if (t)
2066230592Sken		*t = nd;
2067212420Sken	/* find next separator */
2068212420Sken	if (p)
2069230592Sken		p = strpbrk(p, ",{");
2070212420Sken	if (p && *p == '{') {
2071212420Sken		/*
2072212420Sken		 * We have a set of addresses. They are stored as follows:
2073230592Sken		 *   arg1	is the set size (powers of 2, 2..256)
2074212420Sken		 *   addr	is the base address IN HOST FORMAT
2075218812Sken		 *   mask..	is an array of arg1 bits (rounded up to
2076218812Sken		 *		the next multiple of 32) with bits set
2077218812Sken		 *		for each host in the map.
2078218812Sken		 */
2079218812Sken		uint32_t *map = (uint32_t *)&cmd->mask;
2080212420Sken		int low, high;
2081212420Sken		int i = contigmask((uint8_t *)&(d[1]), 32);
2082212420Sken
2083212420Sken		if (len > 0)
2084241844Seadler			errx(EX_DATAERR, "address set cannot be in a list");
2085212420Sken		if (i < 24 || i > 31)
2086212420Sken			errx(EX_DATAERR, "invalid set with mask %d\n", i);
2087212420Sken		cmd->o.arg1 = 1<<(32-i);	/* map length		*/
2088212420Sken		d[0] = ntohl(d[0]);		/* base addr in host format */
2089230592Sken		cmd->o.opcode = O_IP_DST_SET;	/* default */
2090230592Sken		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32;
2091230592Sken		for (i = 0; i < (cmd->o.arg1+31)/32 ; i++)
2092254615Sken			map[i] = 0;	/* clear map */
2093230592Sken
2094230592Sken		av = p + 1;
2095230592Sken		low = d[0] & 0xff;
2096230592Sken		high = low + cmd->o.arg1 - 1;
2097253460Sscottl		/*
2098230592Sken		 * Here, i stores the previous value when we specify a range
2099230592Sken		 * of addresses within a mask, e.g. 45-63. i = -1 means we
2100230592Sken		 * have no previous value.
2101237683Sken		 */
2102237683Sken		i = -1;	/* previous value in a range */
2103230592Sken		while (isdigit(*av)) {
2104253460Sscottl			char *s;
2105230592Sken			int a = strtol(av, &s, 0);
2106230592Sken
2107230592Sken			if (s == av) { /* no parameter */
2108230592Sken			    if (*av != '}')
2109253460Sscottl				errx(EX_DATAERR, "set not closed\n");
2110230592Sken			    if (i != -1)
2111230592Sken				errx(EX_DATAERR, "incomplete range %d-", i);
2112230592Sken			    break;
2113237683Sken			}
2114237683Sken			if (a < low || a > high)
2115230592Sken			    errx(EX_DATAERR, "addr %d out of range [%d-%d]\n",
2116253460Sscottl				a, low, high);
2117230592Sken			a -= low;
2118230592Sken			if (i == -1)	/* no previous in range */
2119230592Sken			    i = a;
2120253460Sscottl			else {		/* check that range is valid */
2121230592Sken			    if (i > a)
2122230592Sken				errx(EX_DATAERR, "invalid range %d-%d",
2123230592Sken					i+low, a+low);
2124230592Sken			    if (*s == '-')
2125218812Sken				errx(EX_DATAERR, "double '-' in range");
2126218812Sken			}
2127218812Sken			for (; i <= a; i++)
2128218812Sken			    map[i/32] |= 1<<(i & 31);
2129218812Sken			i = -1;
2130218812Sken			if (*s == '-')
2131218812Sken			    i = a;
2132218812Sken			else if (*s == '}')
2133218812Sken			    break;
2134268197Sscottl			av = s+1;
2135218812Sken		}
2136218812Sken		return;
2137218812Sken	}
2138218812Sken	av = p;
2139218812Sken	if (av)			/* then *av must be a ',' */
2140218812Sken		av++;
2141218812Sken
2142218812Sken	/* Check this entry */
2143218812Sken	if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */
2144218812Sken		/*
2145218812Sken		 * 'any' turns the entire list into a NOP.
2146218812Sken		 * 'not any' never matches, so it is removed from the
2147218812Sken		 * list unless it is the only item, in which case we
2148218812Sken		 * report an error.
2149218812Sken		 */
2150253460Sscottl		if (cmd->o.len & F_NOT) {	/* "not any" never matches */
2151219036Sken			if (av == NULL && len == 0) /* only this entry */
2152218812Sken				errx(EX_DATAERR, "not any never matches");
2153212420Sken		}
2154212420Sken		/* else do nothing and skip this entry */
2155270250Sslm		return;
2156270250Sslm	}
2157270250Sslm	/* A single IP can be stored in an optimized format */
2158270250Sslm	if (d[1] == (uint32_t)~0 && av == NULL && len == 0) {
2159270250Sslm		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2160270250Sslm		return;
2161270250Sslm	}
2162270250Sslm	len += 2;	/* two words... */
2163270250Sslm	d += 2;
2164270250Sslm    } /* end while */
2165270250Sslm    if (len + 1 > F_LEN_MASK)
2166270250Sslm	errx(EX_DATAERR, "address list too long");
2167212420Sken    cmd->o.len |= len+1;
2168212420Sken}
2169268197Sscottl
2170230592Sken
2171268197Sscottl/* n2mask sets n bits of the mask */
2172230592Skenvoid
2173268197Sscottln2mask(struct in6_addr *mask, int n)
2174230592Sken{
2175230592Sken	static int	minimask[9] =
2176218812Sken	    { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
2177218812Sken	u_char		*p;
2178218812Sken
2179253460Sscottl	memset(mask, 0, sizeof(struct in6_addr));
2180253550Sken	p = (u_char *) mask;
2181218812Sken	for (; n > 0; p++, n -= 8) {
2182230592Sken		if (n >= 8)
2183230592Sken			*p = 0xff;
2184230592Sken		else
2185230592Sken			*p = minimask[n];
2186230592Sken	}
2187230592Sken	return;
2188230592Sken}
2189268197Sscottl
2190230592Sken
2191230592Sken/*
2192268197Sscottl * helper function to process a set of flags and set bits in the
2193230592Sken * appropriate masks.
2194230592Sken */
2195218812Skenstatic void
2196218812Skenfill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
2197218811Sken	struct _s_x *flags, char *p)
2198212420Sken{
2199212420Sken	uint8_t set=0, clear=0;
2200212420Sken
2201212420Sken	while (p && *p) {
2202212420Sken		char *q;	/* points to the separator */
2203253460Sscottl		int val;
2204253460Sscottl		uint8_t *which;	/* mask we are working on */
2205253460Sscottl
2206253460Sscottl		if (*p == '!') {
2207212420Sken			p++;
2208230592Sken			which = &clear;
2209230592Sken		} else
2210230592Sken			which = &set;
2211230592Sken		q = strchr(p, ',');
2212230592Sken		if (q)
2213230592Sken			*q++ = '\0';
2214230592Sken		val = match_token(flags, p);
2215230592Sken		if (val <= 0)
2216268197Sscottl			errx(EX_DATAERR, "invalid flag %s", p);
2217230592Sken		*which |= (uint8_t)val;
2218230592Sken		p = q;
2219268197Sscottl	}
2220268197Sscottl        cmd->opcode = opcode;
2221230592Sken        cmd->len =  (cmd->len & (F_NOT | F_OR)) | 1;
2222237683Sken        cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
2223212420Sken}
2224237683Sken
2225212420Sken
2226212420Skenvoid
2227212420Skenipfw_delete(char *av[])
2228230592Sken{
2229237683Sken	uint32_t rulenum;
2230230592Sken	int i;
2231253460Sscottl	int exitval = EX_OK;
2232230592Sken	int do_set = 0;
2233230592Sken
2234230592Sken	av++;
2235230592Sken	NEED1("missing rule specification");
2236268197Sscottl	if ( *av && _substrcmp(*av, "set") == 0) {
2237230592Sken		/* Do not allow using the following syntax:
2238230592Sken		 *	ipfw set N delete set M
2239230592Sken		 */
2240230592Sken		if (co.use_set)
2241230592Sken			errx(EX_DATAERR, "invalid syntax");
2242230592Sken		do_set = 1;	/* delete set */
2243230592Sken		av++;
2244230592Sken	}
2245268197Sscottl
2246230592Sken	/* Rule number */
2247230592Sken	while (*av && isdigit(**av)) {
2248230592Sken		i = atoi(*av); av++;
2249230592Sken		if (co.do_nat) {
2250230592Sken			exitval = do_cmd(IP_FW_NAT_DEL, &i, sizeof i);
2251230592Sken			if (exitval) {
2252230592Sken				exitval = EX_UNAVAILABLE;
2253230592Sken				warn("rule %u not available", i);
2254230592Sken			}
2255237683Sken 		} else if (co.do_pipe) {
2256230592Sken			exitval = ipfw_delete_pipe(co.do_pipe, i);
2257230592Sken		} else {
2258230592Sken			if (co.use_set)
2259230592Sken				rulenum = (i & 0xffff) | (5 << 24) |
2260230592Sken				    ((co.use_set - 1) << 16);
2261230592Sken			else
2262230592Sken			rulenum =  (i & 0xffff) | (do_set << 24);
2263230592Sken			i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum);
2264230592Sken			if (i) {
2265230592Sken				exitval = EX_UNAVAILABLE;
2266230592Sken				warn("rule %u: setsockopt(IP_FW_DEL)",
2267230592Sken				    rulenum);
2268230592Sken			}
2269268197Sscottl		}
2270230592Sken	}
2271230592Sken	if (exitval != EX_OK)
2272230592Sken		exit(exitval);
2273230592Sken}
2274230592Sken
2275230592Sken
2276268197Sscottl/*
2277230592Sken * fill the interface structure. We do not check the name as we can
2278268197Sscottl * create interfaces dynamically, so checking them at insert time
2279230592Sken * makes relatively little sense.
2280230592Sken * Interface names containing '*', '?', or '[' are assumed to be shell
2281230592Sken * patterns which match interfaces.
2282230592Sken */
2283237683Skenstatic void
2284230592Skenfill_iface(ipfw_insn_if *cmd, char *arg)
2285230592Sken{
2286230592Sken	cmd->name[0] = '\0';
2287230592Sken	cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
2288230592Sken
2289230592Sken	/* Parse the interface or address */
2290230592Sken	if (strcmp(arg, "any") == 0)
2291230592Sken		cmd->o.len = 0;		/* effectively ignore this command */
2292230592Sken	else if (!isdigit(*arg)) {
2293230592Sken		strlcpy(cmd->name, arg, sizeof(cmd->name));
2294237546Skevlo		cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
2295230592Sken	} else if (!inet_aton(arg, &cmd->p.ip))
2296230592Sken		errx(EX_DATAERR, "bad ip address ``%s''", arg);
2297230592Sken}
2298230592Sken
2299230592Skenstatic void
2300230592Skenget_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask)
2301230592Sken{
2302230592Sken	int i;
2303230592Sken	size_t l;
2304230592Sken	char *ap, *ptr, *optr;
2305230592Sken	struct ether_addr *mac;
2306230592Sken	const char *macset = "0123456789abcdefABCDEF:";
2307230592Sken
2308230592Sken	if (strcmp(p, "any") == 0) {
2309230592Sken		for (i = 0; i < ETHER_ADDR_LEN; i++)
2310248825Smav			addr[i] = mask[i] = 0;
2311266548Sken		return;
2312266548Sken	}
2313266548Sken
2314230592Sken	optr = ptr = strdup(p);
2315230592Sken	if ((ap = strsep(&ptr, "&/")) != NULL && *ap != 0) {
2316230592Sken		l = strlen(ap);
2317230592Sken		if (strspn(ap, macset) != l || (mac = ether_aton(ap)) == NULL)
2318230592Sken			errx(EX_DATAERR, "Incorrect MAC address");
2319230592Sken		bcopy(mac, addr, ETHER_ADDR_LEN);
2320230592Sken	} else
2321230592Sken		errx(EX_DATAERR, "Incorrect MAC address");
2322230592Sken
2323230592Sken	if (ptr != NULL) { /* we have mask? */
2324266548Sken		if (p[ptr - optr - 1] == '/') { /* mask len */
2325230592Sken			long ml = strtol(ptr, &ap, 10);
2326230592Sken			if (*ap != 0 || ml > ETHER_ADDR_LEN * 8 || ml < 0)
2327230592Sken				errx(EX_DATAERR, "Incorrect mask length");
2328230592Sken			for (i = 0; ml > 0 && i < ETHER_ADDR_LEN; ml -= 8, i++)
2329230592Sken				mask[i] = (ml >= 8) ? 0xff: (~0) << (8 - ml);
2330230592Sken		} else { /* mask */
2331230592Sken			l = strlen(ptr);
2332212420Sken			if (strspn(ptr, macset) != l ||
2333212420Sken			    (mac = ether_aton(ptr)) == NULL)
2334212420Sken				errx(EX_DATAERR, "Incorrect mask");
2335230592Sken			bcopy(mac, mask, ETHER_ADDR_LEN);
2336230592Sken		}
2337230592Sken	} else { /* default mask: ff:ff:ff:ff:ff:ff */
2338230592Sken		for (i = 0; i < ETHER_ADDR_LEN; i++)
2339230592Sken			mask[i] = 0xff;
2340230592Sken	}
2341230592Sken	for (i = 0; i < ETHER_ADDR_LEN; i++)
2342268197Sscottl		addr[i] &= mask[i];
2343230592Sken
2344268197Sscottl	free(optr);
2345212420Sken}
2346230592Sken
2347230592Sken/*
2348268197Sscottl * helper function, updates the pointer to cmd with the length
2349230592Sken * of the current command, and also cleans up the first word of
2350212420Sken * the new command in case it has been clobbered before.
2351212420Sken */
2352212420Skenstatic ipfw_insn *
2353212420Skennext_cmd(ipfw_insn *cmd)
2354212420Sken{
2355212420Sken	cmd += F_LEN(cmd);
2356212420Sken	bzero(cmd, sizeof(*cmd));
2357212420Sken	return cmd;
2358212420Sken}
2359212420Sken
2360212420Sken/*
2361268197Sscottl * Takes arguments and copies them into a comment
2362212420Sken */
2363268197Sscottlstatic void
2364212420Skenfill_comment(ipfw_insn *cmd, char **av)
2365230592Sken{
2366230592Sken	int i, l;
2367230592Sken	char *p = (char *)(cmd + 1);
2368268197Sscottl
2369230592Sken	cmd->opcode = O_NOP;
2370212420Sken	cmd->len =  (cmd->len & (F_NOT | F_OR));
2371212420Sken
2372230592Sken	/* Compute length of comment string. */
2373230592Sken	for (i = 0, l = 0; av[i] != NULL; i++)
2374230592Sken		l += strlen(av[i]) + 1;
2375230592Sken	if (l == 0)
2376230592Sken		return;
2377268197Sscottl	if (l > 84)
2378253460Sscottl		errx(EX_DATAERR,
2379230592Sken		    "comment too long (max 80 chars)");
2380253460Sscottl	l = 1 + (l+3)/4;
2381253460Sscottl	cmd->len =  (cmd->len & (F_NOT | F_OR)) | l;
2382212420Sken	for (i = 0; av[i] != NULL; i++) {
2383212420Sken		strcpy(p, av[i]);
2384212420Sken		p += strlen(av[i]);
2385212420Sken		*p++ = ' ';
2386212420Sken	}
2387212420Sken	*(--p) = '\0';
2388212420Sken}
2389212420Sken
2390212420Sken/*
2391212420Sken * A function to fill simple commands of size 1.
2392212420Sken * Existing flags are preserved.
2393212420Sken */
2394253460Sscottlstatic void
2395230592Skenfill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg)
2396253460Sscottl{
2397253460Sscottl	cmd->opcode = opcode;
2398230592Sken	cmd->len =  ((cmd->len | flags) & (F_NOT | F_OR)) | 1;
2399268197Sscottl	cmd->arg1 = arg;
2400230592Sken}
2401212420Sken
2402231240Sken/*
2403231240Sken * Fetch and add the MAC address and type, with masks. This generates one or
2404212420Sken * two microinstructions, and returns the pointer to the last one.
2405230592Sken */
2406230592Skenstatic ipfw_insn *
2407230592Skenadd_mac(ipfw_insn *cmd, char *av[])
2408253460Sscottl{
2409253550Sken	ipfw_insn_mac *mac;
2410230592Sken
2411212420Sken	if ( ( av[0] == NULL ) || ( av[1] == NULL ) )
2412268197Sscottl		errx(EX_DATAERR, "MAC dst src");
2413230592Sken
2414230592Sken	cmd->opcode = O_MACADDR2;
2415230592Sken	cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
2416212420Sken
2417230592Sken	mac = (ipfw_insn_mac *)cmd;
2418230592Sken	get_mac_addr_mask(av[0], mac->addr, mac->mask);	/* dst */
2419230592Sken	get_mac_addr_mask(av[1], &(mac->addr[ETHER_ADDR_LEN]),
2420230592Sken	    &(mac->mask[ETHER_ADDR_LEN])); /* src */
2421237683Sken	return cmd;
2422230592Sken}
2423230592Sken
2424230592Skenstatic ipfw_insn *
2425230592Skenadd_mactype(ipfw_insn *cmd, char *av)
2426230592Sken{
2427230592Sken	if (!av)
2428230592Sken		errx(EX_DATAERR, "missing MAC type");
2429230592Sken	if (strcmp(av, "any") != 0) { /* we have a non-null type */
2430230592Sken		fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE);
2431230592Sken		cmd->opcode = O_MAC_TYPE;
2432230592Sken		return cmd;
2433230592Sken	} else
2434230592Sken		return NULL;
2435230592Sken}
2436230592Sken
2437230592Skenstatic ipfw_insn *
2438230592Skenadd_proto0(ipfw_insn *cmd, char *av, u_char *protop)
2439230592Sken{
2440230592Sken	struct protoent *pe;
2441230592Sken	char *ep;
2442230592Sken	int proto;
2443230592Sken
2444230592Sken	proto = strtol(av, &ep, 10);
2445230592Sken	if (*ep != '\0' || proto <= 0) {
2446230592Sken		if ((pe = getprotobyname(av)) == NULL)
2447230592Sken			return NULL;
2448230592Sken		proto = pe->p_proto;
2449230592Sken	}
2450230592Sken
2451230592Sken	fill_cmd(cmd, O_PROTO, 0, proto);
2452230592Sken	*protop = proto;
2453230592Sken	return cmd;
2454230592Sken}
2455230592Sken
2456230592Skenstatic ipfw_insn *
2457230592Skenadd_proto(ipfw_insn *cmd, char *av, u_char *protop)
2458230592Sken{
2459230592Sken	u_char proto = IPPROTO_IP;
2460230592Sken
2461230592Sken	if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
2462230592Sken		; /* do not set O_IP4 nor O_IP6 */
2463230592Sken	else if (strcmp(av, "ip4") == 0)
2464230592Sken		/* explicit "just IPv4" rule */
2465230592Sken		fill_cmd(cmd, O_IP4, 0, 0);
2466230592Sken	else if (strcmp(av, "ip6") == 0) {
2467230592Sken		/* explicit "just IPv6" rule */
2468230592Sken		proto = IPPROTO_IPV6;
2469230592Sken		fill_cmd(cmd, O_IP6, 0, 0);
2470230592Sken	} else
2471230592Sken		return add_proto0(cmd, av, protop);
2472230592Sken
2473230592Sken	*protop = proto;
2474230592Sken	return cmd;
2475230592Sken}
2476230592Sken
2477230592Skenstatic ipfw_insn *
2478230592Skenadd_proto_compat(ipfw_insn *cmd, char *av, u_char *protop)
2479230592Sken{
2480237683Sken	u_char proto = IPPROTO_IP;
2481237683Sken
2482230592Sken	if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
2483230592Sken		; /* do not set O_IP4 nor O_IP6 */
2484230592Sken	else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0)
2485230592Sken		/* explicit "just IPv4" rule */
2486230592Sken		fill_cmd(cmd, O_IP4, 0, 0);
2487230592Sken	else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) {
2488230592Sken		/* explicit "just IPv6" rule */
2489230592Sken		proto = IPPROTO_IPV6;
2490230592Sken		fill_cmd(cmd, O_IP6, 0, 0);
2491230592Sken	} else
2492230592Sken		return add_proto0(cmd, av, protop);
2493230592Sken
2494230592Sken	*protop = proto;
2495230592Sken	return cmd;
2496230592Sken}
2497230592Sken
2498230592Skenstatic ipfw_insn *
2499230592Skenadd_srcip(ipfw_insn *cmd, char *av)
2500230592Sken{
2501230592Sken	fill_ip((ipfw_insn_ip *)cmd, av);
2502230592Sken	if (cmd->opcode == O_IP_DST_SET)			/* set */
2503212420Sken		cmd->opcode = O_IP_SRC_SET;
2504230592Sken	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
2505212420Sken		cmd->opcode = O_IP_SRC_LOOKUP;
2506212420Sken	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
2507230592Sken		cmd->opcode = O_IP_SRC_ME;
2508237683Sken	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
2509230592Sken		cmd->opcode = O_IP_SRC;
2510230592Sken	else							/* addr/mask */
2511230592Sken		cmd->opcode = O_IP_SRC_MASK;
2512237683Sken	return cmd;
2513237683Sken}
2514230592Sken
2515230592Skenstatic ipfw_insn *
2516230592Skenadd_dstip(ipfw_insn *cmd, char *av)
2517237683Sken{
2518237683Sken	fill_ip((ipfw_insn_ip *)cmd, av);
2519237683Sken	if (cmd->opcode == O_IP_DST_SET)			/* set */
2520230592Sken		;
2521237683Sken	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
2522237683Sken		;
2523230592Sken	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
2524230592Sken		cmd->opcode = O_IP_DST_ME;
2525230592Sken	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
2526230592Sken		cmd->opcode = O_IP_DST;
2527230592Sken	else							/* addr/mask */
2528225950Sken		cmd->opcode = O_IP_DST_MASK;
2529230592Sken	return cmd;
2530230592Sken}
2531230592Sken
2532230592Skenstatic ipfw_insn *
2533237683Skenadd_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
2534237683Sken{
2535237683Sken	/* XXX "any" is trapped before. Perhaps "to" */
2536237683Sken	if (_substrcmp(av, "any") == 0) {
2537230592Sken		return NULL;
2538230592Sken	} else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) {
2539230592Sken		/* XXX todo: check that we have a protocol with ports */
2540230592Sken		cmd->opcode = opcode;
2541225950Sken		return cmd;
2542230592Sken	}
2543230592Sken	return NULL;
2544230592Sken}
2545230592Sken
2546230592Skenstatic ipfw_insn *
2547230592Skenadd_src(ipfw_insn *cmd, char *av, u_char proto)
2548230592Sken{
2549230592Sken	struct in6_addr a;
2550230592Sken	char *host, *ch;
2551230592Sken	ipfw_insn *ret = NULL;
2552230592Sken
2553230592Sken	if ((host = strdup(av)) == NULL)
2554230592Sken		return NULL;
2555230592Sken	if ((ch = strrchr(host, '/')) != NULL)
2556230592Sken		*ch = '\0';
2557230592Sken
2558230592Sken	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
2559230592Sken	    inet_pton(AF_INET6, host, &a) == 1)
2560230592Sken		ret = add_srcip6(cmd, av);
2561230592Sken	/* XXX: should check for IPv4, not !IPv6 */
2562230592Sken	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
2563230592Sken	    inet_pton(AF_INET6, host, &a) != 1))
2564230592Sken		ret = add_srcip(cmd, av);
2565230592Sken	if (ret == NULL && strcmp(av, "any") != 0)
2566230592Sken		ret = cmd;
2567230592Sken
2568230592Sken	free(host);
2569237683Sken	return ret;
2570237683Sken}
2571230592Sken
2572230592Skenstatic ipfw_insn *
2573212420Skenadd_dst(ipfw_insn *cmd, char *av, u_char proto)
2574230592Sken{
2575230592Sken	struct in6_addr a;
2576230592Sken	char *host, *ch;
2577230592Sken	ipfw_insn *ret = NULL;
2578230592Sken
2579230592Sken	if ((host = strdup(av)) == NULL)
2580230592Sken		return NULL;
2581230592Sken	if ((ch = strrchr(host, '/')) != NULL)
2582230592Sken		*ch = '\0';
2583230592Sken
2584230592Sken	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
2585230592Sken	    inet_pton(AF_INET6, host, &a) == 1)
2586230592Sken		ret = add_dstip6(cmd, av);
2587230592Sken	/* XXX: should check for IPv4, not !IPv6 */
2588230592Sken	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
2589230592Sken	    inet_pton(AF_INET6, host, &a) != 1))
2590230592Sken		ret = add_dstip(cmd, av);
2591230592Sken	if (ret == NULL && strcmp(av, "any") != 0)
2592230592Sken		ret = cmd;
2593212420Sken
2594230592Sken	free(host);
2595230592Sken	return ret;
2596230592Sken}
2597230592Sken
2598230592Sken/*
2599230592Sken * Parse arguments and assemble the microinstructions which make up a rule.
2600230592Sken * Rules are added into the 'rulebuf' and then copied in the correct order
2601230592Sken * into the actual rule.
2602230592Sken *
2603230592Sken * The syntax for a rule starts with the action, followed by
2604230592Sken * optional action parameters, and the various match patterns.
2605230592Sken * In the assembled microcode, the first opcode must be an O_PROBE_STATE
2606230592Sken * (generated if the rule includes a keep-state option), then the
2607212420Sken * various match patterns, log/altq actions, and the actual action.
2608230592Sken *
2609230592Sken */
2610230592Skenvoid
2611230592Skenipfw_add(char *av[])
2612230592Sken{
2613230592Sken	/*
2614230592Sken	 * rules are added into the 'rulebuf' and then copied in
2615230592Sken	 * the correct order into the actual rule.
2616230592Sken	 * Some things that need to go out of order (prob, action etc.)
2617230592Sken	 * go into actbuf[].
2618230592Sken	 */
2619230592Sken	static uint32_t rulebuf[255], actbuf[255], cmdbuf[255];
2620230592Sken
2621230592Sken	ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
2622230592Sken	ipfw_insn *first_cmd;	/* first match pattern */
2623230592Sken
2624230592Sken	struct ip_fw *rule;
2625230592Sken
2626230592Sken	/*
2627230592Sken	 * various flags used to record that we entered some fields.
2628230592Sken	 */
2629230592Sken	ipfw_insn *have_state = NULL;	/* check-state or keep-state */
2630230592Sken	ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL;
2631230592Sken	size_t len;
2632230592Sken
2633230592Sken	int i;
2634230592Sken
2635230592Sken	int open_par = 0;	/* open parenthesis ( */
2636230592Sken
2637230592Sken	/* proto is here because it is used to fetch ports */
2638230592Sken	u_char proto = IPPROTO_IP;	/* default protocol */
2639230592Sken
2640230592Sken	double match_prob = 1; /* match probability, default is always match */
2641230592Sken
2642230592Sken	bzero(actbuf, sizeof(actbuf));		/* actions go here */
2643230592Sken	bzero(cmdbuf, sizeof(cmdbuf));
2644230592Sken	bzero(rulebuf, sizeof(rulebuf));
2645230592Sken
2646230592Sken	rule = (struct ip_fw *)rulebuf;
2647237683Sken	cmd = (ipfw_insn *)cmdbuf;
2648237683Sken	action = (ipfw_insn *)actbuf;
2649230592Sken
2650230592Sken	av++;
2651230592Sken
2652230592Sken	/* [rule N]	-- Rule number optional */
2653230592Sken	if (av[0] && isdigit(**av)) {
2654230592Sken		rule->rulenum = atoi(*av);
2655230592Sken		av++;
2656230592Sken	}
2657230592Sken
2658230592Sken	/* [set N]	-- set number (0..RESVD_SET), optional */
2659230592Sken	if (av[0] && !av[1] && _substrcmp(*av, "set") == 0) {
2660230592Sken		int set = strtoul(av[1], NULL, 10);
2661230592Sken		if (set < 0 || set > RESVD_SET)
2662230592Sken			errx(EX_DATAERR, "illegal set %s", av[1]);
2663230592Sken		rule->set = set;
2664230592Sken		av += 2;
2665230592Sken	}
2666230592Sken
2667230592Sken	/* [prob D]	-- match probability, optional */
2668230592Sken	if (av[0] && av[1] && _substrcmp(*av, "prob") == 0) {
2669230592Sken		match_prob = strtod(av[1], NULL);
2670230592Sken
2671230592Sken		if (match_prob <= 0 || match_prob > 1)
2672230592Sken			errx(EX_DATAERR, "illegal match prob. %s", av[1]);
2673230592Sken		av += 2;
2674230592Sken	}
2675230592Sken
2676230592Sken	/* action	-- mandatory */
2677230592Sken	NEED1("missing action");
2678230592Sken	i = match_token(rule_actions, *av);
2679230592Sken	av++;
2680230592Sken	action->len = 1;	/* default */
2681230592Sken	switch(i) {
2682230592Sken	case TOK_CHECKSTATE:
2683230592Sken		have_state = action;
2684230592Sken		action->opcode = O_CHECK_STATE;
2685230592Sken		break;
2686230592Sken
2687218812Sken	case TOK_ACCEPT:
2688212420Sken		action->opcode = O_ACCEPT;
2689212420Sken		break;
2690216088Sken
2691212420Sken	case TOK_DENY:
2692216088Sken		action->opcode = O_DENY;
2693216088Sken		action->arg1 = 0;
2694216088Sken		break;
2695216088Sken
2696216088Sken	case TOK_REJECT:
2697216088Sken		action->opcode = O_REJECT;
2698216088Sken		action->arg1 = ICMP_UNREACH_HOST;
2699216088Sken		break;
2700218812Sken
2701218812Sken	case TOK_RESET:
2702218812Sken		action->opcode = O_REJECT;
2703218812Sken		action->arg1 = ICMP_REJECT_RST;
2704218812Sken		break;
2705218812Sken
2706218812Sken	case TOK_RESET6:
2707218812Sken		action->opcode = O_UNREACH6;
2708253460Sscottl		action->arg1 = ICMP6_UNREACH_RST;
2709218812Sken		break;
2710268197Sscottl
2711218812Sken	case TOK_UNREACH:
2712230592Sken		action->opcode = O_REJECT;
2713218812Sken		NEED1("missing reject code");
2714216088Sken		fill_reject_code(&action->arg1, *av);
2715216088Sken		av++;
2716253460Sscottl		break;
2717268197Sscottl
2718216088Sken	case TOK_UNREACH6:
2719216088Sken		action->opcode = O_UNREACH6;
2720216088Sken		NEED1("missing unreach code");
2721216088Sken		fill_unreach6_code(&action->arg1, *av);
2722216088Sken		av++;
2723216088Sken		break;
2724216088Sken
2725253550Sken	case TOK_COUNT:
2726253550Sken		action->opcode = O_COUNT;
2727216088Sken		break;
2728253460Sscottl
2729237683Sken	case TOK_NAT:
2730268197Sscottl 		action->opcode = O_NAT;
2731216088Sken 		action->len = F_INSN_SIZE(ipfw_insn_nat);
2732216088Sken		goto chkarg;
2733216088Sken
2734253460Sscottl	case TOK_QUEUE:
2735216088Sken		action->opcode = O_QUEUE;
2736216088Sken		goto chkarg;
2737216088Sken	case TOK_PIPE:
2738216088Sken		action->opcode = O_PIPE;
2739268197Sscottl		goto chkarg;
2740216088Sken	case TOK_SKIPTO:
2741268197Sscottl		action->opcode = O_SKIPTO;
2742216088Sken		goto chkarg;
2743216088Sken	case TOK_NETGRAPH:
2744216088Sken		action->opcode = O_NETGRAPH;
2745216088Sken		goto chkarg;
2746216088Sken	case TOK_NGTEE:
2747216088Sken		action->opcode = O_NGTEE;
2748216088Sken		goto chkarg;
2749216088Sken	case TOK_DIVERT:
2750216088Sken		action->opcode = O_DIVERT;
2751216088Sken		goto chkarg;
2752216088Sken	case TOK_TEE:
2753216088Sken		action->opcode = O_TEE;
2754216088Skenchkarg:
2755216088Sken		if (!av[0])
2756216088Sken			errx(EX_USAGE, "missing argument for %s", *(av - 1));
2757216088Sken		if (isdigit(**av)) {
2758216088Sken			action->arg1 = strtoul(*av, NULL, 10);
2759216088Sken			if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG)
2760216088Sken				errx(EX_DATAERR, "illegal argument for %s",
2761216088Sken				    *(av - 1));
2762216088Sken		} else if (_substrcmp(*av, "tablearg") == 0) {
2763216088Sken			action->arg1 = IP_FW_TABLEARG;
2764216088Sken		} else if (i == TOK_DIVERT || i == TOK_TEE) {
2765216088Sken			struct servent *s;
2766216088Sken			setservent(1);
2767216088Sken			s = getservbyname(av[0], "divert");
2768216088Sken			if (s != NULL)
2769216088Sken				action->arg1 = ntohs(s->s_port);
2770216088Sken			else
2771216088Sken				errx(EX_DATAERR, "illegal divert/tee port");
2772246713Skib		} else
2773246713Skib			errx(EX_DATAERR, "illegal argument for %s", *(av - 1));
2774246713Skib		av++;
2775253460Sscottl		break;
2776253460Sscottl
2777268197Sscottl	case TOK_FORWARD: {
2778216088Sken		ipfw_insn_sa *p = (ipfw_insn_sa *)action;
2779216088Sken		char *s, *end;
2780246713Skib
2781216088Sken		NEED1("missing forward address[:port]");
2782216088Sken
2783216088Sken		action->opcode = O_FORWARD_IP;
2784216088Sken		action->len = F_INSN_SIZE(ipfw_insn_sa);
2785216088Sken
2786216088Sken		/*
2787253460Sscottl		 * In the kernel we assume AF_INET and use only
2788253460Sscottl		 * sin_port and sin_addr. Remember to set sin_len as
2789216088Sken		 * the routing code seems to use it too.
2790216088Sken		 */
2791268197Sscottl		p->sa.sin_family = AF_INET;
2792216088Sken		p->sa.sin_len = sizeof(struct sockaddr_in);
2793216088Sken		p->sa.sin_port = 0;
2794216088Sken		/*
2795216088Sken		 * locate the address-port separator (':' or ',')
2796216088Sken		 */
2797216088Sken		s = strchr(*av, ':');
2798216088Sken		if (s == NULL)
2799216088Sken			s = strchr(*av, ',');
2800216088Sken		if (s != NULL) {
2801216088Sken			*(s++) = '\0';
2802216088Sken			i = strtoport(s, &end, 0 /* base */, 0 /* proto */);
2803216088Sken			if (s == end)
2804216088Sken				errx(EX_DATAERR,
2805216088Sken				    "illegal forwarding port ``%s''", s);
2806216088Sken			p->sa.sin_port = (u_short)i;
2807216088Sken		}
2808241145Sken		if (_substrcmp(*av, "tablearg") == 0)
2809216088Sken			p->sa.sin_addr.s_addr = INADDR_ANY;
2810216088Sken		else
2811216088Sken			lookup_host(*av, &(p->sa.sin_addr));
2812216088Sken		av++;
2813216088Sken		break;
2814216088Sken	    }
2815216088Sken	case TOK_COMMENT:
2816241145Sken		/* pretend it is a 'count' rule followed by the comment */
2817216088Sken		action->opcode = O_COUNT;
2818216088Sken		av--;		/* go back... */
2819246713Skib		break;
2820246713Skib
2821216088Sken	case TOK_SETFIB:
2822216088Sken	    {
2823246713Skib		int numfibs;
2824246713Skib		size_t intsize = sizeof(int);
2825268197Sscottl
2826246713Skib		action->opcode = O_SETFIB;
2827246713Skib 		NEED1("missing fib number");
2828216088Sken 	        action->arg1 = strtoul(*av, NULL, 10);
2829216088Sken		if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1)
2830216088Sken			errx(EX_DATAERR, "fibs not suported.\n");
2831216088Sken		if (action->arg1 >= numfibs)  /* Temporary */
2832253460Sscottl			errx(EX_DATAERR, "fib too large.\n");
2833253460Sscottl 		av++;
2834268197Sscottl 		break;
2835216088Sken	    }
2836216088Sken
2837216088Sken	case TOK_REASS:
2838216088Sken		action->opcode = O_REASS;
2839216088Sken		break;
2840216088Sken
2841216088Sken	default:
2842216088Sken		errx(EX_DATAERR, "invalid action %s\n", av[-1]);
2843216088Sken	}
2844216088Sken	action = next_cmd(action);
2845216088Sken
2846237683Sken	/*
2847216088Sken	 * [altq queuename] -- altq tag, optional
2848216088Sken	 * [log [logamount N]]	-- log, optional
2849216088Sken	 *
2850253460Sscottl	 * If they exist, it go first in the cmdbuf, but then it is
2851253550Sken	 * skipped in the copy section to the end of the buffer.
2852216088Sken	 */
2853216088Sken	while (av[0] != NULL && (i = match_token(rule_action_params, *av)) != -1) {
2854216088Sken		av++;
2855216088Sken		switch (i) {
2856216088Sken		case TOK_LOG:
2857216088Sken		    {
2858216088Sken			ipfw_insn_log *c = (ipfw_insn_log *)cmd;
2859216088Sken			int l;
2860216088Sken
2861216088Sken			if (have_log)
2862216088Sken				errx(EX_DATAERR,
2863216088Sken				    "log cannot be specified more than once");
2864216088Sken			have_log = (ipfw_insn *)c;
2865216088Sken			cmd->len = F_INSN_SIZE(ipfw_insn_log);
2866216088Sken			cmd->opcode = O_LOG;
2867216088Sken			if (av[0] && _substrcmp(*av, "logamount") == 0) {
2868216088Sken				av++;
2869216088Sken				NEED1("logamount requires argument");
2870216088Sken				l = atoi(*av);
2871237683Sken				if (l < 0)
2872216088Sken					errx(EX_DATAERR,
2873216088Sken					    "logamount must be positive");
2874216088Sken				c->max_log = l;
2875216088Sken				av++;
2876216088Sken			} else {
2877216088Sken				len = sizeof(c->max_log);
2878216088Sken				if (sysctlbyname("net.inet.ip.fw.verbose_limit",
2879216088Sken				    &c->max_log, &len, NULL, 0) == -1)
2880216088Sken					errx(1, "sysctlbyname(\"%s\")",
2881216088Sken					    "net.inet.ip.fw.verbose_limit");
2882216088Sken			}
2883216088Sken		    }
2884216088Sken			break;
2885216088Sken
2886216088Sken#ifndef NO_ALTQ
2887216088Sken		case TOK_ALTQ:
2888216088Sken		    {
2889216088Sken			ipfw_insn_altq *a = (ipfw_insn_altq *)cmd;
2890216088Sken
2891216088Sken			NEED1("missing altq queue name");
2892216088Sken			if (have_altq)
2893216088Sken				errx(EX_DATAERR,
2894216088Sken				    "altq cannot be specified more than once");
2895216088Sken			have_altq = (ipfw_insn *)a;
2896216088Sken			cmd->len = F_INSN_SIZE(ipfw_insn_altq);
2897216088Sken			cmd->opcode = O_ALTQ;
2898216088Sken			a->qid = altq_name_to_qid(*av);
2899216088Sken			av++;
2900216088Sken		    }
2901216088Sken			break;
2902216088Sken#endif
2903216088Sken
2904216088Sken		case TOK_TAG:
2905216088Sken		case TOK_UNTAG: {
2906216088Sken			uint16_t tag;
2907216088Sken
2908216088Sken			if (have_tag)
2909216088Sken				errx(EX_USAGE, "tag and untag cannot be "
2910216088Sken				    "specified more than once");
2911216088Sken			GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX, i,
2912253460Sscottl			   rule_action_params);
2913253460Sscottl			have_tag = cmd;
2914216088Sken			fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag);
2915216088Sken			av++;
2916216088Sken			break;
2917216088Sken		}
2918216088Sken
2919216088Sken		default:
2920216088Sken			abort();
2921216088Sken		}
2922268197Sscottl		cmd = next_cmd(cmd);
2923216088Sken	}
2924216088Sken
2925216088Sken	if (have_state)	/* must be a check-state, we are done */
2926216088Sken		goto done;
2927216088Sken
2928216088Sken#define OR_START(target)					\
2929216088Sken	if (av[0] && (*av[0] == '(' || *av[0] == '{')) { 	\
2930216088Sken		if (open_par)					\
2931216088Sken			errx(EX_USAGE, "nested \"(\" not allowed\n"); \
2932216088Sken		prev = NULL;					\
2933216088Sken		open_par = 1;					\
2934216088Sken		if ( (av[0])[1] == '\0') {			\
2935216088Sken			av++;					\
2936216088Sken		} else						\
2937216088Sken			(*av)++;				\
2938216088Sken	}							\
2939216088Sken	target:							\
2940264492Sscottl
2941264492Sscottl
2942216088Sken#define	CLOSE_PAR						\
2943216088Sken	if (open_par) {						\
2944253460Sscottl		if (av[0] && (					\
2945253460Sscottl		    strcmp(*av, ")") == 0 ||			\
2946216088Sken		    strcmp(*av, "}") == 0)) {			\
2947268197Sscottl			prev = NULL;				\
2948216088Sken			open_par = 0;				\
2949216088Sken			av++;					\
2950216088Sken		} else						\
2951216088Sken			errx(EX_USAGE, "missing \")\"\n");	\
2952216088Sken	}
2953216088Sken
2954216088Sken#define NOT_BLOCK						\
2955216088Sken	if (av[0] && _substrcmp(*av, "not") == 0) {		\
2956216088Sken		if (cmd->len & F_NOT)				\
2957216088Sken			errx(EX_USAGE, "double \"not\" not allowed\n"); \
2958216088Sken		cmd->len |= F_NOT;				\
2959216088Sken		av++;						\
2960216088Sken	}
2961216088Sken
2962216088Sken#define OR_BLOCK(target)					\
2963216088Sken	if (av[0] && _substrcmp(*av, "or") == 0) {		\
2964216088Sken		if (prev == NULL || open_par == 0)		\
2965216088Sken			errx(EX_DATAERR, "invalid OR block");	\
2966216088Sken		prev->len |= F_OR;				\
2967216088Sken		av++;					\
2968216088Sken		goto target;					\
2969216088Sken	}							\
2970216088Sken	CLOSE_PAR;
2971216088Sken
2972216088Sken	first_cmd = cmd;
2973216088Sken
2974216088Sken#if 0
2975216088Sken	/*
2976216088Sken	 * MAC addresses, optional.
2977216088Sken	 * If we have this, we skip the part "proto from src to dst"
2978216088Sken	 * and jump straight to the option parsing.
2979216088Sken	 */
2980216088Sken	NOT_BLOCK;
2981216088Sken	NEED1("missing protocol");
2982216088Sken	if (_substrcmp(*av, "MAC") == 0 ||
2983216088Sken	    _substrcmp(*av, "mac") == 0) {
2984216088Sken		av++;			/* the "MAC" keyword */
2985216088Sken		add_mac(cmd, av);	/* exits in case of errors */
2986216088Sken		cmd = next_cmd(cmd);
2987216088Sken		av += 2;		/* dst-mac and src-mac */
2988230592Sken		NOT_BLOCK;
2989216088Sken		NEED1("missing mac type");
2990230592Sken		if (add_mactype(cmd, av[0]))
2991216088Sken			cmd = next_cmd(cmd);
2992216088Sken		av++;			/* any or mac-type */
2993253460Sscottl		goto read_options;
2994253460Sscottl	}
2995216088Sken#endif
2996270250Sslm
2997216088Sken	/*
2998216088Sken	 * protocol, mandatory
2999230592Sken	 */
3000230592Sken    OR_START(get_proto);
3001230592Sken	NOT_BLOCK;
3002216088Sken	NEED1("missing protocol");
3003216088Sken	if (add_proto_compat(cmd, *av, &proto)) {
3004253460Sscottl		av++;
3005253460Sscottl		if (F_LEN(cmd) != 0) {
3006216088Sken			prev = cmd;
3007270250Sslm			cmd = next_cmd(cmd);
3008216088Sken		}
3009216088Sken	} else if (first_cmd != cmd) {
3010216088Sken		errx(EX_DATAERR, "invalid protocol ``%s''", *av);
3011216088Sken	} else
3012216088Sken		goto read_options;
3013253460Sscottl    OR_BLOCK(get_proto);
3014253460Sscottl
3015216088Sken	/*
3016216088Sken	 * "from", mandatory
3017270250Sslm	 */
3018216088Sken	if ((av[0] == NULL) || _substrcmp(*av, "from") != 0)
3019216088Sken		errx(EX_USAGE, "missing ``from''");
3020216088Sken	av++;
3021216088Sken
3022216088Sken	/*
3023230592Sken	 * source IP, mandatory
3024230592Sken	 */
3025230592Sken    OR_START(source_ip);
3026253460Sscottl	NOT_BLOCK;	/* optional "not" */
3027253460Sscottl	NEED1("missing source address");
3028230592Sken	if (add_src(cmd, *av, proto)) {
3029230592Sken		av++;
3030270250Sslm		if (F_LEN(cmd) != 0) {	/* ! any */
3031230592Sken			prev = cmd;
3032230592Sken			cmd = next_cmd(cmd);
3033230592Sken		}
3034230592Sken	} else
3035253460Sscottl		errx(EX_USAGE, "bad source address %s", *av);
3036253460Sscottl    OR_BLOCK(source_ip);
3037230592Sken
3038230592Sken	/*
3039270250Sslm	 * source ports, optional
3040230592Sken	 */
3041230592Sken	NOT_BLOCK;	/* optional "not" */
3042230592Sken	if ( av[0] != NULL ) {
3043230592Sken		if (_substrcmp(*av, "any") == 0 ||
3044230592Sken		    add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
3045230592Sken			av++;
3046216088Sken			if (F_LEN(cmd) != 0)
3047216088Sken				cmd = next_cmd(cmd);
3048216088Sken		}
3049253460Sscottl	}
3050253460Sscottl
3051216088Sken	/*
3052270250Sslm	 * "to", mandatory
3053216088Sken	 */
3054216088Sken	if ( (av[0] == NULL) || _substrcmp(*av, "to") != 0 )
3055216088Sken		errx(EX_USAGE, "missing ``to''");
3056216088Sken	av++;
3057216088Sken
3058216088Sken	/*
3059216088Sken	 * destination, mandatory
3060216088Sken	 */
3061216088Sken    OR_START(dest_ip);
3062216088Sken	NOT_BLOCK;	/* optional "not" */
3063230592Sken	NEED1("missing dst address");
3064216088Sken	if (add_dst(cmd, *av, proto)) {
3065216088Sken		av++;
3066212420Sken		if (F_LEN(cmd) != 0) {	/* ! any */
3067212420Sken			prev = cmd;
3068230592Sken			cmd = next_cmd(cmd);
3069212420Sken		}
3070230592Sken	} else
3071212420Sken		errx( EX_USAGE, "bad destination address %s", *av);
3072212420Sken    OR_BLOCK(dest_ip);
3073253460Sscottl
3074230592Sken	/*
3075230592Sken	 * dest. ports, optional
3076264492Sscottl	 */
3077264492Sscottl	NOT_BLOCK;	/* optional "not" */
3078264492Sscottl	if (av[0]) {
3079212420Sken		if (_substrcmp(*av, "any") == 0 ||
3080230592Sken		    add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
3081230592Sken			av++;
3082253460Sscottl			if (F_LEN(cmd) != 0)
3083253809Sscottl				cmd = next_cmd(cmd);
3084268197Sscottl		}
3085212420Sken	}
3086212420Sken
3087212420Skenread_options:
3088212420Sken	if (av[0] && first_cmd == cmd) {
3089230592Sken		/*
3090230592Sken		 * nothing specified so far, store in the rule to ease
3091237683Sken		 * printout later.
3092212420Sken		 */
3093212420Sken		 rule->_pad = 1;
3094212420Sken	}
3095212420Sken	prev = NULL;
3096212420Sken	while ( av[0] != NULL ) {
3097212420Sken		char *s;
3098230592Sken		ipfw_insn_u32 *cmd32;	/* alias for cmd */
3099230592Sken
3100230592Sken		s = *av;
3101230592Sken		cmd32 = (ipfw_insn_u32 *)cmd;
3102238969Smav
3103230592Sken		if (*s == '!') {	/* alternate syntax for NOT */
3104212420Sken			if (cmd->len & F_NOT)
3105212420Sken				errx(EX_USAGE, "double \"not\" not allowed\n");
3106212420Sken			cmd->len = F_NOT;
3107230592Sken			s++;
3108212420Sken		}
3109212420Sken		i = match_token(rule_options, s);
3110212420Sken		av++;
3111212420Sken		switch(i) {
3112253460Sscottl		case TOK_NOT:
3113230592Sken			if (cmd->len & F_NOT)
3114212420Sken				errx(EX_USAGE, "double \"not\" not allowed\n");
3115230592Sken			cmd->len = F_NOT;
3116230592Sken			break;
3117212420Sken
3118230592Sken		case TOK_OR:
3119230592Sken			if (open_par == 0 || prev == NULL)
3120230592Sken				errx(EX_USAGE, "invalid \"or\" block\n");
3121230592Sken			prev->len |= F_OR;
3122230592Sken			break;
3123230592Sken
3124218812Sken		case TOK_STARTBRACE:
3125218812Sken			if (open_par)
3126230592Sken				errx(EX_USAGE, "+nested \"(\" not allowed\n");
3127218812Sken			open_par = 1;
3128253460Sscottl			break;
3129253460Sscottl
3130230592Sken		case TOK_ENDBRACE:
3131218812Sken			if (!open_par)
3132268197Sscottl				errx(EX_USAGE, "+missing \")\"\n");
3133218812Sken			open_par = 0;
3134218812Sken			prev = NULL;
3135218812Sken        		break;
3136253460Sscottl
3137253460Sscottl		case TOK_IN:
3138237683Sken			fill_cmd(cmd, O_IN, 0, 0);
3139212420Sken			break;
3140237683Sken
3141268197Sscottl		case TOK_OUT:
3142230592Sken			cmd->len ^= F_NOT; /* toggle F_NOT */
3143230592Sken			fill_cmd(cmd, O_IN, 0, 0);
3144230592Sken			break;
3145212420Sken
3146268197Sscottl		case TOK_DIVERTED:
3147212420Sken			fill_cmd(cmd, O_DIVERTED, 0, 3);
3148218812Sken			break;
3149212772Sken
3150230592Sken		case TOK_DIVERTEDLOOPBACK:
3151212420Sken			fill_cmd(cmd, O_DIVERTED, 0, 1);
3152212420Sken			break;
3153212420Sken
3154212420Sken		case TOK_DIVERTEDOUTPUT:
3155212420Sken			fill_cmd(cmd, O_DIVERTED, 0, 2);
3156212420Sken			break;
3157212420Sken
3158212420Sken		case TOK_FRAG:
3159212420Sken			fill_cmd(cmd, O_FRAG, 0, 0);
3160230592Sken			break;
3161230592Sken
3162230592Sken		case TOK_LAYER2:
3163230592Sken			fill_cmd(cmd, O_LAYER2, 0, 0);
3164230592Sken			break;
3165230592Sken
3166230592Sken		case TOK_XMIT:
3167230592Sken		case TOK_RECV:
3168230592Sken		case TOK_VIA:
3169212420Sken			NEED1("recv, xmit, via require interface name"
3170212420Sken				" or address");
3171212420Sken			fill_iface((ipfw_insn_if *)cmd, av[0]);
3172212420Sken			av++;
3173230592Sken			if (F_LEN(cmd) == 0)	/* not a valid address */
3174230592Sken				break;
3175230592Sken			if (i == TOK_XMIT)
3176230592Sken				cmd->opcode = O_XMIT;
3177230592Sken			else if (i == TOK_RECV)
3178230592Sken				cmd->opcode = O_RECV;
3179230592Sken			else if (i == TOK_VIA)
3180230592Sken				cmd->opcode = O_VIA;
3181253549Sken			break;
3182253549Sken
3183230592Sken		case TOK_ICMPTYPES:
3184230592Sken			NEED1("icmptypes requires list of types");
3185230592Sken			fill_icmptypes((ipfw_insn_u32 *)cmd, *av);
3186230592Sken			av++;
3187230592Sken			break;
3188230592Sken
3189230592Sken		case TOK_ICMP6TYPES:
3190230592Sken			NEED1("icmptypes requires list of types");
3191230592Sken			fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av);
3192230592Sken			av++;
3193230592Sken			break;
3194230592Sken
3195230592Sken		case TOK_IPTTL:
3196230592Sken			NEED1("ipttl requires TTL");
3197230592Sken			if (strpbrk(*av, "-,")) {
3198230592Sken			    if (!add_ports(cmd, *av, 0, O_IPTTL))
3199230592Sken				errx(EX_DATAERR, "invalid ipttl %s", *av);
3200230592Sken			} else
3201230592Sken			    fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
3202230592Sken			av++;
3203230592Sken			break;
3204230592Sken
3205230592Sken		case TOK_IPID:
3206230592Sken			NEED1("ipid requires id");
3207264492Sscottl			if (strpbrk(*av, "-,")) {
3208264492Sscottl			    if (!add_ports(cmd, *av, 0, O_IPID))
3209264492Sscottl				errx(EX_DATAERR, "invalid ipid %s", *av);
3210230592Sken			} else
3211230592Sken			    fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0));
3212230592Sken			av++;
3213230592Sken			break;
3214230592Sken
3215230592Sken		case TOK_IPLEN:
3216230592Sken			NEED1("iplen requires length");
3217230592Sken			if (strpbrk(*av, "-,")) {
3218230592Sken			    if (!add_ports(cmd, *av, 0, O_IPLEN))
3219230592Sken				errx(EX_DATAERR, "invalid ip len %s", *av);
3220230592Sken			} else
3221230592Sken			    fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0));
3222230592Sken			av++;
3223230592Sken			break;
3224230592Sken
3225230592Sken		case TOK_IPVER:
3226230592Sken			NEED1("ipver requires version");
3227253460Sscottl			fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0));
3228230592Sken			av++;
3229230592Sken			break;
3230230592Sken
3231230592Sken		case TOK_IPPRECEDENCE:
3232230592Sken			NEED1("ipprecedence requires value");
3233230592Sken			fill_cmd(cmd, O_IPPRECEDENCE, 0,
3234230592Sken			    (strtoul(*av, NULL, 0) & 7) << 5);
3235230592Sken			av++;
3236230592Sken			break;
3237230592Sken
3238230592Sken		case TOK_IPOPTS:
3239230592Sken			NEED1("missing argument for ipoptions");
3240230592Sken			fill_flags(cmd, O_IPOPT, f_ipopts, *av);
3241230592Sken			av++;
3242230592Sken			break;
3243230592Sken
3244230592Sken		case TOK_IPTOS:
3245230592Sken			NEED1("missing argument for iptos");
3246230592Sken			fill_flags(cmd, O_IPTOS, f_iptos, *av);
3247230592Sken			av++;
3248268197Sscottl			break;
3249230592Sken
3250230592Sken		case TOK_UID:
3251230592Sken			NEED1("uid requires argument");
3252230592Sken		    {
3253230592Sken			char *end;
3254230592Sken			uid_t uid;
3255230592Sken			struct passwd *pwd;
3256230592Sken
3257230592Sken			cmd->opcode = O_UID;
3258253549Sken			uid = strtoul(*av, &end, 0);
3259253549Sken			pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av);
3260253549Sken			if (pwd == NULL)
3261253549Sken				errx(EX_DATAERR, "uid \"%s\" nonexistent", *av);
3262253549Sken			cmd32->d[0] = pwd->pw_uid;
3263253549Sken			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
3264253549Sken			av++;
3265253549Sken		    }
3266253549Sken			break;
3267230592Sken
3268230592Sken		case TOK_GID:
3269230592Sken			NEED1("gid requires argument");
3270230592Sken		    {
3271230592Sken			char *end;
3272253549Sken			gid_t gid;
3273253549Sken			struct group *grp;
3274230592Sken
3275253549Sken			cmd->opcode = O_GID;
3276253549Sken			gid = strtoul(*av, &end, 0);
3277230592Sken			grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av);
3278253549Sken			if (grp == NULL)
3279230592Sken				errx(EX_DATAERR, "gid \"%s\" nonexistent", *av);
3280230592Sken			cmd32->d[0] = grp->gr_gid;
3281230592Sken			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
3282253549Sken			av++;
3283230592Sken		    }
3284230592Sken			break;
3285253549Sken
3286253549Sken		case TOK_JAIL:
3287230592Sken			NEED1("jail requires argument");
3288230592Sken		    {
3289230592Sken			char *end;
3290237683Sken			int jid;
3291230592Sken
3292253549Sken			cmd->opcode = O_JAIL;
3293253549Sken			jid = (int)strtol(*av, &end, 0);
3294253549Sken			if (jid < 0 || *end != '\0')
3295253549Sken				errx(EX_DATAERR, "jail requires prison ID");
3296253549Sken			cmd32->d[0] = (uint32_t)jid;
3297264492Sscottl			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
3298264492Sscottl			av++;
3299264492Sscottl		    }
3300253549Sken			break;
3301253549Sken
3302253549Sken		case TOK_ESTAB:
3303253549Sken			fill_cmd(cmd, O_ESTAB, 0, 0);
3304230592Sken			break;
3305253549Sken
3306253549Sken		case TOK_SETUP:
3307253549Sken			fill_cmd(cmd, O_TCPFLAGS, 0,
3308253549Sken				(TH_SYN) | ( (TH_ACK) & 0xff) <<8 );
3309253549Sken			break;
3310253549Sken
3311253549Sken		case TOK_TCPDATALEN:
3312230592Sken			NEED1("tcpdatalen requires length");
3313253549Sken			if (strpbrk(*av, "-,")) {
3314253549Sken			    if (!add_ports(cmd, *av, 0, O_TCPDATALEN))
3315230592Sken				errx(EX_DATAERR, "invalid tcpdata len %s", *av);
3316253549Sken			} else
3317253549Sken			    fill_cmd(cmd, O_TCPDATALEN, 0,
3318253549Sken				    strtoul(*av, NULL, 0));
3319253549Sken			av++;
3320253549Sken			break;
3321253549Sken
3322253549Sken		case TOK_TCPOPTS:
3323253549Sken			NEED1("missing argument for tcpoptions");
3324253549Sken			fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av);
3325253549Sken			av++;
3326253549Sken			break;
3327230592Sken
3328253549Sken		case TOK_TCPSEQ:
3329253549Sken		case TOK_TCPACK:
3330253549Sken			NEED1("tcpseq/tcpack requires argument");
3331253549Sken			cmd->len = F_INSN_SIZE(ipfw_insn_u32);
3332253549Sken			cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK;
3333253549Sken			cmd32->d[0] = htonl(strtoul(*av, NULL, 0));
3334253549Sken			av++;
3335230592Sken			break;
3336253549Sken
3337253549Sken		case TOK_TCPWIN:
3338253549Sken			NEED1("tcpwin requires length");
3339253549Sken			fill_cmd(cmd, O_TCPWIN, 0,
3340253549Sken			    htons(strtoul(*av, NULL, 0)));
3341253549Sken			av++;
3342253549Sken			break;
3343253549Sken
3344253549Sken		case TOK_TCPFLAGS:
3345253549Sken			NEED1("missing argument for tcpflags");
3346253549Sken			cmd->opcode = O_TCPFLAGS;
3347253549Sken			fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av);
3348253549Sken			av++;
3349253549Sken			break;
3350253549Sken
3351253549Sken		case TOK_KEEPSTATE:
3352253549Sken			if (open_par)
3353253549Sken				errx(EX_USAGE, "keep-state cannot be part "
3354253549Sken				    "of an or block");
3355253549Sken			if (have_state)
3356253549Sken				errx(EX_USAGE, "only one of keep-state "
3357253549Sken					"and limit is allowed");
3358253549Sken			have_state = cmd;
3359253549Sken			fill_cmd(cmd, O_KEEP_STATE, 0, 0);
3360253549Sken			break;
3361230592Sken
3362253549Sken		case TOK_LIMIT: {
3363270250Sslm			ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
3364270250Sslm			int val;
3365270250Sslm
3366270250Sslm			if (open_par)
3367270250Sslm				errx(EX_USAGE,
3368270250Sslm				    "limit cannot be part of an or block");
3369270250Sslm			if (have_state)
3370270250Sslm				errx(EX_USAGE, "only one of keep-state and "
3371270250Sslm				    "limit is allowed");
3372270250Sslm			have_state = cmd;
3373270250Sslm
3374270250Sslm			cmd->len = F_INSN_SIZE(ipfw_insn_limit);
3375270250Sslm			cmd->opcode = O_LIMIT;
3376270250Sslm			c->limit_mask = c->conn_limit = 0;
3377253549Sken
3378253549Sken			while ( av[0] != NULL ) {
3379237683Sken				if ((val = match_token(limit_masks, *av)) <= 0)
3380253549Sken					break;
3381253549Sken				c->limit_mask |= val;
3382253549Sken				av++;
3383253549Sken			}
3384253549Sken
3385253549Sken			if (c->limit_mask == 0)
3386253549Sken				errx(EX_USAGE, "limit: missing limit mask");
3387253549Sken
3388253549Sken			GET_UINT_ARG(c->conn_limit, IPFW_ARG_MIN, IPFW_ARG_MAX,
3389253549Sken			    TOK_LIMIT, rule_options);
3390253549Sken
3391253549Sken			av++;
3392253549Sken			break;
3393253549Sken		}
3394253549Sken
3395253549Sken		case TOK_PROTO:
3396253549Sken			NEED1("missing protocol");
3397253549Sken			if (add_proto(cmd, *av, &proto)) {
3398253549Sken				av++;
3399253549Sken			} else
3400253549Sken				errx(EX_DATAERR, "invalid protocol ``%s''",
3401253549Sken				    *av);
3402253549Sken			break;
3403253549Sken
3404253549Sken		case TOK_SRCIP:
3405253549Sken			NEED1("missing source IP");
3406237683Sken			if (add_srcip(cmd, *av)) {
3407253549Sken				av++;
3408253549Sken			}
3409253549Sken			break;
3410253549Sken
3411253549Sken		case TOK_DSTIP:
3412230592Sken			NEED1("missing destination IP");
3413253549Sken			if (add_dstip(cmd, *av)) {
3414253549Sken				av++;
3415230592Sken			}
3416230592Sken			break;
3417230592Sken
3418230592Sken		case TOK_SRCIP6:
3419230592Sken			NEED1("missing source IP6");
3420230592Sken			if (add_srcip6(cmd, *av)) {
3421230592Sken				av++;
3422230592Sken			}
3423230592Sken			break;
3424230592Sken
3425230592Sken		case TOK_DSTIP6:
3426230592Sken			NEED1("missing destination IP6");
3427231240Sken			if (add_dstip6(cmd, *av)) {
3428231240Sken				av++;
3429231240Sken			}
3430231240Sken			break;
3431231240Sken
3432231240Sken		case TOK_SRCPORT:
3433231240Sken			NEED1("missing source port");
3434231240Sken			if (_substrcmp(*av, "any") == 0 ||
3435231240Sken			    add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
3436231240Sken				av++;
3437231240Sken			} else
3438231240Sken				errx(EX_DATAERR, "invalid source port %s", *av);
3439231240Sken			break;
3440231240Sken
3441230592Sken		case TOK_DSTPORT:
3442230592Sken			NEED1("missing destination port");
3443230592Sken			if (_substrcmp(*av, "any") == 0 ||
3444230592Sken			    add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
3445230592Sken				av++;
3446230592Sken			} else
3447230592Sken				errx(EX_DATAERR, "invalid destination port %s",
3448230592Sken				    *av);
3449264492Sscottl			break;
3450264492Sscottl
3451264492Sscottl		case TOK_MAC:
3452230592Sken			if (add_mac(cmd, av))
3453230592Sken				av += 2;
3454230592Sken			break;
3455230592Sken
3456230592Sken		case TOK_MACTYPE:
3457230592Sken			NEED1("missing mac type");
3458230592Sken			if (!add_mactype(cmd, *av))
3459230592Sken				errx(EX_DATAERR, "invalid mac type %s", *av);
3460230592Sken			av++;
3461230592Sken			break;
3462230592Sken
3463230592Sken		case TOK_VERREVPATH:
3464268197Sscottl			fill_cmd(cmd, O_VERREVPATH, 0, 0);
3465230592Sken			break;
3466230592Sken
3467230592Sken		case TOK_VERSRCREACH:
3468230592Sken			fill_cmd(cmd, O_VERSRCREACH, 0, 0);
3469230592Sken			break;
3470230592Sken
3471230592Sken		case TOK_ANTISPOOF:
3472253550Sken			fill_cmd(cmd, O_ANTISPOOF, 0, 0);
3473253550Sken			break;
3474253550Sken
3475253550Sken		case TOK_IPSEC:
3476230592Sken			fill_cmd(cmd, O_IPSEC, 0, 0);
3477230592Sken			break;
3478230592Sken
3479230592Sken		case TOK_IPV6:
3480230592Sken			fill_cmd(cmd, O_IP6, 0, 0);
3481230592Sken			break;
3482230592Sken
3483230592Sken		case TOK_IPV4:
3484230592Sken			fill_cmd(cmd, O_IP4, 0, 0);
3485230592Sken			break;
3486230592Sken
3487253549Sken		case TOK_EXT6HDR:
3488253549Sken			fill_ext6hdr( cmd, *av );
3489230592Sken			av++;
3490230592Sken			break;
3491230592Sken
3492230592Sken		case TOK_FLOWID:
3493230592Sken			if (proto != IPPROTO_IPV6 )
3494230592Sken				errx( EX_USAGE, "flow-id filter is active "
3495230592Sken				    "only for ipv6 protocol\n");
3496230592Sken			fill_flow6( (ipfw_insn_u32 *) cmd, *av );
3497230592Sken			av++;
3498230592Sken			break;
3499230592Sken
3500230592Sken		case TOK_COMMENT:
3501230592Sken			fill_comment(cmd, av);
3502230592Sken			av[0]=NULL;
3503230592Sken			break;
3504230592Sken
3505230592Sken		case TOK_TAGGED:
3506230592Sken			if (av[0] && strpbrk(*av, "-,")) {
3507230592Sken				if (!add_ports(cmd, *av, 0, O_TAGGED))
3508230592Sken					errx(EX_DATAERR, "tagged: invalid tag"
3509230592Sken					    " list: %s", *av);
3510253460Sscottl			}
3511230592Sken			else {
3512230592Sken				uint16_t tag;
3513230592Sken
3514230592Sken				GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX,
3515230592Sken				    TOK_TAGGED, rule_options);
3516230592Sken				fill_cmd(cmd, O_TAGGED, 0, tag);
3517230592Sken			}
3518230592Sken			av++;
3519230592Sken			break;
3520230592Sken
3521230592Sken		case TOK_FIB:
3522230592Sken			NEED1("fib requires fib number");
3523230592Sken			fill_cmd(cmd, O_FIB, 0, strtoul(*av, NULL, 0));
3524253460Sscottl			av++;
3525230592Sken			break;
3526230592Sken
3527230592Sken		case TOK_LOOKUP: {
3528230592Sken			ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd;
3529230592Sken			char *p;
3530230592Sken			int j;
3531230592Sken
3532230592Sken			if (!av[0] || !av[1])
3533230592Sken				errx(EX_USAGE, "format: lookup argument tablenum");
3534230592Sken			cmd->opcode = O_IP_DST_LOOKUP;
3535230592Sken			cmd->len |= F_INSN_SIZE(ipfw_insn) + 2;
3536253460Sscottl			i = match_token(rule_options, *av);
3537230592Sken			for (j = 0; lookup_key[j] >= 0 ; j++) {
3538230592Sken				if (i == lookup_key[j])
3539230592Sken					break;
3540230592Sken			}
3541230592Sken			if (lookup_key[j] <= 0)
3542230592Sken				errx(EX_USAGE, "format: cannot lookup on %s", *av);
3543230592Sken			c->d[1] = j; // i converted to option
3544230592Sken			av++;
3545253460Sscottl			cmd->arg1 = strtoul(*av, &p, 0);
3546230592Sken			if (p && *p)
3547230592Sken				errx(EX_USAGE, "format: lookup argument tablenum");
3548230592Sken			av++;
3549230592Sken		    }
3550230592Sken			break;
3551230592Sken
3552237683Sken		default:
3553230592Sken			errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
3554230592Sken		}
3555230592Sken		if (F_LEN(cmd) > 0) {	/* prepare to advance */
3556230592Sken			prev = cmd;
3557230592Sken			cmd = next_cmd(cmd);
3558253460Sscottl		}
3559230592Sken	}
3560230592Sken
3561230592Skendone:
3562230592Sken	/*
3563230592Sken	 * Now copy stuff into the rule.
3564230592Sken	 * If we have a keep-state option, the first instruction
3565230592Sken	 * must be a PROBE_STATE (which is generated here).
3566230592Sken	 * If we have a LOG option, it was stored as the first command,
3567230592Sken	 * and now must be moved to the top of the action part.
3568230592Sken	 */
3569230592Sken	dst = (ipfw_insn *)rule->cmd;
3570230592Sken
3571230592Sken	/*
3572230592Sken	 * First thing to write into the command stream is the match probability.
3573230592Sken	 */
3574230592Sken	if (match_prob != 1) { /* 1 means always match */
3575230592Sken		dst->opcode = O_PROB;
3576230592Sken		dst->len = 2;
3577230592Sken		*((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff);
3578230592Sken		dst += dst->len;
3579230592Sken	}
3580230592Sken
3581237683Sken	/*
3582230592Sken	 * generate O_PROBE_STATE if necessary
3583230592Sken	 */
3584230592Sken	if (have_state && have_state->opcode != O_CHECK_STATE) {
3585254116Sscottl		fill_cmd(dst, O_PROBE_STATE, 0, 0);
3586254116Sscottl		dst = next_cmd(dst);
3587254116Sscottl	}
3588254116Sscottl
3589254116Sscottl	/* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */
3590254116Sscottl	for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
3591254116Sscottl		i = F_LEN(src);
3592254116Sscottl
3593254116Sscottl		switch (src->opcode) {
3594254116Sscottl		case O_LOG:
3595254116Sscottl		case O_KEEP_STATE:
3596254116Sscottl		case O_LIMIT:
3597254116Sscottl		case O_ALTQ:
3598254116Sscottl		case O_TAG:
3599254116Sscottl			break;
3600254116Sscottl		default:
3601254116Sscottl			bcopy(src, dst, i * sizeof(uint32_t));
3602268197Sscottl			dst += i;
3603268197Sscottl		}
3604268197Sscottl	}
3605268197Sscottl
3606268197Sscottl	/*
3607268197Sscottl	 * put back the have_state command as last opcode
3608268197Sscottl	 */
3609268197Sscottl	if (have_state && have_state->opcode != O_CHECK_STATE) {
3610268197Sscottl		i = F_LEN(have_state);
3611268197Sscottl		bcopy(have_state, dst, i * sizeof(uint32_t));
3612268197Sscottl		dst += i;
3613268197Sscottl	}
3614268197Sscottl	/*
3615268197Sscottl	 * start action section
3616268197Sscottl	 */
3617268197Sscottl	rule->act_ofs = dst - rule->cmd;
3618268197Sscottl
3619268197Sscottl	/* put back O_LOG, O_ALTQ, O_TAG if necessary */
3620268197Sscottl	if (have_log) {
3621268197Sscottl		i = F_LEN(have_log);
3622268197Sscottl		bcopy(have_log, dst, i * sizeof(uint32_t));
3623268197Sscottl		dst += i;
3624268197Sscottl	}
3625268197Sscottl	if (have_altq) {
3626268197Sscottl		i = F_LEN(have_altq);
3627268197Sscottl		bcopy(have_altq, dst, i * sizeof(uint32_t));
3628268197Sscottl		dst += i;
3629268197Sscottl	}
3630268197Sscottl	if (have_tag) {
3631268197Sscottl		i = F_LEN(have_tag);
3632		bcopy(have_tag, dst, i * sizeof(uint32_t));
3633		dst += i;
3634	}
3635	/*
3636	 * copy all other actions
3637	 */
3638	for (src = (ipfw_insn *)actbuf; src != action; src += i) {
3639		i = F_LEN(src);
3640		bcopy(src, dst, i * sizeof(uint32_t));
3641		dst += i;
3642	}
3643
3644	rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd);
3645	i = (char *)dst - (char *)rule;
3646	if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1)
3647		err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
3648	if (!co.do_quiet)
3649		show_ipfw(rule, 0, 0);
3650}
3651
3652/*
3653 * clear the counters or the log counters.
3654 */
3655void
3656ipfw_zero(int ac, char *av[], int optname /* 0 = IP_FW_ZERO, 1 = IP_FW_RESETLOG */)
3657{
3658	uint32_t arg, saved_arg;
3659	int failed = EX_OK;
3660	char const *errstr;
3661	char const *name = optname ? "RESETLOG" : "ZERO";
3662
3663	optname = optname ? IP_FW_RESETLOG : IP_FW_ZERO;
3664
3665	av++; ac--;
3666
3667	if (!ac) {
3668		/* clear all entries */
3669		if (do_cmd(optname, NULL, 0) < 0)
3670			err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name);
3671		if (!co.do_quiet)
3672			printf("%s.\n", optname == IP_FW_ZERO ?
3673			    "Accounting cleared":"Logging counts reset");
3674
3675		return;
3676	}
3677
3678	while (ac) {
3679		/* Rule number */
3680		if (isdigit(**av)) {
3681			arg = strtonum(*av, 0, 0xffff, &errstr);
3682			if (errstr)
3683				errx(EX_DATAERR,
3684				    "invalid rule number %s\n", *av);
3685			saved_arg = arg;
3686			if (co.use_set)
3687				arg |= (1 << 24) | ((co.use_set - 1) << 16);
3688			av++;
3689			ac--;
3690			if (do_cmd(optname, &arg, sizeof(arg))) {
3691				warn("rule %u: setsockopt(IP_FW_%s)",
3692				    saved_arg, name);
3693				failed = EX_UNAVAILABLE;
3694			} else if (!co.do_quiet)
3695				printf("Entry %d %s.\n", saved_arg,
3696				    optname == IP_FW_ZERO ?
3697					"cleared" : "logging count reset");
3698		} else {
3699			errx(EX_USAGE, "invalid rule number ``%s''", *av);
3700		}
3701	}
3702	if (failed != EX_OK)
3703		exit(failed);
3704}
3705
3706void
3707ipfw_flush(int force)
3708{
3709	int cmd = co.do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH;
3710
3711	if (!force && !co.do_quiet) { /* need to ask user */
3712		int c;
3713
3714		printf("Are you sure? [yn] ");
3715		fflush(stdout);
3716		do {
3717			c = toupper(getc(stdin));
3718			while (c != '\n' && getc(stdin) != '\n')
3719				if (feof(stdin))
3720					return; /* and do not flush */
3721		} while (c != 'Y' && c != 'N');
3722		printf("\n");
3723		if (c == 'N')	/* user said no */
3724			return;
3725	}
3726	if (co.do_pipe) {
3727		dummynet_flush();
3728		return;
3729	}
3730	/* `ipfw set N flush` - is the same that `ipfw delete set N` */
3731	if (co.use_set) {
3732		uint32_t arg = ((co.use_set - 1) & 0xffff) | (1 << 24);
3733		if (do_cmd(IP_FW_DEL, &arg, sizeof(arg)) < 0)
3734			err(EX_UNAVAILABLE, "setsockopt(IP_FW_DEL)");
3735	} else if (do_cmd(cmd, NULL, 0) < 0)
3736		err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
3737		    co.do_pipe ? "DUMMYNET" : "FW");
3738	if (!co.do_quiet)
3739		printf("Flushed all %s.\n", co.do_pipe ? "pipes" : "rules");
3740}
3741
3742
3743static void table_list(ipfw_table_entry ent, int need_header);
3744
3745/*
3746 * This one handles all table-related commands
3747 * 	ipfw table N add addr[/masklen] [value]
3748 * 	ipfw table N delete addr[/masklen]
3749 * 	ipfw table {N | all} flush
3750 * 	ipfw table {N | all} list
3751 */
3752void
3753ipfw_table_handler(int ac, char *av[])
3754{
3755	ipfw_table_entry ent;
3756	int do_add;
3757	int is_all;
3758	size_t len;
3759	char *p;
3760	uint32_t a;
3761	uint32_t tables_max;
3762
3763	len = sizeof(tables_max);
3764	if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len,
3765		NULL, 0) == -1) {
3766#ifdef IPFW_TABLES_MAX
3767		warn("Warn: Failed to get the max tables number via sysctl. "
3768		     "Using the compiled in defaults. \nThe reason was");
3769		tables_max = IPFW_TABLES_MAX;
3770#else
3771		errx(1, "Failed sysctlbyname(\"net.inet.ip.fw.tables_max\")");
3772#endif
3773	}
3774
3775	ac--; av++;
3776	if (ac && isdigit(**av)) {
3777		ent.tbl = atoi(*av);
3778		is_all = 0;
3779		ac--; av++;
3780	} else if (ac && _substrcmp(*av, "all") == 0) {
3781		ent.tbl = 0;
3782		is_all = 1;
3783		ac--; av++;
3784	} else
3785		errx(EX_USAGE, "table number or 'all' keyword required");
3786	if (ent.tbl >= tables_max)
3787		errx(EX_USAGE, "The table number exceeds the maximum allowed "
3788			"value (%d)", tables_max - 1);
3789	NEED1("table needs command");
3790	if (is_all && _substrcmp(*av, "list") != 0
3791		   && _substrcmp(*av, "flush") != 0)
3792		errx(EX_USAGE, "table number required");
3793
3794	if (_substrcmp(*av, "add") == 0 ||
3795	    _substrcmp(*av, "delete") == 0) {
3796		do_add = **av == 'a';
3797		ac--; av++;
3798		if (!ac)
3799			errx(EX_USAGE, "IP address required");
3800		p = strchr(*av, '/');
3801		if (p) {
3802			*p++ = '\0';
3803			ent.masklen = atoi(p);
3804			if (ent.masklen > 32)
3805				errx(EX_DATAERR, "bad width ``%s''", p);
3806		} else
3807			ent.masklen = 32;
3808		if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0)
3809			errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
3810		ac--; av++;
3811		if (do_add && ac) {
3812			unsigned int tval;
3813			/* isdigit is a bit of a hack here.. */
3814			if (strchr(*av, (int)'.') == NULL && isdigit(**av))  {
3815				ent.value = strtoul(*av, NULL, 0);
3816			} else {
3817		        	if (lookup_host(*av, (struct in_addr *)&tval) == 0) {
3818					/* The value must be stored in host order	 *
3819					 * so that the values < 65k can be distinguished */
3820		       			ent.value = ntohl(tval);
3821				} else {
3822					errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
3823				}
3824			}
3825		} else
3826			ent.value = 0;
3827		if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL,
3828		    &ent, sizeof(ent)) < 0) {
3829			/* If running silent, don't bomb out on these errors. */
3830			if (!(co.do_quiet && (errno == (do_add ? EEXIST : ESRCH))))
3831				err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
3832				    do_add ? "ADD" : "DEL");
3833			/* In silent mode, react to a failed add by deleting */
3834			if (do_add) {
3835				do_cmd(IP_FW_TABLE_DEL, &ent, sizeof(ent));
3836				if (do_cmd(IP_FW_TABLE_ADD,
3837				    &ent, sizeof(ent)) < 0)
3838					err(EX_OSERR,
3839				            "setsockopt(IP_FW_TABLE_ADD)");
3840			}
3841		}
3842	} else if (_substrcmp(*av, "flush") == 0) {
3843		a = is_all ? tables_max : (uint32_t)(ent.tbl + 1);
3844		do {
3845			if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl,
3846			    sizeof(ent.tbl)) < 0)
3847				err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
3848		} while (++ent.tbl < a);
3849	} else if (_substrcmp(*av, "list") == 0) {
3850		a = is_all ? tables_max : (uint32_t)(ent.tbl + 1);
3851		do {
3852			table_list(ent, is_all);
3853		} while (++ent.tbl < a);
3854	} else
3855		errx(EX_USAGE, "invalid table command %s", *av);
3856}
3857
3858static void
3859table_list(ipfw_table_entry ent, int need_header)
3860{
3861	ipfw_table *tbl;
3862	socklen_t l;
3863	uint32_t a;
3864
3865	a = ent.tbl;
3866	l = sizeof(a);
3867	if (do_cmd(IP_FW_TABLE_GETSIZE, &a, (uintptr_t)&l) < 0)
3868		err(EX_OSERR, "getsockopt(IP_FW_TABLE_GETSIZE)");
3869
3870	/* If a is zero we have nothing to do, the table is empty. */
3871	if (a == 0)
3872		return;
3873
3874	l = sizeof(*tbl) + a * sizeof(ipfw_table_entry);
3875	tbl = safe_calloc(1, l);
3876	tbl->tbl = ent.tbl;
3877	if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0)
3878		err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)");
3879	if (tbl->cnt && need_header)
3880		printf("---table(%d)---\n", tbl->tbl);
3881	for (a = 0; a < tbl->cnt; a++) {
3882		unsigned int tval;
3883		tval = tbl->ent[a].value;
3884		if (co.do_value_as_ip) {
3885			char tbuf[128];
3886			strncpy(tbuf, inet_ntoa(*(struct in_addr *)
3887				&tbl->ent[a].addr), 127);
3888			/* inet_ntoa expects network order */
3889			tval = htonl(tval);
3890			printf("%s/%u %s\n", tbuf, tbl->ent[a].masklen,
3891				inet_ntoa(*(struct in_addr *)&tval));
3892		} else {
3893			printf("%s/%u %u\n",
3894				inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
3895				tbl->ent[a].masklen, tval);
3896		}
3897	}
3898	free(tbl);
3899}
3900