ipfw2.c revision 220802
198943Sluigi/*
2117328Sluigi * Copyright (c) 2002-2003 Luigi Rizzo
398943Sluigi * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
498943Sluigi * Copyright (c) 1994 Ugen J.S.Antsilevich
598943Sluigi *
698943Sluigi * Idea and grammar partially left from:
798943Sluigi * Copyright (c) 1993 Daniel Boulet
898943Sluigi *
998943Sluigi * Redistribution and use in source forms, with and without modification,
1098943Sluigi * are permitted provided that this entire comment appears intact.
1198943Sluigi *
1298943Sluigi * Redistribution in binary form may occur without any restrictions.
1398943Sluigi * Obviously, it would be nice if you gave credit where credit is due
1498943Sluigi * but requiring it would be too onerous.
1598943Sluigi *
1698943Sluigi * This software is provided ``AS IS'' without any warranties of any kind.
1798943Sluigi *
1898943Sluigi * NEW command line interface for IP firewall facility
1998943Sluigi *
2098943Sluigi * $FreeBSD: head/sbin/ipfw/ipfw2.c 220802 2011-04-18 21:18:22Z glebius $
2198943Sluigi */
2298943Sluigi
23187604Sluigi#include <sys/types.h>
2498943Sluigi#include <sys/socket.h>
2598943Sluigi#include <sys/sockio.h>
2698943Sluigi#include <sys/sysctl.h>
2798943Sluigi
28187767Sluigi#include "ipfw2.h"
29187767Sluigi
3098943Sluigi#include <ctype.h>
3198943Sluigi#include <err.h>
3298943Sluigi#include <errno.h>
3398943Sluigi#include <grp.h>
3498943Sluigi#include <netdb.h>
3598943Sluigi#include <pwd.h>
3698943Sluigi#include <stdio.h>
3798943Sluigi#include <stdlib.h>
3898943Sluigi#include <string.h>
3998943Sluigi#include <sysexits.h>
40187983Sluigi#include <time.h>	/* ctime */
41187604Sluigi#include <timeconv.h>	/* _long_to_time */
42136071Sgreen#include <unistd.h>
43136071Sgreen#include <fcntl.h>
4498943Sluigi
45169424Smaxim#include <net/ethernet.h>
46187983Sluigi#include <net/if.h>		/* only IFNAMSIZ */
4798943Sluigi#include <netinet/in.h>
48187983Sluigi#include <netinet/in_systm.h>	/* only n_short, n_long */
4998943Sluigi#include <netinet/ip.h>
5098943Sluigi#include <netinet/ip_icmp.h>
5198943Sluigi#include <netinet/ip_fw.h>
5298943Sluigi#include <netinet/tcp.h>
5398943Sluigi#include <arpa/inet.h>
5498943Sluigi
55187767Sluigistruct cmdline_opts co;	/* global options */
5698943Sluigi
57187767Sluigiint resvd_set_number = RESVD_SET;
58187764Sluigi
59159636Soleg#define GET_UINT_ARG(arg, min, max, tok, s_x) do {			\
60204591Sluigi	if (!av[0])							\
61159636Soleg		errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \
62159636Soleg	if (_substrcmp(*av, "tablearg") == 0) {				\
63159636Soleg		arg = IP_FW_TABLEARG;					\
64159636Soleg		break;							\
65159636Soleg	}								\
66159636Soleg									\
67159636Soleg	{								\
68204591Sluigi	long _xval;							\
69159636Soleg	char *end;							\
70159636Soleg									\
71204591Sluigi	_xval = strtol(*av, &end, 10);					\
72159636Soleg									\
73204591Sluigi	if (!isdigit(**av) || *end != '\0' || (_xval == 0 && errno == EINVAL)) \
74159636Soleg		errx(EX_DATAERR, "%s: invalid argument: %s",		\
75159636Soleg		    match_value(s_x, tok), *av);			\
76159636Soleg									\
77204591Sluigi	if (errno == ERANGE || _xval < min || _xval > max)		\
78159636Soleg		errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \
79159636Soleg		    match_value(s_x, tok), min, max, *av);		\
80159636Soleg									\
81204591Sluigi	if (_xval == IP_FW_TABLEARG)					\
82159636Soleg		errx(EX_DATAERR, "%s: illegal argument value: %s",	\
83159636Soleg		    match_value(s_x, tok), *av);			\
84204591Sluigi	arg = _xval;							\
85159636Soleg	}								\
86158879Soleg} while (0)
87158879Soleg
88187762Sluigistatic void
89187762SluigiPRINT_UINT_ARG(const char *str, uint32_t arg)
90187762Sluigi{
91187762Sluigi	if (str != NULL)
92187762Sluigi		printf("%s",str);
93187762Sluigi	if (arg == IP_FW_TABLEARG)
94187762Sluigi		printf("tablearg");
95187762Sluigi	else
96187762Sluigi		printf("%u", arg);
97187762Sluigi}
98159636Soleg
9998943Sluigistatic struct _s_x f_tcpflags[] = {
10098943Sluigi	{ "syn", TH_SYN },
10198943Sluigi	{ "fin", TH_FIN },
10298943Sluigi	{ "ack", TH_ACK },
10398943Sluigi	{ "psh", TH_PUSH },
10498943Sluigi	{ "rst", TH_RST },
10598943Sluigi	{ "urg", TH_URG },
10698943Sluigi	{ "tcp flag", 0 },
10798943Sluigi	{ NULL,	0 }
10898943Sluigi};
10998943Sluigi
11098943Sluigistatic struct _s_x f_tcpopts[] = {
11198943Sluigi	{ "mss",	IP_FW_TCPOPT_MSS },
11298943Sluigi	{ "maxseg",	IP_FW_TCPOPT_MSS },
11398943Sluigi	{ "window",	IP_FW_TCPOPT_WINDOW },
11498943Sluigi	{ "sack",	IP_FW_TCPOPT_SACK },
11598943Sluigi	{ "ts",		IP_FW_TCPOPT_TS },
11698943Sluigi	{ "timestamp",	IP_FW_TCPOPT_TS },
11798943Sluigi	{ "cc",		IP_FW_TCPOPT_CC },
11898943Sluigi	{ "tcp option",	0 },
11998943Sluigi	{ NULL,	0 }
12098943Sluigi};
12198943Sluigi
12298943Sluigi/*
12398943Sluigi * IP options span the range 0 to 255 so we need to remap them
12498943Sluigi * (though in fact only the low 5 bits are significant).
12598943Sluigi */
12698943Sluigistatic struct _s_x f_ipopts[] = {
12798943Sluigi	{ "ssrr",	IP_FW_IPOPT_SSRR},
12898943Sluigi	{ "lsrr",	IP_FW_IPOPT_LSRR},
12998943Sluigi	{ "rr",		IP_FW_IPOPT_RR},
13098943Sluigi	{ "ts",		IP_FW_IPOPT_TS},
13198943Sluigi	{ "ip option",	0 },
13298943Sluigi	{ NULL,	0 }
13398943Sluigi};
13498943Sluigi
13598943Sluigistatic struct _s_x f_iptos[] = {
13698943Sluigi	{ "lowdelay",	IPTOS_LOWDELAY},
13798943Sluigi	{ "throughput",	IPTOS_THROUGHPUT},
13898943Sluigi	{ "reliability", IPTOS_RELIABILITY},
13998943Sluigi	{ "mincost",	IPTOS_MINCOST},
140172801Srpaulo	{ "congestion",	IPTOS_ECN_CE},
141172801Srpaulo	{ "ecntransport", IPTOS_ECN_ECT0},
14298943Sluigi	{ "ip tos option", 0},
14398943Sluigi	{ NULL,	0 }
14498943Sluigi};
14598943Sluigi
14698943Sluigistatic struct _s_x limit_masks[] = {
14798943Sluigi	{"all",		DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
14898943Sluigi	{"src-addr",	DYN_SRC_ADDR},
14998943Sluigi	{"src-port",	DYN_SRC_PORT},
15098943Sluigi	{"dst-addr",	DYN_DST_ADDR},
15198943Sluigi	{"dst-port",	DYN_DST_PORT},
15298943Sluigi	{NULL,		0}
15398943Sluigi};
15498943Sluigi
15598943Sluigi/*
15698943Sluigi * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines
15798943Sluigi * This is only used in this code.
15898943Sluigi */
15998943Sluigi#define IPPROTO_ETHERTYPE	0x1000
16098943Sluigistatic struct _s_x ether_types[] = {
16198943Sluigi    /*
16298943Sluigi     * Note, we cannot use "-:&/" in the names because they are field
16398943Sluigi     * separators in the type specifications. Also, we use s = NULL as
16498943Sluigi     * end-delimiter, because a type of 0 can be legal.
16598943Sluigi     */
16698943Sluigi	{ "ip",		0x0800 },
16798943Sluigi	{ "ipv4",	0x0800 },
16898943Sluigi	{ "ipv6",	0x86dd },
16998943Sluigi	{ "arp",	0x0806 },
17098943Sluigi	{ "rarp",	0x8035 },
17198943Sluigi	{ "vlan",	0x8100 },
17298943Sluigi	{ "loop",	0x9000 },
17398943Sluigi	{ "trail",	0x1000 },
17498943Sluigi	{ "at",		0x809b },
17598943Sluigi	{ "atalk",	0x809b },
17698943Sluigi	{ "aarp",	0x80f3 },
17798943Sluigi	{ "pppoe_disc",	0x8863 },
17898943Sluigi	{ "pppoe_sess",	0x8864 },
17998943Sluigi	{ "ipx_8022",	0x00E0 },
18098943Sluigi	{ "ipx_8023",	0x0000 },
18198943Sluigi	{ "ipx_ii",	0x8137 },
18298943Sluigi	{ "ipx_snap",	0x8137 },
18398943Sluigi	{ "ipx",	0x8137 },
18498943Sluigi	{ "ns",		0x0600 },
18598943Sluigi	{ NULL,		0 }
18698943Sluigi};
18798943Sluigi
18898943Sluigi
189187769Sluigistatic struct _s_x rule_actions[] = {
19098943Sluigi	{ "accept",		TOK_ACCEPT },
19198943Sluigi	{ "pass",		TOK_ACCEPT },
19298943Sluigi	{ "allow",		TOK_ACCEPT },
19398943Sluigi	{ "permit",		TOK_ACCEPT },
19498943Sluigi	{ "count",		TOK_COUNT },
19598943Sluigi	{ "pipe",		TOK_PIPE },
19698943Sluigi	{ "queue",		TOK_QUEUE },
19798943Sluigi	{ "divert",		TOK_DIVERT },
19898943Sluigi	{ "tee",		TOK_TEE },
199141351Sglebius	{ "netgraph",		TOK_NETGRAPH },
200141351Sglebius	{ "ngtee",		TOK_NGTEE },
20198943Sluigi	{ "fwd",		TOK_FORWARD },
20298943Sluigi	{ "forward",		TOK_FORWARD },
20398943Sluigi	{ "skipto",		TOK_SKIPTO },
20498943Sluigi	{ "deny",		TOK_DENY },
20598943Sluigi	{ "drop",		TOK_DENY },
20698943Sluigi	{ "reject",		TOK_REJECT },
207149020Sbz	{ "reset6",		TOK_RESET6 },
20898943Sluigi	{ "reset",		TOK_RESET },
209149020Sbz	{ "unreach6",		TOK_UNREACH6 },
21099475Sluigi	{ "unreach",		TOK_UNREACH },
21198943Sluigi	{ "check-state",	TOK_CHECKSTATE },
212117469Sluigi	{ "//",			TOK_COMMENT },
213220802Sglebius	{ "nat",		TOK_NAT },
214190633Spiso	{ "reass",		TOK_REASS },
215178888Sjulian	{ "setfib",		TOK_SETFIB },
216117328Sluigi	{ NULL, 0 }	/* terminator */
21798943Sluigi};
21898943Sluigi
219187769Sluigistatic struct _s_x rule_action_params[] = {
220136071Sgreen	{ "altq",		TOK_ALTQ },
221136071Sgreen	{ "log",		TOK_LOG },
222158879Soleg	{ "tag",		TOK_TAG },
223158879Soleg	{ "untag",		TOK_UNTAG },
224136071Sgreen	{ NULL, 0 }	/* terminator */
225136071Sgreen};
226136071Sgreen
227200567Sluigi/*
228200567Sluigi * The 'lookup' instruction accepts one of the following arguments.
229200567Sluigi * -1 is a terminator for the list.
230200567Sluigi * Arguments are passed as v[1] in O_DST_LOOKUP options.
231200567Sluigi */
232200567Sluigistatic int lookup_key[] = {
233200567Sluigi	TOK_DSTIP, TOK_SRCIP, TOK_DSTPORT, TOK_SRCPORT,
234205169Sluigi	TOK_UID, TOK_JAIL, TOK_DSCP, -1 };
235200567Sluigi
236187769Sluigistatic struct _s_x rule_options[] = {
237158879Soleg	{ "tagged",		TOK_TAGGED },
23898943Sluigi	{ "uid",		TOK_UID },
23998943Sluigi	{ "gid",		TOK_GID },
240133600Scsjp	{ "jail",		TOK_JAIL },
24198943Sluigi	{ "in",			TOK_IN },
24298943Sluigi	{ "limit",		TOK_LIMIT },
24398943Sluigi	{ "keep-state",		TOK_KEEPSTATE },
24498943Sluigi	{ "bridged",		TOK_LAYER2 },
24598943Sluigi	{ "layer2",		TOK_LAYER2 },
24698943Sluigi	{ "out",		TOK_OUT },
247136073Sgreen	{ "diverted",		TOK_DIVERTED },
248136073Sgreen	{ "diverted-loopback",	TOK_DIVERTEDLOOPBACK },
249136073Sgreen	{ "diverted-output",	TOK_DIVERTEDOUTPUT },
25098943Sluigi	{ "xmit",		TOK_XMIT },
25198943Sluigi	{ "recv",		TOK_RECV },
25298943Sluigi	{ "via",		TOK_VIA },
25398943Sluigi	{ "fragment",		TOK_FRAG },
25498943Sluigi	{ "frag",		TOK_FRAG },
255178888Sjulian	{ "fib",		TOK_FIB },
25698943Sluigi	{ "ipoptions",		TOK_IPOPTS },
25798943Sluigi	{ "ipopts",		TOK_IPOPTS },
25898943Sluigi	{ "iplen",		TOK_IPLEN },
25998943Sluigi	{ "ipid",		TOK_IPID },
26098943Sluigi	{ "ipprecedence",	TOK_IPPRECEDENCE },
261205169Sluigi	{ "dscp",		TOK_DSCP },
26298943Sluigi	{ "iptos",		TOK_IPTOS },
26398943Sluigi	{ "ipttl",		TOK_IPTTL },
26498943Sluigi	{ "ipversion",		TOK_IPVER },
26598943Sluigi	{ "ipver",		TOK_IPVER },
26698943Sluigi	{ "estab",		TOK_ESTAB },
26798943Sluigi	{ "established",	TOK_ESTAB },
26898943Sluigi	{ "setup",		TOK_SETUP },
269215179Sluigi	{ "sockarg",		TOK_SOCKARG },
270136075Sgreen	{ "tcpdatalen",		TOK_TCPDATALEN },
27198943Sluigi	{ "tcpflags",		TOK_TCPFLAGS },
27298943Sluigi	{ "tcpflgs",		TOK_TCPFLAGS },
27398943Sluigi	{ "tcpoptions",		TOK_TCPOPTS },
27498943Sluigi	{ "tcpopts",		TOK_TCPOPTS },
27598943Sluigi	{ "tcpseq",		TOK_TCPSEQ },
27698943Sluigi	{ "tcpack",		TOK_TCPACK },
27798943Sluigi	{ "tcpwin",		TOK_TCPWIN },
27899909Sluigi	{ "icmptype",		TOK_ICMPTYPES },
27998943Sluigi	{ "icmptypes",		TOK_ICMPTYPES },
280102087Sluigi	{ "dst-ip",		TOK_DSTIP },
281102087Sluigi	{ "src-ip",		TOK_SRCIP },
282102087Sluigi	{ "dst-port",		TOK_DSTPORT },
283102087Sluigi	{ "src-port",		TOK_SRCPORT },
284102087Sluigi	{ "proto",		TOK_PROTO },
285102087Sluigi	{ "MAC",		TOK_MAC },
286102087Sluigi	{ "mac",		TOK_MAC },
287102087Sluigi	{ "mac-type",		TOK_MACTYPE },
288112250Scjc	{ "verrevpath",		TOK_VERREVPATH },
289128575Sandre	{ "versrcreach",	TOK_VERSRCREACH },
290133387Sandre	{ "antispoof",		TOK_ANTISPOOF },
291117241Sluigi	{ "ipsec",		TOK_IPSEC },
292145246Sbrooks	{ "icmp6type",		TOK_ICMP6TYPES },
293145246Sbrooks	{ "icmp6types",		TOK_ICMP6TYPES },
294145246Sbrooks	{ "ext6hdr",		TOK_EXT6HDR},
295145246Sbrooks	{ "flow-id",		TOK_FLOWID},
296145246Sbrooks	{ "ipv6",		TOK_IPV6},
297145246Sbrooks	{ "ip6",		TOK_IPV6},
298146894Smlaier	{ "ipv4",		TOK_IPV4},
299146894Smlaier	{ "ip4",		TOK_IPV4},
300145246Sbrooks	{ "dst-ipv6",		TOK_DSTIP6},
301145246Sbrooks	{ "dst-ip6",		TOK_DSTIP6},
302145246Sbrooks	{ "src-ipv6",		TOK_SRCIP6},
303145246Sbrooks	{ "src-ip6",		TOK_SRCIP6},
304200567Sluigi	{ "lookup",		TOK_LOOKUP},
305117469Sluigi	{ "//",			TOK_COMMENT },
30698943Sluigi
30798943Sluigi	{ "not",		TOK_NOT },		/* pseudo option */
30898943Sluigi	{ "!", /* escape ? */	TOK_NOT },		/* pseudo option */
30998943Sluigi	{ "or",			TOK_OR },		/* pseudo option */
31098943Sluigi	{ "|", /* escape */	TOK_OR },		/* pseudo option */
311101641Sluigi	{ "{",			TOK_STARTBRACE },	/* pseudo option */
312101641Sluigi	{ "(",			TOK_STARTBRACE },	/* pseudo option */
313101641Sluigi	{ "}",			TOK_ENDBRACE },		/* pseudo option */
314101641Sluigi	{ ")",			TOK_ENDBRACE },		/* pseudo option */
315117328Sluigi	{ NULL, 0 }	/* terminator */
31698943Sluigi};
31798943Sluigi
318206843Sluigi/*
319206843Sluigi * Helper routine to print a possibly unaligned uint64_t on
320206843Sluigi * various platform. If width > 0, print the value with
321206843Sluigi * the desired width, followed by a space;
322206843Sluigi * otherwise, return the required width.
323187787Sluigi */
324206843Sluigiint
325206843Sluigipr_u64(uint64_t *pd, int width)
326187787Sluigi{
327206843Sluigi#ifdef TCC
328206843Sluigi#define U64_FMT "I64"
329206843Sluigi#else
330206843Sluigi#define U64_FMT "llu"
331206843Sluigi#endif
332206846Sluigi	uint64_t u;
333206846Sluigi	unsigned long long d;
334115793Sticso
335206846Sluigi	bcopy (pd, &u, sizeof(u));
336206846Sluigi	d = u;
337206843Sluigi	return (width > 0) ?
338206843Sluigi		printf("%*" U64_FMT " ", width, d) :
339206843Sluigi		snprintf(NULL, 0, "%" U64_FMT, d) ;
340206843Sluigi#undef U64_FMT
341129389Sstefanf}
342115793Sticso
343187767Sluigivoid *
344187716Sluigisafe_calloc(size_t number, size_t size)
345187716Sluigi{
346187716Sluigi	void *ret = calloc(number, size);
347187716Sluigi
348187716Sluigi	if (ret == NULL)
349187716Sluigi		err(EX_OSERR, "calloc");
350187716Sluigi	return ret;
351187716Sluigi}
352187716Sluigi
353187767Sluigivoid *
354187716Sluigisafe_realloc(void *ptr, size_t size)
355187716Sluigi{
356187716Sluigi	void *ret = realloc(ptr, size);
357187716Sluigi
358187716Sluigi	if (ret == NULL)
359187716Sluigi		err(EX_OSERR, "realloc");
360187716Sluigi	return ret;
361187716Sluigi}
362187716Sluigi
363117328Sluigi/*
364117328Sluigi * conditionally runs the command.
365204591Sluigi * Selected options or negative -> getsockopt
366117328Sluigi */
367187769Sluigiint
368119740Stmmdo_cmd(int optname, void *optval, uintptr_t optlen)
369117328Sluigi{
370117328Sluigi	static int s = -1;	/* the socket */
371117328Sluigi	int i;
372117577Sluigi
373187764Sluigi	if (co.test_only)
374117328Sluigi		return 0;
375117328Sluigi
376117328Sluigi	if (s == -1)
377117328Sluigi		s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
378117328Sluigi	if (s < 0)
379117328Sluigi		err(EX_UNAVAILABLE, "socket");
380117328Sluigi
381117328Sluigi	if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
382130281Sru	    optname == IP_FW_ADD || optname == IP_FW_TABLE_LIST ||
383220802Sglebius	    optname == IP_FW_TABLE_GETSIZE ||
384220802Sglebius	    optname == IP_FW_NAT_GET_CONFIG ||
385204591Sluigi	    optname < 0 ||
386204591Sluigi	    optname == IP_FW_NAT_GET_LOG) {
387204591Sluigi		if (optname < 0)
388204591Sluigi			optname = -optname;
389117328Sluigi		i = getsockopt(s, IPPROTO_IP, optname, optval,
390117328Sluigi			(socklen_t *)optlen);
391204591Sluigi	} else {
392117328Sluigi		i = setsockopt(s, IPPROTO_IP, optname, optval, optlen);
393204591Sluigi	}
394117328Sluigi	return i;
395117328Sluigi}
396117328Sluigi
39798943Sluigi/**
39898943Sluigi * match_token takes a table and a string, returns the value associated
399117328Sluigi * with the string (-1 in case of failure).
40098943Sluigi */
401187769Sluigiint
40298943Sluigimatch_token(struct _s_x *table, char *string)
40398943Sluigi{
40498943Sluigi	struct _s_x *pt;
405117469Sluigi	uint i = strlen(string);
40698943Sluigi
40798943Sluigi	for (pt = table ; i && pt->s != NULL ; pt++)
40898943Sluigi		if (strlen(pt->s) == i && !bcmp(string, pt->s, i))
40998943Sluigi			return pt->x;
41098943Sluigi	return -1;
411129389Sstefanf}
41298943Sluigi
413117328Sluigi/**
414117328Sluigi * match_value takes a table and a value, returns the string associated
415117328Sluigi * with the value (NULL in case of failure).
416117328Sluigi */
417187770Sluigichar const *
418117469Sluigimatch_value(struct _s_x *p, int value)
41998943Sluigi{
42098943Sluigi	for (; p->s != NULL; p++)
42198943Sluigi		if (p->x == value)
42298943Sluigi			return p->s;
42398943Sluigi	return NULL;
42498943Sluigi}
42598943Sluigi
42698943Sluigi/*
427140271Sbrooks * _substrcmp takes two strings and returns 1 if they do not match,
428140271Sbrooks * and 0 if they match exactly or the first string is a sub-string
429140271Sbrooks * of the second.  A warning is printed to stderr in the case that the
430140271Sbrooks * first string is a sub-string of the second.
431140271Sbrooks *
432140271Sbrooks * This function will be removed in the future through the usual
433140271Sbrooks * deprecation process.
434140271Sbrooks */
435187767Sluigiint
436140271Sbrooks_substrcmp(const char *str1, const char* str2)
437140271Sbrooks{
438140271Sbrooks
439140271Sbrooks	if (strncmp(str1, str2, strlen(str1)) != 0)
440140271Sbrooks		return 1;
441140271Sbrooks
442140271Sbrooks	if (strlen(str1) != strlen(str2))
443140271Sbrooks		warnx("DEPRECATED: '%s' matched '%s' as a sub-string",
444140271Sbrooks		    str1, str2);
445140271Sbrooks	return 0;
446140271Sbrooks}
447140271Sbrooks
448140271Sbrooks/*
449140271Sbrooks * _substrcmp2 takes three strings and returns 1 if the first two do not match,
450140271Sbrooks * and 0 if they match exactly or the second string is a sub-string
451140271Sbrooks * of the first.  A warning is printed to stderr in the case that the
452140271Sbrooks * first string does not match the third.
453140271Sbrooks *
454140271Sbrooks * This function exists to warn about the bizzare construction
455140271Sbrooks * strncmp(str, "by", 2) which is used to allow people to use a shotcut
456140271Sbrooks * for "bytes".  The problem is that in addition to accepting "by",
457140271Sbrooks * "byt", "byte", and "bytes", it also excepts "by_rabid_dogs" and any
458140271Sbrooks * other string beginning with "by".
459140271Sbrooks *
460140271Sbrooks * This function will be removed in the future through the usual
461140271Sbrooks * deprecation process.
462140271Sbrooks */
463187769Sluigiint
464140271Sbrooks_substrcmp2(const char *str1, const char* str2, const char* str3)
465140271Sbrooks{
466140271Sbrooks
467140271Sbrooks	if (strncmp(str1, str2, strlen(str2)) != 0)
468140271Sbrooks		return 1;
469140271Sbrooks
470140271Sbrooks	if (strcmp(str1, str3) != 0)
471140271Sbrooks		warnx("DEPRECATED: '%s' matched '%s'",
472140271Sbrooks		    str1, str3);
473140271Sbrooks	return 0;
474140271Sbrooks}
475140271Sbrooks
476140271Sbrooks/*
47798943Sluigi * prints one port, symbolic or numeric
47898943Sluigi */
47998943Sluigistatic void
480117328Sluigiprint_port(int proto, uint16_t port)
48198943Sluigi{
48298943Sluigi
48398943Sluigi	if (proto == IPPROTO_ETHERTYPE) {
484117469Sluigi		char const *s;
48598943Sluigi
486187764Sluigi		if (co.do_resolv && (s = match_value(ether_types, port)) )
48798943Sluigi			printf("%s", s);
48898943Sluigi		else
48998943Sluigi			printf("0x%04x", port);
49098943Sluigi	} else {
49198943Sluigi		struct servent *se = NULL;
492187764Sluigi		if (co.do_resolv) {
49398943Sluigi			struct protoent *pe = getprotobynumber(proto);
49498943Sluigi
49598943Sluigi			se = getservbyport(htons(port), pe ? pe->p_name : NULL);
49698943Sluigi		}
49798943Sluigi		if (se)
49898943Sluigi			printf("%s", se->s_name);
49998943Sluigi		else
50098943Sluigi			printf("%d", port);
50198943Sluigi	}
50298943Sluigi}
50398943Sluigi
504187769Sluigistatic struct _s_x _port_name[] = {
505117328Sluigi	{"dst-port",	O_IP_DSTPORT},
506117328Sluigi	{"src-port",	O_IP_SRCPORT},
507117328Sluigi	{"ipid",	O_IPID},
508117328Sluigi	{"iplen",	O_IPLEN},
509117328Sluigi	{"ipttl",	O_IPTTL},
510117328Sluigi	{"mac-type",	O_MAC_TYPE},
511136075Sgreen	{"tcpdatalen",	O_TCPDATALEN},
512158879Soleg	{"tagged",	O_TAGGED},
513117328Sluigi	{NULL,		0}
514117328Sluigi};
515117328Sluigi
51698943Sluigi/*
517117328Sluigi * Print the values in a list 16-bit items of the types above.
51898943Sluigi * XXX todo: add support for mask.
51998943Sluigi */
52098943Sluigistatic void
521102087Sluigiprint_newports(ipfw_insn_u16 *cmd, int proto, int opcode)
52298943Sluigi{
523117328Sluigi	uint16_t *p = cmd->ports;
52498943Sluigi	int i;
525117469Sluigi	char const *sep;
52698943Sluigi
527116690Sluigi	if (opcode != 0) {
528117328Sluigi		sep = match_value(_port_name, opcode);
529117328Sluigi		if (sep == NULL)
530116690Sluigi			sep = "???";
531116690Sluigi		printf (" %s", sep);
532116690Sluigi	}
533116690Sluigi	sep = " ";
53498943Sluigi	for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
535193702Sluigi		printf("%s", sep);
53698943Sluigi		print_port(proto, p[0]);
53798943Sluigi		if (p[0] != p[1]) {
53898943Sluigi			printf("-");
53998943Sluigi			print_port(proto, p[1]);
54098943Sluigi		}
54198943Sluigi		sep = ",";
54298943Sluigi	}
54398943Sluigi}
54498943Sluigi
54598943Sluigi/*
54698943Sluigi * Like strtol, but also translates service names into port numbers
54798943Sluigi * for some protocols.
54898943Sluigi * In particular:
54998943Sluigi *	proto == -1 disables the protocol check;
55098943Sluigi *	proto == IPPROTO_ETHERTYPE looks up an internal table
55198943Sluigi *	proto == <some value in /etc/protocols> matches the values there.
552101628Sluigi * Returns *end == s in case the parameter is not found.
55398943Sluigi */
55498943Sluigistatic int
55598943Sluigistrtoport(char *s, char **end, int base, int proto)
55698943Sluigi{
557101628Sluigi	char *p, *buf;
558101628Sluigi	char *s1;
55998943Sluigi	int i;
56098943Sluigi
561101628Sluigi	*end = s;		/* default - not found */
562117577Sluigi	if (*s == '\0')
563101628Sluigi		return 0;	/* not found */
564106505Smaxim
56598943Sluigi	if (isdigit(*s))
56698943Sluigi		return strtol(s, end, base);
56798943Sluigi
56898943Sluigi	/*
569101628Sluigi	 * find separator. '\\' escapes the next char.
57098943Sluigi	 */
571101628Sluigi	for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++)
572101628Sluigi		if (*s1 == '\\' && s1[1] != '\0')
573101628Sluigi			s1++;
57498943Sluigi
575187716Sluigi	buf = safe_calloc(s1 - s + 1, 1);
576101628Sluigi
577101628Sluigi	/*
578101628Sluigi	 * copy into a buffer skipping backslashes
579101628Sluigi	 */
580101628Sluigi	for (p = s, i = 0; p != s1 ; p++)
581117577Sluigi		if (*p != '\\')
582101628Sluigi			buf[i++] = *p;
583101628Sluigi	buf[i++] = '\0';
584101628Sluigi
58598943Sluigi	if (proto == IPPROTO_ETHERTYPE) {
586101628Sluigi		i = match_token(ether_types, buf);
587101628Sluigi		free(buf);
588101628Sluigi		if (i != -1) {	/* found */
58998943Sluigi			*end = s1;
59098943Sluigi			return i;
59198943Sluigi		}
59298943Sluigi	} else {
59398943Sluigi		struct protoent *pe = NULL;
59498943Sluigi		struct servent *se;
59598943Sluigi
59698943Sluigi		if (proto != 0)
59798943Sluigi			pe = getprotobynumber(proto);
59898943Sluigi		setservent(1);
599101628Sluigi		se = getservbyname(buf, pe ? pe->p_name : NULL);
600101628Sluigi		free(buf);
60198943Sluigi		if (se != NULL) {
60298943Sluigi			*end = s1;
60398943Sluigi			return ntohs(se->s_port);
60498943Sluigi		}
60598943Sluigi	}
606101628Sluigi	return 0;	/* not found */
60798943Sluigi}
60898943Sluigi
60998943Sluigi/*
610117328Sluigi * Fill the body of the command with the list of port ranges.
61198943Sluigi */
61298943Sluigistatic int
61398943Sluigifill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
61498943Sluigi{
615117328Sluigi	uint16_t a, b, *p = cmd->ports;
61698943Sluigi	int i = 0;
617102087Sluigi	char *s = av;
61898943Sluigi
619102087Sluigi	while (*s) {
62098943Sluigi		a = strtoport(av, &s, 0, proto);
621159636Soleg		if (s == av) 			/* empty or invalid argument */
622159636Soleg			return (0);
623159636Soleg
624159636Soleg		switch (*s) {
625159636Soleg		case '-':			/* a range */
626159636Soleg			av = s + 1;
62798943Sluigi			b = strtoport(av, &s, 0, proto);
628159636Soleg			/* Reject expressions like '1-abc' or '1-2-3'. */
629159636Soleg			if (s == av || (*s != ',' && *s != '\0'))
630159636Soleg				return (0);
63198943Sluigi			p[0] = a;
63298943Sluigi			p[1] = b;
633159636Soleg			break;
634159636Soleg		case ',':			/* comma separated list */
635159636Soleg		case '\0':
63698943Sluigi			p[0] = p[1] = a;
637159636Soleg			break;
638159636Soleg		default:
639159636Soleg			warnx("port list: invalid separator <%c> in <%s>",
640101978Sluigi				*s, av);
641159636Soleg			return (0);
642159636Soleg		}
643159636Soleg
644102087Sluigi		i++;
645102087Sluigi		p += 2;
646159636Soleg		av = s + 1;
64798943Sluigi	}
64898943Sluigi	if (i > 0) {
649159636Soleg		if (i + 1 > F_LEN_MASK)
650102087Sluigi			errx(EX_DATAERR, "too many ports/ranges\n");
651159636Soleg		cmd->o.len |= i + 1;	/* leave F_NOT and F_OR untouched */
65298943Sluigi	}
653159636Soleg	return (i);
65498943Sluigi}
65598943Sluigi
65698943Sluigistatic struct _s_x icmpcodes[] = {
65798943Sluigi      { "net",			ICMP_UNREACH_NET },
65898943Sluigi      { "host",			ICMP_UNREACH_HOST },
65998943Sluigi      { "protocol",		ICMP_UNREACH_PROTOCOL },
66098943Sluigi      { "port",			ICMP_UNREACH_PORT },
66198943Sluigi      { "needfrag",		ICMP_UNREACH_NEEDFRAG },
66298943Sluigi      { "srcfail",		ICMP_UNREACH_SRCFAIL },
66398943Sluigi      { "net-unknown",		ICMP_UNREACH_NET_UNKNOWN },
66498943Sluigi      { "host-unknown",		ICMP_UNREACH_HOST_UNKNOWN },
66598943Sluigi      { "isolated",		ICMP_UNREACH_ISOLATED },
66698943Sluigi      { "net-prohib",		ICMP_UNREACH_NET_PROHIB },
66798943Sluigi      { "host-prohib",		ICMP_UNREACH_HOST_PROHIB },
66898943Sluigi      { "tosnet",		ICMP_UNREACH_TOSNET },
66998943Sluigi      { "toshost",		ICMP_UNREACH_TOSHOST },
67098943Sluigi      { "filter-prohib",	ICMP_UNREACH_FILTER_PROHIB },
67198943Sluigi      { "host-precedence",	ICMP_UNREACH_HOST_PRECEDENCE },
67298943Sluigi      { "precedence-cutoff",	ICMP_UNREACH_PRECEDENCE_CUTOFF },
67398943Sluigi      { NULL, 0 }
67498943Sluigi};
67598943Sluigi
67698943Sluigistatic void
67798943Sluigifill_reject_code(u_short *codep, char *str)
67898943Sluigi{
67998943Sluigi	int val;
68098943Sluigi	char *s;
68198943Sluigi
68298943Sluigi	val = strtoul(str, &s, 0);
68398943Sluigi	if (s == str || *s != '\0' || val >= 0x100)
68498943Sluigi		val = match_token(icmpcodes, str);
685102087Sluigi	if (val < 0)
68698943Sluigi		errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
68798943Sluigi	*codep = val;
68898943Sluigi	return;
68998943Sluigi}
69098943Sluigi
69198943Sluigistatic void
692117328Sluigiprint_reject_code(uint16_t code)
69398943Sluigi{
694117469Sluigi	char const *s = match_value(icmpcodes, code);
69598943Sluigi
69698943Sluigi	if (s != NULL)
69799475Sluigi		printf("unreach %s", s);
69898943Sluigi	else
69999475Sluigi		printf("unreach %u", code);
70098943Sluigi}
70198943Sluigi
70298943Sluigi/*
70398943Sluigi * Returns the number of bits set (from left) in a contiguous bitmask,
70498943Sluigi * or -1 if the mask is not contiguous.
70598943Sluigi * XXX this needs a proper fix.
70698943Sluigi * This effectively works on masks in big-endian (network) format.
70798943Sluigi * when compiled on little endian architectures.
70898943Sluigi *
70998943Sluigi * First bit is bit 7 of the first byte -- note, for MAC addresses,
71098943Sluigi * the first bit on the wire is bit 0 of the first byte.
71198943Sluigi * len is the max length in bits.
71298943Sluigi */
713187770Sluigiint
714117577Sluigicontigmask(uint8_t *p, int len)
71598943Sluigi{
71698943Sluigi	int i, n;
717117577Sluigi
71898943Sluigi	for (i=0; i<len ; i++)
71998943Sluigi		if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
72098943Sluigi			break;
72198943Sluigi	for (n=i+1; n < len; n++)
72298943Sluigi		if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
72398943Sluigi			return -1; /* mask not contiguous */
72498943Sluigi	return i;
72598943Sluigi}
72698943Sluigi
72798943Sluigi/*
72898943Sluigi * print flags set/clear in the two bitmasks passed as parameters.
72998943Sluigi * There is a specialized check for f_tcpflags.
73098943Sluigi */
73198943Sluigistatic void
732117469Sluigiprint_flags(char const *name, ipfw_insn *cmd, struct _s_x *list)
73398943Sluigi{
734117469Sluigi	char const *comma = "";
73598943Sluigi	int i;
736117577Sluigi	uint8_t set = cmd->arg1 & 0xff;
737117577Sluigi	uint8_t clear = (cmd->arg1 >> 8) & 0xff;
73898943Sluigi
73998943Sluigi	if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) {
74098943Sluigi		printf(" setup");
74198943Sluigi		return;
74298943Sluigi	}
74398943Sluigi
74498943Sluigi	printf(" %s ", name);
74598943Sluigi	for (i=0; list[i].x != 0; i++) {
74698943Sluigi		if (set & list[i].x) {
74798943Sluigi			set &= ~list[i].x;
74898943Sluigi			printf("%s%s", comma, list[i].s);
74998943Sluigi			comma = ",";
75098943Sluigi		}
75198943Sluigi		if (clear & list[i].x) {
75298943Sluigi			clear &= ~list[i].x;
75398943Sluigi			printf("%s!%s", comma, list[i].s);
75498943Sluigi			comma = ",";
75598943Sluigi		}
75698943Sluigi	}
75798943Sluigi}
75898943Sluigi
75998943Sluigi/*
76098943Sluigi * Print the ip address contained in a command.
76198943Sluigi */
76298943Sluigistatic void
763117469Sluigiprint_ip(ipfw_insn_ip *cmd, char const *s)
76498943Sluigi{
76598943Sluigi	struct hostent *he = NULL;
766204591Sluigi	uint32_t len = F_LEN((ipfw_insn *)cmd);
767117328Sluigi	uint32_t *a = ((ipfw_insn_u32 *)cmd)->d;
76898943Sluigi
769200567Sluigi	if (cmd->o.opcode == O_IP_DST_LOOKUP && len > F_INSN_SIZE(ipfw_insn_u32)) {
770200567Sluigi		uint32_t d = a[1];
771200567Sluigi		const char *arg = "<invalid>";
772200567Sluigi
773200567Sluigi		if (d < sizeof(lookup_key)/sizeof(lookup_key[0]))
774200567Sluigi			arg = match_value(rule_options, lookup_key[d]);
775200567Sluigi		printf("%s lookup %s %d", cmd->o.len & F_NOT ? " not": "",
776200567Sluigi			arg, cmd->o.arg1);
777200567Sluigi		return;
778200567Sluigi	}
779102087Sluigi	printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
78098943Sluigi
78198943Sluigi	if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) {
78298943Sluigi		printf("me");
78398943Sluigi		return;
78498943Sluigi	}
785130281Sru	if (cmd->o.opcode == O_IP_SRC_LOOKUP ||
786130281Sru	    cmd->o.opcode == O_IP_DST_LOOKUP) {
787130281Sru		printf("table(%u", ((ipfw_insn *)cmd)->arg1);
788130281Sru		if (len == F_INSN_SIZE(ipfw_insn_u32))
789130281Sru			printf(",%u", *a);
790130281Sru		printf(")");
791130281Sru		return;
792130281Sru	}
79398943Sluigi	if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) {
794117328Sluigi		uint32_t x, *map = (uint32_t *)&(cmd->mask);
795116716Sluigi		int i, j;
79698943Sluigi		char comma = '{';
79798943Sluigi
79898943Sluigi		x = cmd->o.arg1 - 1;
79998943Sluigi		x = htonl( ~x );
80098943Sluigi		cmd->addr.s_addr = htonl(cmd->addr.s_addr);
80198943Sluigi		printf("%s/%d", inet_ntoa(cmd->addr),
802117577Sluigi			contigmask((uint8_t *)&x, 32));
80398943Sluigi		x = cmd->addr.s_addr = htonl(cmd->addr.s_addr);
80498943Sluigi		x &= 0xff; /* base */
805116716Sluigi		/*
806116716Sluigi		 * Print bits and ranges.
807116716Sluigi		 * Locate first bit set (i), then locate first bit unset (j).
808116716Sluigi		 * If we have 3+ consecutive bits set, then print them as a
809116716Sluigi		 * range, otherwise only print the initial bit and rescan.
810116716Sluigi		 */
81198943Sluigi		for (i=0; i < cmd->o.arg1; i++)
812117328Sluigi			if (map[i/32] & (1<<(i & 31))) {
813116716Sluigi				for (j=i+1; j < cmd->o.arg1; j++)
814117328Sluigi					if (!(map[ j/32] & (1<<(j & 31))))
815116716Sluigi						break;
81698943Sluigi				printf("%c%d", comma, i+x);
817116716Sluigi				if (j>i+2) { /* range has at least 3 elements */
818116716Sluigi					printf("-%d", j-1+x);
819116716Sluigi					i = j-1;
820116716Sluigi				}
82198943Sluigi				comma = ',';
82298943Sluigi			}
82398943Sluigi		printf("}");
82498943Sluigi		return;
82598943Sluigi	}
826117328Sluigi	/*
827117328Sluigi	 * len == 2 indicates a single IP, whereas lists of 1 or more
828117328Sluigi	 * addr/mask pairs have len = (2n+1). We convert len to n so we
829117328Sluigi	 * use that to count the number of entries.
830117328Sluigi	 */
831117328Sluigi    for (len = len / 2; len > 0; len--, a += 2) {
832117328Sluigi	int mb =	/* mask length */
833117328Sluigi	    (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ?
834117577Sluigi		32 : contigmask((uint8_t *)&(a[1]), 32);
835187764Sluigi	if (mb == 32 && co.do_resolv)
836117328Sluigi		he = gethostbyaddr((char *)&(a[0]), sizeof(u_long), AF_INET);
83798943Sluigi	if (he != NULL)		/* resolved to name */
83898943Sluigi		printf("%s", he->h_name);
83998943Sluigi	else if (mb == 0)	/* any */
84098943Sluigi		printf("any");
84198943Sluigi	else {		/* numeric IP followed by some kind of mask */
842117328Sluigi		printf("%s", inet_ntoa( *((struct in_addr *)&a[0]) ) );
84398943Sluigi		if (mb < 0)
844117328Sluigi			printf(":%s", inet_ntoa( *((struct in_addr *)&a[1]) ) );
84598943Sluigi		else if (mb < 32)
84698943Sluigi			printf("/%d", mb);
84798943Sluigi	}
848117328Sluigi	if (len > 1)
849117328Sluigi		printf(",");
850117328Sluigi    }
85198943Sluigi}
85298943Sluigi
85398943Sluigi/*
85498943Sluigi * prints a MAC address/mask pair
85598943Sluigi */
85698943Sluigistatic void
857117577Sluigiprint_mac(uint8_t *addr, uint8_t *mask)
85898943Sluigi{
85998943Sluigi	int l = contigmask(mask, 48);
86098943Sluigi
86198943Sluigi	if (l == 0)
86298943Sluigi		printf(" any");
86398943Sluigi	else {
86498943Sluigi		printf(" %02x:%02x:%02x:%02x:%02x:%02x",
86598943Sluigi		    addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
86698943Sluigi		if (l == -1)
86798943Sluigi			printf("&%02x:%02x:%02x:%02x:%02x:%02x",
86898943Sluigi			    mask[0], mask[1], mask[2],
86998943Sluigi			    mask[3], mask[4], mask[5]);
87098943Sluigi		else if (l < 48)
87198943Sluigi			printf("/%d", l);
87298943Sluigi	}
87398943Sluigi}
87498943Sluigi
87599475Sluigistatic void
87699475Sluigifill_icmptypes(ipfw_insn_u32 *cmd, char *av)
87799475Sluigi{
878117328Sluigi	uint8_t type;
87998943Sluigi
88099475Sluigi	cmd->d[0] = 0;
88199475Sluigi	while (*av) {
88299475Sluigi		if (*av == ',')
88399475Sluigi			av++;
88499475Sluigi
88599475Sluigi		type = strtoul(av, &av, 0);
88699475Sluigi
88799475Sluigi		if (*av != ',' && *av != '\0')
88899475Sluigi			errx(EX_DATAERR, "invalid ICMP type");
88999475Sluigi
89099475Sluigi		if (type > 31)
89199475Sluigi			errx(EX_DATAERR, "ICMP type out of range");
89299475Sluigi
89399475Sluigi		cmd->d[0] |= 1 << type;
89499475Sluigi	}
89599475Sluigi	cmd->o.opcode = O_ICMPTYPE;
89699475Sluigi	cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
89799475Sluigi}
89899475Sluigi
89999475Sluigistatic void
90099475Sluigiprint_icmptypes(ipfw_insn_u32 *cmd)
90199475Sluigi{
90299475Sluigi	int i;
90399475Sluigi	char sep= ' ';
90499475Sluigi
90599475Sluigi	printf(" icmptypes");
90699475Sluigi	for (i = 0; i < 32; i++) {
90799475Sluigi		if ( (cmd->d[0] & (1 << (i))) == 0)
90899475Sluigi			continue;
90999475Sluigi		printf("%c%d", sep, i);
91099475Sluigi		sep = ',';
91199475Sluigi	}
91299475Sluigi}
91399475Sluigi
91498943Sluigi/*
91598943Sluigi * show_ipfw() prints the body of an ipfw rule.
91698943Sluigi * Because the standard rule has at least proto src_ip dst_ip, we use
91798943Sluigi * a helper function to produce these entries if not provided explicitly.
918102087Sluigi * The first argument is the list of fields we have, the second is
919102087Sluigi * the list of fields we want to be printed.
920101978Sluigi *
921102087Sluigi * Special cases if we have provided a MAC header:
922102087Sluigi *   + if the rule does not contain IP addresses/ports, do not print them;
923102087Sluigi *   + if the rule does not contain an IP proto, print "all" instead of "ip";
924102087Sluigi *
925102087Sluigi * Once we have 'have_options', IP header fields are printed as options.
92698943Sluigi */
927101978Sluigi#define	HAVE_PROTO	0x0001
928101978Sluigi#define	HAVE_SRCIP	0x0002
929101978Sluigi#define	HAVE_DSTIP	0x0004
930169139Smaxim#define	HAVE_PROTO4	0x0008
931169139Smaxim#define	HAVE_PROTO6	0x0010
932205179Sluigi#define	HAVE_IP		0x0100
933102087Sluigi#define	HAVE_OPTIONS	0x8000
93498943Sluigi
93598943Sluigistatic void
936187477Sluigishow_prerequisites(int *flags, int want, int cmd __unused)
93798943Sluigi{
938187764Sluigi	if (co.comment_only)
939123495Sluigi		return;
940102087Sluigi	if ( (*flags & HAVE_IP) == HAVE_IP)
941102087Sluigi		*flags |= HAVE_OPTIONS;
942102087Sluigi
943102087Sluigi	if ( !(*flags & HAVE_OPTIONS)) {
944187477Sluigi		if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO)) {
945146894Smlaier			if ( (*flags & HAVE_PROTO4))
946146894Smlaier				printf(" ip4");
947146894Smlaier			else if ( (*flags & HAVE_PROTO6))
948146894Smlaier				printf(" ip6");
949146894Smlaier			else
950146894Smlaier				printf(" ip");
951187477Sluigi		}
952102087Sluigi		if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
953102087Sluigi			printf(" from any");
954102087Sluigi		if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP))
955102087Sluigi			printf(" to any");
956102087Sluigi	}
95798943Sluigi	*flags |= want;
95898943Sluigi}
95998943Sluigi
96098943Sluigistatic void
961112189Smaximshow_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
96298943Sluigi{
963107291Skeramida	static int twidth = 0;
96498943Sluigi	int l;
965158879Soleg	ipfw_insn *cmd, *tagptr = NULL;
966187477Sluigi	const char *comment = NULL;	/* ptr to comment if we have one */
96798943Sluigi	int proto = 0;		/* default */
96898943Sluigi	int flags = 0;	/* prerequisites */
96998943Sluigi	ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */
970136071Sgreen	ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */
97198943Sluigi	int or_block = 0;	/* we are in an or block */
972117328Sluigi	uint32_t set_disable;
97398943Sluigi
974115793Sticso	bcopy(&rule->next_rule, &set_disable, sizeof(set_disable));
975101628Sluigi
976101628Sluigi	if (set_disable & (1 << rule->set)) { /* disabled */
977187764Sluigi		if (!co.show_sets)
978101628Sluigi			return;
979101628Sluigi		else
980101628Sluigi			printf("# DISABLED ");
981101628Sluigi	}
98298943Sluigi	printf("%05u ", rule->rulenum);
98398943Sluigi
984206843Sluigi	if (pcwidth > 0 || bcwidth > 0) {
985206843Sluigi		pr_u64(&rule->pcnt, pcwidth);
986206843Sluigi		pr_u64(&rule->bcnt, bcwidth);
987206843Sluigi	}
98898943Sluigi
989187764Sluigi	if (co.do_time == 2)
990117472Sluigi		printf("%10u ", rule->timestamp);
991187764Sluigi	else if (co.do_time == 1) {
992107291Skeramida		char timestr[30];
993107291Skeramida		time_t t = (time_t)0;
994107291Skeramida
995107291Skeramida		if (twidth == 0) {
996107291Skeramida			strcpy(timestr, ctime(&t));
997107291Skeramida			*strchr(timestr, '\n') = '\0';
998107291Skeramida			twidth = strlen(timestr);
999107291Skeramida		}
100098943Sluigi		if (rule->timestamp) {
1001107291Skeramida			t = _long_to_time(rule->timestamp);
100298943Sluigi
100398943Sluigi			strcpy(timestr, ctime(&t));
100498943Sluigi			*strchr(timestr, '\n') = '\0';
100598943Sluigi			printf("%s ", timestr);
100698943Sluigi		} else {
1007107291Skeramida			printf("%*s", twidth, " ");
100898943Sluigi		}
100998943Sluigi	}
101098943Sluigi
1011187764Sluigi	if (co.show_sets)
1012101628Sluigi		printf("set %d ", rule->set);
1013101628Sluigi
101498943Sluigi	/*
1015107289Sluigi	 * print the optional "match probability"
1016107289Sluigi	 */
1017107289Sluigi	if (rule->cmd_len > 0) {
1018107289Sluigi		cmd = rule->cmd ;
1019107289Sluigi		if (cmd->opcode == O_PROB) {
1020107289Sluigi			ipfw_insn_u32 *p = (ipfw_insn_u32 *)cmd;
1021107289Sluigi			double d = 1.0 * p->d[0];
1022107289Sluigi
1023107289Sluigi			d = (d / 0x7fffffff);
1024107289Sluigi			printf("prob %f ", d);
1025107289Sluigi		}
1026107289Sluigi	}
1027107289Sluigi
1028107289Sluigi	/*
102998943Sluigi	 * first print actions
103098943Sluigi	 */
1031220802Sglebius	for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule);
103298943Sluigi			l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
103398943Sluigi		switch(cmd->opcode) {
103498943Sluigi		case O_CHECK_STATE:
103598943Sluigi			printf("check-state");
1036205179Sluigi			/* avoid printing anything else */
1037205179Sluigi			flags = HAVE_PROTO | HAVE_SRCIP |
1038205179Sluigi				HAVE_DSTIP | HAVE_IP;
103998943Sluigi			break;
104098943Sluigi
104198943Sluigi		case O_ACCEPT:
104298943Sluigi			printf("allow");
104398943Sluigi			break;
104498943Sluigi
104598943Sluigi		case O_COUNT:
104698943Sluigi			printf("count");
104798943Sluigi			break;
104898943Sluigi
104998943Sluigi		case O_DENY:
105098943Sluigi			printf("deny");
105198943Sluigi			break;
105298943Sluigi
105399475Sluigi		case O_REJECT:
105499475Sluigi			if (cmd->arg1 == ICMP_REJECT_RST)
105599475Sluigi				printf("reset");
105699475Sluigi			else if (cmd->arg1 == ICMP_UNREACH_HOST)
105799475Sluigi				printf("reject");
105899475Sluigi			else
105999475Sluigi				print_reject_code(cmd->arg1);
106099475Sluigi			break;
106199475Sluigi
1062149020Sbz		case O_UNREACH6:
1063149020Sbz			if (cmd->arg1 == ICMP6_UNREACH_RST)
1064149020Sbz				printf("reset6");
1065149020Sbz			else
1066149020Sbz				print_unreach6_code(cmd->arg1);
1067149020Sbz			break;
1068149020Sbz
1069159636Soleg		case O_SKIPTO:
1070159636Soleg			PRINT_UINT_ARG("skipto ", cmd->arg1);
107198943Sluigi			break;
107298943Sluigi
107398943Sluigi		case O_PIPE:
1074159636Soleg			PRINT_UINT_ARG("pipe ", cmd->arg1);
1075159636Soleg			break;
1076159636Soleg
107798943Sluigi		case O_QUEUE:
1078159636Soleg			PRINT_UINT_ARG("queue ", cmd->arg1);
1079159636Soleg			break;
1080159636Soleg
108198943Sluigi		case O_DIVERT:
1082159636Soleg			PRINT_UINT_ARG("divert ", cmd->arg1);
1083159636Soleg			break;
1084159636Soleg
108598943Sluigi		case O_TEE:
1086159636Soleg			PRINT_UINT_ARG("tee ", cmd->arg1);
1087159636Soleg			break;
1088159636Soleg
1089141351Sglebius		case O_NETGRAPH:
1090159636Soleg			PRINT_UINT_ARG("netgraph ", cmd->arg1);
1091159636Soleg			break;
1092159636Soleg
1093141351Sglebius		case O_NGTEE:
1094159636Soleg			PRINT_UINT_ARG("ngtee ", cmd->arg1);
1095159636Soleg			break;
1096141351Sglebius
109798943Sluigi		case O_FORWARD_IP:
109898943Sluigi		    {
109998943Sluigi			ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
110098943Sluigi
1101161424Sjulian			if (s->sa.sin_addr.s_addr == INADDR_ANY) {
1102161424Sjulian				printf("fwd tablearg");
1103161424Sjulian			} else {
1104161424Sjulian				printf("fwd %s", inet_ntoa(s->sa.sin_addr));
1105161424Sjulian			}
110698943Sluigi			if (s->sa.sin_port)
1107103241Sluigi				printf(",%d", s->sa.sin_port);
110898943Sluigi		    }
110998943Sluigi			break;
111098943Sluigi
111198943Sluigi		case O_LOG: /* O_LOG is printed last */
111298943Sluigi			logptr = (ipfw_insn_log *)cmd;
111398943Sluigi			break;
111498943Sluigi
1115136071Sgreen		case O_ALTQ: /* O_ALTQ is printed after O_LOG */
1116136071Sgreen			altqptr = (ipfw_insn_altq *)cmd;
1117136071Sgreen			break;
1118136071Sgreen
1119158879Soleg		case O_TAG:
1120158879Soleg			tagptr = cmd;
1121158879Soleg			break;
1122158879Soleg
1123165648Spiso		case O_NAT:
1124176517Spiso			PRINT_UINT_ARG("nat ", cmd->arg1);
1125165648Spiso 			break;
1126165648Spiso
1127178888Sjulian		case O_SETFIB:
1128178888Sjulian			PRINT_UINT_ARG("setfib ", cmd->arg1);
1129178888Sjulian 			break;
1130190633Spiso
1131190633Spiso		case O_REASS:
1132190633Spiso			printf("reass");
1133190633Spiso			break;
1134178888Sjulian
113598943Sluigi		default:
1136136071Sgreen			printf("** unrecognized action %d len %d ",
113798943Sluigi				cmd->opcode, cmd->len);
113898943Sluigi		}
113998943Sluigi	}
114098943Sluigi	if (logptr) {
114198943Sluigi		if (logptr->max_log > 0)
114299909Sluigi			printf(" log logamount %d", logptr->max_log);
114398943Sluigi		else
114499909Sluigi			printf(" log");
114598943Sluigi	}
1146204591Sluigi#ifndef NO_ALTQ
1147136071Sgreen	if (altqptr) {
1148187983Sluigi		print_altq_cmd(altqptr);
1149136071Sgreen	}
1150204591Sluigi#endif
1151158879Soleg	if (tagptr) {
1152158879Soleg		if (tagptr->len & F_NOT)
1153159636Soleg			PRINT_UINT_ARG(" untag ", tagptr->arg1);
1154158879Soleg		else
1155159636Soleg			PRINT_UINT_ARG(" tag ", tagptr->arg1);
1156158879Soleg	}
1157136071Sgreen
115898943Sluigi	/*
1159102087Sluigi	 * then print the body.
116098943Sluigi	 */
1161220802Sglebius	for (l = rule->act_ofs, cmd = rule->cmd ;
1162146894Smlaier			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
1163146894Smlaier		if ((cmd->len & F_OR) || (cmd->len & F_NOT))
1164146894Smlaier			continue;
1165146894Smlaier		if (cmd->opcode == O_IP4) {
1166146894Smlaier			flags |= HAVE_PROTO4;
1167146894Smlaier			break;
1168146894Smlaier		} else if (cmd->opcode == O_IP6) {
1169146894Smlaier			flags |= HAVE_PROTO6;
1170146894Smlaier			break;
1171146894Smlaier		}
1172146894Smlaier	}
1173102087Sluigi	if (rule->_pad & 1) {	/* empty rules before options */
1174187764Sluigi		if (!co.do_compact) {
1175146894Smlaier			show_prerequisites(&flags, HAVE_PROTO, 0);
1176146894Smlaier			printf(" from any to any");
1177146894Smlaier		}
1178205179Sluigi		flags |= HAVE_IP | HAVE_OPTIONS | HAVE_PROTO |
1179205179Sluigi			 HAVE_SRCIP | HAVE_DSTIP;
1180102087Sluigi	}
1181102087Sluigi
1182187764Sluigi	if (co.comment_only)
1183123495Sluigi		comment = "...";
1184123495Sluigi
1185220802Sglebius	for (l = rule->act_ofs, cmd = rule->cmd ;
118698943Sluigi			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
118799475Sluigi		/* useful alias */
118899475Sluigi		ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
118998943Sluigi
1190187764Sluigi		if (co.comment_only) {
1191123495Sluigi			if (cmd->opcode != O_NOP)
1192123495Sluigi				continue;
1193123495Sluigi			printf(" // %s\n", (char *)(cmd + 1));
1194123495Sluigi			return;
1195123495Sluigi		}
1196123495Sluigi
1197102087Sluigi		show_prerequisites(&flags, 0, cmd->opcode);
1198102087Sluigi
119998943Sluigi		switch(cmd->opcode) {
1200117577Sluigi		case O_PROB:
1201107289Sluigi			break;	/* done already */
1202107289Sluigi
120398943Sluigi		case O_PROBE_STATE:
120498943Sluigi			break; /* no need to print anything here */
120598943Sluigi
120698943Sluigi		case O_IP_SRC:
1207130281Sru		case O_IP_SRC_LOOKUP:
120898943Sluigi		case O_IP_SRC_MASK:
120998943Sluigi		case O_IP_SRC_ME:
121098943Sluigi		case O_IP_SRC_SET:
1211102087Sluigi			show_prerequisites(&flags, HAVE_PROTO, 0);
121298943Sluigi			if (!(flags & HAVE_SRCIP))
121398943Sluigi				printf(" from");
121498943Sluigi			if ((cmd->len & F_OR) && !or_block)
121598943Sluigi				printf(" {");
1216102087Sluigi			print_ip((ipfw_insn_ip *)cmd,
1217102087Sluigi				(flags & HAVE_OPTIONS) ? " src-ip" : "");
121898943Sluigi			flags |= HAVE_SRCIP;
121998943Sluigi			break;
122098943Sluigi
122198943Sluigi		case O_IP_DST:
1222130281Sru		case O_IP_DST_LOOKUP:
122398943Sluigi		case O_IP_DST_MASK:
122498943Sluigi		case O_IP_DST_ME:
122598943Sluigi		case O_IP_DST_SET:
1226102087Sluigi			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
122798943Sluigi			if (!(flags & HAVE_DSTIP))
122898943Sluigi				printf(" to");
122998943Sluigi			if ((cmd->len & F_OR) && !or_block)
123098943Sluigi				printf(" {");
1231102087Sluigi			print_ip((ipfw_insn_ip *)cmd,
1232102087Sluigi				(flags & HAVE_OPTIONS) ? " dst-ip" : "");
123398943Sluigi			flags |= HAVE_DSTIP;
123498943Sluigi			break;
123598943Sluigi
1236145246Sbrooks		case O_IP6_SRC:
1237145246Sbrooks		case O_IP6_SRC_MASK:
1238145246Sbrooks		case O_IP6_SRC_ME:
1239147105Smlaier			show_prerequisites(&flags, HAVE_PROTO, 0);
1240145246Sbrooks			if (!(flags & HAVE_SRCIP))
1241145246Sbrooks				printf(" from");
1242145246Sbrooks			if ((cmd->len & F_OR) && !or_block)
1243145246Sbrooks				printf(" {");
1244145246Sbrooks			print_ip6((ipfw_insn_ip6 *)cmd,
1245145246Sbrooks			    (flags & HAVE_OPTIONS) ? " src-ip6" : "");
1246145246Sbrooks			flags |= HAVE_SRCIP | HAVE_PROTO;
1247145246Sbrooks			break;
1248145246Sbrooks
1249145246Sbrooks		case O_IP6_DST:
1250145246Sbrooks		case O_IP6_DST_MASK:
1251145246Sbrooks		case O_IP6_DST_ME:
1252145246Sbrooks			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1253145246Sbrooks			if (!(flags & HAVE_DSTIP))
1254145246Sbrooks				printf(" to");
1255145246Sbrooks			if ((cmd->len & F_OR) && !or_block)
1256145246Sbrooks				printf(" {");
1257145246Sbrooks			print_ip6((ipfw_insn_ip6 *)cmd,
1258145246Sbrooks			    (flags & HAVE_OPTIONS) ? " dst-ip6" : "");
1259145246Sbrooks			flags |= HAVE_DSTIP;
1260145246Sbrooks			break;
1261145246Sbrooks
1262145246Sbrooks		case O_FLOW6ID:
1263145246Sbrooks		print_flow6id( (ipfw_insn_u32 *) cmd );
1264145246Sbrooks		flags |= HAVE_OPTIONS;
1265145246Sbrooks		break;
1266145246Sbrooks
126798943Sluigi		case O_IP_DSTPORT:
1268205179Sluigi			show_prerequisites(&flags,
1269205179Sluigi				HAVE_PROTO | HAVE_SRCIP |
1270205179Sluigi				HAVE_DSTIP | HAVE_IP, 0);
127198943Sluigi		case O_IP_SRCPORT:
1272205179Sluigi			show_prerequisites(&flags,
1273205179Sluigi				HAVE_PROTO | HAVE_SRCIP, 0);
1274101641Sluigi			if ((cmd->len & F_OR) && !or_block)
1275101641Sluigi				printf(" {");
1276172306Smaxim			if (cmd->len & F_NOT)
1277172306Smaxim				printf(" not");
1278102087Sluigi			print_newports((ipfw_insn_u16 *)cmd, proto,
1279102087Sluigi				(flags & HAVE_OPTIONS) ? cmd->opcode : 0);
128098943Sluigi			break;
128198943Sluigi
128298943Sluigi		case O_PROTO: {
1283145246Sbrooks			struct protoent *pe = NULL;
128498943Sluigi
128598943Sluigi			if ((cmd->len & F_OR) && !or_block)
128698943Sluigi				printf(" {");
128798943Sluigi			if (cmd->len & F_NOT)
128898943Sluigi				printf(" not");
128998943Sluigi			proto = cmd->arg1;
1290145567Sbrooks			pe = getprotobynumber(cmd->arg1);
1291146894Smlaier			if ((flags & (HAVE_PROTO4 | HAVE_PROTO6)) &&
1292146894Smlaier			    !(flags & HAVE_PROTO))
1293146894Smlaier				show_prerequisites(&flags,
1294205179Sluigi				    HAVE_PROTO | HAVE_IP | HAVE_SRCIP |
1295205179Sluigi				    HAVE_DSTIP | HAVE_OPTIONS, 0);
1296102087Sluigi			if (flags & HAVE_OPTIONS)
1297102087Sluigi				printf(" proto");
129898943Sluigi			if (pe)
129998943Sluigi				printf(" %s", pe->p_name);
130098943Sluigi			else
130198943Sluigi				printf(" %u", cmd->arg1);
130298943Sluigi			}
130398943Sluigi			flags |= HAVE_PROTO;
130498943Sluigi			break;
1305106505Smaxim
130698943Sluigi		default: /*options ... */
1307146894Smlaier			if (!(cmd->len & (F_OR|F_NOT)))
1308146894Smlaier				if (((cmd->opcode == O_IP6) &&
1309146894Smlaier				    (flags & HAVE_PROTO6)) ||
1310146894Smlaier				    ((cmd->opcode == O_IP4) &&
1311146894Smlaier				    (flags & HAVE_PROTO4)))
1312146894Smlaier					break;
1313205179Sluigi			show_prerequisites(&flags, HAVE_PROTO | HAVE_SRCIP |
1314205179Sluigi				    HAVE_DSTIP | HAVE_IP | HAVE_OPTIONS, 0);
131598943Sluigi			if ((cmd->len & F_OR) && !or_block)
131698943Sluigi				printf(" {");
131798943Sluigi			if (cmd->len & F_NOT && cmd->opcode != O_IN)
131898943Sluigi				printf(" not");
131998943Sluigi			switch(cmd->opcode) {
1320169139Smaxim			case O_MACADDR2: {
1321169139Smaxim				ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
1322169139Smaxim
1323169139Smaxim				printf(" MAC");
1324169139Smaxim				print_mac(m->addr, m->mask);
1325169139Smaxim				print_mac(m->addr + 6, m->mask + 6);
1326169139Smaxim				}
1327169139Smaxim				break;
1328169139Smaxim
1329169139Smaxim			case O_MAC_TYPE:
1330169139Smaxim				print_newports((ipfw_insn_u16 *)cmd,
1331169139Smaxim						IPPROTO_ETHERTYPE, cmd->opcode);
1332169139Smaxim				break;
1333169139Smaxim
1334169139Smaxim
133598943Sluigi			case O_FRAG:
133698943Sluigi				printf(" frag");
133798943Sluigi				break;
133898943Sluigi
1339178888Sjulian			case O_FIB:
1340178888Sjulian				printf(" fib %u", cmd->arg1 );
1341178888Sjulian				break;
1342215179Sluigi			case O_SOCKARG:
1343215179Sluigi				printf(" sockarg");
1344215179Sluigi				break;
1345178888Sjulian
134698943Sluigi			case O_IN:
134798943Sluigi				printf(cmd->len & F_NOT ? " out" : " in");
134898943Sluigi				break;
134998943Sluigi
1350136073Sgreen			case O_DIVERTED:
1351136073Sgreen				switch (cmd->arg1) {
1352136073Sgreen				case 3:
1353136073Sgreen					printf(" diverted");
1354136073Sgreen					break;
1355136073Sgreen				case 1:
1356136073Sgreen					printf(" diverted-loopback");
1357136073Sgreen					break;
1358136073Sgreen				case 2:
1359136073Sgreen					printf(" diverted-output");
1360136073Sgreen					break;
1361136073Sgreen				default:
1362136073Sgreen					printf(" diverted-?<%u>", cmd->arg1);
1363136073Sgreen					break;
1364136073Sgreen				}
1365136073Sgreen				break;
1366136073Sgreen
136798943Sluigi			case O_LAYER2:
136898943Sluigi				printf(" layer2");
136998943Sluigi				break;
137098943Sluigi			case O_XMIT:
137198943Sluigi			case O_RECV:
1372140423Sglebius			case O_VIA:
1373140423Sglebius			    {
1374117469Sluigi				char const *s;
137598943Sluigi				ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd;
137698943Sluigi
137798943Sluigi				if (cmd->opcode == O_XMIT)
137898943Sluigi					s = "xmit";
137998943Sluigi				else if (cmd->opcode == O_RECV)
138098943Sluigi					s = "recv";
1381117469Sluigi				else /* if (cmd->opcode == O_VIA) */
138298943Sluigi					s = "via";
138398943Sluigi				if (cmdif->name[0] == '\0')
138499475Sluigi					printf(" %s %s", s,
138599475Sluigi					    inet_ntoa(cmdif->p.ip));
1386140423Sglebius				else
1387140423Sglebius					printf(" %s %s", s, cmdif->name);
1388140423Sglebius
138998943Sluigi				break;
1390140423Sglebius			    }
139198943Sluigi			case O_IPID:
1392116690Sluigi				if (F_LEN(cmd) == 1)
1393116690Sluigi				    printf(" ipid %u", cmd->arg1 );
1394116690Sluigi				else
1395116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1396116690Sluigi					O_IPID);
139798943Sluigi				break;
139898943Sluigi
139998943Sluigi			case O_IPTTL:
1400116690Sluigi				if (F_LEN(cmd) == 1)
1401116690Sluigi				    printf(" ipttl %u", cmd->arg1 );
1402116690Sluigi				else
1403116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1404116690Sluigi					O_IPTTL);
140598943Sluigi				break;
140698943Sluigi
140798943Sluigi			case O_IPVER:
140898943Sluigi				printf(" ipver %u", cmd->arg1 );
140998943Sluigi				break;
141098943Sluigi
141199475Sluigi			case O_IPPRECEDENCE:
141299475Sluigi				printf(" ipprecedence %u", (cmd->arg1) >> 5 );
141399475Sluigi				break;
141499475Sluigi
141598943Sluigi			case O_IPLEN:
1416116690Sluigi				if (F_LEN(cmd) == 1)
1417116690Sluigi				    printf(" iplen %u", cmd->arg1 );
1418116690Sluigi				else
1419116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1420116690Sluigi					O_IPLEN);
142198943Sluigi				break;
142298943Sluigi
1423101116Sluigi			case O_IPOPT:
142498943Sluigi				print_flags("ipoptions", cmd, f_ipopts);
142598943Sluigi				break;
142698943Sluigi
142799475Sluigi			case O_IPTOS:
142899475Sluigi				print_flags("iptos", cmd, f_iptos);
142999475Sluigi				break;
143099475Sluigi
143199475Sluigi			case O_ICMPTYPE:
143299475Sluigi				print_icmptypes((ipfw_insn_u32 *)cmd);
143399475Sluigi				break;
143499475Sluigi
143598943Sluigi			case O_ESTAB:
143698943Sluigi				printf(" established");
143798943Sluigi				break;
143898943Sluigi
1439136075Sgreen			case O_TCPDATALEN:
1440136075Sgreen				if (F_LEN(cmd) == 1)
1441136075Sgreen				    printf(" tcpdatalen %u", cmd->arg1 );
1442136075Sgreen				else
1443136075Sgreen				    print_newports((ipfw_insn_u16 *)cmd, 0,
1444136075Sgreen					O_TCPDATALEN);
1445136075Sgreen				break;
1446136075Sgreen
144798943Sluigi			case O_TCPFLAGS:
144898943Sluigi				print_flags("tcpflags", cmd, f_tcpflags);
144998943Sluigi				break;
145098943Sluigi
145198943Sluigi			case O_TCPOPTS:
145298943Sluigi				print_flags("tcpoptions", cmd, f_tcpopts);
145398943Sluigi				break;
145498943Sluigi
145598943Sluigi			case O_TCPWIN:
145698943Sluigi				printf(" tcpwin %d", ntohs(cmd->arg1));
145798943Sluigi				break;
145898943Sluigi
145998943Sluigi			case O_TCPACK:
146098943Sluigi				printf(" tcpack %d", ntohl(cmd32->d[0]));
146198943Sluigi				break;
146298943Sluigi
146398943Sluigi			case O_TCPSEQ:
146498943Sluigi				printf(" tcpseq %d", ntohl(cmd32->d[0]));
146598943Sluigi				break;
146698943Sluigi
146798943Sluigi			case O_UID:
146898943Sluigi			    {
146998943Sluigi				struct passwd *pwd = getpwuid(cmd32->d[0]);
147098943Sluigi
147198943Sluigi				if (pwd)
147298943Sluigi					printf(" uid %s", pwd->pw_name);
147398943Sluigi				else
147498943Sluigi					printf(" uid %u", cmd32->d[0]);
147598943Sluigi			    }
147698943Sluigi				break;
147798943Sluigi
147898943Sluigi			case O_GID:
147998943Sluigi			    {
148098943Sluigi				struct group *grp = getgrgid(cmd32->d[0]);
148198943Sluigi
148298943Sluigi				if (grp)
148398943Sluigi					printf(" gid %s", grp->gr_name);
148498943Sluigi				else
148598943Sluigi					printf(" gid %u", cmd32->d[0]);
148698943Sluigi			    }
148798943Sluigi				break;
148898943Sluigi
1489133600Scsjp			case O_JAIL:
1490133600Scsjp				printf(" jail %d", cmd32->d[0]);
1491133600Scsjp				break;
1492133600Scsjp
1493112250Scjc			case O_VERREVPATH:
1494112250Scjc				printf(" verrevpath");
1495112250Scjc				break;
1496116919Sluigi
1497128575Sandre			case O_VERSRCREACH:
1498128575Sandre				printf(" versrcreach");
1499128575Sandre				break;
1500128575Sandre
1501133387Sandre			case O_ANTISPOOF:
1502133387Sandre				printf(" antispoof");
1503133387Sandre				break;
1504133387Sandre
1505117241Sluigi			case O_IPSEC:
1506117241Sluigi				printf(" ipsec");
1507117241Sluigi				break;
1508117241Sluigi
1509117469Sluigi			case O_NOP:
1510117626Sluigi				comment = (char *)(cmd + 1);
1511117469Sluigi				break;
1512117469Sluigi
151398943Sluigi			case O_KEEP_STATE:
151498943Sluigi				printf(" keep-state");
151598943Sluigi				break;
151698943Sluigi
1517159636Soleg			case O_LIMIT: {
151898943Sluigi				struct _s_x *p = limit_masks;
151998943Sluigi				ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
1520117328Sluigi				uint8_t x = c->limit_mask;
1521117469Sluigi				char const *comma = " ";
152298943Sluigi
152398943Sluigi				printf(" limit");
1524117577Sluigi				for (; p->x != 0 ; p++)
152599909Sluigi					if ((x & p->x) == p->x) {
152698943Sluigi						x &= ~p->x;
152798943Sluigi						printf("%s%s", comma, p->s);
152898943Sluigi						comma = ",";
152998943Sluigi					}
1530159636Soleg				PRINT_UINT_ARG(" ", c->conn_limit);
153198943Sluigi				break;
1532159636Soleg			}
153398943Sluigi
1534146894Smlaier			case O_IP6:
1535152923Sume				printf(" ip6");
1536145246Sbrooks				break;
1537145246Sbrooks
1538146894Smlaier			case O_IP4:
1539152923Sume				printf(" ip4");
1540146894Smlaier				break;
1541146894Smlaier
1542145246Sbrooks			case O_ICMP6TYPE:
1543145246Sbrooks				print_icmp6types((ipfw_insn_u32 *)cmd);
1544145246Sbrooks				break;
1545145246Sbrooks
1546145246Sbrooks			case O_EXT_HDR:
1547145246Sbrooks				print_ext6hdr( (ipfw_insn *) cmd );
1548145246Sbrooks				break;
1549145246Sbrooks
1550158879Soleg			case O_TAGGED:
1551158879Soleg				if (F_LEN(cmd) == 1)
1552159636Soleg					PRINT_UINT_ARG(" tagged ", cmd->arg1);
1553158879Soleg				else
1554159636Soleg					print_newports((ipfw_insn_u16 *)cmd, 0,
1555159636Soleg					    O_TAGGED);
1556158879Soleg				break;
1557158879Soleg
155898943Sluigi			default:
155998943Sluigi				printf(" [opcode %d len %d]",
156098943Sluigi				    cmd->opcode, cmd->len);
156198943Sluigi			}
156298943Sluigi		}
156398943Sluigi		if (cmd->len & F_OR) {
156498943Sluigi			printf(" or");
156598943Sluigi			or_block = 1;
156698943Sluigi		} else if (or_block) {
156798943Sluigi			printf(" }");
156898943Sluigi			or_block = 0;
156998943Sluigi		}
157098943Sluigi	}
1571205179Sluigi	show_prerequisites(&flags, HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP
1572220802Sglebius					      | HAVE_IP, 0);
1573117626Sluigi	if (comment)
1574117626Sluigi		printf(" // %s", comment);
157598943Sluigi	printf("\n");
157698943Sluigi}
157798943Sluigi
157898943Sluigistatic void
1579112189Smaximshow_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth)
158098943Sluigi{
158198943Sluigi	struct protoent *pe;
158298943Sluigi	struct in_addr a;
1583115793Sticso	uint16_t rulenum;
1584159160Smlaier	char buf[INET6_ADDRSTRLEN];
158598943Sluigi
1586187764Sluigi	if (!co.do_expired) {
158798943Sluigi		if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT))
158898943Sluigi			return;
158998943Sluigi	}
1590115793Sticso	bcopy(&d->rule, &rulenum, sizeof(rulenum));
1591117328Sluigi	printf("%05d", rulenum);
1592206843Sluigi	if (pcwidth > 0 || bcwidth > 0) {
1593206843Sluigi		printf(" ");
1594206843Sluigi		pr_u64(&d->pcnt, pcwidth);
1595206843Sluigi		pr_u64(&d->bcnt, bcwidth);
1596206843Sluigi		printf("(%ds)", d->expire);
1597206843Sluigi	}
159898943Sluigi	switch (d->dyn_type) {
159998943Sluigi	case O_LIMIT_PARENT:
160098943Sluigi		printf(" PARENT %d", d->count);
160198943Sluigi		break;
160298943Sluigi	case O_LIMIT:
160398943Sluigi		printf(" LIMIT");
160498943Sluigi		break;
160598943Sluigi	case O_KEEP_STATE: /* bidir, no mask */
1606106505Smaxim		printf(" STATE");
160798943Sluigi		break;
160898943Sluigi	}
160998943Sluigi
161098943Sluigi	if ((pe = getprotobynumber(d->id.proto)) != NULL)
161198943Sluigi		printf(" %s", pe->p_name);
161298943Sluigi	else
161398943Sluigi		printf(" proto %u", d->id.proto);
161498943Sluigi
1615159160Smlaier	if (d->id.addr_type == 4) {
1616159160Smlaier		a.s_addr = htonl(d->id.src_ip);
1617159160Smlaier		printf(" %s %d", inet_ntoa(a), d->id.src_port);
161898943Sluigi
1619159160Smlaier		a.s_addr = htonl(d->id.dst_ip);
1620159160Smlaier		printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port);
1621159160Smlaier	} else if (d->id.addr_type == 6) {
1622159160Smlaier		printf(" %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf,
1623159160Smlaier		    sizeof(buf)), d->id.src_port);
1624159160Smlaier		printf(" <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf,
1625159160Smlaier		    sizeof(buf)), d->id.dst_port);
1626159160Smlaier	} else
1627159160Smlaier		printf(" UNKNOWN <-> UNKNOWN\n");
1628159160Smlaier
162998943Sluigi	printf("\n");
163098943Sluigi}
163198943Sluigi
1632101978Sluigi/*
1633101978Sluigi * This one handles all set-related commands
1634101978Sluigi * 	ipfw set { show | enable | disable }
1635101978Sluigi * 	ipfw set swap X Y
1636101978Sluigi * 	ipfw set move X to Y
1637101978Sluigi * 	ipfw set move rule X to Y
1638101978Sluigi */
1639187767Sluigivoid
1640204591Sluigiipfw_sets_handler(char *av[])
1641101978Sluigi{
1642117328Sluigi	uint32_t set_disable, masks[2];
1643101978Sluigi	int i, nbytes;
1644117328Sluigi	uint16_t rulenum;
1645117328Sluigi	uint8_t cmd, new_set;
1646101978Sluigi
1647101978Sluigi	av++;
1648101978Sluigi
1649204591Sluigi	if (av[0] == NULL)
1650101978Sluigi		errx(EX_USAGE, "set needs command");
1651140271Sbrooks	if (_substrcmp(*av, "show") == 0) {
1652204717Sluigi		void *data = NULL;
1653117469Sluigi		char const *msg;
1654204717Sluigi		int nalloc;
1655101978Sluigi
1656204717Sluigi		nalloc = nbytes = sizeof(struct ip_fw);
1657204717Sluigi		while (nbytes >= nalloc) {
1658204717Sluigi			if (data)
1659204717Sluigi				free(data);
1660204717Sluigi			nalloc = nalloc * 2 + 200;
1661204717Sluigi			nbytes = nalloc;
1662206843Sluigi			data = safe_calloc(1, nbytes);
1663206843Sluigi			if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0)
1664206843Sluigi				err(EX_OSERR, "getsockopt(IP_FW_GET)");
1665204717Sluigi		}
1666204717Sluigi
1667115793Sticso		bcopy(&((struct ip_fw *)data)->next_rule,
1668115793Sticso			&set_disable, sizeof(set_disable));
1669101978Sluigi
1670117655Sluigi		for (i = 0, msg = "disable" ; i < RESVD_SET; i++)
1671117577Sluigi			if ((set_disable & (1<<i))) {
1672101978Sluigi				printf("%s %d", msg, i);
1673101978Sluigi				msg = "";
1674101978Sluigi			}
1675101978Sluigi		msg = (set_disable) ? " enable" : "enable";
1676117655Sluigi		for (i = 0; i < RESVD_SET; i++)
1677117577Sluigi			if (!(set_disable & (1<<i))) {
1678101978Sluigi				printf("%s %d", msg, i);
1679101978Sluigi				msg = "";
1680101978Sluigi			}
1681101978Sluigi		printf("\n");
1682140271Sbrooks	} else if (_substrcmp(*av, "swap") == 0) {
1683204591Sluigi		av++;
1684204591Sluigi		if ( av[0] == NULL || av[1] == NULL )
1685101978Sluigi			errx(EX_USAGE, "set swap needs 2 set numbers\n");
1686101978Sluigi		rulenum = atoi(av[0]);
1687101978Sluigi		new_set = atoi(av[1]);
1688117655Sluigi		if (!isdigit(*(av[0])) || rulenum > RESVD_SET)
1689101978Sluigi			errx(EX_DATAERR, "invalid set number %s\n", av[0]);
1690117655Sluigi		if (!isdigit(*(av[1])) || new_set > RESVD_SET)
1691101978Sluigi			errx(EX_DATAERR, "invalid set number %s\n", av[1]);
1692101978Sluigi		masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
1693117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
1694140271Sbrooks	} else if (_substrcmp(*av, "move") == 0) {
1695204591Sluigi		av++;
1696204717Sluigi		if (av[0] && _substrcmp(*av, "rule") == 0) {
1697101978Sluigi			cmd = 2;
1698204591Sluigi			av++;
1699101978Sluigi		} else
1700101978Sluigi			cmd = 3;
1701204591Sluigi		if (av[0] == NULL || av[1] == NULL || av[2] == NULL ||
1702204591Sluigi				av[3] != NULL ||  _substrcmp(av[1], "to") != 0)
1703101978Sluigi			errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
1704101978Sluigi		rulenum = atoi(av[0]);
1705101978Sluigi		new_set = atoi(av[2]);
1706117655Sluigi		if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) ||
1707182823Srik			(cmd == 2 && rulenum == IPFW_DEFAULT_RULE) )
1708101978Sluigi			errx(EX_DATAERR, "invalid source number %s\n", av[0]);
1709117655Sluigi		if (!isdigit(*(av[2])) || new_set > RESVD_SET)
1710101978Sluigi			errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
1711101978Sluigi		masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
1712117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
1713140271Sbrooks	} else if (_substrcmp(*av, "disable") == 0 ||
1714140271Sbrooks		   _substrcmp(*av, "enable") == 0 ) {
1715140271Sbrooks		int which = _substrcmp(*av, "enable") == 0 ? 1 : 0;
1716101978Sluigi
1717204591Sluigi		av++;
1718101978Sluigi		masks[0] = masks[1] = 0;
1719101978Sluigi
1720204717Sluigi		while (av[0]) {
1721101978Sluigi			if (isdigit(**av)) {
1722101978Sluigi				i = atoi(*av);
1723117655Sluigi				if (i < 0 || i > RESVD_SET)
1724101978Sluigi					errx(EX_DATAERR,
1725101978Sluigi					    "invalid set number %d\n", i);
1726101978Sluigi				masks[which] |= (1<<i);
1727140271Sbrooks			} else if (_substrcmp(*av, "disable") == 0)
1728101978Sluigi				which = 0;
1729140271Sbrooks			else if (_substrcmp(*av, "enable") == 0)
1730101978Sluigi				which = 1;
1731101978Sluigi			else
1732101978Sluigi				errx(EX_DATAERR,
1733101978Sluigi					"invalid set command %s\n", *av);
1734204591Sluigi			av++;
1735101978Sluigi		}
1736101978Sluigi		if ( (masks[0] & masks[1]) != 0 )
1737101978Sluigi			errx(EX_DATAERR,
1738101978Sluigi			    "cannot enable and disable the same set\n");
1739101978Sluigi
1740117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(masks));
1741101978Sluigi		if (i)
1742101978Sluigi			warn("set enable/disable: setsockopt(IP_FW_DEL)");
1743101978Sluigi	} else
1744101978Sluigi		errx(EX_USAGE, "invalid set command %s\n", *av);
1745101978Sluigi}
1746101978Sluigi
1747187767Sluigivoid
1748204591Sluigiipfw_sysctl_handler(char *av[], int which)
1749109126Sdillon{
1750109126Sdillon	av++;
1751109126Sdillon
1752204591Sluigi	if (av[0] == NULL) {
1753109126Sdillon		warnx("missing keyword to enable/disable\n");
1754140271Sbrooks	} else if (_substrcmp(*av, "firewall") == 0) {
1755116770Sluigi		sysctlbyname("net.inet.ip.fw.enable", NULL, 0,
1756116770Sluigi		    &which, sizeof(which));
1757206266Sume		sysctlbyname("net.inet6.ip6.fw.enable", NULL, 0,
1758206266Sume		    &which, sizeof(which));
1759140271Sbrooks	} else if (_substrcmp(*av, "one_pass") == 0) {
1760116770Sluigi		sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0,
1761116770Sluigi		    &which, sizeof(which));
1762140271Sbrooks	} else if (_substrcmp(*av, "debug") == 0) {
1763116770Sluigi		sysctlbyname("net.inet.ip.fw.debug", NULL, 0,
1764116770Sluigi		    &which, sizeof(which));
1765140271Sbrooks	} else if (_substrcmp(*av, "verbose") == 0) {
1766116770Sluigi		sysctlbyname("net.inet.ip.fw.verbose", NULL, 0,
1767116770Sluigi		    &which, sizeof(which));
1768140271Sbrooks	} else if (_substrcmp(*av, "dyn_keepalive") == 0) {
1769116770Sluigi		sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0,
1770116770Sluigi		    &which, sizeof(which));
1771204591Sluigi#ifndef NO_ALTQ
1772140271Sbrooks	} else if (_substrcmp(*av, "altq") == 0) {
1773136071Sgreen		altq_set_enabled(which);
1774204591Sluigi#endif
1775109126Sdillon	} else {
1776109126Sdillon		warnx("unrecognize enable/disable keyword: %s\n", *av);
1777109126Sdillon	}
1778109126Sdillon}
1779109126Sdillon
1780187767Sluigivoid
1781187767Sluigiipfw_list(int ac, char *av[], int show_counters)
178298943Sluigi{
178398943Sluigi	struct ip_fw *r;
178498943Sluigi	ipfw_dyn_rule *dynrules, *d;
178598943Sluigi
1786117469Sluigi#define NEXT(r)	((struct ip_fw *)((char *)r + RULESIZE(r)))
1787117469Sluigi	char *lim;
1788117469Sluigi	void *data = NULL;
1789112189Smaxim	int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width;
179098943Sluigi	int exitval = EX_OK;
179198943Sluigi	int lac;
179298943Sluigi	char **lav;
1793117469Sluigi	u_long rnum, last;
179498943Sluigi	char *endptr;
179598943Sluigi	int seen = 0;
1796170923Smaxim	uint8_t set;
179798943Sluigi
1798187764Sluigi	const int ocmd = co.do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
179998943Sluigi	int nalloc = 1024;	/* start somewhere... */
180098943Sluigi
1801135036Smaxim	last = 0;
1802135036Smaxim
1803187764Sluigi	if (co.test_only) {
1804117328Sluigi		fprintf(stderr, "Testing only, list disabled\n");
1805117328Sluigi		return;
1806117328Sluigi	}
1807204591Sluigi	if (co.do_pipe) {
1808204591Sluigi		dummynet_list(ac, av, show_counters);
1809204591Sluigi		return;
1810204591Sluigi	}
1811117328Sluigi
181298943Sluigi	ac--;
181398943Sluigi	av++;
181498943Sluigi
181598943Sluigi	/* get rules or pipes from kernel, resizing array as necessary */
181698943Sluigi	nbytes = nalloc;
181798943Sluigi
181898943Sluigi	while (nbytes >= nalloc) {
181998943Sluigi		nalloc = nalloc * 2 + 200;
182098943Sluigi		nbytes = nalloc;
1821187716Sluigi		data = safe_realloc(data, nbytes);
1822119740Stmm		if (do_cmd(ocmd, data, (uintptr_t)&nbytes) < 0)
182398943Sluigi			err(EX_OSERR, "getsockopt(IP_%s_GET)",
1824187764Sluigi				co.do_pipe ? "DUMMYNET" : "FW");
182598943Sluigi	}
182698943Sluigi
182798943Sluigi	/*
182898943Sluigi	 * Count static rules. They have variable size so we
182998943Sluigi	 * need to scan the list to count them.
183098943Sluigi	 */
1831117469Sluigi	for (nstat = 1, r = data, lim = (char *)data + nbytes;
1832182823Srik		    r->rulenum < IPFW_DEFAULT_RULE && (char *)r < lim;
1833117469Sluigi		    ++nstat, r = NEXT(r) )
183498943Sluigi		; /* nothing */
183598943Sluigi
183698943Sluigi	/*
183798943Sluigi	 * Count dynamic rules. This is easier as they have
183898943Sluigi	 * fixed size.
183998943Sluigi	 */
1840117469Sluigi	r = NEXT(r);
184198943Sluigi	dynrules = (ipfw_dyn_rule *)r ;
1842117469Sluigi	n = (char *)r - (char *)data;
184398943Sluigi	ndyn = (nbytes - n) / sizeof *dynrules;
184498943Sluigi
1845112189Smaxim	/* if showing stats, figure out column widths ahead of time */
1846112189Smaxim	bcwidth = pcwidth = 0;
1847117469Sluigi	if (show_counters) {
1848117469Sluigi		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
1849170923Smaxim			/* skip rules from another set */
1850187764Sluigi			if (co.use_set && r->set != co.use_set - 1)
1851170923Smaxim				continue;
1852170923Smaxim
1853112189Smaxim			/* packet counter */
1854206843Sluigi			width = pr_u64(&r->pcnt, 0);
1855112189Smaxim			if (width > pcwidth)
1856112189Smaxim				pcwidth = width;
1857112189Smaxim
1858112189Smaxim			/* byte counter */
1859206843Sluigi			width = pr_u64(&r->bcnt, 0);
1860112189Smaxim			if (width > bcwidth)
1861112189Smaxim				bcwidth = width;
1862112189Smaxim		}
1863112189Smaxim	}
1864187764Sluigi	if (co.do_dynamic && ndyn) {
1865112189Smaxim		for (n = 0, d = dynrules; n < ndyn; n++, d++) {
1866187764Sluigi			if (co.use_set) {
1867170923Smaxim				/* skip rules from another set */
1868171989Smaxim				bcopy((char *)&d->rule + sizeof(uint16_t),
1869170923Smaxim				      &set, sizeof(uint8_t));
1870187764Sluigi				if (set != co.use_set - 1)
1871170923Smaxim					continue;
1872170923Smaxim			}
1873206843Sluigi			width = pr_u64(&d->pcnt, 0);
1874112189Smaxim			if (width > pcwidth)
1875112189Smaxim				pcwidth = width;
1876112189Smaxim
1877206843Sluigi			width = pr_u64(&d->bcnt, 0);
1878112189Smaxim			if (width > bcwidth)
1879112189Smaxim				bcwidth = width;
1880112189Smaxim		}
1881112189Smaxim	}
188298943Sluigi	/* if no rule numbers were specified, list all rules */
188398943Sluigi	if (ac == 0) {
1884170923Smaxim		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
1885187764Sluigi			if (co.use_set && r->set != co.use_set - 1)
1886170923Smaxim				continue;
1887112189Smaxim			show_ipfw(r, pcwidth, bcwidth);
1888170923Smaxim		}
188998943Sluigi
1890187764Sluigi		if (co.do_dynamic && ndyn) {
189198943Sluigi			printf("## Dynamic rules (%d):\n", ndyn);
1892170923Smaxim			for (n = 0, d = dynrules; n < ndyn; n++, d++) {
1893187764Sluigi				if (co.use_set) {
1894171989Smaxim					bcopy((char *)&d->rule + sizeof(uint16_t),
1895170923Smaxim					      &set, sizeof(uint8_t));
1896187764Sluigi					if (set != co.use_set - 1)
1897170923Smaxim						continue;
1898170923Smaxim				}
1899112189Smaxim				show_dyn_ipfw(d, pcwidth, bcwidth);
190098943Sluigi		}
1901170923Smaxim		}
190298943Sluigi		goto done;
190398943Sluigi	}
190498943Sluigi
190598943Sluigi	/* display specific rules requested on command line */
190698943Sluigi
190798943Sluigi	for (lac = ac, lav = av; lac != 0; lac--) {
190898943Sluigi		/* convert command line rule # */
1909117469Sluigi		last = rnum = strtoul(*lav++, &endptr, 10);
1910117469Sluigi		if (*endptr == '-')
1911117469Sluigi			last = strtoul(endptr+1, &endptr, 10);
191298943Sluigi		if (*endptr) {
191398943Sluigi			exitval = EX_USAGE;
191498943Sluigi			warnx("invalid rule number: %s", *(lav - 1));
191598943Sluigi			continue;
191698943Sluigi		}
1917117469Sluigi		for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) {
1918117469Sluigi			if (r->rulenum > last)
191998943Sluigi				break;
1920187764Sluigi			if (co.use_set && r->set != co.use_set - 1)
1921170923Smaxim				continue;
1922117469Sluigi			if (r->rulenum >= rnum && r->rulenum <= last) {
1923112189Smaxim				show_ipfw(r, pcwidth, bcwidth);
192498943Sluigi				seen = 1;
192598943Sluigi			}
192698943Sluigi		}
192798943Sluigi		if (!seen) {
192898943Sluigi			/* give precedence to other error(s) */
192998943Sluigi			if (exitval == EX_OK)
193098943Sluigi				exitval = EX_UNAVAILABLE;
193198943Sluigi			warnx("rule %lu does not exist", rnum);
193298943Sluigi		}
193398943Sluigi	}
193498943Sluigi
1935187764Sluigi	if (co.do_dynamic && ndyn) {
193698943Sluigi		printf("## Dynamic rules:\n");
193798943Sluigi		for (lac = ac, lav = av; lac != 0; lac--) {
1938145246Sbrooks			last = rnum = strtoul(*lav++, &endptr, 10);
1939117469Sluigi			if (*endptr == '-')
1940117469Sluigi				last = strtoul(endptr+1, &endptr, 10);
194198943Sluigi			if (*endptr)
194298943Sluigi				/* already warned */
194398943Sluigi				continue;
194498943Sluigi			for (n = 0, d = dynrules; n < ndyn; n++, d++) {
1945115793Sticso				uint16_t rulenum;
1946115793Sticso
1947115793Sticso				bcopy(&d->rule, &rulenum, sizeof(rulenum));
1948115793Sticso				if (rulenum > rnum)
194998943Sluigi					break;
1950187764Sluigi				if (co.use_set) {
1951171989Smaxim					bcopy((char *)&d->rule + sizeof(uint16_t),
1952170923Smaxim					      &set, sizeof(uint8_t));
1953187764Sluigi					if (set != co.use_set - 1)
1954170923Smaxim						continue;
1955170923Smaxim				}
1956117469Sluigi				if (r->rulenum >= rnum && r->rulenum <= last)
1957112189Smaxim					show_dyn_ipfw(d, pcwidth, bcwidth);
195898943Sluigi			}
195998943Sluigi		}
196098943Sluigi	}
196198943Sluigi
196298943Sluigi	ac = 0;
196398943Sluigi
196498943Sluigidone:
196598943Sluigi	free(data);
196698943Sluigi
196798943Sluigi	if (exitval != EX_OK)
196898943Sluigi		exit(exitval);
1969117469Sluigi#undef NEXT
197098943Sluigi}
197198943Sluigi
197298943Sluigistatic int
197398943Sluigilookup_host (char *host, struct in_addr *ipaddr)
197498943Sluigi{
197598943Sluigi	struct hostent *he;
197698943Sluigi
197798943Sluigi	if (!inet_aton(host, ipaddr)) {
197898943Sluigi		if ((he = gethostbyname(host)) == NULL)
197998943Sluigi			return(-1);
198098943Sluigi		*ipaddr = *(struct in_addr *)he->h_addr_list[0];
198198943Sluigi	}
198298943Sluigi	return(0);
198398943Sluigi}
198498943Sluigi
198598943Sluigi/*
198698943Sluigi * fills the addr and mask fields in the instruction as appropriate from av.
198798943Sluigi * Update length as appropriate.
198898943Sluigi * The following formats are allowed:
198998943Sluigi *	me	returns O_IP_*_ME
199098943Sluigi *	1.2.3.4		single IP address
199198943Sluigi *	1.2.3.4:5.6.7.8	address:mask
199298943Sluigi *	1.2.3.4/24	address/mask
199398943Sluigi *	1.2.3.4/26{1,6,5,4,23}	set of addresses in a subnet
1994117328Sluigi * We can have multiple comma-separated address/mask entries.
199598943Sluigi */
199698943Sluigistatic void
199798943Sluigifill_ip(ipfw_insn_ip *cmd, char *av)
199898943Sluigi{
1999117328Sluigi	int len = 0;
2000117328Sluigi	uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
200198943Sluigi
200298943Sluigi	cmd->o.len &= ~F_LEN_MASK;	/* zero len */
200398943Sluigi
2004140271Sbrooks	if (_substrcmp(av, "any") == 0)
200598943Sluigi		return;
200698943Sluigi
2007140271Sbrooks	if (_substrcmp(av, "me") == 0) {
200898943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn);
200998943Sluigi		return;
201098943Sluigi	}
201198943Sluigi
2012140271Sbrooks	if (strncmp(av, "table(", 6) == 0) {
2013130281Sru		char *p = strchr(av + 6, ',');
2014130281Sru
2015130281Sru		if (p)
2016130281Sru			*p++ = '\0';
2017130281Sru		cmd->o.opcode = O_IP_DST_LOOKUP;
2018130281Sru		cmd->o.arg1 = strtoul(av + 6, NULL, 0);
2019130281Sru		if (p) {
2020130281Sru			cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2021130281Sru			d[0] = strtoul(p, NULL, 0);
2022130281Sru		} else
2023130281Sru			cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2024130281Sru		return;
2025130281Sru	}
2026130281Sru
2027117328Sluigi    while (av) {
2028117328Sluigi	/*
2029117328Sluigi	 * After the address we can have '/' or ':' indicating a mask,
2030117328Sluigi	 * ',' indicating another address follows, '{' indicating a
2031117328Sluigi	 * set of addresses of unspecified size.
2032117328Sluigi	 */
2033165851Smlaier	char *t = NULL, *p = strpbrk(av, "/:,{");
2034117328Sluigi	int masklen;
2035187477Sluigi	char md, nd = '\0';
2036117328Sluigi
203798943Sluigi	if (p) {
203898943Sluigi		md = *p;
203998943Sluigi		*p++ = '\0';
2040165851Smlaier		if ((t = strpbrk(p, ",{")) != NULL) {
2041165851Smlaier			nd = *t;
2042165851Smlaier			*t = '\0';
2043165851Smlaier		}
2044117328Sluigi	} else
2045117328Sluigi		md = '\0';
204698943Sluigi
2047117328Sluigi	if (lookup_host(av, (struct in_addr *)&d[0]) != 0)
204898943Sluigi		errx(EX_NOHOST, "hostname ``%s'' unknown", av);
204998943Sluigi	switch (md) {
205098943Sluigi	case ':':
2051117328Sluigi		if (!inet_aton(p, (struct in_addr *)&d[1]))
205298943Sluigi			errx(EX_DATAERR, "bad netmask ``%s''", p);
205398943Sluigi		break;
205498943Sluigi	case '/':
2055117328Sluigi		masklen = atoi(p);
2056117328Sluigi		if (masklen == 0)
2057117328Sluigi			d[1] = htonl(0);	/* mask */
2058117328Sluigi		else if (masklen > 32)
205998943Sluigi			errx(EX_DATAERR, "bad width ``%s''", p);
206098943Sluigi		else
2061117328Sluigi			d[1] = htonl(~0 << (32 - masklen));
206298943Sluigi		break;
2063117328Sluigi	case '{':	/* no mask, assume /24 and put back the '{' */
2064117328Sluigi		d[1] = htonl(~0 << (32 - 24));
2065117328Sluigi		*(--p) = md;
2066117328Sluigi		break;
2067117328Sluigi
2068117328Sluigi	case ',':	/* single address plus continuation */
2069117328Sluigi		*(--p) = md;
2070117328Sluigi		/* FALLTHROUGH */
2071117328Sluigi	case 0:		/* initialization value */
207298943Sluigi	default:
2073117328Sluigi		d[1] = htonl(~0);	/* force /32 */
207498943Sluigi		break;
207598943Sluigi	}
2076117328Sluigi	d[0] &= d[1];		/* mask base address with mask */
2077165851Smlaier	if (t)
2078165851Smlaier		*t = nd;
2079117328Sluigi	/* find next separator */
208098943Sluigi	if (p)
2081117328Sluigi		p = strpbrk(p, ",{");
2082117328Sluigi	if (p && *p == '{') {
2083117328Sluigi		/*
2084117328Sluigi		 * We have a set of addresses. They are stored as follows:
2085117328Sluigi		 *   arg1	is the set size (powers of 2, 2..256)
2086117328Sluigi		 *   addr	is the base address IN HOST FORMAT
2087117328Sluigi		 *   mask..	is an array of arg1 bits (rounded up to
2088117328Sluigi		 *		the next multiple of 32) with bits set
2089117328Sluigi		 *		for each host in the map.
2090117328Sluigi		 */
2091117328Sluigi		uint32_t *map = (uint32_t *)&cmd->mask;
209298943Sluigi		int low, high;
2093117577Sluigi		int i = contigmask((uint8_t *)&(d[1]), 32);
209498943Sluigi
2095117328Sluigi		if (len > 0)
2096117328Sluigi			errx(EX_DATAERR, "address set cannot be in a list");
2097117328Sluigi		if (i < 24 || i > 31)
2098117328Sluigi			errx(EX_DATAERR, "invalid set with mask %d\n", i);
2099117328Sluigi		cmd->o.arg1 = 1<<(32-i);	/* map length		*/
2100117328Sluigi		d[0] = ntohl(d[0]);		/* base addr in host format */
210198943Sluigi		cmd->o.opcode = O_IP_DST_SET;	/* default */
210298943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32;
2103101117Sluigi		for (i = 0; i < (cmd->o.arg1+31)/32 ; i++)
2104117328Sluigi			map[i] = 0;	/* clear map */
210598943Sluigi
2106117328Sluigi		av = p + 1;
2107117328Sluigi		low = d[0] & 0xff;
210898943Sluigi		high = low + cmd->o.arg1 - 1;
2109117328Sluigi		/*
2110117328Sluigi		 * Here, i stores the previous value when we specify a range
2111117328Sluigi		 * of addresses within a mask, e.g. 45-63. i = -1 means we
2112117328Sluigi		 * have no previous value.
2113117328Sluigi		 */
2114116716Sluigi		i = -1;	/* previous value in a range */
211598943Sluigi		while (isdigit(*av)) {
211698943Sluigi			char *s;
2117117328Sluigi			int a = strtol(av, &s, 0);
211898943Sluigi
2119117328Sluigi			if (s == av) { /* no parameter */
2120117328Sluigi			    if (*av != '}')
2121117328Sluigi				errx(EX_DATAERR, "set not closed\n");
2122117328Sluigi			    if (i != -1)
2123117328Sluigi				errx(EX_DATAERR, "incomplete range %d-", i);
2124117328Sluigi			    break;
2125117328Sluigi			}
2126117328Sluigi			if (a < low || a > high)
2127117328Sluigi			    errx(EX_DATAERR, "addr %d out of range [%d-%d]\n",
212898943Sluigi				a, low, high);
212998943Sluigi			a -= low;
2130116716Sluigi			if (i == -1)	/* no previous in range */
2131116716Sluigi			    i = a;
2132116716Sluigi			else {		/* check that range is valid */
2133116716Sluigi			    if (i > a)
2134116716Sluigi				errx(EX_DATAERR, "invalid range %d-%d",
2135116716Sluigi					i+low, a+low);
2136116716Sluigi			    if (*s == '-')
2137116716Sluigi				errx(EX_DATAERR, "double '-' in range");
2138116716Sluigi			}
2139116716Sluigi			for (; i <= a; i++)
2140117328Sluigi			    map[i/32] |= 1<<(i & 31);
2141116716Sluigi			i = -1;
2142116716Sluigi			if (*s == '-')
2143116716Sluigi			    i = a;
2144117328Sluigi			else if (*s == '}')
2145116716Sluigi			    break;
214698943Sluigi			av = s+1;
214798943Sluigi		}
214898943Sluigi		return;
214998943Sluigi	}
2150117328Sluigi	av = p;
2151117328Sluigi	if (av)			/* then *av must be a ',' */
2152117328Sluigi		av++;
215398943Sluigi
2154117328Sluigi	/* Check this entry */
2155117328Sluigi	if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */
2156117328Sluigi		/*
2157117328Sluigi		 * 'any' turns the entire list into a NOP.
2158117328Sluigi		 * 'not any' never matches, so it is removed from the
2159117328Sluigi		 * list unless it is the only item, in which case we
2160117328Sluigi		 * report an error.
2161117328Sluigi		 */
2162117328Sluigi		if (cmd->o.len & F_NOT) {	/* "not any" never matches */
2163117328Sluigi			if (av == NULL && len == 0) /* only this entry */
2164117328Sluigi				errx(EX_DATAERR, "not any never matches");
2165117328Sluigi		}
2166117328Sluigi		/* else do nothing and skip this entry */
2167128067Smaxim		return;
2168117328Sluigi	}
2169117328Sluigi	/* A single IP can be stored in an optimized format */
2170204591Sluigi	if (d[1] == (uint32_t)~0 && av == NULL && len == 0) {
217198943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2172117328Sluigi		return;
2173117328Sluigi	}
2174117328Sluigi	len += 2;	/* two words... */
2175117328Sluigi	d += 2;
2176117328Sluigi    } /* end while */
2177162363Sjhay    if (len + 1 > F_LEN_MASK)
2178162363Sjhay	errx(EX_DATAERR, "address list too long");
2179117328Sluigi    cmd->o.len |= len+1;
218098943Sluigi}
218198943Sluigi
218298943Sluigi
2183145246Sbrooks/* n2mask sets n bits of the mask */
2184187769Sluigivoid
2185145246Sbrooksn2mask(struct in6_addr *mask, int n)
2186145246Sbrooks{
2187145246Sbrooks	static int	minimask[9] =
2188145246Sbrooks	    { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
2189145246Sbrooks	u_char		*p;
2190145246Sbrooks
2191145246Sbrooks	memset(mask, 0, sizeof(struct in6_addr));
2192145246Sbrooks	p = (u_char *) mask;
2193145246Sbrooks	for (; n > 0; p++, n -= 8) {
2194145246Sbrooks		if (n >= 8)
2195145246Sbrooks			*p = 0xff;
2196145246Sbrooks		else
2197145246Sbrooks			*p = minimask[n];
2198145246Sbrooks	}
2199145246Sbrooks	return;
2200145246Sbrooks}
2201145246Sbrooks
220298943Sluigi/*
220398943Sluigi * helper function to process a set of flags and set bits in the
220498943Sluigi * appropriate masks.
220598943Sluigi */
220698943Sluigistatic void
220798943Sluigifill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
220898943Sluigi	struct _s_x *flags, char *p)
220998943Sluigi{
2210117328Sluigi	uint8_t set=0, clear=0;
221198943Sluigi
221298943Sluigi	while (p && *p) {
221398943Sluigi		char *q;	/* points to the separator */
221498943Sluigi		int val;
2215117328Sluigi		uint8_t *which;	/* mask we are working on */
221698943Sluigi
221798943Sluigi		if (*p == '!') {
221898943Sluigi			p++;
221998943Sluigi			which = &clear;
222098943Sluigi		} else
222198943Sluigi			which = &set;
222298943Sluigi		q = strchr(p, ',');
222398943Sluigi		if (q)
222498943Sluigi			*q++ = '\0';
222598943Sluigi		val = match_token(flags, p);
222698943Sluigi		if (val <= 0)
222798943Sluigi			errx(EX_DATAERR, "invalid flag %s", p);
2228117328Sluigi		*which |= (uint8_t)val;
222998943Sluigi		p = q;
223098943Sluigi	}
2231220802Sglebius	cmd->opcode = opcode;
2232220802Sglebius	cmd->len =  (cmd->len & (F_NOT | F_OR)) | 1;
2233220802Sglebius	cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
223498943Sluigi}
223598943Sluigi
223698943Sluigi
2237187767Sluigivoid
2238204591Sluigiipfw_delete(char *av[])
223998943Sluigi{
2240117328Sluigi	uint32_t rulenum;
224198943Sluigi	int i;
224298943Sluigi	int exitval = EX_OK;
2243101628Sluigi	int do_set = 0;
224498943Sluigi
2245204591Sluigi	av++;
2246130013Scsjp	NEED1("missing rule specification");
2247204591Sluigi	if ( *av && _substrcmp(*av, "set") == 0) {
2248170923Smaxim		/* Do not allow using the following syntax:
2249170923Smaxim		 *	ipfw set N delete set M
2250170923Smaxim		 */
2251187764Sluigi		if (co.use_set)
2252170923Smaxim			errx(EX_DATAERR, "invalid syntax");
2253101978Sluigi		do_set = 1;	/* delete set */
2254204591Sluigi		av++;
2255101978Sluigi	}
225698943Sluigi
225798943Sluigi	/* Rule number */
2258204591Sluigi	while (*av && isdigit(**av)) {
2259204591Sluigi		i = atoi(*av); av++;
2260187764Sluigi		if (co.do_nat) {
2261165648Spiso			exitval = do_cmd(IP_FW_NAT_DEL, &i, sizeof i);
2262165648Spiso			if (exitval) {
2263165648Spiso				exitval = EX_UNAVAILABLE;
2264165648Spiso				warn("rule %u not available", i);
2265165648Spiso			}
2266187764Sluigi 		} else if (co.do_pipe) {
2267187769Sluigi			exitval = ipfw_delete_pipe(co.do_pipe, i);
226898943Sluigi		} else {
2269187764Sluigi			if (co.use_set)
2270170923Smaxim				rulenum = (i & 0xffff) | (5 << 24) |
2271187764Sluigi				    ((co.use_set - 1) << 16);
2272170923Smaxim			else
2273101978Sluigi			rulenum =  (i & 0xffff) | (do_set << 24);
2274117328Sluigi			i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum);
227598943Sluigi			if (i) {
227698943Sluigi				exitval = EX_UNAVAILABLE;
227798943Sluigi				warn("rule %u: setsockopt(IP_FW_DEL)",
227898943Sluigi				    rulenum);
227998943Sluigi			}
228098943Sluigi		}
228198943Sluigi	}
228298943Sluigi	if (exitval != EX_OK)
228398943Sluigi		exit(exitval);
228498943Sluigi}
228598943Sluigi
228698943Sluigi
228798943Sluigi/*
228898943Sluigi * fill the interface structure. We do not check the name as we can
228998943Sluigi * create interfaces dynamically, so checking them at insert time
229098943Sluigi * makes relatively little sense.
2291220802Sglebius * Interface names containing '*', '?', or '[' are assumed to be shell
2292121816Sbrooks * patterns which match interfaces.
229398943Sluigi */
229498943Sluigistatic void
229598943Sluigifill_iface(ipfw_insn_if *cmd, char *arg)
229698943Sluigi{
229798943Sluigi	cmd->name[0] = '\0';
229898943Sluigi	cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
229998943Sluigi
230098943Sluigi	/* Parse the interface or address */
2301140271Sbrooks	if (strcmp(arg, "any") == 0)
230298943Sluigi		cmd->o.len = 0;		/* effectively ignore this command */
230398943Sluigi	else if (!isdigit(*arg)) {
2304121816Sbrooks		strlcpy(cmd->name, arg, sizeof(cmd->name));
2305121816Sbrooks		cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
230698943Sluigi	} else if (!inet_aton(arg, &cmd->p.ip))
230798943Sluigi		errx(EX_DATAERR, "bad ip address ``%s''", arg);
230898943Sluigi}
230998943Sluigi
231098943Sluigistatic void
2311169424Smaximget_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask)
231298943Sluigi{
2313204591Sluigi	int i;
2314204591Sluigi	size_t l;
2315169424Smaxim	char *ap, *ptr, *optr;
2316169424Smaxim	struct ether_addr *mac;
2317169424Smaxim	const char *macset = "0123456789abcdefABCDEF:";
231898943Sluigi
2319169424Smaxim	if (strcmp(p, "any") == 0) {
2320169424Smaxim		for (i = 0; i < ETHER_ADDR_LEN; i++)
2321169424Smaxim			addr[i] = mask[i] = 0;
232298943Sluigi		return;
2323169424Smaxim	}
232498943Sluigi
2325169424Smaxim	optr = ptr = strdup(p);
2326169424Smaxim	if ((ap = strsep(&ptr, "&/")) != NULL && *ap != 0) {
2327169424Smaxim		l = strlen(ap);
2328169424Smaxim		if (strspn(ap, macset) != l || (mac = ether_aton(ap)) == NULL)
2329169424Smaxim			errx(EX_DATAERR, "Incorrect MAC address");
2330169424Smaxim		bcopy(mac, addr, ETHER_ADDR_LEN);
2331169424Smaxim	} else
2332169424Smaxim		errx(EX_DATAERR, "Incorrect MAC address");
2333169424Smaxim
2334169424Smaxim	if (ptr != NULL) { /* we have mask? */
2335169424Smaxim		if (p[ptr - optr - 1] == '/') { /* mask len */
2336204591Sluigi			long ml = strtol(ptr, &ap, 10);
2337204591Sluigi			if (*ap != 0 || ml > ETHER_ADDR_LEN * 8 || ml < 0)
2338169424Smaxim				errx(EX_DATAERR, "Incorrect mask length");
2339204591Sluigi			for (i = 0; ml > 0 && i < ETHER_ADDR_LEN; ml -= 8, i++)
2340204591Sluigi				mask[i] = (ml >= 8) ? 0xff: (~0) << (8 - ml);
2341169424Smaxim		} else { /* mask */
2342169424Smaxim			l = strlen(ptr);
2343169424Smaxim			if (strspn(ptr, macset) != l ||
2344169424Smaxim			    (mac = ether_aton(ptr)) == NULL)
2345169424Smaxim				errx(EX_DATAERR, "Incorrect mask");
2346169424Smaxim			bcopy(mac, mask, ETHER_ADDR_LEN);
234798943Sluigi		}
2348169424Smaxim	} else { /* default mask: ff:ff:ff:ff:ff:ff */
2349169424Smaxim		for (i = 0; i < ETHER_ADDR_LEN; i++)
235098943Sluigi			mask[i] = 0xff;
235198943Sluigi	}
2352169424Smaxim	for (i = 0; i < ETHER_ADDR_LEN; i++)
235398943Sluigi		addr[i] &= mask[i];
2354169424Smaxim
2355169424Smaxim	free(optr);
235698943Sluigi}
235798943Sluigi
235898943Sluigi/*
235998943Sluigi * helper function, updates the pointer to cmd with the length
236098943Sluigi * of the current command, and also cleans up the first word of
236198943Sluigi * the new command in case it has been clobbered before.
236298943Sluigi */
236398943Sluigistatic ipfw_insn *
236498943Sluiginext_cmd(ipfw_insn *cmd)
236598943Sluigi{
236698943Sluigi	cmd += F_LEN(cmd);
236798943Sluigi	bzero(cmd, sizeof(*cmd));
236898943Sluigi	return cmd;
236998943Sluigi}
237098943Sluigi
237198943Sluigi/*
2372117469Sluigi * Takes arguments and copies them into a comment
2373117469Sluigi */
2374117469Sluigistatic void
2375204591Sluigifill_comment(ipfw_insn *cmd, char **av)
2376117469Sluigi{
2377117469Sluigi	int i, l;
2378117469Sluigi	char *p = (char *)(cmd + 1);
2379117577Sluigi
2380117469Sluigi	cmd->opcode = O_NOP;
2381117469Sluigi	cmd->len =  (cmd->len & (F_NOT | F_OR));
2382117469Sluigi
2383117469Sluigi	/* Compute length of comment string. */
2384204591Sluigi	for (i = 0, l = 0; av[i] != NULL; i++)
2385117469Sluigi		l += strlen(av[i]) + 1;
2386117469Sluigi	if (l == 0)
2387117469Sluigi		return;
2388117469Sluigi	if (l > 84)
2389117469Sluigi		errx(EX_DATAERR,
2390117469Sluigi		    "comment too long (max 80 chars)");
2391117469Sluigi	l = 1 + (l+3)/4;
2392117469Sluigi	cmd->len =  (cmd->len & (F_NOT | F_OR)) | l;
2393204591Sluigi	for (i = 0; av[i] != NULL; i++) {
2394117469Sluigi		strcpy(p, av[i]);
2395117469Sluigi		p += strlen(av[i]);
2396117469Sluigi		*p++ = ' ';
2397117469Sluigi	}
2398117469Sluigi	*(--p) = '\0';
2399117469Sluigi}
2400117577Sluigi
2401117469Sluigi/*
240298943Sluigi * A function to fill simple commands of size 1.
240398943Sluigi * Existing flags are preserved.
240498943Sluigi */
240598943Sluigistatic void
2406117328Sluigifill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg)
240798943Sluigi{
240898943Sluigi	cmd->opcode = opcode;
240998943Sluigi	cmd->len =  ((cmd->len | flags) & (F_NOT | F_OR)) | 1;
241098943Sluigi	cmd->arg1 = arg;
241198943Sluigi}
241298943Sluigi
241398943Sluigi/*
241498943Sluigi * Fetch and add the MAC address and type, with masks. This generates one or
241598943Sluigi * two microinstructions, and returns the pointer to the last one.
241698943Sluigi */
241798943Sluigistatic ipfw_insn *
2418204591Sluigiadd_mac(ipfw_insn *cmd, char *av[])
241998943Sluigi{
2420102087Sluigi	ipfw_insn_mac *mac;
242198943Sluigi
2422204591Sluigi	if ( ( av[0] == NULL ) || ( av[1] == NULL ) )
2423102098Sluigi		errx(EX_DATAERR, "MAC dst src");
242498943Sluigi
242598943Sluigi	cmd->opcode = O_MACADDR2;
242698943Sluigi	cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
242798943Sluigi
242898943Sluigi	mac = (ipfw_insn_mac *)cmd;
2429101978Sluigi	get_mac_addr_mask(av[0], mac->addr, mac->mask);	/* dst */
2430169424Smaxim	get_mac_addr_mask(av[1], &(mac->addr[ETHER_ADDR_LEN]),
2431169424Smaxim	    &(mac->mask[ETHER_ADDR_LEN])); /* src */
2432102087Sluigi	return cmd;
2433102087Sluigi}
243498943Sluigi
2435102087Sluigistatic ipfw_insn *
2436204591Sluigiadd_mactype(ipfw_insn *cmd, char *av)
2437102087Sluigi{
2438204591Sluigi	if (!av)
2439102087Sluigi		errx(EX_DATAERR, "missing MAC type");
2440102087Sluigi	if (strcmp(av, "any") != 0) { /* we have a non-null type */
2441102087Sluigi		fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE);
244298943Sluigi		cmd->opcode = O_MAC_TYPE;
2443102087Sluigi		return cmd;
2444102087Sluigi	} else
2445102087Sluigi		return NULL;
2446102087Sluigi}
244798943Sluigi
2448102087Sluigistatic ipfw_insn *
2449152923Sumeadd_proto0(ipfw_insn *cmd, char *av, u_char *protop)
2450102087Sluigi{
2451102087Sluigi	struct protoent *pe;
2452152923Sume	char *ep;
2453152923Sume	int proto;
2454102087Sluigi
2455156315Sume	proto = strtol(av, &ep, 10);
2456156315Sume	if (*ep != '\0' || proto <= 0) {
2457152923Sume		if ((pe = getprotobyname(av)) == NULL)
2458152923Sume			return NULL;
2459152923Sume		proto = pe->p_proto;
2460152923Sume	}
2461145246Sbrooks
2462152923Sume	fill_cmd(cmd, O_PROTO, 0, proto);
2463152923Sume	*protop = proto;
2464152923Sume	return cmd;
2465152923Sume}
2466152923Sume
2467152923Sumestatic ipfw_insn *
2468152923Sumeadd_proto(ipfw_insn *cmd, char *av, u_char *protop)
2469152923Sume{
2470152923Sume	u_char proto = IPPROTO_IP;
2471152923Sume
2472156315Sume	if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
2473146894Smlaier		; /* do not set O_IP4 nor O_IP6 */
2474152923Sume	else if (strcmp(av, "ip4") == 0)
2475152923Sume		/* explicit "just IPv4" rule */
2476152923Sume		fill_cmd(cmd, O_IP4, 0, 0);
2477152923Sume	else if (strcmp(av, "ip6") == 0) {
2478152923Sume		/* explicit "just IPv6" rule */
2479152923Sume		proto = IPPROTO_IPV6;
2480152923Sume		fill_cmd(cmd, O_IP6, 0, 0);
2481152923Sume	} else
2482152923Sume		return add_proto0(cmd, av, protop);
2483152923Sume
2484152923Sume	*protop = proto;
2485152923Sume	return cmd;
2486152923Sume}
2487152923Sume
2488152923Sumestatic ipfw_insn *
2489152923Sumeadd_proto_compat(ipfw_insn *cmd, char *av, u_char *protop)
2490152923Sume{
2491152923Sume	u_char proto = IPPROTO_IP;
2492152923Sume
2493152923Sume	if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
2494152923Sume		; /* do not set O_IP4 nor O_IP6 */
2495146894Smlaier	else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0)
2496146894Smlaier		/* explicit "just IPv4" rule */
2497146894Smlaier		fill_cmd(cmd, O_IP4, 0, 0);
2498146894Smlaier	else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) {
2499146894Smlaier		/* explicit "just IPv6" rule */
2500152923Sume		proto = IPPROTO_IPV6;
2501146894Smlaier		fill_cmd(cmd, O_IP6, 0, 0);
2502152923Sume	} else
2503152923Sume		return add_proto0(cmd, av, protop);
2504145246Sbrooks
2505152923Sume	*protop = proto;
250698943Sluigi	return cmd;
250798943Sluigi}
250898943Sluigi
2509102087Sluigistatic ipfw_insn *
2510102087Sluigiadd_srcip(ipfw_insn *cmd, char *av)
2511102087Sluigi{
2512102087Sluigi	fill_ip((ipfw_insn_ip *)cmd, av);
2513102087Sluigi	if (cmd->opcode == O_IP_DST_SET)			/* set */
2514102087Sluigi		cmd->opcode = O_IP_SRC_SET;
2515130281Sru	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
2516130281Sru		cmd->opcode = O_IP_SRC_LOOKUP;
2517102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
2518102087Sluigi		cmd->opcode = O_IP_SRC_ME;
2519102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
2520102087Sluigi		cmd->opcode = O_IP_SRC;
2521117328Sluigi	else							/* addr/mask */
2522102087Sluigi		cmd->opcode = O_IP_SRC_MASK;
2523102087Sluigi	return cmd;
2524102087Sluigi}
2525102087Sluigi
2526102087Sluigistatic ipfw_insn *
2527102087Sluigiadd_dstip(ipfw_insn *cmd, char *av)
2528102087Sluigi{
2529102087Sluigi	fill_ip((ipfw_insn_ip *)cmd, av);
2530102087Sluigi	if (cmd->opcode == O_IP_DST_SET)			/* set */
2531102087Sluigi		;
2532130281Sru	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
2533130281Sru		;
2534102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
2535102087Sluigi		cmd->opcode = O_IP_DST_ME;
2536102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
2537102087Sluigi		cmd->opcode = O_IP_DST;
2538117328Sluigi	else							/* addr/mask */
2539102087Sluigi		cmd->opcode = O_IP_DST_MASK;
2540102087Sluigi	return cmd;
2541102087Sluigi}
2542102087Sluigi
2543102087Sluigistatic ipfw_insn *
2544102087Sluigiadd_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
2545102087Sluigi{
2546204591Sluigi	/* XXX "any" is trapped before. Perhaps "to" */
2547140271Sbrooks	if (_substrcmp(av, "any") == 0) {
2548102087Sluigi		return NULL;
2549102087Sluigi	} else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) {
2550102087Sluigi		/* XXX todo: check that we have a protocol with ports */
2551102087Sluigi		cmd->opcode = opcode;
2552102087Sluigi		return cmd;
2553102087Sluigi	}
2554102087Sluigi	return NULL;
2555102087Sluigi}
2556102087Sluigi
2557145246Sbrooksstatic ipfw_insn *
2558145246Sbrooksadd_src(ipfw_insn *cmd, char *av, u_char proto)
2559145246Sbrooks{
2560145246Sbrooks	struct in6_addr a;
2561158553Smlaier	char *host, *ch;
2562158553Smlaier	ipfw_insn *ret = NULL;
2563145246Sbrooks
2564158553Smlaier	if ((host = strdup(av)) == NULL)
2565158553Smlaier		return NULL;
2566158553Smlaier	if ((ch = strrchr(host, '/')) != NULL)
2567158553Smlaier		*ch = '\0';
2568158553Smlaier
2569145246Sbrooks	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
2570204591Sluigi	    inet_pton(AF_INET6, host, &a) == 1)
2571158553Smlaier		ret = add_srcip6(cmd, av);
2572145246Sbrooks	/* XXX: should check for IPv4, not !IPv6 */
2573161483Sdwmalone	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
2574204591Sluigi	    inet_pton(AF_INET6, host, &a) != 1))
2575158553Smlaier		ret = add_srcip(cmd, av);
2576161483Sdwmalone	if (ret == NULL && strcmp(av, "any") != 0)
2577158553Smlaier		ret = cmd;
2578145246Sbrooks
2579158553Smlaier	free(host);
2580158553Smlaier	return ret;
2581145246Sbrooks}
2582145246Sbrooks
2583145246Sbrooksstatic ipfw_insn *
2584145246Sbrooksadd_dst(ipfw_insn *cmd, char *av, u_char proto)
2585145246Sbrooks{
2586145246Sbrooks	struct in6_addr a;
2587158553Smlaier	char *host, *ch;
2588158553Smlaier	ipfw_insn *ret = NULL;
2589145246Sbrooks
2590158553Smlaier	if ((host = strdup(av)) == NULL)
2591158553Smlaier		return NULL;
2592158553Smlaier	if ((ch = strrchr(host, '/')) != NULL)
2593158553Smlaier		*ch = '\0';
2594158553Smlaier
2595145246Sbrooks	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
2596204591Sluigi	    inet_pton(AF_INET6, host, &a) == 1)
2597158553Smlaier		ret = add_dstip6(cmd, av);
2598145246Sbrooks	/* XXX: should check for IPv4, not !IPv6 */
2599161483Sdwmalone	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
2600204591Sluigi	    inet_pton(AF_INET6, host, &a) != 1))
2601158553Smlaier		ret = add_dstip(cmd, av);
2602161483Sdwmalone	if (ret == NULL && strcmp(av, "any") != 0)
2603158553Smlaier		ret = cmd;
2604145246Sbrooks
2605158553Smlaier	free(host);
2606158553Smlaier	return ret;
2607145246Sbrooks}
2608145246Sbrooks
260998943Sluigi/*
261098943Sluigi * Parse arguments and assemble the microinstructions which make up a rule.
261198943Sluigi * Rules are added into the 'rulebuf' and then copied in the correct order
261298943Sluigi * into the actual rule.
261398943Sluigi *
2614136071Sgreen * The syntax for a rule starts with the action, followed by
2615136071Sgreen * optional action parameters, and the various match patterns.
2616108533Sschweikh * In the assembled microcode, the first opcode must be an O_PROBE_STATE
261798943Sluigi * (generated if the rule includes a keep-state option), then the
2618136071Sgreen * various match patterns, log/altq actions, and the actual action.
2619106505Smaxim *
262098943Sluigi */
2621187767Sluigivoid
2622204591Sluigiipfw_add(char *av[])
262398943Sluigi{
262498943Sluigi	/*
262598943Sluigi	 * rules are added into the 'rulebuf' and then copied in
262698943Sluigi	 * the correct order into the actual rule.
262798943Sluigi	 * Some things that need to go out of order (prob, action etc.)
262898943Sluigi	 * go into actbuf[].
262998943Sluigi	 */
2630117328Sluigi	static uint32_t rulebuf[255], actbuf[255], cmdbuf[255];
263198943Sluigi
2632117469Sluigi	ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
2633102087Sluigi	ipfw_insn *first_cmd;	/* first match pattern */
263498943Sluigi
263598943Sluigi	struct ip_fw *rule;
263698943Sluigi
263798943Sluigi	/*
263898943Sluigi	 * various flags used to record that we entered some fields.
263998943Sluigi	 */
2640101116Sluigi	ipfw_insn *have_state = NULL;	/* check-state or keep-state */
2641158879Soleg	ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL;
2642134475Smaxim	size_t len;
264398943Sluigi
264498943Sluigi	int i;
264598943Sluigi
264698943Sluigi	int open_par = 0;	/* open parenthesis ( */
264798943Sluigi
264898943Sluigi	/* proto is here because it is used to fetch ports */
264998943Sluigi	u_char proto = IPPROTO_IP;	/* default protocol */
265098943Sluigi
2651107289Sluigi	double match_prob = 1; /* match probability, default is always match */
2652107289Sluigi
265398943Sluigi	bzero(actbuf, sizeof(actbuf));		/* actions go here */
265498943Sluigi	bzero(cmdbuf, sizeof(cmdbuf));
265598943Sluigi	bzero(rulebuf, sizeof(rulebuf));
265698943Sluigi
265798943Sluigi	rule = (struct ip_fw *)rulebuf;
265898943Sluigi	cmd = (ipfw_insn *)cmdbuf;
265998943Sluigi	action = (ipfw_insn *)actbuf;
266098943Sluigi
2661204591Sluigi	av++;
266298943Sluigi
266398943Sluigi	/* [rule N]	-- Rule number optional */
2664204591Sluigi	if (av[0] && isdigit(**av)) {
266598943Sluigi		rule->rulenum = atoi(*av);
266698943Sluigi		av++;
266798943Sluigi	}
266898943Sluigi
2669117655Sluigi	/* [set N]	-- set number (0..RESVD_SET), optional */
2670205631Sluigi	if (av[0] && av[1] && _substrcmp(*av, "set") == 0) {
2671101628Sluigi		int set = strtoul(av[1], NULL, 10);
2672117655Sluigi		if (set < 0 || set > RESVD_SET)
2673101628Sluigi			errx(EX_DATAERR, "illegal set %s", av[1]);
2674101628Sluigi		rule->set = set;
2675204591Sluigi		av += 2;
2676101628Sluigi	}
2677101628Sluigi
267898943Sluigi	/* [prob D]	-- match probability, optional */
2679204591Sluigi	if (av[0] && av[1] && _substrcmp(*av, "prob") == 0) {
2680107289Sluigi		match_prob = strtod(av[1], NULL);
268198943Sluigi
2682107289Sluigi		if (match_prob <= 0 || match_prob > 1)
268398943Sluigi			errx(EX_DATAERR, "illegal match prob. %s", av[1]);
2684204591Sluigi		av += 2;
268598943Sluigi	}
268698943Sluigi
268798943Sluigi	/* action	-- mandatory */
268898943Sluigi	NEED1("missing action");
268998943Sluigi	i = match_token(rule_actions, *av);
2690204591Sluigi	av++;
269198943Sluigi	action->len = 1;	/* default */
269298943Sluigi	switch(i) {
269398943Sluigi	case TOK_CHECKSTATE:
2694101116Sluigi		have_state = action;
269598943Sluigi		action->opcode = O_CHECK_STATE;
269698943Sluigi		break;
269798943Sluigi
269898943Sluigi	case TOK_ACCEPT:
269998943Sluigi		action->opcode = O_ACCEPT;
270098943Sluigi		break;
270198943Sluigi
270298943Sluigi	case TOK_DENY:
270398943Sluigi		action->opcode = O_DENY;
270499475Sluigi		action->arg1 = 0;
270598943Sluigi		break;
270698943Sluigi
270799475Sluigi	case TOK_REJECT:
270899475Sluigi		action->opcode = O_REJECT;
270999475Sluigi		action->arg1 = ICMP_UNREACH_HOST;
271099475Sluigi		break;
271199475Sluigi
271299475Sluigi	case TOK_RESET:
271399475Sluigi		action->opcode = O_REJECT;
271499475Sluigi		action->arg1 = ICMP_REJECT_RST;
271599475Sluigi		break;
271699475Sluigi
2717149020Sbz	case TOK_RESET6:
2718149020Sbz		action->opcode = O_UNREACH6;
2719149020Sbz		action->arg1 = ICMP6_UNREACH_RST;
2720149020Sbz		break;
2721149020Sbz
272299475Sluigi	case TOK_UNREACH:
272399475Sluigi		action->opcode = O_REJECT;
272499475Sluigi		NEED1("missing reject code");
272599475Sluigi		fill_reject_code(&action->arg1, *av);
2726204591Sluigi		av++;
272799475Sluigi		break;
272899475Sluigi
2729149020Sbz	case TOK_UNREACH6:
2730149020Sbz		action->opcode = O_UNREACH6;
2731149020Sbz		NEED1("missing unreach code");
2732149020Sbz		fill_unreach6_code(&action->arg1, *av);
2733204591Sluigi		av++;
2734149020Sbz		break;
2735149020Sbz
273698943Sluigi	case TOK_COUNT:
273798943Sluigi		action->opcode = O_COUNT;
273898943Sluigi		break;
273998943Sluigi
2740176517Spiso	case TOK_NAT:
2741176517Spiso 		action->opcode = O_NAT;
2742176517Spiso 		action->len = F_INSN_SIZE(ipfw_insn_nat);
2743176517Spiso		goto chkarg;
2744178888Sjulian
274598943Sluigi	case TOK_QUEUE:
2746153374Sglebius		action->opcode = O_QUEUE;
2747153374Sglebius		goto chkarg;
274898943Sluigi	case TOK_PIPE:
2749153374Sglebius		action->opcode = O_PIPE;
2750153374Sglebius		goto chkarg;
275198943Sluigi	case TOK_SKIPTO:
2752153374Sglebius		action->opcode = O_SKIPTO;
2753153374Sglebius		goto chkarg;
2754153374Sglebius	case TOK_NETGRAPH:
2755153374Sglebius		action->opcode = O_NETGRAPH;
2756153374Sglebius		goto chkarg;
2757153374Sglebius	case TOK_NGTEE:
2758153374Sglebius		action->opcode = O_NGTEE;
2759153374Sglebius		goto chkarg;
276098943Sluigi	case TOK_DIVERT:
2761153374Sglebius		action->opcode = O_DIVERT;
2762153374Sglebius		goto chkarg;
276398943Sluigi	case TOK_TEE:
2764153374Sglebius		action->opcode = O_TEE;
2765153374Sglebiuschkarg:
2766204591Sluigi		if (!av[0])
2767153374Sglebius			errx(EX_USAGE, "missing argument for %s", *(av - 1));
2768153374Sglebius		if (isdigit(**av)) {
2769153374Sglebius			action->arg1 = strtoul(*av, NULL, 10);
2770153374Sglebius			if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG)
2771153374Sglebius				errx(EX_DATAERR, "illegal argument for %s",
2772153374Sglebius				    *(av - 1));
2773187762Sluigi		} else if (_substrcmp(*av, "tablearg") == 0) {
2774153374Sglebius			action->arg1 = IP_FW_TABLEARG;
2775153374Sglebius		} else if (i == TOK_DIVERT || i == TOK_TEE) {
277698943Sluigi			struct servent *s;
277798943Sluigi			setservent(1);
277898943Sluigi			s = getservbyname(av[0], "divert");
277998943Sluigi			if (s != NULL)
278098943Sluigi				action->arg1 = ntohs(s->s_port);
278198943Sluigi			else
278298943Sluigi				errx(EX_DATAERR, "illegal divert/tee port");
2783153374Sglebius		} else
2784153374Sglebius			errx(EX_DATAERR, "illegal argument for %s", *(av - 1));
2785204591Sluigi		av++;
278698943Sluigi		break;
278798943Sluigi
278898943Sluigi	case TOK_FORWARD: {
278998943Sluigi		ipfw_insn_sa *p = (ipfw_insn_sa *)action;
279098943Sluigi		char *s, *end;
279198943Sluigi
279298943Sluigi		NEED1("missing forward address[:port]");
279398943Sluigi
279498943Sluigi		action->opcode = O_FORWARD_IP;
279598943Sluigi		action->len = F_INSN_SIZE(ipfw_insn_sa);
279698943Sluigi
2797188005Sluigi		/*
2798188005Sluigi		 * In the kernel we assume AF_INET and use only
2799200183Sluigi		 * sin_port and sin_addr. Remember to set sin_len as
2800200183Sluigi		 * the routing code seems to use it too.
2801188005Sluigi		 */
280298943Sluigi		p->sa.sin_family = AF_INET;
2803200183Sluigi		p->sa.sin_len = sizeof(struct sockaddr_in);
280498943Sluigi		p->sa.sin_port = 0;
280598943Sluigi		/*
280698943Sluigi		 * locate the address-port separator (':' or ',')
280798943Sluigi		 */
280898943Sluigi		s = strchr(*av, ':');
280998943Sluigi		if (s == NULL)
281098943Sluigi			s = strchr(*av, ',');
281198943Sluigi		if (s != NULL) {
281298943Sluigi			*(s++) = '\0';
281398943Sluigi			i = strtoport(s, &end, 0 /* base */, 0 /* proto */);
281498943Sluigi			if (s == end)
281598943Sluigi				errx(EX_DATAERR,
281698943Sluigi				    "illegal forwarding port ``%s''", s);
2817103241Sluigi			p->sa.sin_port = (u_short)i;
281898943Sluigi		}
2819220802Sglebius		if (_substrcmp(*av, "tablearg") == 0)
2820161456Sjulian			p->sa.sin_addr.s_addr = INADDR_ANY;
2821161456Sjulian		else
2822161424Sjulian			lookup_host(*av, &(p->sa.sin_addr));
2823204591Sluigi		av++;
282498943Sluigi		break;
2825161424Sjulian	    }
2826117469Sluigi	case TOK_COMMENT:
2827117469Sluigi		/* pretend it is a 'count' rule followed by the comment */
2828117469Sluigi		action->opcode = O_COUNT;
2829204591Sluigi		av--;		/* go back... */
2830117469Sluigi		break;
2831178888Sjulian
2832178888Sjulian	case TOK_SETFIB:
2833178888Sjulian	    {
2834178888Sjulian		int numfibs;
2835178916Sjulian		size_t intsize = sizeof(int);
2836178888Sjulian
2837178888Sjulian		action->opcode = O_SETFIB;
2838178888Sjulian 		NEED1("missing fib number");
2839220802Sglebius 		action->arg1 = strtoul(*av, NULL, 10);
2840178916Sjulian		if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1)
2841178888Sjulian			errx(EX_DATAERR, "fibs not suported.\n");
2842178888Sjulian		if (action->arg1 >= numfibs)  /* Temporary */
2843178888Sjulian			errx(EX_DATAERR, "fib too large.\n");
2844204591Sluigi 		av++;
2845178888Sjulian 		break;
2846178888Sjulian	    }
2847190633Spiso
2848190633Spiso	case TOK_REASS:
2849190633Spiso		action->opcode = O_REASS;
2850190633Spiso		break;
2851165648Spiso
285298943Sluigi	default:
2853102087Sluigi		errx(EX_DATAERR, "invalid action %s\n", av[-1]);
285498943Sluigi	}
285598943Sluigi	action = next_cmd(action);
285698943Sluigi
285798943Sluigi	/*
2858136071Sgreen	 * [altq queuename] -- altq tag, optional
285998943Sluigi	 * [log [logamount N]]	-- log, optional
286098943Sluigi	 *
2861136071Sgreen	 * If they exist, it go first in the cmdbuf, but then it is
286298943Sluigi	 * skipped in the copy section to the end of the buffer.
286398943Sluigi	 */
2864204591Sluigi	while (av[0] != NULL && (i = match_token(rule_action_params, *av)) != -1) {
2865204591Sluigi		av++;
2866136071Sgreen		switch (i) {
2867136071Sgreen		case TOK_LOG:
2868136071Sgreen		    {
2869136071Sgreen			ipfw_insn_log *c = (ipfw_insn_log *)cmd;
2870136071Sgreen			int l;
287198943Sluigi
2872136071Sgreen			if (have_log)
2873136071Sgreen				errx(EX_DATAERR,
2874136071Sgreen				    "log cannot be specified more than once");
2875136071Sgreen			have_log = (ipfw_insn *)c;
2876136071Sgreen			cmd->len = F_INSN_SIZE(ipfw_insn_log);
2877136071Sgreen			cmd->opcode = O_LOG;
2878204591Sluigi			if (av[0] && _substrcmp(*av, "logamount") == 0) {
2879204591Sluigi				av++;
2880136071Sgreen				NEED1("logamount requires argument");
2881136071Sgreen				l = atoi(*av);
2882136071Sgreen				if (l < 0)
2883136071Sgreen					errx(EX_DATAERR,
2884136071Sgreen					    "logamount must be positive");
2885136071Sgreen				c->max_log = l;
2886204591Sluigi				av++;
2887136071Sgreen			} else {
2888136071Sgreen				len = sizeof(c->max_log);
2889136071Sgreen				if (sysctlbyname("net.inet.ip.fw.verbose_limit",
2890136071Sgreen				    &c->max_log, &len, NULL, 0) == -1)
2891136071Sgreen					errx(1, "sysctlbyname(\"%s\")",
2892136071Sgreen					    "net.inet.ip.fw.verbose_limit");
2893136071Sgreen			}
2894136071Sgreen		    }
2895136071Sgreen			break;
2896136071Sgreen
2897204591Sluigi#ifndef NO_ALTQ
2898136071Sgreen		case TOK_ALTQ:
2899136071Sgreen		    {
2900136071Sgreen			ipfw_insn_altq *a = (ipfw_insn_altq *)cmd;
2901136071Sgreen
2902136071Sgreen			NEED1("missing altq queue name");
2903136071Sgreen			if (have_altq)
2904136071Sgreen				errx(EX_DATAERR,
2905136071Sgreen				    "altq cannot be specified more than once");
2906136071Sgreen			have_altq = (ipfw_insn *)a;
2907136071Sgreen			cmd->len = F_INSN_SIZE(ipfw_insn_altq);
2908136071Sgreen			cmd->opcode = O_ALTQ;
2909187983Sluigi			a->qid = altq_name_to_qid(*av);
2910204591Sluigi			av++;
2911136071Sgreen		    }
2912136071Sgreen			break;
2913204591Sluigi#endif
2914136071Sgreen
2915158879Soleg		case TOK_TAG:
2916159636Soleg		case TOK_UNTAG: {
2917159636Soleg			uint16_t tag;
2918159636Soleg
2919158879Soleg			if (have_tag)
2920159636Soleg				errx(EX_USAGE, "tag and untag cannot be "
2921159636Soleg				    "specified more than once");
2922193516Sluigi			GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX, i,
2923182823Srik			   rule_action_params);
2924158879Soleg			have_tag = cmd;
2925159636Soleg			fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag);
2926204591Sluigi			av++;
2927158879Soleg			break;
2928159636Soleg		}
2929158879Soleg
2930136071Sgreen		default:
2931136071Sgreen			abort();
293298943Sluigi		}
293398943Sluigi		cmd = next_cmd(cmd);
293498943Sluigi	}
293598943Sluigi
2936101116Sluigi	if (have_state)	/* must be a check-state, we are done */
293798943Sluigi		goto done;
293898943Sluigi
293998943Sluigi#define OR_START(target)					\
2940204591Sluigi	if (av[0] && (*av[0] == '(' || *av[0] == '{')) { 	\
294198943Sluigi		if (open_par)					\
294298943Sluigi			errx(EX_USAGE, "nested \"(\" not allowed\n"); \
2943101641Sluigi		prev = NULL;					\
294498943Sluigi		open_par = 1;					\
294598943Sluigi		if ( (av[0])[1] == '\0') {			\
2946204591Sluigi			av++;					\
294798943Sluigi		} else						\
294898943Sluigi			(*av)++;				\
294998943Sluigi	}							\
295098943Sluigi	target:							\
295198943Sluigi
295298943Sluigi
295398943Sluigi#define	CLOSE_PAR						\
295498943Sluigi	if (open_par) {						\
2955204591Sluigi		if (av[0] && (					\
2956140271Sbrooks		    strcmp(*av, ")") == 0 ||			\
2957140271Sbrooks		    strcmp(*av, "}") == 0)) {			\
2958101641Sluigi			prev = NULL;				\
295998943Sluigi			open_par = 0;				\
2960204591Sluigi			av++;					\
296198943Sluigi		} else						\
296298943Sluigi			errx(EX_USAGE, "missing \")\"\n");	\
296398943Sluigi	}
2964106505Smaxim
296598943Sluigi#define NOT_BLOCK						\
2966204591Sluigi	if (av[0] && _substrcmp(*av, "not") == 0) {		\
296798943Sluigi		if (cmd->len & F_NOT)				\
296898943Sluigi			errx(EX_USAGE, "double \"not\" not allowed\n"); \
296998943Sluigi		cmd->len |= F_NOT;				\
2970204591Sluigi		av++;						\
297198943Sluigi	}
297298943Sluigi
297398943Sluigi#define OR_BLOCK(target)					\
2974204591Sluigi	if (av[0] && _substrcmp(*av, "or") == 0) {		\
297598943Sluigi		if (prev == NULL || open_par == 0)		\
297698943Sluigi			errx(EX_DATAERR, "invalid OR block");	\
297798943Sluigi		prev->len |= F_OR;				\
2978204591Sluigi		av++;					\
297998943Sluigi		goto target;					\
298098943Sluigi	}							\
298198943Sluigi	CLOSE_PAR;
298298943Sluigi
2983102087Sluigi	first_cmd = cmd;
2984102098Sluigi
2985102098Sluigi#if 0
298698943Sluigi	/*
2987102087Sluigi	 * MAC addresses, optional.
2988102087Sluigi	 * If we have this, we skip the part "proto from src to dst"
2989102087Sluigi	 * and jump straight to the option parsing.
2990102087Sluigi	 */
2991102087Sluigi	NOT_BLOCK;
2992102087Sluigi	NEED1("missing protocol");
2993140271Sbrooks	if (_substrcmp(*av, "MAC") == 0 ||
2994140271Sbrooks	    _substrcmp(*av, "mac") == 0) {
2995204591Sluigi		av++;			/* the "MAC" keyword */
2996204591Sluigi		add_mac(cmd, av);	/* exits in case of errors */
2997102087Sluigi		cmd = next_cmd(cmd);
2998204591Sluigi		av += 2;		/* dst-mac and src-mac */
2999102087Sluigi		NOT_BLOCK;
3000102087Sluigi		NEED1("missing mac type");
3001204591Sluigi		if (add_mactype(cmd, av[0]))
3002102087Sluigi			cmd = next_cmd(cmd);
3003204591Sluigi		av++;			/* any or mac-type */
3004102087Sluigi		goto read_options;
3005102087Sluigi	}
3006102098Sluigi#endif
3007102087Sluigi
3008102087Sluigi	/*
300998943Sluigi	 * protocol, mandatory
301098943Sluigi	 */
301198943Sluigi    OR_START(get_proto);
301298943Sluigi	NOT_BLOCK;
301398943Sluigi	NEED1("missing protocol");
3014152923Sume	if (add_proto_compat(cmd, *av, &proto)) {
3015204591Sluigi		av++;
3016147105Smlaier		if (F_LEN(cmd) != 0) {
3017102087Sluigi			prev = cmd;
3018102087Sluigi			cmd = next_cmd(cmd);
3019102087Sluigi		}
3020102098Sluigi	} else if (first_cmd != cmd) {
3021116438Smaxim		errx(EX_DATAERR, "invalid protocol ``%s''", *av);
3022102098Sluigi	} else
3023102098Sluigi		goto read_options;
302498943Sluigi    OR_BLOCK(get_proto);
302598943Sluigi
302698943Sluigi	/*
3027102087Sluigi	 * "from", mandatory
302898943Sluigi	 */
3029204591Sluigi	if ((av[0] == NULL) || _substrcmp(*av, "from") != 0)
303098943Sluigi		errx(EX_USAGE, "missing ``from''");
3031204591Sluigi	av++;
303298943Sluigi
303398943Sluigi	/*
303498943Sluigi	 * source IP, mandatory
303598943Sluigi	 */
303698943Sluigi    OR_START(source_ip);
303798943Sluigi	NOT_BLOCK;	/* optional "not" */
303898943Sluigi	NEED1("missing source address");
3039145246Sbrooks	if (add_src(cmd, *av, proto)) {
3040204591Sluigi		av++;
3041102087Sluigi		if (F_LEN(cmd) != 0) {	/* ! any */
3042102087Sluigi			prev = cmd;
3043102087Sluigi			cmd = next_cmd(cmd);
3044102087Sluigi		}
3045145246Sbrooks	} else
3046145246Sbrooks		errx(EX_USAGE, "bad source address %s", *av);
304798943Sluigi    OR_BLOCK(source_ip);
304898943Sluigi
304998943Sluigi	/*
305098943Sluigi	 * source ports, optional
305198943Sluigi	 */
305298943Sluigi	NOT_BLOCK;	/* optional "not" */
3053204591Sluigi	if ( av[0] != NULL ) {
3054140271Sbrooks		if (_substrcmp(*av, "any") == 0 ||
3055102087Sluigi		    add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
3056204591Sluigi			av++;
3057102087Sluigi			if (F_LEN(cmd) != 0)
3058102087Sluigi				cmd = next_cmd(cmd);
3059101641Sluigi		}
306098943Sluigi	}
306198943Sluigi
306298943Sluigi	/*
3063102087Sluigi	 * "to", mandatory
306498943Sluigi	 */
3065204591Sluigi	if ( (av[0] == NULL) || _substrcmp(*av, "to") != 0 )
306698943Sluigi		errx(EX_USAGE, "missing ``to''");
3067204591Sluigi	av++;
306898943Sluigi
306998943Sluigi	/*
307098943Sluigi	 * destination, mandatory
307198943Sluigi	 */
307298943Sluigi    OR_START(dest_ip);
307398943Sluigi	NOT_BLOCK;	/* optional "not" */
307498943Sluigi	NEED1("missing dst address");
3075145246Sbrooks	if (add_dst(cmd, *av, proto)) {
3076204591Sluigi		av++;
3077102087Sluigi		if (F_LEN(cmd) != 0) {	/* ! any */
3078102087Sluigi			prev = cmd;
3079102087Sluigi			cmd = next_cmd(cmd);
3080102087Sluigi		}
3081145246Sbrooks	} else
3082145246Sbrooks		errx( EX_USAGE, "bad destination address %s", *av);
308398943Sluigi    OR_BLOCK(dest_ip);
308498943Sluigi
308598943Sluigi	/*
308698943Sluigi	 * dest. ports, optional
308798943Sluigi	 */
308898943Sluigi	NOT_BLOCK;	/* optional "not" */
3089204591Sluigi	if (av[0]) {
3090140271Sbrooks		if (_substrcmp(*av, "any") == 0 ||
3091102087Sluigi		    add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
3092204591Sluigi			av++;
3093102087Sluigi			if (F_LEN(cmd) != 0)
3094102087Sluigi				cmd = next_cmd(cmd);
3095101641Sluigi		}
309698943Sluigi	}
309798943Sluigi
309898943Sluigiread_options:
3099204591Sluigi	if (av[0] && first_cmd == cmd) {
3100102087Sluigi		/*
3101102087Sluigi		 * nothing specified so far, store in the rule to ease
3102102087Sluigi		 * printout later.
3103102087Sluigi		 */
3104102087Sluigi		 rule->_pad = 1;
3105102087Sluigi	}
310698943Sluigi	prev = NULL;
3107204591Sluigi	while ( av[0] != NULL ) {
3108101641Sluigi		char *s;
3109101641Sluigi		ipfw_insn_u32 *cmd32;	/* alias for cmd */
311098943Sluigi
3111101641Sluigi		s = *av;
3112101641Sluigi		cmd32 = (ipfw_insn_u32 *)cmd;
3113101641Sluigi
311498943Sluigi		if (*s == '!') {	/* alternate syntax for NOT */
311598943Sluigi			if (cmd->len & F_NOT)
311698943Sluigi				errx(EX_USAGE, "double \"not\" not allowed\n");
311798943Sluigi			cmd->len = F_NOT;
311898943Sluigi			s++;
311998943Sluigi		}
312098943Sluigi		i = match_token(rule_options, s);
3121204591Sluigi		av++;
312298943Sluigi		switch(i) {
312398943Sluigi		case TOK_NOT:
312498943Sluigi			if (cmd->len & F_NOT)
312598943Sluigi				errx(EX_USAGE, "double \"not\" not allowed\n");
312698943Sluigi			cmd->len = F_NOT;
312798943Sluigi			break;
312898943Sluigi
312998943Sluigi		case TOK_OR:
3130101641Sluigi			if (open_par == 0 || prev == NULL)
313198943Sluigi				errx(EX_USAGE, "invalid \"or\" block\n");
313298943Sluigi			prev->len |= F_OR;
313398943Sluigi			break;
3134101641Sluigi
3135101641Sluigi		case TOK_STARTBRACE:
3136101641Sluigi			if (open_par)
3137101641Sluigi				errx(EX_USAGE, "+nested \"(\" not allowed\n");
3138101641Sluigi			open_par = 1;
3139101641Sluigi			break;
3140101641Sluigi
3141101641Sluigi		case TOK_ENDBRACE:
3142101641Sluigi			if (!open_par)
3143101641Sluigi				errx(EX_USAGE, "+missing \")\"\n");
3144101641Sluigi			open_par = 0;
3145102087Sluigi			prev = NULL;
3146220802Sglebius			break;
3147101641Sluigi
314898943Sluigi		case TOK_IN:
314998943Sluigi			fill_cmd(cmd, O_IN, 0, 0);
315098943Sluigi			break;
315198943Sluigi
315298943Sluigi		case TOK_OUT:
315398943Sluigi			cmd->len ^= F_NOT; /* toggle F_NOT */
315498943Sluigi			fill_cmd(cmd, O_IN, 0, 0);
315598943Sluigi			break;
315698943Sluigi
3157136073Sgreen		case TOK_DIVERTED:
3158136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 3);
3159136073Sgreen			break;
3160136073Sgreen
3161136073Sgreen		case TOK_DIVERTEDLOOPBACK:
3162136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 1);
3163136073Sgreen			break;
3164136073Sgreen
3165136073Sgreen		case TOK_DIVERTEDOUTPUT:
3166136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 2);
3167136073Sgreen			break;
3168136073Sgreen
316998943Sluigi		case TOK_FRAG:
317098943Sluigi			fill_cmd(cmd, O_FRAG, 0, 0);
317198943Sluigi			break;
317298943Sluigi
317398943Sluigi		case TOK_LAYER2:
317498943Sluigi			fill_cmd(cmd, O_LAYER2, 0, 0);
317598943Sluigi			break;
317698943Sluigi
317798943Sluigi		case TOK_XMIT:
317898943Sluigi		case TOK_RECV:
317998943Sluigi		case TOK_VIA:
318098943Sluigi			NEED1("recv, xmit, via require interface name"
318198943Sluigi				" or address");
318298943Sluigi			fill_iface((ipfw_insn_if *)cmd, av[0]);
3183204591Sluigi			av++;
318498943Sluigi			if (F_LEN(cmd) == 0)	/* not a valid address */
318598943Sluigi				break;
318698943Sluigi			if (i == TOK_XMIT)
318798943Sluigi				cmd->opcode = O_XMIT;
318898943Sluigi			else if (i == TOK_RECV)
318998943Sluigi				cmd->opcode = O_RECV;
319098943Sluigi			else if (i == TOK_VIA)
319198943Sluigi				cmd->opcode = O_VIA;
319298943Sluigi			break;
319398943Sluigi
319499475Sluigi		case TOK_ICMPTYPES:
319599475Sluigi			NEED1("icmptypes requires list of types");
319699475Sluigi			fill_icmptypes((ipfw_insn_u32 *)cmd, *av);
3197204591Sluigi			av++;
319899475Sluigi			break;
3199145246Sbrooks
3200145246Sbrooks		case TOK_ICMP6TYPES:
3201145246Sbrooks			NEED1("icmptypes requires list of types");
3202145246Sbrooks			fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av);
3203204591Sluigi			av++;
3204145246Sbrooks			break;
320599475Sluigi
320698943Sluigi		case TOK_IPTTL:
320798943Sluigi			NEED1("ipttl requires TTL");
3208116690Sluigi			if (strpbrk(*av, "-,")) {
3209116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPTTL))
3210116690Sluigi				errx(EX_DATAERR, "invalid ipttl %s", *av);
3211116690Sluigi			} else
3212116690Sluigi			    fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
3213204591Sluigi			av++;
321498943Sluigi			break;
321598943Sluigi
321698943Sluigi		case TOK_IPID:
3217116690Sluigi			NEED1("ipid requires id");
3218116690Sluigi			if (strpbrk(*av, "-,")) {
3219116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPID))
3220116690Sluigi				errx(EX_DATAERR, "invalid ipid %s", *av);
3221116690Sluigi			} else
3222116690Sluigi			    fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0));
3223204591Sluigi			av++;
322498943Sluigi			break;
322598943Sluigi
322698943Sluigi		case TOK_IPLEN:
322798943Sluigi			NEED1("iplen requires length");
3228116690Sluigi			if (strpbrk(*av, "-,")) {
3229116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPLEN))
3230116690Sluigi				errx(EX_DATAERR, "invalid ip len %s", *av);
3231116690Sluigi			} else
3232116690Sluigi			    fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0));
3233204591Sluigi			av++;
323498943Sluigi			break;
323598943Sluigi
323698943Sluigi		case TOK_IPVER:
323798943Sluigi			NEED1("ipver requires version");
323898943Sluigi			fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0));
3239204591Sluigi			av++;
324098943Sluigi			break;
324198943Sluigi
324299475Sluigi		case TOK_IPPRECEDENCE:
324399475Sluigi			NEED1("ipprecedence requires value");
324499475Sluigi			fill_cmd(cmd, O_IPPRECEDENCE, 0,
324599475Sluigi			    (strtoul(*av, NULL, 0) & 7) << 5);
3246204591Sluigi			av++;
324799475Sluigi			break;
324899475Sluigi
324998943Sluigi		case TOK_IPOPTS:
325098943Sluigi			NEED1("missing argument for ipoptions");
3251101116Sluigi			fill_flags(cmd, O_IPOPT, f_ipopts, *av);
3252204591Sluigi			av++;
325398943Sluigi			break;
325498943Sluigi
325599475Sluigi		case TOK_IPTOS:
325699475Sluigi			NEED1("missing argument for iptos");
3257101116Sluigi			fill_flags(cmd, O_IPTOS, f_iptos, *av);
3258204591Sluigi			av++;
325999475Sluigi			break;
326099475Sluigi
326198943Sluigi		case TOK_UID:
326298943Sluigi			NEED1("uid requires argument");
326398943Sluigi		    {
326498943Sluigi			char *end;
326598943Sluigi			uid_t uid;
326698943Sluigi			struct passwd *pwd;
326798943Sluigi
326898943Sluigi			cmd->opcode = O_UID;
326998943Sluigi			uid = strtoul(*av, &end, 0);
327098943Sluigi			pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av);
327198943Sluigi			if (pwd == NULL)
327298943Sluigi				errx(EX_DATAERR, "uid \"%s\" nonexistent", *av);
3273106504Smaxim			cmd32->d[0] = pwd->pw_uid;
3274135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
3275204591Sluigi			av++;
327698943Sluigi		    }
327798943Sluigi			break;
327898943Sluigi
327998943Sluigi		case TOK_GID:
328098943Sluigi			NEED1("gid requires argument");
328198943Sluigi		    {
328298943Sluigi			char *end;
328398943Sluigi			gid_t gid;
328498943Sluigi			struct group *grp;
328598943Sluigi
328698943Sluigi			cmd->opcode = O_GID;
328798943Sluigi			gid = strtoul(*av, &end, 0);
328898943Sluigi			grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av);
328998943Sluigi			if (grp == NULL)
329098943Sluigi				errx(EX_DATAERR, "gid \"%s\" nonexistent", *av);
3291106504Smaxim			cmd32->d[0] = grp->gr_gid;
3292135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
3293204591Sluigi			av++;
329498943Sluigi		    }
329598943Sluigi			break;
329698943Sluigi
3297133600Scsjp		case TOK_JAIL:
3298133600Scsjp			NEED1("jail requires argument");
3299133600Scsjp		    {
3300133600Scsjp			char *end;
3301133600Scsjp			int jid;
3302133600Scsjp
3303133600Scsjp			cmd->opcode = O_JAIL;
3304133600Scsjp			jid = (int)strtol(*av, &end, 0);
3305133600Scsjp			if (jid < 0 || *end != '\0')
3306133600Scsjp				errx(EX_DATAERR, "jail requires prison ID");
3307135554Scsjp			cmd32->d[0] = (uint32_t)jid;
3308135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
3309204591Sluigi			av++;
3310133600Scsjp		    }
3311133600Scsjp			break;
3312133600Scsjp
331398943Sluigi		case TOK_ESTAB:
331498943Sluigi			fill_cmd(cmd, O_ESTAB, 0, 0);
331598943Sluigi			break;
331698943Sluigi
331798943Sluigi		case TOK_SETUP:
331898943Sluigi			fill_cmd(cmd, O_TCPFLAGS, 0,
331998943Sluigi				(TH_SYN) | ( (TH_ACK) & 0xff) <<8 );
332098943Sluigi			break;
332198943Sluigi
3322136075Sgreen		case TOK_TCPDATALEN:
3323136075Sgreen			NEED1("tcpdatalen requires length");
3324136075Sgreen			if (strpbrk(*av, "-,")) {
3325136075Sgreen			    if (!add_ports(cmd, *av, 0, O_TCPDATALEN))
3326136075Sgreen				errx(EX_DATAERR, "invalid tcpdata len %s", *av);
3327136075Sgreen			} else
3328136075Sgreen			    fill_cmd(cmd, O_TCPDATALEN, 0,
3329136075Sgreen				    strtoul(*av, NULL, 0));
3330204591Sluigi			av++;
3331136075Sgreen			break;
3332136075Sgreen
333398943Sluigi		case TOK_TCPOPTS:
333498943Sluigi			NEED1("missing argument for tcpoptions");
333598943Sluigi			fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av);
3336204591Sluigi			av++;
333798943Sluigi			break;
333898943Sluigi
333998943Sluigi		case TOK_TCPSEQ:
334098943Sluigi		case TOK_TCPACK:
334198943Sluigi			NEED1("tcpseq/tcpack requires argument");
334298943Sluigi			cmd->len = F_INSN_SIZE(ipfw_insn_u32);
334398943Sluigi			cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK;
334498943Sluigi			cmd32->d[0] = htonl(strtoul(*av, NULL, 0));
3345204591Sluigi			av++;
334698943Sluigi			break;
334798943Sluigi
334898943Sluigi		case TOK_TCPWIN:
334998943Sluigi			NEED1("tcpwin requires length");
335098943Sluigi			fill_cmd(cmd, O_TCPWIN, 0,
335198943Sluigi			    htons(strtoul(*av, NULL, 0)));
3352204591Sluigi			av++;
335398943Sluigi			break;
335498943Sluigi
335598943Sluigi		case TOK_TCPFLAGS:
335698943Sluigi			NEED1("missing argument for tcpflags");
335798943Sluigi			cmd->opcode = O_TCPFLAGS;
335898943Sluigi			fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av);
3359204591Sluigi			av++;
336098943Sluigi			break;
336198943Sluigi
336298943Sluigi		case TOK_KEEPSTATE:
3363101641Sluigi			if (open_par)
3364101641Sluigi				errx(EX_USAGE, "keep-state cannot be part "
3365101641Sluigi				    "of an or block");
336699909Sluigi			if (have_state)
3367101116Sluigi				errx(EX_USAGE, "only one of keep-state "
336899909Sluigi					"and limit is allowed");
3369101116Sluigi			have_state = cmd;
337098943Sluigi			fill_cmd(cmd, O_KEEP_STATE, 0, 0);
337198943Sluigi			break;
337298943Sluigi
3373159636Soleg		case TOK_LIMIT: {
3374159636Soleg			ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
3375159636Soleg			int val;
3376159636Soleg
3377101641Sluigi			if (open_par)
3378159636Soleg				errx(EX_USAGE,
3379159636Soleg				    "limit cannot be part of an or block");
338099909Sluigi			if (have_state)
3381168819Smaxim				errx(EX_USAGE, "only one of keep-state and "
3382159636Soleg				    "limit is allowed");
3383101116Sluigi			have_state = cmd;
338498943Sluigi
338598943Sluigi			cmd->len = F_INSN_SIZE(ipfw_insn_limit);
338698943Sluigi			cmd->opcode = O_LIMIT;
3387159636Soleg			c->limit_mask = c->conn_limit = 0;
338898943Sluigi
3389204591Sluigi			while ( av[0] != NULL ) {
3390159636Soleg				if ((val = match_token(limit_masks, *av)) <= 0)
339198943Sluigi					break;
339298943Sluigi				c->limit_mask |= val;
3393204591Sluigi				av++;
339498943Sluigi			}
3395159636Soleg
339698943Sluigi			if (c->limit_mask == 0)
3397159636Soleg				errx(EX_USAGE, "limit: missing limit mask");
3398159636Soleg
3399193516Sluigi			GET_UINT_ARG(c->conn_limit, IPFW_ARG_MIN, IPFW_ARG_MAX,
3400182823Srik			    TOK_LIMIT, rule_options);
3401159636Soleg
3402204591Sluigi			av++;
340398943Sluigi			break;
3404159636Soleg		}
340598943Sluigi
3406102087Sluigi		case TOK_PROTO:
3407102087Sluigi			NEED1("missing protocol");
3408145246Sbrooks			if (add_proto(cmd, *av, &proto)) {
3409204591Sluigi				av++;
3410102098Sluigi			} else
3411116438Smaxim				errx(EX_DATAERR, "invalid protocol ``%s''",
3412116438Smaxim				    *av);
3413102087Sluigi			break;
3414106505Smaxim
3415102087Sluigi		case TOK_SRCIP:
3416102087Sluigi			NEED1("missing source IP");
3417102087Sluigi			if (add_srcip(cmd, *av)) {
3418204591Sluigi				av++;
3419102087Sluigi			}
3420102087Sluigi			break;
3421102087Sluigi
3422102087Sluigi		case TOK_DSTIP:
3423102087Sluigi			NEED1("missing destination IP");
3424102087Sluigi			if (add_dstip(cmd, *av)) {
3425204591Sluigi				av++;
3426102087Sluigi			}
3427102087Sluigi			break;
3428102087Sluigi
3429145246Sbrooks		case TOK_SRCIP6:
3430145246Sbrooks			NEED1("missing source IP6");
3431145246Sbrooks			if (add_srcip6(cmd, *av)) {
3432204591Sluigi				av++;
3433145246Sbrooks			}
3434145246Sbrooks			break;
3435145246Sbrooks
3436145246Sbrooks		case TOK_DSTIP6:
3437145246Sbrooks			NEED1("missing destination IP6");
3438145246Sbrooks			if (add_dstip6(cmd, *av)) {
3439204591Sluigi				av++;
3440145246Sbrooks			}
3441145246Sbrooks			break;
3442145246Sbrooks
3443102087Sluigi		case TOK_SRCPORT:
3444102087Sluigi			NEED1("missing source port");
3445140271Sbrooks			if (_substrcmp(*av, "any") == 0 ||
3446102087Sluigi			    add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
3447204591Sluigi				av++;
3448102087Sluigi			} else
3449102087Sluigi				errx(EX_DATAERR, "invalid source port %s", *av);
3450102087Sluigi			break;
3451102087Sluigi
3452102087Sluigi		case TOK_DSTPORT:
3453102087Sluigi			NEED1("missing destination port");
3454140271Sbrooks			if (_substrcmp(*av, "any") == 0 ||
3455102087Sluigi			    add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
3456204591Sluigi				av++;
3457102087Sluigi			} else
3458102087Sluigi				errx(EX_DATAERR, "invalid destination port %s",
3459102087Sluigi				    *av);
3460102087Sluigi			break;
3461102087Sluigi
3462102087Sluigi		case TOK_MAC:
3463204591Sluigi			if (add_mac(cmd, av))
3464204591Sluigi				av += 2;
3465102087Sluigi			break;
3466102087Sluigi
3467102087Sluigi		case TOK_MACTYPE:
3468102087Sluigi			NEED1("missing mac type");
3469204591Sluigi			if (!add_mactype(cmd, *av))
3470116438Smaxim				errx(EX_DATAERR, "invalid mac type %s", *av);
3471204591Sluigi			av++;
3472102087Sluigi			break;
3473102087Sluigi
3474112250Scjc		case TOK_VERREVPATH:
3475112250Scjc			fill_cmd(cmd, O_VERREVPATH, 0, 0);
3476112250Scjc			break;
3477116919Sluigi
3478128575Sandre		case TOK_VERSRCREACH:
3479128575Sandre			fill_cmd(cmd, O_VERSRCREACH, 0, 0);
3480128575Sandre			break;
3481128575Sandre
3482133387Sandre		case TOK_ANTISPOOF:
3483133387Sandre			fill_cmd(cmd, O_ANTISPOOF, 0, 0);
3484133387Sandre			break;
3485133387Sandre
3486117241Sluigi		case TOK_IPSEC:
3487117241Sluigi			fill_cmd(cmd, O_IPSEC, 0, 0);
3488117241Sluigi			break;
3489117241Sluigi
3490145246Sbrooks		case TOK_IPV6:
3491145246Sbrooks			fill_cmd(cmd, O_IP6, 0, 0);
3492145246Sbrooks			break;
3493145246Sbrooks
3494146894Smlaier		case TOK_IPV4:
3495146894Smlaier			fill_cmd(cmd, O_IP4, 0, 0);
3496146894Smlaier			break;
3497146894Smlaier
3498145246Sbrooks		case TOK_EXT6HDR:
3499145246Sbrooks			fill_ext6hdr( cmd, *av );
3500204591Sluigi			av++;
3501145246Sbrooks			break;
3502145246Sbrooks
3503145246Sbrooks		case TOK_FLOWID:
3504145246Sbrooks			if (proto != IPPROTO_IPV6 )
3505145246Sbrooks				errx( EX_USAGE, "flow-id filter is active "
3506145246Sbrooks				    "only for ipv6 protocol\n");
3507145246Sbrooks			fill_flow6( (ipfw_insn_u32 *) cmd, *av );
3508204591Sluigi			av++;
3509145246Sbrooks			break;
3510145246Sbrooks
3511117469Sluigi		case TOK_COMMENT:
3512204591Sluigi			fill_comment(cmd, av);
3513204591Sluigi			av[0]=NULL;
3514117469Sluigi			break;
3515117469Sluigi
3516158879Soleg		case TOK_TAGGED:
3517204591Sluigi			if (av[0] && strpbrk(*av, "-,")) {
3518158879Soleg				if (!add_ports(cmd, *av, 0, O_TAGGED))
3519159636Soleg					errx(EX_DATAERR, "tagged: invalid tag"
3520159636Soleg					    " list: %s", *av);
3521158879Soleg			}
3522159636Soleg			else {
3523159636Soleg				uint16_t tag;
3524159636Soleg
3525193516Sluigi				GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX,
3526182823Srik				    TOK_TAGGED, rule_options);
3527159636Soleg				fill_cmd(cmd, O_TAGGED, 0, tag);
3528159636Soleg			}
3529204591Sluigi			av++;
3530158879Soleg			break;
3531158879Soleg
3532178888Sjulian		case TOK_FIB:
3533178888Sjulian			NEED1("fib requires fib number");
3534178888Sjulian			fill_cmd(cmd, O_FIB, 0, strtoul(*av, NULL, 0));
3535204591Sluigi			av++;
3536178888Sjulian			break;
3537215179Sluigi		case TOK_SOCKARG:
3538215179Sluigi			fill_cmd(cmd, O_SOCKARG, 0, 0);
3539215179Sluigi			break;
3540178888Sjulian
3541200567Sluigi		case TOK_LOOKUP: {
3542200567Sluigi			ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd;
3543200567Sluigi			char *p;
3544200567Sluigi			int j;
3545200567Sluigi
3546205169Sluigi			if (!av[0] || !av[1])
3547200567Sluigi				errx(EX_USAGE, "format: lookup argument tablenum");
3548200567Sluigi			cmd->opcode = O_IP_DST_LOOKUP;
3549200567Sluigi			cmd->len |= F_INSN_SIZE(ipfw_insn) + 2;
3550200567Sluigi			i = match_token(rule_options, *av);
3551200567Sluigi			for (j = 0; lookup_key[j] >= 0 ; j++) {
3552200567Sluigi				if (i == lookup_key[j])
3553200567Sluigi					break;
3554200567Sluigi			}
3555200567Sluigi			if (lookup_key[j] <= 0)
3556200567Sluigi				errx(EX_USAGE, "format: cannot lookup on %s", *av);
3557200567Sluigi			c->d[1] = j; // i converted to option
3558204591Sluigi			av++;
3559200567Sluigi			cmd->arg1 = strtoul(*av, &p, 0);
3560200567Sluigi			if (p && *p)
3561200567Sluigi				errx(EX_USAGE, "format: lookup argument tablenum");
3562204591Sluigi			av++;
3563200567Sluigi		    }
3564200567Sluigi			break;
3565200567Sluigi
356698943Sluigi		default:
356798943Sluigi			errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
356898943Sluigi		}
356998943Sluigi		if (F_LEN(cmd) > 0) {	/* prepare to advance */
357098943Sluigi			prev = cmd;
357198943Sluigi			cmd = next_cmd(cmd);
357298943Sluigi		}
357398943Sluigi	}
357498943Sluigi
357598943Sluigidone:
357698943Sluigi	/*
357798943Sluigi	 * Now copy stuff into the rule.
357898943Sluigi	 * If we have a keep-state option, the first instruction
357998943Sluigi	 * must be a PROBE_STATE (which is generated here).
358098943Sluigi	 * If we have a LOG option, it was stored as the first command,
358198943Sluigi	 * and now must be moved to the top of the action part.
358298943Sluigi	 */
358398943Sluigi	dst = (ipfw_insn *)rule->cmd;
358498943Sluigi
358598943Sluigi	/*
3586107289Sluigi	 * First thing to write into the command stream is the match probability.
3587107289Sluigi	 */
3588107289Sluigi	if (match_prob != 1) { /* 1 means always match */
3589107289Sluigi		dst->opcode = O_PROB;
3590107289Sluigi		dst->len = 2;
3591107289Sluigi		*((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff);
3592107289Sluigi		dst += dst->len;
3593107289Sluigi	}
3594107289Sluigi
3595107289Sluigi	/*
359698943Sluigi	 * generate O_PROBE_STATE if necessary
359798943Sluigi	 */
3598101116Sluigi	if (have_state && have_state->opcode != O_CHECK_STATE) {
359998943Sluigi		fill_cmd(dst, O_PROBE_STATE, 0, 0);
360098943Sluigi		dst = next_cmd(dst);
360198943Sluigi	}
3602158879Soleg
3603158879Soleg	/* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */
360498943Sluigi	for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
360598943Sluigi		i = F_LEN(src);
360698943Sluigi
3607101116Sluigi		switch (src->opcode) {
3608101116Sluigi		case O_LOG:
3609101116Sluigi		case O_KEEP_STATE:
3610101116Sluigi		case O_LIMIT:
3611136071Sgreen		case O_ALTQ:
3612158879Soleg		case O_TAG:
3613101116Sluigi			break;
3614101116Sluigi		default:
3615117328Sluigi			bcopy(src, dst, i * sizeof(uint32_t));
361698943Sluigi			dst += i;
361798943Sluigi		}
361898943Sluigi	}
361998943Sluigi
362098943Sluigi	/*
3621101116Sluigi	 * put back the have_state command as last opcode
3622101116Sluigi	 */
3623101295Sluigi	if (have_state && have_state->opcode != O_CHECK_STATE) {
3624101116Sluigi		i = F_LEN(have_state);
3625117328Sluigi		bcopy(have_state, dst, i * sizeof(uint32_t));
3626101116Sluigi		dst += i;
3627101116Sluigi	}
3628101116Sluigi	/*
362998943Sluigi	 * start action section
363098943Sluigi	 */
363198943Sluigi	rule->act_ofs = dst - rule->cmd;
363298943Sluigi
3633158879Soleg	/* put back O_LOG, O_ALTQ, O_TAG if necessary */
3634136071Sgreen	if (have_log) {
3635136071Sgreen		i = F_LEN(have_log);
3636136071Sgreen		bcopy(have_log, dst, i * sizeof(uint32_t));
363798943Sluigi		dst += i;
363898943Sluigi	}
3639136071Sgreen	if (have_altq) {
3640136071Sgreen		i = F_LEN(have_altq);
3641136071Sgreen		bcopy(have_altq, dst, i * sizeof(uint32_t));
3642136071Sgreen		dst += i;
3643136071Sgreen	}
3644158879Soleg	if (have_tag) {
3645158879Soleg		i = F_LEN(have_tag);
3646158879Soleg		bcopy(have_tag, dst, i * sizeof(uint32_t));
3647158879Soleg		dst += i;
3648158879Soleg	}
364998943Sluigi	/*
365098943Sluigi	 * copy all other actions
365198943Sluigi	 */
365298943Sluigi	for (src = (ipfw_insn *)actbuf; src != action; src += i) {
365398943Sluigi		i = F_LEN(src);
3654117328Sluigi		bcopy(src, dst, i * sizeof(uint32_t));
365598943Sluigi		dst += i;
365698943Sluigi	}
365798943Sluigi
3658117328Sluigi	rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd);
3659117469Sluigi	i = (char *)dst - (char *)rule;
3660119740Stmm	if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1)
366198943Sluigi		err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
3662187764Sluigi	if (!co.do_quiet)
3663117469Sluigi		show_ipfw(rule, 0, 0);
366498943Sluigi}
366598943Sluigi
3666187767Sluigi/*
3667187767Sluigi * clear the counters or the log counters.
3668187767Sluigi */
3669187767Sluigivoid
3670187767Sluigiipfw_zero(int ac, char *av[], int optname /* 0 = IP_FW_ZERO, 1 = IP_FW_RESETLOG */)
367198943Sluigi{
3672170923Smaxim	uint32_t arg, saved_arg;
367398943Sluigi	int failed = EX_OK;
3674170923Smaxim	char const *errstr;
3675187767Sluigi	char const *name = optname ? "RESETLOG" : "ZERO";
367698943Sluigi
3677187767Sluigi	optname = optname ? IP_FW_RESETLOG : IP_FW_ZERO;
3678187767Sluigi
367998943Sluigi	av++; ac--;
368098943Sluigi
368198943Sluigi	if (!ac) {
368298943Sluigi		/* clear all entries */
3683117328Sluigi		if (do_cmd(optname, NULL, 0) < 0)
3684117328Sluigi			err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name);
3685187764Sluigi		if (!co.do_quiet)
3686117328Sluigi			printf("%s.\n", optname == IP_FW_ZERO ?
3687117328Sluigi			    "Accounting cleared":"Logging counts reset");
368898943Sluigi
368998943Sluigi		return;
369098943Sluigi	}
369198943Sluigi
369298943Sluigi	while (ac) {
369398943Sluigi		/* Rule number */
369498943Sluigi		if (isdigit(**av)) {
3695170923Smaxim			arg = strtonum(*av, 0, 0xffff, &errstr);
3696170923Smaxim			if (errstr)
3697170923Smaxim				errx(EX_DATAERR,
3698170923Smaxim				    "invalid rule number %s\n", *av);
3699170923Smaxim			saved_arg = arg;
3700187764Sluigi			if (co.use_set)
3701187764Sluigi				arg |= (1 << 24) | ((co.use_set - 1) << 16);
370298943Sluigi			av++;
370398943Sluigi			ac--;
3704170923Smaxim			if (do_cmd(optname, &arg, sizeof(arg))) {
3705117328Sluigi				warn("rule %u: setsockopt(IP_FW_%s)",
3706170923Smaxim				    saved_arg, name);
370798943Sluigi				failed = EX_UNAVAILABLE;
3708187764Sluigi			} else if (!co.do_quiet)
3709170923Smaxim				printf("Entry %d %s.\n", saved_arg,
3710117328Sluigi				    optname == IP_FW_ZERO ?
3711117328Sluigi					"cleared" : "logging count reset");
371298943Sluigi		} else {
371398943Sluigi			errx(EX_USAGE, "invalid rule number ``%s''", *av);
371498943Sluigi		}
371598943Sluigi	}
371698943Sluigi	if (failed != EX_OK)
371798943Sluigi		exit(failed);
371898943Sluigi}
371998943Sluigi
3720187767Sluigivoid
3721187767Sluigiipfw_flush(int force)
372298943Sluigi{
3723187764Sluigi	int cmd = co.do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH;
372498943Sluigi
3725187764Sluigi	if (!force && !co.do_quiet) { /* need to ask user */
372698943Sluigi		int c;
372798943Sluigi
372898943Sluigi		printf("Are you sure? [yn] ");
372998943Sluigi		fflush(stdout);
373098943Sluigi		do {
373198943Sluigi			c = toupper(getc(stdin));
373298943Sluigi			while (c != '\n' && getc(stdin) != '\n')
373398943Sluigi				if (feof(stdin))
373498943Sluigi					return; /* and do not flush */
373598943Sluigi		} while (c != 'Y' && c != 'N');
373698943Sluigi		printf("\n");
373798943Sluigi		if (c == 'N')	/* user said no */
373898943Sluigi			return;
373998943Sluigi	}
3740204591Sluigi	if (co.do_pipe) {
3741204591Sluigi		dummynet_flush();
3742204591Sluigi		return;
3743204591Sluigi	}
3744170923Smaxim	/* `ipfw set N flush` - is the same that `ipfw delete set N` */
3745187764Sluigi	if (co.use_set) {
3746187764Sluigi		uint32_t arg = ((co.use_set - 1) & 0xffff) | (1 << 24);
3747170923Smaxim		if (do_cmd(IP_FW_DEL, &arg, sizeof(arg)) < 0)
3748170923Smaxim			err(EX_UNAVAILABLE, "setsockopt(IP_FW_DEL)");
3749170923Smaxim	} else if (do_cmd(cmd, NULL, 0) < 0)
375098943Sluigi		err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
3751187764Sluigi		    co.do_pipe ? "DUMMYNET" : "FW");
3752187764Sluigi	if (!co.do_quiet)
3753187764Sluigi		printf("Flushed all %s.\n", co.do_pipe ? "pipes" : "rules");
375498943Sluigi}
375598943Sluigi
3756117544Sluigi
3757183407Srikstatic void table_list(ipfw_table_entry ent, int need_header);
3758183228Srik
3759117544Sluigi/*
3760130281Sru * This one handles all table-related commands
3761130281Sru * 	ipfw table N add addr[/masklen] [value]
3762130281Sru * 	ipfw table N delete addr[/masklen]
3763183407Srik * 	ipfw table {N | all} flush
3764183407Srik * 	ipfw table {N | all} list
3765130281Sru */
3766187767Sluigivoid
3767187767Sluigiipfw_table_handler(int ac, char *av[])
3768130281Sru{
3769130281Sru	ipfw_table_entry ent;
3770130281Sru	int do_add;
3771183407Srik	int is_all;
3772183241Srik	size_t len;
3773130281Sru	char *p;
3774183407Srik	uint32_t a;
3775183241Srik	uint32_t tables_max;
3776130281Sru
3777183263Skeramida	len = sizeof(tables_max);
3778183241Srik	if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len,
3779183241Srik		NULL, 0) == -1) {
3780183241Srik#ifdef IPFW_TABLES_MAX
3781183241Srik		warn("Warn: Failed to get the max tables number via sysctl. "
3782183241Srik		     "Using the compiled in defaults. \nThe reason was");
3783183241Srik		tables_max = IPFW_TABLES_MAX;
3784183241Srik#else
3785183241Srik		errx(1, "Failed sysctlbyname(\"net.inet.ip.fw.tables_max\")");
3786183241Srik#endif
3787183241Srik	}
3788183241Srik
3789130281Sru	ac--; av++;
3790130281Sru	if (ac && isdigit(**av)) {
3791130281Sru		ent.tbl = atoi(*av);
3792183407Srik		is_all = 0;
3793130281Sru		ac--; av++;
3794183407Srik	} else if (ac && _substrcmp(*av, "all") == 0) {
3795183407Srik		ent.tbl = 0;
3796183407Srik		is_all = 1;
3797183407Srik		ac--; av++;
3798130281Sru	} else
3799183407Srik		errx(EX_USAGE, "table number or 'all' keyword required");
3800183241Srik	if (ent.tbl >= tables_max)
3801183241Srik		errx(EX_USAGE, "The table number exceeds the maximum allowed "
3802183241Srik			"value (%d)", tables_max - 1);
3803130281Sru	NEED1("table needs command");
3804183407Srik	if (is_all && _substrcmp(*av, "list") != 0
3805183407Srik		   && _substrcmp(*av, "flush") != 0)
3806183407Srik		errx(EX_USAGE, "table number required");
3807183407Srik
3808140271Sbrooks	if (_substrcmp(*av, "add") == 0 ||
3809140271Sbrooks	    _substrcmp(*av, "delete") == 0) {
3810130281Sru		do_add = **av == 'a';
3811130281Sru		ac--; av++;
3812130281Sru		if (!ac)
3813130281Sru			errx(EX_USAGE, "IP address required");
3814130281Sru		p = strchr(*av, '/');
3815130281Sru		if (p) {
3816130281Sru			*p++ = '\0';
3817130281Sru			ent.masklen = atoi(p);
3818130281Sru			if (ent.masklen > 32)
3819130281Sru				errx(EX_DATAERR, "bad width ``%s''", p);
3820130281Sru		} else
3821130281Sru			ent.masklen = 32;
3822130281Sru		if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0)
3823130298Sru			errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
3824130281Sru		ac--; av++;
3825161424Sjulian		if (do_add && ac) {
3826161424Sjulian			unsigned int tval;
3827161424Sjulian			/* isdigit is a bit of a hack here.. */
3828161424Sjulian			if (strchr(*av, (int)'.') == NULL && isdigit(**av))  {
3829161424Sjulian				ent.value = strtoul(*av, NULL, 0);
3830161424Sjulian			} else {
3831220802Sglebius				if (lookup_host(*av, (struct in_addr *)&tval) == 0) {
3832161424Sjulian					/* The value must be stored in host order	 *
3833161424Sjulian					 * so that the values < 65k can be distinguished */
3834220802Sglebius		       			ent.value = ntohl(tval);
3835161424Sjulian				} else {
3836161424Sjulian					errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
3837161424Sjulian				}
3838161424Sjulian			}
3839161424Sjulian		} else
3840130281Sru			ent.value = 0;
3841130281Sru		if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL,
3842157335Sjulian		    &ent, sizeof(ent)) < 0) {
3843155639Sjulian			/* If running silent, don't bomb out on these errors. */
3844187764Sluigi			if (!(co.do_quiet && (errno == (do_add ? EEXIST : ESRCH))))
3845155639Sjulian				err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
3846155639Sjulian				    do_add ? "ADD" : "DEL");
3847155639Sjulian			/* In silent mode, react to a failed add by deleting */
3848157332Sjulian			if (do_add) {
3849155639Sjulian				do_cmd(IP_FW_TABLE_DEL, &ent, sizeof(ent));
3850155639Sjulian				if (do_cmd(IP_FW_TABLE_ADD,
3851155639Sjulian				    &ent, sizeof(ent)) < 0)
3852155639Sjulian					err(EX_OSERR,
3853220802Sglebius					    "setsockopt(IP_FW_TABLE_ADD)");
3854157332Sjulian			}
3855157335Sjulian		}
3856140271Sbrooks	} else if (_substrcmp(*av, "flush") == 0) {
3857204591Sluigi		a = is_all ? tables_max : (uint32_t)(ent.tbl + 1);
3858183407Srik		do {
3859183407Srik			if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl,
3860183407Srik			    sizeof(ent.tbl)) < 0)
3861183407Srik				err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
3862183407Srik		} while (++ent.tbl < a);
3863140271Sbrooks	} else if (_substrcmp(*av, "list") == 0) {
3864204591Sluigi		a = is_all ? tables_max : (uint32_t)(ent.tbl + 1);
3865183407Srik		do {
3866183415Srik			table_list(ent, is_all);
3867183407Srik		} while (++ent.tbl < a);
3868183228Srik	} else
3869183228Srik		errx(EX_USAGE, "invalid table command %s", *av);
3870183228Srik}
3871183205Srik
3872183228Srikstatic void
3873183407Sriktable_list(ipfw_table_entry ent, int need_header)
3874183228Srik{
3875183228Srik	ipfw_table *tbl;
3876183228Srik	socklen_t l;
3877183228Srik	uint32_t a;
3878183205Srik
3879183228Srik	a = ent.tbl;
3880183228Srik	l = sizeof(a);
3881183228Srik	if (do_cmd(IP_FW_TABLE_GETSIZE, &a, (uintptr_t)&l) < 0)
3882183228Srik		err(EX_OSERR, "getsockopt(IP_FW_TABLE_GETSIZE)");
3883183228Srik
3884183228Srik	/* If a is zero we have nothing to do, the table is empty. */
3885183228Srik	if (a == 0)
3886183228Srik		return;
3887183228Srik
3888183228Srik	l = sizeof(*tbl) + a * sizeof(ipfw_table_entry);
3889187716Sluigi	tbl = safe_calloc(1, l);
3890183228Srik	tbl->tbl = ent.tbl;
3891183228Srik	if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0)
3892183228Srik		err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)");
3893183407Srik	if (tbl->cnt && need_header)
3894183407Srik		printf("---table(%d)---\n", tbl->tbl);
3895183228Srik	for (a = 0; a < tbl->cnt; a++) {
3896183228Srik		unsigned int tval;
3897183228Srik		tval = tbl->ent[a].value;
3898187764Sluigi		if (co.do_value_as_ip) {
3899183228Srik			char tbuf[128];
3900183228Srik			strncpy(tbuf, inet_ntoa(*(struct in_addr *)
3901161456Sjulian				&tbl->ent[a].addr), 127);
3902183228Srik			/* inet_ntoa expects network order */
3903183228Srik			tval = htonl(tval);
3904183228Srik			printf("%s/%u %s\n", tbuf, tbl->ent[a].masklen,
3905183228Srik				inet_ntoa(*(struct in_addr *)&tval));
3906183228Srik		} else {
3907183228Srik			printf("%s/%u %u\n",
3908183228Srik				inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
3909183228Srik				tbl->ent[a].masklen, tval);
3910130281Sru		}
3911183228Srik	}
3912183228Srik	free(tbl);
3913130281Sru}
3914