ipfw2.c revision 169139
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 169139 2007-04-30 17:39:30Z maxim $
2198943Sluigi */
2298943Sluigi
2398943Sluigi#include <sys/param.h>
2498943Sluigi#include <sys/mbuf.h>
2598943Sluigi#include <sys/socket.h>
2698943Sluigi#include <sys/sockio.h>
2798943Sluigi#include <sys/sysctl.h>
2898943Sluigi#include <sys/time.h>
2998943Sluigi#include <sys/wait.h>
30136071Sgreen#include <sys/queue.h>
3198943Sluigi
3298943Sluigi#include <ctype.h>
3398943Sluigi#include <err.h>
3498943Sluigi#include <errno.h>
3598943Sluigi#include <grp.h>
3698943Sluigi#include <limits.h>
3798943Sluigi#include <netdb.h>
3898943Sluigi#include <pwd.h>
3998943Sluigi#include <signal.h>
4098943Sluigi#include <stdio.h>
4198943Sluigi#include <stdlib.h>
4298943Sluigi#include <stdarg.h>
4398943Sluigi#include <string.h>
44117469Sluigi#include <timeconv.h>	/* XXX do we need this ? */
4598943Sluigi#include <unistd.h>
4698943Sluigi#include <sysexits.h>
47136071Sgreen#include <unistd.h>
48136071Sgreen#include <fcntl.h>
4998943Sluigi
5098943Sluigi#include <net/if.h>
51165648Spiso#include <net/if_dl.h>
52136071Sgreen#include <net/pfvar.h>
53145246Sbrooks#include <net/route.h> /* def. of struct route */
5498943Sluigi#include <netinet/in.h>
5598943Sluigi#include <netinet/in_systm.h>
5698943Sluigi#include <netinet/ip.h>
5798943Sluigi#include <netinet/ip_icmp.h>
58145246Sbrooks#include <netinet/icmp6.h>
5998943Sluigi#include <netinet/ip_fw.h>
6098943Sluigi#include <netinet/ip_dummynet.h>
6198943Sluigi#include <netinet/tcp.h>
6298943Sluigi#include <arpa/inet.h>
63165648Spiso#include <alias.h>
6498943Sluigi
65117328Sluigiint
6698943Sluigi		do_resolv,		/* Would try to resolve all */
6798943Sluigi		do_time,		/* Show time stamps */
6898943Sluigi		do_quiet,		/* Be quiet in add and flush */
6998943Sluigi		do_pipe,		/* this cmd refers to a pipe */
70165648Spiso	        do_nat, 		/* Nat configuration. */
7198943Sluigi		do_sort,		/* field to sort results (0 = no) */
7298943Sluigi		do_dynamic,		/* display dynamic rules */
7398943Sluigi		do_expired,		/* display expired dynamic rules */
74102098Sluigi		do_compact,		/* show rules in compact mode */
75123804Smaxim		do_force,		/* do not ask for confirmation */
76101628Sluigi		show_sets,		/* display rule sets */
77117328Sluigi		test_only,		/* only check syntax */
78123495Sluigi		comment_only,		/* only print action and comment */
7998943Sluigi		verbose;
8098943Sluigi
8198943Sluigi#define	IP_MASK_ALL	0xffffffff
82130013Scsjp/*
83130013Scsjp * the following macro returns an error message if we run out of
84130013Scsjp * arguments.
85130013Scsjp */
86130013Scsjp#define NEED1(msg)      {if (!ac) errx(EX_USAGE, msg);}
8798943Sluigi
88159636Soleg#define GET_UINT_ARG(arg, min, max, tok, s_x) do {			\
89159636Soleg	if (!ac)							\
90159636Soleg		errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \
91159636Soleg	if (_substrcmp(*av, "tablearg") == 0) {				\
92159636Soleg		arg = IP_FW_TABLEARG;					\
93159636Soleg		break;							\
94159636Soleg	}								\
95159636Soleg									\
96159636Soleg	{								\
97159636Soleg	long val;							\
98159636Soleg	char *end;							\
99159636Soleg									\
100159636Soleg	val = strtol(*av, &end, 10);					\
101159636Soleg									\
102159636Soleg	if (!isdigit(**av) || *end != '\0' || (val == 0 && errno == EINVAL)) \
103159636Soleg		errx(EX_DATAERR, "%s: invalid argument: %s",		\
104159636Soleg		    match_value(s_x, tok), *av);			\
105159636Soleg									\
106159636Soleg	if (errno == ERANGE || val < min || val > max)			\
107159636Soleg		errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \
108159636Soleg		    match_value(s_x, tok), min, max, *av);		\
109159636Soleg									\
110159636Soleg	if (val == IP_FW_TABLEARG)					\
111159636Soleg		errx(EX_DATAERR, "%s: illegal argument value: %s",	\
112159636Soleg		    match_value(s_x, tok), *av);			\
113159636Soleg	arg = val;							\
114159636Soleg	}								\
115158879Soleg} while (0)
116158879Soleg
117159636Soleg#define PRINT_UINT_ARG(str, arg) do {					\
118159636Soleg	if (str != NULL)						\
119159636Soleg		printf("%s",str);					\
120159636Soleg	if (arg == IP_FW_TABLEARG)					\
121159636Soleg		printf("tablearg");					\
122159636Soleg	else								\
123159636Soleg		printf("%u", (uint32_t)arg);				\
124159636Soleg} while (0)
125159636Soleg
12698943Sluigi/*
127117328Sluigi * _s_x is a structure that stores a string <-> token pairs, used in
128117328Sluigi * various places in the parser. Entries are stored in arrays,
129117328Sluigi * with an entry with s=NULL as terminator.
130117328Sluigi * The search routines are match_token() and match_value().
131117328Sluigi * Often, an element with x=0 contains an error string.
13298943Sluigi *
13398943Sluigi */
13498943Sluigistruct _s_x {
135117469Sluigi	char const *s;
13698943Sluigi	int x;
13798943Sluigi};
13898943Sluigi
13998943Sluigistatic struct _s_x f_tcpflags[] = {
14098943Sluigi	{ "syn", TH_SYN },
14198943Sluigi	{ "fin", TH_FIN },
14298943Sluigi	{ "ack", TH_ACK },
14398943Sluigi	{ "psh", TH_PUSH },
14498943Sluigi	{ "rst", TH_RST },
14598943Sluigi	{ "urg", TH_URG },
14698943Sluigi	{ "tcp flag", 0 },
14798943Sluigi	{ NULL,	0 }
14898943Sluigi};
14998943Sluigi
15098943Sluigistatic struct _s_x f_tcpopts[] = {
15198943Sluigi	{ "mss",	IP_FW_TCPOPT_MSS },
15298943Sluigi	{ "maxseg",	IP_FW_TCPOPT_MSS },
15398943Sluigi	{ "window",	IP_FW_TCPOPT_WINDOW },
15498943Sluigi	{ "sack",	IP_FW_TCPOPT_SACK },
15598943Sluigi	{ "ts",		IP_FW_TCPOPT_TS },
15698943Sluigi	{ "timestamp",	IP_FW_TCPOPT_TS },
15798943Sluigi	{ "cc",		IP_FW_TCPOPT_CC },
15898943Sluigi	{ "tcp option",	0 },
15998943Sluigi	{ NULL,	0 }
16098943Sluigi};
16198943Sluigi
16298943Sluigi/*
16398943Sluigi * IP options span the range 0 to 255 so we need to remap them
16498943Sluigi * (though in fact only the low 5 bits are significant).
16598943Sluigi */
16698943Sluigistatic struct _s_x f_ipopts[] = {
16798943Sluigi	{ "ssrr",	IP_FW_IPOPT_SSRR},
16898943Sluigi	{ "lsrr",	IP_FW_IPOPT_LSRR},
16998943Sluigi	{ "rr",		IP_FW_IPOPT_RR},
17098943Sluigi	{ "ts",		IP_FW_IPOPT_TS},
17198943Sluigi	{ "ip option",	0 },
17298943Sluigi	{ NULL,	0 }
17398943Sluigi};
17498943Sluigi
17598943Sluigistatic struct _s_x f_iptos[] = {
17698943Sluigi	{ "lowdelay",	IPTOS_LOWDELAY},
17798943Sluigi	{ "throughput",	IPTOS_THROUGHPUT},
17898943Sluigi	{ "reliability", IPTOS_RELIABILITY},
17998943Sluigi	{ "mincost",	IPTOS_MINCOST},
18098943Sluigi	{ "congestion",	IPTOS_CE},
18198943Sluigi	{ "ecntransport", IPTOS_ECT},
18298943Sluigi	{ "ip tos option", 0},
18398943Sluigi	{ NULL,	0 }
18498943Sluigi};
18598943Sluigi
18698943Sluigistatic struct _s_x limit_masks[] = {
18798943Sluigi	{"all",		DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
18898943Sluigi	{"src-addr",	DYN_SRC_ADDR},
18998943Sluigi	{"src-port",	DYN_SRC_PORT},
19098943Sluigi	{"dst-addr",	DYN_DST_ADDR},
19198943Sluigi	{"dst-port",	DYN_DST_PORT},
19298943Sluigi	{NULL,		0}
19398943Sluigi};
19498943Sluigi
19598943Sluigi/*
19698943Sluigi * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines
19798943Sluigi * This is only used in this code.
19898943Sluigi */
19998943Sluigi#define IPPROTO_ETHERTYPE	0x1000
20098943Sluigistatic struct _s_x ether_types[] = {
20198943Sluigi    /*
20298943Sluigi     * Note, we cannot use "-:&/" in the names because they are field
20398943Sluigi     * separators in the type specifications. Also, we use s = NULL as
20498943Sluigi     * end-delimiter, because a type of 0 can be legal.
20598943Sluigi     */
20698943Sluigi	{ "ip",		0x0800 },
20798943Sluigi	{ "ipv4",	0x0800 },
20898943Sluigi	{ "ipv6",	0x86dd },
20998943Sluigi	{ "arp",	0x0806 },
21098943Sluigi	{ "rarp",	0x8035 },
21198943Sluigi	{ "vlan",	0x8100 },
21298943Sluigi	{ "loop",	0x9000 },
21398943Sluigi	{ "trail",	0x1000 },
21498943Sluigi	{ "at",		0x809b },
21598943Sluigi	{ "atalk",	0x809b },
21698943Sluigi	{ "aarp",	0x80f3 },
21798943Sluigi	{ "pppoe_disc",	0x8863 },
21898943Sluigi	{ "pppoe_sess",	0x8864 },
21998943Sluigi	{ "ipx_8022",	0x00E0 },
22098943Sluigi	{ "ipx_8023",	0x0000 },
22198943Sluigi	{ "ipx_ii",	0x8137 },
22298943Sluigi	{ "ipx_snap",	0x8137 },
22398943Sluigi	{ "ipx",	0x8137 },
22498943Sluigi	{ "ns",		0x0600 },
22598943Sluigi	{ NULL,		0 }
22698943Sluigi};
22798943Sluigi
22898943Sluigistatic void show_usage(void);
22998943Sluigi
23098943Sluigienum tokens {
23198943Sluigi	TOK_NULL=0,
23298943Sluigi
23398943Sluigi	TOK_OR,
23498943Sluigi	TOK_NOT,
235101641Sluigi	TOK_STARTBRACE,
236101641Sluigi	TOK_ENDBRACE,
23798943Sluigi
23898943Sluigi	TOK_ACCEPT,
23998943Sluigi	TOK_COUNT,
24098943Sluigi	TOK_PIPE,
24198943Sluigi	TOK_QUEUE,
24298943Sluigi	TOK_DIVERT,
24398943Sluigi	TOK_TEE,
244141351Sglebius	TOK_NETGRAPH,
245141351Sglebius	TOK_NGTEE,
24698943Sluigi	TOK_FORWARD,
24798943Sluigi	TOK_SKIPTO,
24898943Sluigi	TOK_DENY,
24998943Sluigi	TOK_REJECT,
25098943Sluigi	TOK_RESET,
25198943Sluigi	TOK_UNREACH,
25298943Sluigi	TOK_CHECKSTATE,
253165648Spiso	TOK_NAT,
25498943Sluigi
255136071Sgreen	TOK_ALTQ,
256136071Sgreen	TOK_LOG,
257158879Soleg	TOK_TAG,
258158879Soleg	TOK_UNTAG,
259136071Sgreen
260158879Soleg	TOK_TAGGED,
26198943Sluigi	TOK_UID,
26298943Sluigi	TOK_GID,
263133600Scsjp	TOK_JAIL,
26498943Sluigi	TOK_IN,
26598943Sluigi	TOK_LIMIT,
26698943Sluigi	TOK_KEEPSTATE,
26798943Sluigi	TOK_LAYER2,
26898943Sluigi	TOK_OUT,
269136073Sgreen	TOK_DIVERTED,
270136073Sgreen	TOK_DIVERTEDLOOPBACK,
271136073Sgreen	TOK_DIVERTEDOUTPUT,
27298943Sluigi	TOK_XMIT,
27398943Sluigi	TOK_RECV,
27498943Sluigi	TOK_VIA,
27598943Sluigi	TOK_FRAG,
27698943Sluigi	TOK_IPOPTS,
27798943Sluigi	TOK_IPLEN,
27898943Sluigi	TOK_IPID,
27998943Sluigi	TOK_IPPRECEDENCE,
28098943Sluigi	TOK_IPTOS,
28198943Sluigi	TOK_IPTTL,
28298943Sluigi	TOK_IPVER,
28398943Sluigi	TOK_ESTAB,
28498943Sluigi	TOK_SETUP,
285136075Sgreen	TOK_TCPDATALEN,
28698943Sluigi	TOK_TCPFLAGS,
28798943Sluigi	TOK_TCPOPTS,
28898943Sluigi	TOK_TCPSEQ,
28998943Sluigi	TOK_TCPACK,
29098943Sluigi	TOK_TCPWIN,
29198943Sluigi	TOK_ICMPTYPES,
292102087Sluigi	TOK_MAC,
293102087Sluigi	TOK_MACTYPE,
294112250Scjc	TOK_VERREVPATH,
295128575Sandre	TOK_VERSRCREACH,
296133387Sandre	TOK_ANTISPOOF,
297117241Sluigi	TOK_IPSEC,
298117469Sluigi	TOK_COMMENT,
29998943Sluigi
30098943Sluigi	TOK_PLR,
301101978Sluigi	TOK_NOERROR,
30298943Sluigi	TOK_BUCKETS,
30398943Sluigi	TOK_DSTIP,
30498943Sluigi	TOK_SRCIP,
30598943Sluigi	TOK_DSTPORT,
30698943Sluigi	TOK_SRCPORT,
30798943Sluigi	TOK_ALL,
30898943Sluigi	TOK_MASK,
30998943Sluigi	TOK_BW,
31098943Sluigi	TOK_DELAY,
31198943Sluigi	TOK_RED,
31298943Sluigi	TOK_GRED,
31398943Sluigi	TOK_DROPTAIL,
31498943Sluigi	TOK_PROTO,
31598943Sluigi	TOK_WEIGHT,
316165648Spiso	TOK_IP,
317165648Spiso	TOK_IF,
318165648Spiso 	TOK_ALOG,
319165648Spiso 	TOK_DENY_INC,
320165648Spiso 	TOK_SAME_PORTS,
321165648Spiso 	TOK_UNREG_ONLY,
322165648Spiso 	TOK_RESET_ADDR,
323165648Spiso 	TOK_ALIAS_REV,
324165648Spiso 	TOK_PROXY_ONLY,
325165648Spiso	TOK_REDIR_ADDR,
326165648Spiso	TOK_REDIR_PORT,
327165648Spiso	TOK_REDIR_PROTO,
328145246Sbrooks
329145246Sbrooks	TOK_IPV6,
330145246Sbrooks	TOK_FLOWID,
331145246Sbrooks	TOK_ICMP6TYPES,
332145246Sbrooks	TOK_EXT6HDR,
333145246Sbrooks	TOK_DSTIP6,
334145246Sbrooks	TOK_SRCIP6,
335146894Smlaier
336146894Smlaier	TOK_IPV4,
337149020Sbz	TOK_UNREACH6,
338149020Sbz	TOK_RESET6,
33998943Sluigi};
34098943Sluigi
34198943Sluigistruct _s_x dummynet_params[] = {
34298943Sluigi	{ "plr",		TOK_PLR },
343101978Sluigi	{ "noerror",		TOK_NOERROR },
34498943Sluigi	{ "buckets",		TOK_BUCKETS },
34598943Sluigi	{ "dst-ip",		TOK_DSTIP },
34698943Sluigi	{ "src-ip",		TOK_SRCIP },
34798943Sluigi	{ "dst-port",		TOK_DSTPORT },
34898943Sluigi	{ "src-port",		TOK_SRCPORT },
34998943Sluigi	{ "proto",		TOK_PROTO },
35098943Sluigi	{ "weight",		TOK_WEIGHT },
35198943Sluigi	{ "all",		TOK_ALL },
35298943Sluigi	{ "mask",		TOK_MASK },
35398943Sluigi	{ "droptail",		TOK_DROPTAIL },
35498943Sluigi	{ "red",		TOK_RED },
35598943Sluigi	{ "gred",		TOK_GRED },
35698943Sluigi	{ "bw",			TOK_BW },
35798943Sluigi	{ "bandwidth",		TOK_BW },
35898943Sluigi	{ "delay",		TOK_DELAY },
35999475Sluigi	{ "pipe",		TOK_PIPE },
36098943Sluigi	{ "queue",		TOK_QUEUE },
361145246Sbrooks	{ "flow-id",		TOK_FLOWID},
362145246Sbrooks	{ "dst-ipv6",		TOK_DSTIP6},
363145246Sbrooks	{ "dst-ip6",		TOK_DSTIP6},
364145246Sbrooks	{ "src-ipv6",		TOK_SRCIP6},
365145246Sbrooks	{ "src-ip6",		TOK_SRCIP6},
36698943Sluigi	{ "dummynet-params",	TOK_NULL },
367117328Sluigi	{ NULL, 0 }	/* terminator */
36898943Sluigi};
36998943Sluigi
370165648Spisostruct _s_x nat_params[] = {
371165648Spiso	{ "ip",	                TOK_IP },
372165648Spiso	{ "if",	                TOK_IF },
373165648Spiso 	{ "log",                TOK_ALOG },
374165648Spiso 	{ "deny_in",	        TOK_DENY_INC },
375165648Spiso 	{ "same_ports",	        TOK_SAME_PORTS },
376165648Spiso 	{ "unreg_only",	        TOK_UNREG_ONLY },
377165648Spiso 	{ "reset",	        TOK_RESET_ADDR },
378165648Spiso 	{ "reverse",	        TOK_ALIAS_REV },
379165648Spiso 	{ "proxy_only",	        TOK_PROXY_ONLY },
380165648Spiso	{ "redirect_addr",	TOK_REDIR_ADDR },
381165648Spiso	{ "redirect_port",	TOK_REDIR_PORT },
382165648Spiso	{ "redirect_proto",	TOK_REDIR_PROTO },
383165648Spiso 	{ NULL, 0 }	/* terminator */
384165648Spiso};
385165648Spiso
38698943Sluigistruct _s_x rule_actions[] = {
38798943Sluigi	{ "accept",		TOK_ACCEPT },
38898943Sluigi	{ "pass",		TOK_ACCEPT },
38998943Sluigi	{ "allow",		TOK_ACCEPT },
39098943Sluigi	{ "permit",		TOK_ACCEPT },
39198943Sluigi	{ "count",		TOK_COUNT },
39298943Sluigi	{ "pipe",		TOK_PIPE },
39398943Sluigi	{ "queue",		TOK_QUEUE },
39498943Sluigi	{ "divert",		TOK_DIVERT },
39598943Sluigi	{ "tee",		TOK_TEE },
396141351Sglebius	{ "netgraph",		TOK_NETGRAPH },
397141351Sglebius	{ "ngtee",		TOK_NGTEE },
39898943Sluigi	{ "fwd",		TOK_FORWARD },
39998943Sluigi	{ "forward",		TOK_FORWARD },
40098943Sluigi	{ "skipto",		TOK_SKIPTO },
40198943Sluigi	{ "deny",		TOK_DENY },
40298943Sluigi	{ "drop",		TOK_DENY },
40398943Sluigi	{ "reject",		TOK_REJECT },
404149020Sbz	{ "reset6",		TOK_RESET6 },
40598943Sluigi	{ "reset",		TOK_RESET },
406149020Sbz	{ "unreach6",		TOK_UNREACH6 },
40799475Sluigi	{ "unreach",		TOK_UNREACH },
40898943Sluigi	{ "check-state",	TOK_CHECKSTATE },
409117469Sluigi	{ "//",			TOK_COMMENT },
410165648Spiso	{ "nat",                TOK_NAT },
411117328Sluigi	{ NULL, 0 }	/* terminator */
41298943Sluigi};
41398943Sluigi
414136071Sgreenstruct _s_x rule_action_params[] = {
415136071Sgreen	{ "altq",		TOK_ALTQ },
416136071Sgreen	{ "log",		TOK_LOG },
417158879Soleg	{ "tag",		TOK_TAG },
418158879Soleg	{ "untag",		TOK_UNTAG },
419136071Sgreen	{ NULL, 0 }	/* terminator */
420136071Sgreen};
421136071Sgreen
42298943Sluigistruct _s_x rule_options[] = {
423158879Soleg	{ "tagged",		TOK_TAGGED },
42498943Sluigi	{ "uid",		TOK_UID },
42598943Sluigi	{ "gid",		TOK_GID },
426133600Scsjp	{ "jail",		TOK_JAIL },
42798943Sluigi	{ "in",			TOK_IN },
42898943Sluigi	{ "limit",		TOK_LIMIT },
42998943Sluigi	{ "keep-state",		TOK_KEEPSTATE },
43098943Sluigi	{ "bridged",		TOK_LAYER2 },
43198943Sluigi	{ "layer2",		TOK_LAYER2 },
43298943Sluigi	{ "out",		TOK_OUT },
433136073Sgreen	{ "diverted",		TOK_DIVERTED },
434136073Sgreen	{ "diverted-loopback",	TOK_DIVERTEDLOOPBACK },
435136073Sgreen	{ "diverted-output",	TOK_DIVERTEDOUTPUT },
43698943Sluigi	{ "xmit",		TOK_XMIT },
43798943Sluigi	{ "recv",		TOK_RECV },
43898943Sluigi	{ "via",		TOK_VIA },
43998943Sluigi	{ "fragment",		TOK_FRAG },
44098943Sluigi	{ "frag",		TOK_FRAG },
44198943Sluigi	{ "ipoptions",		TOK_IPOPTS },
44298943Sluigi	{ "ipopts",		TOK_IPOPTS },
44398943Sluigi	{ "iplen",		TOK_IPLEN },
44498943Sluigi	{ "ipid",		TOK_IPID },
44598943Sluigi	{ "ipprecedence",	TOK_IPPRECEDENCE },
44698943Sluigi	{ "iptos",		TOK_IPTOS },
44798943Sluigi	{ "ipttl",		TOK_IPTTL },
44898943Sluigi	{ "ipversion",		TOK_IPVER },
44998943Sluigi	{ "ipver",		TOK_IPVER },
45098943Sluigi	{ "estab",		TOK_ESTAB },
45198943Sluigi	{ "established",	TOK_ESTAB },
45298943Sluigi	{ "setup",		TOK_SETUP },
453136075Sgreen	{ "tcpdatalen",		TOK_TCPDATALEN },
45498943Sluigi	{ "tcpflags",		TOK_TCPFLAGS },
45598943Sluigi	{ "tcpflgs",		TOK_TCPFLAGS },
45698943Sluigi	{ "tcpoptions",		TOK_TCPOPTS },
45798943Sluigi	{ "tcpopts",		TOK_TCPOPTS },
45898943Sluigi	{ "tcpseq",		TOK_TCPSEQ },
45998943Sluigi	{ "tcpack",		TOK_TCPACK },
46098943Sluigi	{ "tcpwin",		TOK_TCPWIN },
46199909Sluigi	{ "icmptype",		TOK_ICMPTYPES },
46298943Sluigi	{ "icmptypes",		TOK_ICMPTYPES },
463102087Sluigi	{ "dst-ip",		TOK_DSTIP },
464102087Sluigi	{ "src-ip",		TOK_SRCIP },
465102087Sluigi	{ "dst-port",		TOK_DSTPORT },
466102087Sluigi	{ "src-port",		TOK_SRCPORT },
467102087Sluigi	{ "proto",		TOK_PROTO },
468102087Sluigi	{ "MAC",		TOK_MAC },
469102087Sluigi	{ "mac",		TOK_MAC },
470102087Sluigi	{ "mac-type",		TOK_MACTYPE },
471112250Scjc	{ "verrevpath",		TOK_VERREVPATH },
472128575Sandre	{ "versrcreach",	TOK_VERSRCREACH },
473133387Sandre	{ "antispoof",		TOK_ANTISPOOF },
474117241Sluigi	{ "ipsec",		TOK_IPSEC },
475145246Sbrooks	{ "icmp6type",		TOK_ICMP6TYPES },
476145246Sbrooks	{ "icmp6types",		TOK_ICMP6TYPES },
477145246Sbrooks	{ "ext6hdr",		TOK_EXT6HDR},
478145246Sbrooks	{ "flow-id",		TOK_FLOWID},
479145246Sbrooks	{ "ipv6",		TOK_IPV6},
480145246Sbrooks	{ "ip6",		TOK_IPV6},
481146894Smlaier	{ "ipv4",		TOK_IPV4},
482146894Smlaier	{ "ip4",		TOK_IPV4},
483145246Sbrooks	{ "dst-ipv6",		TOK_DSTIP6},
484145246Sbrooks	{ "dst-ip6",		TOK_DSTIP6},
485145246Sbrooks	{ "src-ipv6",		TOK_SRCIP6},
486145246Sbrooks	{ "src-ip6",		TOK_SRCIP6},
487117469Sluigi	{ "//",			TOK_COMMENT },
48898943Sluigi
48998943Sluigi	{ "not",		TOK_NOT },		/* pseudo option */
49098943Sluigi	{ "!", /* escape ? */	TOK_NOT },		/* pseudo option */
49198943Sluigi	{ "or",			TOK_OR },		/* pseudo option */
49298943Sluigi	{ "|", /* escape */	TOK_OR },		/* pseudo option */
493101641Sluigi	{ "{",			TOK_STARTBRACE },	/* pseudo option */
494101641Sluigi	{ "(",			TOK_STARTBRACE },	/* pseudo option */
495101641Sluigi	{ "}",			TOK_ENDBRACE },		/* pseudo option */
496101641Sluigi	{ ")",			TOK_ENDBRACE },		/* pseudo option */
497117328Sluigi	{ NULL, 0 }	/* terminator */
49898943Sluigi};
49998943Sluigi
500153374Sglebius#define	TABLEARG	"tablearg"
501153374Sglebius
502117328Sluigistatic __inline uint64_t
503117328Sluigialign_uint64(uint64_t *pll) {
504117328Sluigi	uint64_t ret;
505115793Sticso
506115793Sticso	bcopy (pll, &ret, sizeof(ret));
507115793Sticso	return ret;
508129389Sstefanf}
509115793Sticso
510117328Sluigi/*
511117328Sluigi * conditionally runs the command.
512117328Sluigi */
513117469Sluigistatic int
514119740Stmmdo_cmd(int optname, void *optval, uintptr_t optlen)
515117328Sluigi{
516117328Sluigi	static int s = -1;	/* the socket */
517117328Sluigi	int i;
518117577Sluigi
519117328Sluigi	if (test_only)
520117328Sluigi		return 0;
521117328Sluigi
522117328Sluigi	if (s == -1)
523117328Sluigi		s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
524117328Sluigi	if (s < 0)
525117328Sluigi		err(EX_UNAVAILABLE, "socket");
526117328Sluigi
527117328Sluigi	if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
528130281Sru	    optname == IP_FW_ADD || optname == IP_FW_TABLE_LIST ||
529165648Spiso	    optname == IP_FW_TABLE_GETSIZE ||
530165648Spiso	    optname == IP_FW_NAT_GET_CONFIG ||
531165648Spiso	    optname == IP_FW_NAT_GET_LOG)
532117328Sluigi		i = getsockopt(s, IPPROTO_IP, optname, optval,
533117328Sluigi			(socklen_t *)optlen);
534117328Sluigi	else
535117328Sluigi		i = setsockopt(s, IPPROTO_IP, optname, optval, optlen);
536117328Sluigi	return i;
537117328Sluigi}
538117328Sluigi
53998943Sluigi/**
54098943Sluigi * match_token takes a table and a string, returns the value associated
541117328Sluigi * with the string (-1 in case of failure).
54298943Sluigi */
54398943Sluigistatic int
54498943Sluigimatch_token(struct _s_x *table, char *string)
54598943Sluigi{
54698943Sluigi	struct _s_x *pt;
547117469Sluigi	uint i = strlen(string);
54898943Sluigi
54998943Sluigi	for (pt = table ; i && pt->s != NULL ; pt++)
55098943Sluigi		if (strlen(pt->s) == i && !bcmp(string, pt->s, i))
55198943Sluigi			return pt->x;
55298943Sluigi	return -1;
553129389Sstefanf}
55498943Sluigi
555117328Sluigi/**
556117328Sluigi * match_value takes a table and a value, returns the string associated
557117328Sluigi * with the value (NULL in case of failure).
558117328Sluigi */
559117469Sluigistatic char const *
560117469Sluigimatch_value(struct _s_x *p, int value)
56198943Sluigi{
56298943Sluigi	for (; p->s != NULL; p++)
56398943Sluigi		if (p->x == value)
56498943Sluigi			return p->s;
56598943Sluigi	return NULL;
56698943Sluigi}
56798943Sluigi
56898943Sluigi/*
569140271Sbrooks * _substrcmp takes two strings and returns 1 if they do not match,
570140271Sbrooks * and 0 if they match exactly or the first string is a sub-string
571140271Sbrooks * of the second.  A warning is printed to stderr in the case that the
572140271Sbrooks * first string is a sub-string of the second.
573140271Sbrooks *
574140271Sbrooks * This function will be removed in the future through the usual
575140271Sbrooks * deprecation process.
576140271Sbrooks */
577140271Sbrooksstatic int
578140271Sbrooks_substrcmp(const char *str1, const char* str2)
579140271Sbrooks{
580140271Sbrooks
581140271Sbrooks	if (strncmp(str1, str2, strlen(str1)) != 0)
582140271Sbrooks		return 1;
583140271Sbrooks
584140271Sbrooks	if (strlen(str1) != strlen(str2))
585140271Sbrooks		warnx("DEPRECATED: '%s' matched '%s' as a sub-string",
586140271Sbrooks		    str1, str2);
587140271Sbrooks	return 0;
588140271Sbrooks}
589140271Sbrooks
590140271Sbrooks/*
591140271Sbrooks * _substrcmp2 takes three strings and returns 1 if the first two do not match,
592140271Sbrooks * and 0 if they match exactly or the second string is a sub-string
593140271Sbrooks * of the first.  A warning is printed to stderr in the case that the
594140271Sbrooks * first string does not match the third.
595140271Sbrooks *
596140271Sbrooks * This function exists to warn about the bizzare construction
597140271Sbrooks * strncmp(str, "by", 2) which is used to allow people to use a shotcut
598140271Sbrooks * for "bytes".  The problem is that in addition to accepting "by",
599140271Sbrooks * "byt", "byte", and "bytes", it also excepts "by_rabid_dogs" and any
600140271Sbrooks * other string beginning with "by".
601140271Sbrooks *
602140271Sbrooks * This function will be removed in the future through the usual
603140271Sbrooks * deprecation process.
604140271Sbrooks */
605140271Sbrooksstatic int
606140271Sbrooks_substrcmp2(const char *str1, const char* str2, const char* str3)
607140271Sbrooks{
608140271Sbrooks
609140271Sbrooks	if (strncmp(str1, str2, strlen(str2)) != 0)
610140271Sbrooks		return 1;
611140271Sbrooks
612140271Sbrooks	if (strcmp(str1, str3) != 0)
613140271Sbrooks		warnx("DEPRECATED: '%s' matched '%s'",
614140271Sbrooks		    str1, str3);
615140271Sbrooks	return 0;
616140271Sbrooks}
617140271Sbrooks
618140271Sbrooks/*
61998943Sluigi * prints one port, symbolic or numeric
62098943Sluigi */
62198943Sluigistatic void
622117328Sluigiprint_port(int proto, uint16_t port)
62398943Sluigi{
62498943Sluigi
62598943Sluigi	if (proto == IPPROTO_ETHERTYPE) {
626117469Sluigi		char const *s;
62798943Sluigi
62898943Sluigi		if (do_resolv && (s = match_value(ether_types, port)) )
62998943Sluigi			printf("%s", s);
63098943Sluigi		else
63198943Sluigi			printf("0x%04x", port);
63298943Sluigi	} else {
63398943Sluigi		struct servent *se = NULL;
63498943Sluigi		if (do_resolv) {
63598943Sluigi			struct protoent *pe = getprotobynumber(proto);
63698943Sluigi
63798943Sluigi			se = getservbyport(htons(port), pe ? pe->p_name : NULL);
63898943Sluigi		}
63998943Sluigi		if (se)
64098943Sluigi			printf("%s", se->s_name);
64198943Sluigi		else
64298943Sluigi			printf("%d", port);
64398943Sluigi	}
64498943Sluigi}
64598943Sluigi
646117328Sluigistruct _s_x _port_name[] = {
647117328Sluigi	{"dst-port",	O_IP_DSTPORT},
648117328Sluigi	{"src-port",	O_IP_SRCPORT},
649117328Sluigi	{"ipid",	O_IPID},
650117328Sluigi	{"iplen",	O_IPLEN},
651117328Sluigi	{"ipttl",	O_IPTTL},
652117328Sluigi	{"mac-type",	O_MAC_TYPE},
653136075Sgreen	{"tcpdatalen",	O_TCPDATALEN},
654158879Soleg	{"tagged",	O_TAGGED},
655117328Sluigi	{NULL,		0}
656117328Sluigi};
657117328Sluigi
65898943Sluigi/*
659117328Sluigi * Print the values in a list 16-bit items of the types above.
66098943Sluigi * XXX todo: add support for mask.
66198943Sluigi */
66298943Sluigistatic void
663102087Sluigiprint_newports(ipfw_insn_u16 *cmd, int proto, int opcode)
66498943Sluigi{
665117328Sluigi	uint16_t *p = cmd->ports;
66698943Sluigi	int i;
667117469Sluigi	char const *sep;
66898943Sluigi
66998943Sluigi	if (cmd->o.len & F_NOT)
67098943Sluigi		printf(" not");
671116690Sluigi	if (opcode != 0) {
672117328Sluigi		sep = match_value(_port_name, opcode);
673117328Sluigi		if (sep == NULL)
674116690Sluigi			sep = "???";
675116690Sluigi		printf (" %s", sep);
676116690Sluigi	}
677116690Sluigi	sep = " ";
67898943Sluigi	for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
67998943Sluigi		printf(sep);
68098943Sluigi		print_port(proto, p[0]);
68198943Sluigi		if (p[0] != p[1]) {
68298943Sluigi			printf("-");
68398943Sluigi			print_port(proto, p[1]);
68498943Sluigi		}
68598943Sluigi		sep = ",";
68698943Sluigi	}
68798943Sluigi}
68898943Sluigi
68998943Sluigi/*
69098943Sluigi * Like strtol, but also translates service names into port numbers
69198943Sluigi * for some protocols.
69298943Sluigi * In particular:
69398943Sluigi *	proto == -1 disables the protocol check;
69498943Sluigi *	proto == IPPROTO_ETHERTYPE looks up an internal table
69598943Sluigi *	proto == <some value in /etc/protocols> matches the values there.
696101628Sluigi * Returns *end == s in case the parameter is not found.
69798943Sluigi */
69898943Sluigistatic int
69998943Sluigistrtoport(char *s, char **end, int base, int proto)
70098943Sluigi{
701101628Sluigi	char *p, *buf;
702101628Sluigi	char *s1;
70398943Sluigi	int i;
70498943Sluigi
705101628Sluigi	*end = s;		/* default - not found */
706117577Sluigi	if (*s == '\0')
707101628Sluigi		return 0;	/* not found */
708106505Smaxim
70998943Sluigi	if (isdigit(*s))
71098943Sluigi		return strtol(s, end, base);
71198943Sluigi
71298943Sluigi	/*
713101628Sluigi	 * find separator. '\\' escapes the next char.
71498943Sluigi	 */
715101628Sluigi	for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++)
716101628Sluigi		if (*s1 == '\\' && s1[1] != '\0')
717101628Sluigi			s1++;
71898943Sluigi
719101628Sluigi	buf = malloc(s1 - s + 1);
720101628Sluigi	if (buf == NULL)
721101628Sluigi		return 0;
722101628Sluigi
723101628Sluigi	/*
724101628Sluigi	 * copy into a buffer skipping backslashes
725101628Sluigi	 */
726101628Sluigi	for (p = s, i = 0; p != s1 ; p++)
727117577Sluigi		if (*p != '\\')
728101628Sluigi			buf[i++] = *p;
729101628Sluigi	buf[i++] = '\0';
730101628Sluigi
73198943Sluigi	if (proto == IPPROTO_ETHERTYPE) {
732101628Sluigi		i = match_token(ether_types, buf);
733101628Sluigi		free(buf);
734101628Sluigi		if (i != -1) {	/* found */
73598943Sluigi			*end = s1;
73698943Sluigi			return i;
73798943Sluigi		}
73898943Sluigi	} else {
73998943Sluigi		struct protoent *pe = NULL;
74098943Sluigi		struct servent *se;
74198943Sluigi
74298943Sluigi		if (proto != 0)
74398943Sluigi			pe = getprotobynumber(proto);
74498943Sluigi		setservent(1);
745101628Sluigi		se = getservbyname(buf, pe ? pe->p_name : NULL);
746101628Sluigi		free(buf);
74798943Sluigi		if (se != NULL) {
74898943Sluigi			*end = s1;
74998943Sluigi			return ntohs(se->s_port);
75098943Sluigi		}
75198943Sluigi	}
752101628Sluigi	return 0;	/* not found */
75398943Sluigi}
75498943Sluigi
75598943Sluigi/*
756136071Sgreen * Map between current altq queue id numbers and names.
757136071Sgreen */
758136071Sgreenstatic int altq_fetched = 0;
759136071Sgreenstatic TAILQ_HEAD(, pf_altq) altq_entries =
760136071Sgreen	TAILQ_HEAD_INITIALIZER(altq_entries);
761136071Sgreen
762136071Sgreenstatic void
763136071Sgreenaltq_set_enabled(int enabled)
764136071Sgreen{
765136071Sgreen	int pffd;
766136071Sgreen
767136071Sgreen	pffd = open("/dev/pf", O_RDWR);
768136071Sgreen	if (pffd == -1)
769136071Sgreen		err(EX_UNAVAILABLE,
770136071Sgreen		    "altq support opening pf(4) control device");
771136071Sgreen	if (enabled) {
772136071Sgreen		if (ioctl(pffd, DIOCSTARTALTQ) != 0 && errno != EEXIST)
773136071Sgreen			err(EX_UNAVAILABLE, "enabling altq");
774136071Sgreen	} else {
775136071Sgreen		if (ioctl(pffd, DIOCSTOPALTQ) != 0 && errno != ENOENT)
776136071Sgreen			err(EX_UNAVAILABLE, "disabling altq");
777136071Sgreen	}
778136071Sgreen	close(pffd);
779136071Sgreen}
780136071Sgreen
781136071Sgreenstatic void
782136071Sgreenaltq_fetch()
783136071Sgreen{
784136071Sgreen	struct pfioc_altq pfioc;
785136071Sgreen	struct pf_altq *altq;
786136071Sgreen	int pffd, mnr;
787136071Sgreen
788136071Sgreen	if (altq_fetched)
789136071Sgreen		return;
790136071Sgreen	altq_fetched = 1;
791136071Sgreen	pffd = open("/dev/pf", O_RDONLY);
792136071Sgreen	if (pffd == -1) {
793136071Sgreen		warn("altq support opening pf(4) control device");
794136071Sgreen		return;
795136071Sgreen	}
796136071Sgreen	bzero(&pfioc, sizeof(pfioc));
797136071Sgreen	if (ioctl(pffd, DIOCGETALTQS, &pfioc) != 0) {
798136071Sgreen		warn("altq support getting queue list");
799136071Sgreen		close(pffd);
800136071Sgreen		return;
801136071Sgreen	}
802136071Sgreen	mnr = pfioc.nr;
803136071Sgreen	for (pfioc.nr = 0; pfioc.nr < mnr; pfioc.nr++) {
804136071Sgreen		if (ioctl(pffd, DIOCGETALTQ, &pfioc) != 0) {
805136071Sgreen			if (errno == EBUSY)
806136071Sgreen				break;
807136071Sgreen			warn("altq support getting queue list");
808136071Sgreen			close(pffd);
809136071Sgreen			return;
810136071Sgreen		}
811136071Sgreen		if (pfioc.altq.qid == 0)
812136071Sgreen			continue;
813136071Sgreen		altq = malloc(sizeof(*altq));
814136071Sgreen		if (altq == NULL)
815136071Sgreen			err(EX_OSERR, "malloc");
816136071Sgreen		*altq = pfioc.altq;
817136071Sgreen		TAILQ_INSERT_TAIL(&altq_entries, altq, entries);
818136071Sgreen	}
819136071Sgreen	close(pffd);
820136071Sgreen}
821136071Sgreen
822136071Sgreenstatic u_int32_t
823136071Sgreenaltq_name_to_qid(const char *name)
824136071Sgreen{
825136071Sgreen	struct pf_altq *altq;
826136071Sgreen
827136071Sgreen	altq_fetch();
828136071Sgreen	TAILQ_FOREACH(altq, &altq_entries, entries)
829136071Sgreen		if (strcmp(name, altq->qname) == 0)
830136071Sgreen			break;
831136071Sgreen	if (altq == NULL)
832136071Sgreen		errx(EX_DATAERR, "altq has no queue named `%s'", name);
833136071Sgreen	return altq->qid;
834136071Sgreen}
835136071Sgreen
836136071Sgreenstatic const char *
837136071Sgreenaltq_qid_to_name(u_int32_t qid)
838136071Sgreen{
839136071Sgreen	struct pf_altq *altq;
840136071Sgreen
841136071Sgreen	altq_fetch();
842136071Sgreen	TAILQ_FOREACH(altq, &altq_entries, entries)
843136071Sgreen		if (qid == altq->qid)
844136071Sgreen			break;
845136071Sgreen	if (altq == NULL)
846136071Sgreen		return NULL;
847136071Sgreen	return altq->qname;
848136071Sgreen}
849136071Sgreen
850136071Sgreenstatic void
851136071Sgreenfill_altq_qid(u_int32_t *qid, const char *av)
852136071Sgreen{
853136071Sgreen	*qid = altq_name_to_qid(av);
854136071Sgreen}
855136071Sgreen
856136071Sgreen/*
857117328Sluigi * Fill the body of the command with the list of port ranges.
85898943Sluigi */
85998943Sluigistatic int
86098943Sluigifill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
86198943Sluigi{
862117328Sluigi	uint16_t a, b, *p = cmd->ports;
86398943Sluigi	int i = 0;
864102087Sluigi	char *s = av;
86598943Sluigi
866102087Sluigi	while (*s) {
86798943Sluigi		a = strtoport(av, &s, 0, proto);
868159636Soleg		if (s == av) 			/* empty or invalid argument */
869159636Soleg			return (0);
870159636Soleg
871159636Soleg		switch (*s) {
872159636Soleg		case '-':			/* a range */
873159636Soleg			av = s + 1;
87498943Sluigi			b = strtoport(av, &s, 0, proto);
875159636Soleg			/* Reject expressions like '1-abc' or '1-2-3'. */
876159636Soleg			if (s == av || (*s != ',' && *s != '\0'))
877159636Soleg				return (0);
87898943Sluigi			p[0] = a;
87998943Sluigi			p[1] = b;
880159636Soleg			break;
881159636Soleg		case ',':			/* comma separated list */
882159636Soleg		case '\0':
88398943Sluigi			p[0] = p[1] = a;
884159636Soleg			break;
885159636Soleg		default:
886159636Soleg			warnx("port list: invalid separator <%c> in <%s>",
887101978Sluigi				*s, av);
888159636Soleg			return (0);
889159636Soleg		}
890159636Soleg
891102087Sluigi		i++;
892102087Sluigi		p += 2;
893159636Soleg		av = s + 1;
89498943Sluigi	}
89598943Sluigi	if (i > 0) {
896159636Soleg		if (i + 1 > F_LEN_MASK)
897102087Sluigi			errx(EX_DATAERR, "too many ports/ranges\n");
898159636Soleg		cmd->o.len |= i + 1;	/* leave F_NOT and F_OR untouched */
89998943Sluigi	}
900159636Soleg	return (i);
90198943Sluigi}
90298943Sluigi
90398943Sluigistatic struct _s_x icmpcodes[] = {
90498943Sluigi      { "net",			ICMP_UNREACH_NET },
90598943Sluigi      { "host",			ICMP_UNREACH_HOST },
90698943Sluigi      { "protocol",		ICMP_UNREACH_PROTOCOL },
90798943Sluigi      { "port",			ICMP_UNREACH_PORT },
90898943Sluigi      { "needfrag",		ICMP_UNREACH_NEEDFRAG },
90998943Sluigi      { "srcfail",		ICMP_UNREACH_SRCFAIL },
91098943Sluigi      { "net-unknown",		ICMP_UNREACH_NET_UNKNOWN },
91198943Sluigi      { "host-unknown",		ICMP_UNREACH_HOST_UNKNOWN },
91298943Sluigi      { "isolated",		ICMP_UNREACH_ISOLATED },
91398943Sluigi      { "net-prohib",		ICMP_UNREACH_NET_PROHIB },
91498943Sluigi      { "host-prohib",		ICMP_UNREACH_HOST_PROHIB },
91598943Sluigi      { "tosnet",		ICMP_UNREACH_TOSNET },
91698943Sluigi      { "toshost",		ICMP_UNREACH_TOSHOST },
91798943Sluigi      { "filter-prohib",	ICMP_UNREACH_FILTER_PROHIB },
91898943Sluigi      { "host-precedence",	ICMP_UNREACH_HOST_PRECEDENCE },
91998943Sluigi      { "precedence-cutoff",	ICMP_UNREACH_PRECEDENCE_CUTOFF },
92098943Sluigi      { NULL, 0 }
92198943Sluigi};
92298943Sluigi
92398943Sluigistatic void
92498943Sluigifill_reject_code(u_short *codep, char *str)
92598943Sluigi{
92698943Sluigi	int val;
92798943Sluigi	char *s;
92898943Sluigi
92998943Sluigi	val = strtoul(str, &s, 0);
93098943Sluigi	if (s == str || *s != '\0' || val >= 0x100)
93198943Sluigi		val = match_token(icmpcodes, str);
932102087Sluigi	if (val < 0)
93398943Sluigi		errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
93498943Sluigi	*codep = val;
93598943Sluigi	return;
93698943Sluigi}
93798943Sluigi
93898943Sluigistatic void
939117328Sluigiprint_reject_code(uint16_t code)
94098943Sluigi{
941117469Sluigi	char const *s = match_value(icmpcodes, code);
94298943Sluigi
94398943Sluigi	if (s != NULL)
94499475Sluigi		printf("unreach %s", s);
94598943Sluigi	else
94699475Sluigi		printf("unreach %u", code);
94798943Sluigi}
94898943Sluigi
949149020Sbzstatic struct _s_x icmp6codes[] = {
950149020Sbz      { "no-route",		ICMP6_DST_UNREACH_NOROUTE },
951149020Sbz      { "admin-prohib",		ICMP6_DST_UNREACH_ADMIN },
952149020Sbz      { "address",		ICMP6_DST_UNREACH_ADDR },
953149020Sbz      { "port",			ICMP6_DST_UNREACH_NOPORT },
954149020Sbz      { NULL, 0 }
955149020Sbz};
956149020Sbz
957149020Sbzstatic void
958149020Sbzfill_unreach6_code(u_short *codep, char *str)
959149020Sbz{
960149020Sbz	int val;
961149020Sbz	char *s;
962149020Sbz
963149020Sbz	val = strtoul(str, &s, 0);
964149020Sbz	if (s == str || *s != '\0' || val >= 0x100)
965149020Sbz		val = match_token(icmp6codes, str);
966149020Sbz	if (val < 0)
967149020Sbz		errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str);
968149020Sbz	*codep = val;
969149020Sbz	return;
970149020Sbz}
971149020Sbz
972149020Sbzstatic void
973149020Sbzprint_unreach6_code(uint16_t code)
974149020Sbz{
975149020Sbz	char const *s = match_value(icmp6codes, code);
976149020Sbz
977149020Sbz	if (s != NULL)
978149020Sbz		printf("unreach6 %s", s);
979149020Sbz	else
980149020Sbz		printf("unreach6 %u", code);
981149020Sbz}
982149020Sbz
98398943Sluigi/*
98498943Sluigi * Returns the number of bits set (from left) in a contiguous bitmask,
98598943Sluigi * or -1 if the mask is not contiguous.
98698943Sluigi * XXX this needs a proper fix.
98798943Sluigi * This effectively works on masks in big-endian (network) format.
98898943Sluigi * when compiled on little endian architectures.
98998943Sluigi *
99098943Sluigi * First bit is bit 7 of the first byte -- note, for MAC addresses,
99198943Sluigi * the first bit on the wire is bit 0 of the first byte.
99298943Sluigi * len is the max length in bits.
99398943Sluigi */
99498943Sluigistatic int
995117577Sluigicontigmask(uint8_t *p, int len)
99698943Sluigi{
99798943Sluigi	int i, n;
998117577Sluigi
99998943Sluigi	for (i=0; i<len ; i++)
100098943Sluigi		if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
100198943Sluigi			break;
100298943Sluigi	for (n=i+1; n < len; n++)
100398943Sluigi		if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
100498943Sluigi			return -1; /* mask not contiguous */
100598943Sluigi	return i;
100698943Sluigi}
100798943Sluigi
100898943Sluigi/*
100998943Sluigi * print flags set/clear in the two bitmasks passed as parameters.
101098943Sluigi * There is a specialized check for f_tcpflags.
101198943Sluigi */
101298943Sluigistatic void
1013117469Sluigiprint_flags(char const *name, ipfw_insn *cmd, struct _s_x *list)
101498943Sluigi{
1015117469Sluigi	char const *comma = "";
101698943Sluigi	int i;
1017117577Sluigi	uint8_t set = cmd->arg1 & 0xff;
1018117577Sluigi	uint8_t clear = (cmd->arg1 >> 8) & 0xff;
101998943Sluigi
102098943Sluigi	if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) {
102198943Sluigi		printf(" setup");
102298943Sluigi		return;
102398943Sluigi	}
102498943Sluigi
102598943Sluigi	printf(" %s ", name);
102698943Sluigi	for (i=0; list[i].x != 0; i++) {
102798943Sluigi		if (set & list[i].x) {
102898943Sluigi			set &= ~list[i].x;
102998943Sluigi			printf("%s%s", comma, list[i].s);
103098943Sluigi			comma = ",";
103198943Sluigi		}
103298943Sluigi		if (clear & list[i].x) {
103398943Sluigi			clear &= ~list[i].x;
103498943Sluigi			printf("%s!%s", comma, list[i].s);
103598943Sluigi			comma = ",";
103698943Sluigi		}
103798943Sluigi	}
103898943Sluigi}
103998943Sluigi
104098943Sluigi/*
104198943Sluigi * Print the ip address contained in a command.
104298943Sluigi */
104398943Sluigistatic void
1044117469Sluigiprint_ip(ipfw_insn_ip *cmd, char const *s)
104598943Sluigi{
104698943Sluigi	struct hostent *he = NULL;
1047117328Sluigi	int len = F_LEN((ipfw_insn *)cmd);
1048117328Sluigi	uint32_t *a = ((ipfw_insn_u32 *)cmd)->d;
104998943Sluigi
1050102087Sluigi	printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
105198943Sluigi
105298943Sluigi	if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) {
105398943Sluigi		printf("me");
105498943Sluigi		return;
105598943Sluigi	}
1056130281Sru	if (cmd->o.opcode == O_IP_SRC_LOOKUP ||
1057130281Sru	    cmd->o.opcode == O_IP_DST_LOOKUP) {
1058130281Sru		printf("table(%u", ((ipfw_insn *)cmd)->arg1);
1059130281Sru		if (len == F_INSN_SIZE(ipfw_insn_u32))
1060130281Sru			printf(",%u", *a);
1061130281Sru		printf(")");
1062130281Sru		return;
1063130281Sru	}
106498943Sluigi	if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) {
1065117328Sluigi		uint32_t x, *map = (uint32_t *)&(cmd->mask);
1066116716Sluigi		int i, j;
106798943Sluigi		char comma = '{';
106898943Sluigi
106998943Sluigi		x = cmd->o.arg1 - 1;
107098943Sluigi		x = htonl( ~x );
107198943Sluigi		cmd->addr.s_addr = htonl(cmd->addr.s_addr);
107298943Sluigi		printf("%s/%d", inet_ntoa(cmd->addr),
1073117577Sluigi			contigmask((uint8_t *)&x, 32));
107498943Sluigi		x = cmd->addr.s_addr = htonl(cmd->addr.s_addr);
107598943Sluigi		x &= 0xff; /* base */
1076116716Sluigi		/*
1077116716Sluigi		 * Print bits and ranges.
1078116716Sluigi		 * Locate first bit set (i), then locate first bit unset (j).
1079116716Sluigi		 * If we have 3+ consecutive bits set, then print them as a
1080116716Sluigi		 * range, otherwise only print the initial bit and rescan.
1081116716Sluigi		 */
108298943Sluigi		for (i=0; i < cmd->o.arg1; i++)
1083117328Sluigi			if (map[i/32] & (1<<(i & 31))) {
1084116716Sluigi				for (j=i+1; j < cmd->o.arg1; j++)
1085117328Sluigi					if (!(map[ j/32] & (1<<(j & 31))))
1086116716Sluigi						break;
108798943Sluigi				printf("%c%d", comma, i+x);
1088116716Sluigi				if (j>i+2) { /* range has at least 3 elements */
1089116716Sluigi					printf("-%d", j-1+x);
1090116716Sluigi					i = j-1;
1091116716Sluigi				}
109298943Sluigi				comma = ',';
109398943Sluigi			}
109498943Sluigi		printf("}");
109598943Sluigi		return;
109698943Sluigi	}
1097117328Sluigi	/*
1098117328Sluigi	 * len == 2 indicates a single IP, whereas lists of 1 or more
1099117328Sluigi	 * addr/mask pairs have len = (2n+1). We convert len to n so we
1100117328Sluigi	 * use that to count the number of entries.
1101117328Sluigi	 */
1102117328Sluigi    for (len = len / 2; len > 0; len--, a += 2) {
1103117328Sluigi	int mb =	/* mask length */
1104117328Sluigi	    (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ?
1105117577Sluigi		32 : contigmask((uint8_t *)&(a[1]), 32);
110698943Sluigi	if (mb == 32 && do_resolv)
1107117328Sluigi		he = gethostbyaddr((char *)&(a[0]), sizeof(u_long), AF_INET);
110898943Sluigi	if (he != NULL)		/* resolved to name */
110998943Sluigi		printf("%s", he->h_name);
111098943Sluigi	else if (mb == 0)	/* any */
111198943Sluigi		printf("any");
111298943Sluigi	else {		/* numeric IP followed by some kind of mask */
1113117328Sluigi		printf("%s", inet_ntoa( *((struct in_addr *)&a[0]) ) );
111498943Sluigi		if (mb < 0)
1115117328Sluigi			printf(":%s", inet_ntoa( *((struct in_addr *)&a[1]) ) );
111698943Sluigi		else if (mb < 32)
111798943Sluigi			printf("/%d", mb);
111898943Sluigi	}
1119117328Sluigi	if (len > 1)
1120117328Sluigi		printf(",");
1121117328Sluigi    }
112298943Sluigi}
112398943Sluigi
112498943Sluigi/*
112598943Sluigi * prints a MAC address/mask pair
112698943Sluigi */
112798943Sluigistatic void
1128117577Sluigiprint_mac(uint8_t *addr, uint8_t *mask)
112998943Sluigi{
113098943Sluigi	int l = contigmask(mask, 48);
113198943Sluigi
113298943Sluigi	if (l == 0)
113398943Sluigi		printf(" any");
113498943Sluigi	else {
113598943Sluigi		printf(" %02x:%02x:%02x:%02x:%02x:%02x",
113698943Sluigi		    addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
113798943Sluigi		if (l == -1)
113898943Sluigi			printf("&%02x:%02x:%02x:%02x:%02x:%02x",
113998943Sluigi			    mask[0], mask[1], mask[2],
114098943Sluigi			    mask[3], mask[4], mask[5]);
114198943Sluigi		else if (l < 48)
114298943Sluigi			printf("/%d", l);
114398943Sluigi	}
114498943Sluigi}
114598943Sluigi
114699475Sluigistatic void
114799475Sluigifill_icmptypes(ipfw_insn_u32 *cmd, char *av)
114899475Sluigi{
1149117328Sluigi	uint8_t type;
115098943Sluigi
115199475Sluigi	cmd->d[0] = 0;
115299475Sluigi	while (*av) {
115399475Sluigi		if (*av == ',')
115499475Sluigi			av++;
115599475Sluigi
115699475Sluigi		type = strtoul(av, &av, 0);
115799475Sluigi
115899475Sluigi		if (*av != ',' && *av != '\0')
115999475Sluigi			errx(EX_DATAERR, "invalid ICMP type");
116099475Sluigi
116199475Sluigi		if (type > 31)
116299475Sluigi			errx(EX_DATAERR, "ICMP type out of range");
116399475Sluigi
116499475Sluigi		cmd->d[0] |= 1 << type;
116599475Sluigi	}
116699475Sluigi	cmd->o.opcode = O_ICMPTYPE;
116799475Sluigi	cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
116899475Sluigi}
116999475Sluigi
117099475Sluigistatic void
117199475Sluigiprint_icmptypes(ipfw_insn_u32 *cmd)
117299475Sluigi{
117399475Sluigi	int i;
117499475Sluigi	char sep= ' ';
117599475Sluigi
117699475Sluigi	printf(" icmptypes");
117799475Sluigi	for (i = 0; i < 32; i++) {
117899475Sluigi		if ( (cmd->d[0] & (1 << (i))) == 0)
117999475Sluigi			continue;
118099475Sluigi		printf("%c%d", sep, i);
118199475Sluigi		sep = ',';
118299475Sluigi	}
118399475Sluigi}
118499475Sluigi
1185145246Sbrooks/*
1186145246Sbrooks * Print the ip address contained in a command.
1187145246Sbrooks */
1188145246Sbrooksstatic void
1189145246Sbrooksprint_ip6(ipfw_insn_ip6 *cmd, char const *s)
1190145246Sbrooks{
1191145246Sbrooks       struct hostent *he = NULL;
1192145246Sbrooks       int len = F_LEN((ipfw_insn *) cmd) - 1;
1193145246Sbrooks       struct in6_addr *a = &(cmd->addr6);
1194145246Sbrooks       char trad[255];
1195145246Sbrooks
1196145246Sbrooks       printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
1197145246Sbrooks
1198145246Sbrooks       if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) {
1199145246Sbrooks               printf("me6");
1200145246Sbrooks               return;
1201145246Sbrooks       }
1202145246Sbrooks       if (cmd->o.opcode == O_IP6) {
1203152923Sume               printf(" ip6");
1204145246Sbrooks               return;
1205145246Sbrooks       }
1206145246Sbrooks
1207145246Sbrooks       /*
1208145246Sbrooks        * len == 4 indicates a single IP, whereas lists of 1 or more
1209145246Sbrooks        * addr/mask pairs have len = (2n+1). We convert len to n so we
1210145246Sbrooks        * use that to count the number of entries.
1211145246Sbrooks        */
1212145246Sbrooks
1213145246Sbrooks       for (len = len / 4; len > 0; len -= 2, a += 2) {
1214145246Sbrooks           int mb =        /* mask length */
1215145246Sbrooks               (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ?
1216145246Sbrooks               128 : contigmask((uint8_t *)&(a[1]), 128);
1217145246Sbrooks
1218145246Sbrooks           if (mb == 128 && do_resolv)
1219145246Sbrooks               he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6);
1220145246Sbrooks           if (he != NULL)             /* resolved to name */
1221145246Sbrooks               printf("%s", he->h_name);
1222145246Sbrooks           else if (mb == 0)           /* any */
1223145246Sbrooks               printf("any");
1224145246Sbrooks           else {          /* numeric IP followed by some kind of mask */
1225145246Sbrooks               if (inet_ntop(AF_INET6,  a, trad, sizeof( trad ) ) == NULL)
1226145246Sbrooks                   printf("Error ntop in print_ip6\n");
1227145246Sbrooks               printf("%s",  trad );
1228145246Sbrooks               if (mb < 0)     /* XXX not really legal... */
1229145246Sbrooks                   printf(":%s",
1230145246Sbrooks                       inet_ntop(AF_INET6, &a[1], trad, sizeof(trad)));
1231145246Sbrooks               else if (mb < 128)
1232145246Sbrooks                   printf("/%d", mb);
1233145246Sbrooks           }
1234145246Sbrooks           if (len > 2)
1235145246Sbrooks               printf(",");
1236145246Sbrooks       }
1237145246Sbrooks}
1238145246Sbrooks
1239145246Sbrooksstatic void
1240145246Sbrooksfill_icmp6types(ipfw_insn_icmp6 *cmd, char *av)
1241145246Sbrooks{
1242145246Sbrooks       uint8_t type;
1243145246Sbrooks
1244162344Sjhay       bzero(cmd, sizeof(*cmd));
1245145246Sbrooks       while (*av) {
1246145246Sbrooks           if (*av == ',')
1247145246Sbrooks               av++;
1248145246Sbrooks           type = strtoul(av, &av, 0);
1249145246Sbrooks           if (*av != ',' && *av != '\0')
1250145246Sbrooks               errx(EX_DATAERR, "invalid ICMP6 type");
1251145246Sbrooks	   /*
1252145246Sbrooks	    * XXX: shouldn't this be 0xFF?  I can't see any reason why
1253145246Sbrooks	    * we shouldn't be able to filter all possiable values
1254145246Sbrooks	    * regardless of the ability of the rest of the kernel to do
1255145246Sbrooks	    * anything useful with them.
1256145246Sbrooks	    */
1257145246Sbrooks           if (type > ICMP6_MAXTYPE)
1258145246Sbrooks               errx(EX_DATAERR, "ICMP6 type out of range");
1259145246Sbrooks           cmd->d[type / 32] |= ( 1 << (type % 32));
1260145246Sbrooks       }
1261145246Sbrooks       cmd->o.opcode = O_ICMP6TYPE;
1262145246Sbrooks       cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6);
1263145246Sbrooks}
1264145246Sbrooks
1265145246Sbrooks
1266145246Sbrooksstatic void
1267145246Sbrooksprint_icmp6types(ipfw_insn_u32 *cmd)
1268145246Sbrooks{
1269145246Sbrooks       int i, j;
1270145246Sbrooks       char sep= ' ';
1271145246Sbrooks
1272152923Sume       printf(" ip6 icmp6types");
1273145246Sbrooks       for (i = 0; i < 7; i++)
1274145246Sbrooks               for (j=0; j < 32; ++j) {
1275145246Sbrooks                       if ( (cmd->d[i] & (1 << (j))) == 0)
1276145246Sbrooks                               continue;
1277145246Sbrooks                       printf("%c%d", sep, (i*32 + j));
1278145246Sbrooks                       sep = ',';
1279145246Sbrooks               }
1280145246Sbrooks}
1281145246Sbrooks
1282145246Sbrooksstatic void
1283145246Sbrooksprint_flow6id( ipfw_insn_u32 *cmd)
1284145246Sbrooks{
1285145246Sbrooks       uint16_t i, limit = cmd->o.arg1;
1286145246Sbrooks       char sep = ',';
1287145246Sbrooks
1288145246Sbrooks       printf(" flow-id ");
1289145246Sbrooks       for( i=0; i < limit; ++i) {
1290145246Sbrooks               if (i == limit - 1)
1291145246Sbrooks                       sep = ' ';
1292145246Sbrooks               printf("%d%c", cmd->d[i], sep);
1293145246Sbrooks       }
1294145246Sbrooks}
1295145246Sbrooks
1296145246Sbrooks/* structure and define for the extension header in ipv6 */
1297145246Sbrooksstatic struct _s_x ext6hdrcodes[] = {
1298145246Sbrooks       { "frag",       EXT_FRAGMENT },
1299145246Sbrooks       { "hopopt",     EXT_HOPOPTS },
1300145246Sbrooks       { "route",      EXT_ROUTING },
1301149020Sbz       { "dstopt",     EXT_DSTOPTS },
1302145246Sbrooks       { "ah",         EXT_AH },
1303145246Sbrooks       { "esp",        EXT_ESP },
1304145246Sbrooks       { NULL,         0 }
1305145246Sbrooks};
1306145246Sbrooks
1307145246Sbrooks/* fills command for the extension header filtering */
1308145246Sbrooksint
1309145246Sbrooksfill_ext6hdr( ipfw_insn *cmd, char *av)
1310145246Sbrooks{
1311145246Sbrooks       int tok;
1312145246Sbrooks       char *s = av;
1313145246Sbrooks
1314145246Sbrooks       cmd->arg1 = 0;
1315145246Sbrooks
1316145246Sbrooks       while(s) {
1317145246Sbrooks           av = strsep( &s, ",") ;
1318145246Sbrooks           tok = match_token(ext6hdrcodes, av);
1319145246Sbrooks           switch (tok) {
1320145246Sbrooks           case EXT_FRAGMENT:
1321145246Sbrooks               cmd->arg1 |= EXT_FRAGMENT;
1322145246Sbrooks               break;
1323145246Sbrooks
1324145246Sbrooks           case EXT_HOPOPTS:
1325145246Sbrooks               cmd->arg1 |= EXT_HOPOPTS;
1326145246Sbrooks               break;
1327145246Sbrooks
1328145246Sbrooks           case EXT_ROUTING:
1329145246Sbrooks               cmd->arg1 |= EXT_ROUTING;
1330145246Sbrooks               break;
1331145246Sbrooks
1332149020Sbz           case EXT_DSTOPTS:
1333149020Sbz               cmd->arg1 |= EXT_DSTOPTS;
1334149020Sbz               break;
1335149020Sbz
1336145246Sbrooks           case EXT_AH:
1337145246Sbrooks               cmd->arg1 |= EXT_AH;
1338145246Sbrooks               break;
1339145246Sbrooks
1340145246Sbrooks           case EXT_ESP:
1341145246Sbrooks               cmd->arg1 |= EXT_ESP;
1342145246Sbrooks               break;
1343145246Sbrooks
1344145246Sbrooks           default:
1345145246Sbrooks               errx( EX_DATAERR, "invalid option for ipv6 exten header" );
1346145246Sbrooks               break;
1347145246Sbrooks           }
1348145246Sbrooks       }
1349145246Sbrooks       if (cmd->arg1 == 0 )
1350145246Sbrooks           return 0;
1351145246Sbrooks       cmd->opcode = O_EXT_HDR;
1352145246Sbrooks       cmd->len |= F_INSN_SIZE( ipfw_insn );
1353145246Sbrooks       return 1;
1354145246Sbrooks}
1355145246Sbrooks
1356145246Sbrooksvoid
1357145246Sbrooksprint_ext6hdr( ipfw_insn *cmd )
1358145246Sbrooks{
1359145246Sbrooks       char sep = ' ';
1360145246Sbrooks
1361145246Sbrooks       printf(" extension header:");
1362145246Sbrooks       if (cmd->arg1 & EXT_FRAGMENT ) {
1363145246Sbrooks           printf("%cfragmentation", sep);
1364145246Sbrooks           sep = ',';
1365145246Sbrooks       }
1366145246Sbrooks       if (cmd->arg1 & EXT_HOPOPTS ) {
1367145246Sbrooks           printf("%chop options", sep);
1368145246Sbrooks           sep = ',';
1369145246Sbrooks       }
1370145246Sbrooks       if (cmd->arg1 & EXT_ROUTING ) {
1371145246Sbrooks           printf("%crouting options", sep);
1372145246Sbrooks           sep = ',';
1373145246Sbrooks       }
1374149020Sbz       if (cmd->arg1 & EXT_DSTOPTS ) {
1375149020Sbz           printf("%cdestination options", sep);
1376149020Sbz           sep = ',';
1377149020Sbz       }
1378145246Sbrooks       if (cmd->arg1 & EXT_AH ) {
1379145246Sbrooks           printf("%cauthentication header", sep);
1380145246Sbrooks           sep = ',';
1381145246Sbrooks       }
1382145246Sbrooks       if (cmd->arg1 & EXT_ESP ) {
1383145246Sbrooks           printf("%cencapsulated security payload", sep);
1384145246Sbrooks       }
1385145246Sbrooks}
1386145246Sbrooks
138798943Sluigi/*
138898943Sluigi * show_ipfw() prints the body of an ipfw rule.
138998943Sluigi * Because the standard rule has at least proto src_ip dst_ip, we use
139098943Sluigi * a helper function to produce these entries if not provided explicitly.
1391102087Sluigi * The first argument is the list of fields we have, the second is
1392102087Sluigi * the list of fields we want to be printed.
1393101978Sluigi *
1394102087Sluigi * Special cases if we have provided a MAC header:
1395102087Sluigi *   + if the rule does not contain IP addresses/ports, do not print them;
1396102087Sluigi *   + if the rule does not contain an IP proto, print "all" instead of "ip";
1397102087Sluigi *
1398102087Sluigi * Once we have 'have_options', IP header fields are printed as options.
139998943Sluigi */
1400101978Sluigi#define	HAVE_PROTO	0x0001
1401101978Sluigi#define	HAVE_SRCIP	0x0002
1402101978Sluigi#define	HAVE_DSTIP	0x0004
1403169139Smaxim#define	HAVE_PROTO4	0x0008
1404169139Smaxim#define	HAVE_PROTO6	0x0010
1405102087Sluigi#define	HAVE_OPTIONS	0x8000
140698943Sluigi
1407101978Sluigi#define	HAVE_IP		(HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP)
140898943Sluigistatic void
1409102087Sluigishow_prerequisites(int *flags, int want, int cmd)
141098943Sluigi{
1411123495Sluigi	if (comment_only)
1412123495Sluigi		return;
1413102087Sluigi	if ( (*flags & HAVE_IP) == HAVE_IP)
1414102087Sluigi		*flags |= HAVE_OPTIONS;
1415102087Sluigi
1416102087Sluigi	if ( !(*flags & HAVE_OPTIONS)) {
1417102087Sluigi		if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO))
1418146894Smlaier			if ( (*flags & HAVE_PROTO4))
1419146894Smlaier				printf(" ip4");
1420146894Smlaier			else if ( (*flags & HAVE_PROTO6))
1421146894Smlaier				printf(" ip6");
1422146894Smlaier			else
1423146894Smlaier				printf(" ip");
1424146894Smlaier
1425102087Sluigi		if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
1426102087Sluigi			printf(" from any");
1427102087Sluigi		if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP))
1428102087Sluigi			printf(" to any");
1429102087Sluigi	}
143098943Sluigi	*flags |= want;
143198943Sluigi}
143298943Sluigi
143398943Sluigistatic void
1434112189Smaximshow_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
143598943Sluigi{
1436107291Skeramida	static int twidth = 0;
143798943Sluigi	int l;
1438158879Soleg	ipfw_insn *cmd, *tagptr = NULL;
1439117626Sluigi	char *comment = NULL;	/* ptr to comment if we have one */
144098943Sluigi	int proto = 0;		/* default */
144198943Sluigi	int flags = 0;	/* prerequisites */
144298943Sluigi	ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */
1443136071Sgreen	ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */
144498943Sluigi	int or_block = 0;	/* we are in an or block */
1445117328Sluigi	uint32_t set_disable;
144698943Sluigi
1447115793Sticso	bcopy(&rule->next_rule, &set_disable, sizeof(set_disable));
1448101628Sluigi
1449101628Sluigi	if (set_disable & (1 << rule->set)) { /* disabled */
1450101628Sluigi		if (!show_sets)
1451101628Sluigi			return;
1452101628Sluigi		else
1453101628Sluigi			printf("# DISABLED ");
1454101628Sluigi	}
145598943Sluigi	printf("%05u ", rule->rulenum);
145698943Sluigi
1457117469Sluigi	if (pcwidth>0 || bcwidth>0)
1458115793Sticso		printf("%*llu %*llu ", pcwidth, align_uint64(&rule->pcnt),
1459115793Sticso		    bcwidth, align_uint64(&rule->bcnt));
146098943Sluigi
1461117472Sluigi	if (do_time == 2)
1462117472Sluigi		printf("%10u ", rule->timestamp);
1463117472Sluigi	else if (do_time == 1) {
1464107291Skeramida		char timestr[30];
1465107291Skeramida		time_t t = (time_t)0;
1466107291Skeramida
1467107291Skeramida		if (twidth == 0) {
1468107291Skeramida			strcpy(timestr, ctime(&t));
1469107291Skeramida			*strchr(timestr, '\n') = '\0';
1470107291Skeramida			twidth = strlen(timestr);
1471107291Skeramida		}
147298943Sluigi		if (rule->timestamp) {
1473107291Skeramida			t = _long_to_time(rule->timestamp);
147498943Sluigi
147598943Sluigi			strcpy(timestr, ctime(&t));
147698943Sluigi			*strchr(timestr, '\n') = '\0';
147798943Sluigi			printf("%s ", timestr);
147898943Sluigi		} else {
1479107291Skeramida			printf("%*s", twidth, " ");
148098943Sluigi		}
148198943Sluigi	}
148298943Sluigi
1483101628Sluigi	if (show_sets)
1484101628Sluigi		printf("set %d ", rule->set);
1485101628Sluigi
148698943Sluigi	/*
1487107289Sluigi	 * print the optional "match probability"
1488107289Sluigi	 */
1489107289Sluigi	if (rule->cmd_len > 0) {
1490107289Sluigi		cmd = rule->cmd ;
1491107289Sluigi		if (cmd->opcode == O_PROB) {
1492107289Sluigi			ipfw_insn_u32 *p = (ipfw_insn_u32 *)cmd;
1493107289Sluigi			double d = 1.0 * p->d[0];
1494107289Sluigi
1495107289Sluigi			d = (d / 0x7fffffff);
1496107289Sluigi			printf("prob %f ", d);
1497107289Sluigi		}
1498107289Sluigi	}
1499107289Sluigi
1500107289Sluigi	/*
150198943Sluigi	 * first print actions
150298943Sluigi	 */
150398943Sluigi        for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule);
150498943Sluigi			l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
150598943Sluigi		switch(cmd->opcode) {
150698943Sluigi		case O_CHECK_STATE:
150798943Sluigi			printf("check-state");
1508102087Sluigi			flags = HAVE_IP; /* avoid printing anything else */
150998943Sluigi			break;
151098943Sluigi
151198943Sluigi		case O_ACCEPT:
151298943Sluigi			printf("allow");
151398943Sluigi			break;
151498943Sluigi
151598943Sluigi		case O_COUNT:
151698943Sluigi			printf("count");
151798943Sluigi			break;
151898943Sluigi
151998943Sluigi		case O_DENY:
152098943Sluigi			printf("deny");
152198943Sluigi			break;
152298943Sluigi
152399475Sluigi		case O_REJECT:
152499475Sluigi			if (cmd->arg1 == ICMP_REJECT_RST)
152599475Sluigi				printf("reset");
152699475Sluigi			else if (cmd->arg1 == ICMP_UNREACH_HOST)
152799475Sluigi				printf("reject");
152899475Sluigi			else
152999475Sluigi				print_reject_code(cmd->arg1);
153099475Sluigi			break;
153199475Sluigi
1532149020Sbz		case O_UNREACH6:
1533149020Sbz			if (cmd->arg1 == ICMP6_UNREACH_RST)
1534149020Sbz				printf("reset6");
1535149020Sbz			else
1536149020Sbz				print_unreach6_code(cmd->arg1);
1537149020Sbz			break;
1538149020Sbz
1539159636Soleg		case O_SKIPTO:
1540159636Soleg			PRINT_UINT_ARG("skipto ", cmd->arg1);
154198943Sluigi			break;
154298943Sluigi
154398943Sluigi		case O_PIPE:
1544159636Soleg			PRINT_UINT_ARG("pipe ", cmd->arg1);
1545159636Soleg			break;
1546159636Soleg
154798943Sluigi		case O_QUEUE:
1548159636Soleg			PRINT_UINT_ARG("queue ", cmd->arg1);
1549159636Soleg			break;
1550159636Soleg
155198943Sluigi		case O_DIVERT:
1552159636Soleg			PRINT_UINT_ARG("divert ", cmd->arg1);
1553159636Soleg			break;
1554159636Soleg
155598943Sluigi		case O_TEE:
1556159636Soleg			PRINT_UINT_ARG("tee ", cmd->arg1);
1557159636Soleg			break;
1558159636Soleg
1559141351Sglebius		case O_NETGRAPH:
1560159636Soleg			PRINT_UINT_ARG("netgraph ", cmd->arg1);
1561159636Soleg			break;
1562159636Soleg
1563141351Sglebius		case O_NGTEE:
1564159636Soleg			PRINT_UINT_ARG("ngtee ", cmd->arg1);
1565159636Soleg			break;
1566141351Sglebius
156798943Sluigi		case O_FORWARD_IP:
156898943Sluigi		    {
156998943Sluigi			ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
157098943Sluigi
1571161424Sjulian			if (s->sa.sin_addr.s_addr == INADDR_ANY) {
1572161424Sjulian				printf("fwd tablearg");
1573161424Sjulian			} else {
1574161424Sjulian				printf("fwd %s", inet_ntoa(s->sa.sin_addr));
1575161424Sjulian			}
157698943Sluigi			if (s->sa.sin_port)
1577103241Sluigi				printf(",%d", s->sa.sin_port);
157898943Sluigi		    }
157998943Sluigi			break;
158098943Sluigi
158198943Sluigi		case O_LOG: /* O_LOG is printed last */
158298943Sluigi			logptr = (ipfw_insn_log *)cmd;
158398943Sluigi			break;
158498943Sluigi
1585136071Sgreen		case O_ALTQ: /* O_ALTQ is printed after O_LOG */
1586136071Sgreen			altqptr = (ipfw_insn_altq *)cmd;
1587136071Sgreen			break;
1588136071Sgreen
1589158879Soleg		case O_TAG:
1590158879Soleg			tagptr = cmd;
1591158879Soleg			break;
1592158879Soleg
1593165648Spiso		case O_NAT:
1594165648Spiso 			printf("nat %u", cmd->arg1);
1595165648Spiso 			break;
1596165648Spiso
159798943Sluigi		default:
1598136071Sgreen			printf("** unrecognized action %d len %d ",
159998943Sluigi				cmd->opcode, cmd->len);
160098943Sluigi		}
160198943Sluigi	}
160298943Sluigi	if (logptr) {
160398943Sluigi		if (logptr->max_log > 0)
160499909Sluigi			printf(" log logamount %d", logptr->max_log);
160598943Sluigi		else
160699909Sluigi			printf(" log");
160798943Sluigi	}
1608136071Sgreen	if (altqptr) {
1609136071Sgreen		const char *qname;
1610102087Sluigi
1611136071Sgreen		qname = altq_qid_to_name(altqptr->qid);
1612136071Sgreen		if (qname == NULL)
1613136071Sgreen			printf(" altq ?<%u>", altqptr->qid);
1614136071Sgreen		else
1615136071Sgreen			printf(" altq %s", qname);
1616136071Sgreen	}
1617158879Soleg	if (tagptr) {
1618158879Soleg		if (tagptr->len & F_NOT)
1619159636Soleg			PRINT_UINT_ARG(" untag ", tagptr->arg1);
1620158879Soleg		else
1621159636Soleg			PRINT_UINT_ARG(" tag ", tagptr->arg1);
1622158879Soleg	}
1623136071Sgreen
162498943Sluigi	/*
1625102087Sluigi	 * then print the body.
162698943Sluigi	 */
1627146894Smlaier        for (l = rule->act_ofs, cmd = rule->cmd ;
1628146894Smlaier			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
1629146894Smlaier		if ((cmd->len & F_OR) || (cmd->len & F_NOT))
1630146894Smlaier			continue;
1631146894Smlaier		if (cmd->opcode == O_IP4) {
1632146894Smlaier			flags |= HAVE_PROTO4;
1633146894Smlaier			break;
1634146894Smlaier		} else if (cmd->opcode == O_IP6) {
1635146894Smlaier			flags |= HAVE_PROTO6;
1636146894Smlaier			break;
1637146894Smlaier		}
1638146894Smlaier	}
1639102087Sluigi	if (rule->_pad & 1) {	/* empty rules before options */
1640146894Smlaier		if (!do_compact) {
1641146894Smlaier			show_prerequisites(&flags, HAVE_PROTO, 0);
1642146894Smlaier			printf(" from any to any");
1643146894Smlaier		}
1644102087Sluigi		flags |= HAVE_IP | HAVE_OPTIONS;
1645102087Sluigi	}
1646102087Sluigi
1647123495Sluigi	if (comment_only)
1648123495Sluigi		comment = "...";
1649123495Sluigi
165098943Sluigi        for (l = rule->act_ofs, cmd = rule->cmd ;
165198943Sluigi			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
165299475Sluigi		/* useful alias */
165399475Sluigi		ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
165498943Sluigi
1655123495Sluigi		if (comment_only) {
1656123495Sluigi			if (cmd->opcode != O_NOP)
1657123495Sluigi				continue;
1658123495Sluigi			printf(" // %s\n", (char *)(cmd + 1));
1659123495Sluigi			return;
1660123495Sluigi		}
1661123495Sluigi
1662102087Sluigi		show_prerequisites(&flags, 0, cmd->opcode);
1663102087Sluigi
166498943Sluigi		switch(cmd->opcode) {
1665117577Sluigi		case O_PROB:
1666107289Sluigi			break;	/* done already */
1667107289Sluigi
166898943Sluigi		case O_PROBE_STATE:
166998943Sluigi			break; /* no need to print anything here */
167098943Sluigi
167198943Sluigi		case O_IP_SRC:
1672130281Sru		case O_IP_SRC_LOOKUP:
167398943Sluigi		case O_IP_SRC_MASK:
167498943Sluigi		case O_IP_SRC_ME:
167598943Sluigi		case O_IP_SRC_SET:
1676102087Sluigi			show_prerequisites(&flags, HAVE_PROTO, 0);
167798943Sluigi			if (!(flags & HAVE_SRCIP))
167898943Sluigi				printf(" from");
167998943Sluigi			if ((cmd->len & F_OR) && !or_block)
168098943Sluigi				printf(" {");
1681102087Sluigi			print_ip((ipfw_insn_ip *)cmd,
1682102087Sluigi				(flags & HAVE_OPTIONS) ? " src-ip" : "");
168398943Sluigi			flags |= HAVE_SRCIP;
168498943Sluigi			break;
168598943Sluigi
168698943Sluigi		case O_IP_DST:
1687130281Sru		case O_IP_DST_LOOKUP:
168898943Sluigi		case O_IP_DST_MASK:
168998943Sluigi		case O_IP_DST_ME:
169098943Sluigi		case O_IP_DST_SET:
1691102087Sluigi			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
169298943Sluigi			if (!(flags & HAVE_DSTIP))
169398943Sluigi				printf(" to");
169498943Sluigi			if ((cmd->len & F_OR) && !or_block)
169598943Sluigi				printf(" {");
1696102087Sluigi			print_ip((ipfw_insn_ip *)cmd,
1697102087Sluigi				(flags & HAVE_OPTIONS) ? " dst-ip" : "");
169898943Sluigi			flags |= HAVE_DSTIP;
169998943Sluigi			break;
170098943Sluigi
1701145246Sbrooks		case O_IP6_SRC:
1702145246Sbrooks		case O_IP6_SRC_MASK:
1703145246Sbrooks		case O_IP6_SRC_ME:
1704147105Smlaier			show_prerequisites(&flags, HAVE_PROTO, 0);
1705145246Sbrooks			if (!(flags & HAVE_SRCIP))
1706145246Sbrooks				printf(" from");
1707145246Sbrooks			if ((cmd->len & F_OR) && !or_block)
1708145246Sbrooks				printf(" {");
1709145246Sbrooks			print_ip6((ipfw_insn_ip6 *)cmd,
1710145246Sbrooks			    (flags & HAVE_OPTIONS) ? " src-ip6" : "");
1711145246Sbrooks			flags |= HAVE_SRCIP | HAVE_PROTO;
1712145246Sbrooks			break;
1713145246Sbrooks
1714145246Sbrooks		case O_IP6_DST:
1715145246Sbrooks		case O_IP6_DST_MASK:
1716145246Sbrooks		case O_IP6_DST_ME:
1717145246Sbrooks			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1718145246Sbrooks			if (!(flags & HAVE_DSTIP))
1719145246Sbrooks				printf(" to");
1720145246Sbrooks			if ((cmd->len & F_OR) && !or_block)
1721145246Sbrooks				printf(" {");
1722145246Sbrooks			print_ip6((ipfw_insn_ip6 *)cmd,
1723145246Sbrooks			    (flags & HAVE_OPTIONS) ? " dst-ip6" : "");
1724145246Sbrooks			flags |= HAVE_DSTIP;
1725145246Sbrooks			break;
1726145246Sbrooks
1727145246Sbrooks		case O_FLOW6ID:
1728145246Sbrooks		print_flow6id( (ipfw_insn_u32 *) cmd );
1729145246Sbrooks		flags |= HAVE_OPTIONS;
1730145246Sbrooks		break;
1731145246Sbrooks
173298943Sluigi		case O_IP_DSTPORT:
1733102087Sluigi			show_prerequisites(&flags, HAVE_IP, 0);
173498943Sluigi		case O_IP_SRCPORT:
1735102087Sluigi			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1736101641Sluigi			if ((cmd->len & F_OR) && !or_block)
1737101641Sluigi				printf(" {");
1738102087Sluigi			print_newports((ipfw_insn_u16 *)cmd, proto,
1739102087Sluigi				(flags & HAVE_OPTIONS) ? cmd->opcode : 0);
174098943Sluigi			break;
174198943Sluigi
174298943Sluigi		case O_PROTO: {
1743145246Sbrooks			struct protoent *pe = NULL;
174498943Sluigi
174598943Sluigi			if ((cmd->len & F_OR) && !or_block)
174698943Sluigi				printf(" {");
174798943Sluigi			if (cmd->len & F_NOT)
174898943Sluigi				printf(" not");
174998943Sluigi			proto = cmd->arg1;
1750145567Sbrooks			pe = getprotobynumber(cmd->arg1);
1751146894Smlaier			if ((flags & (HAVE_PROTO4 | HAVE_PROTO6)) &&
1752146894Smlaier			    !(flags & HAVE_PROTO))
1753146894Smlaier				show_prerequisites(&flags,
1754146894Smlaier				    HAVE_IP | HAVE_OPTIONS, 0);
1755102087Sluigi			if (flags & HAVE_OPTIONS)
1756102087Sluigi				printf(" proto");
175798943Sluigi			if (pe)
175898943Sluigi				printf(" %s", pe->p_name);
175998943Sluigi			else
176098943Sluigi				printf(" %u", cmd->arg1);
176198943Sluigi			}
176298943Sluigi			flags |= HAVE_PROTO;
176398943Sluigi			break;
1764106505Smaxim
176598943Sluigi		default: /*options ... */
1766146894Smlaier			if (!(cmd->len & (F_OR|F_NOT)))
1767146894Smlaier				if (((cmd->opcode == O_IP6) &&
1768146894Smlaier				    (flags & HAVE_PROTO6)) ||
1769146894Smlaier				    ((cmd->opcode == O_IP4) &&
1770146894Smlaier				    (flags & HAVE_PROTO4)))
1771146894Smlaier					break;
1772102087Sluigi			show_prerequisites(&flags, HAVE_IP | HAVE_OPTIONS, 0);
177398943Sluigi			if ((cmd->len & F_OR) && !or_block)
177498943Sluigi				printf(" {");
177598943Sluigi			if (cmd->len & F_NOT && cmd->opcode != O_IN)
177698943Sluigi				printf(" not");
177798943Sluigi			switch(cmd->opcode) {
1778169139Smaxim			case O_MACADDR2: {
1779169139Smaxim				ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
1780169139Smaxim
1781169139Smaxim				printf(" MAC");
1782169139Smaxim				print_mac(m->addr, m->mask);
1783169139Smaxim				print_mac(m->addr + 6, m->mask + 6);
1784169139Smaxim				}
1785169139Smaxim				break;
1786169139Smaxim
1787169139Smaxim			case O_MAC_TYPE:
1788169139Smaxim				print_newports((ipfw_insn_u16 *)cmd,
1789169139Smaxim						IPPROTO_ETHERTYPE, cmd->opcode);
1790169139Smaxim				break;
1791169139Smaxim
1792169139Smaxim
179398943Sluigi			case O_FRAG:
179498943Sluigi				printf(" frag");
179598943Sluigi				break;
179698943Sluigi
179798943Sluigi			case O_IN:
179898943Sluigi				printf(cmd->len & F_NOT ? " out" : " in");
179998943Sluigi				break;
180098943Sluigi
1801136073Sgreen			case O_DIVERTED:
1802136073Sgreen				switch (cmd->arg1) {
1803136073Sgreen				case 3:
1804136073Sgreen					printf(" diverted");
1805136073Sgreen					break;
1806136073Sgreen				case 1:
1807136073Sgreen					printf(" diverted-loopback");
1808136073Sgreen					break;
1809136073Sgreen				case 2:
1810136073Sgreen					printf(" diverted-output");
1811136073Sgreen					break;
1812136073Sgreen				default:
1813136073Sgreen					printf(" diverted-?<%u>", cmd->arg1);
1814136073Sgreen					break;
1815136073Sgreen				}
1816136073Sgreen				break;
1817136073Sgreen
181898943Sluigi			case O_LAYER2:
181998943Sluigi				printf(" layer2");
182098943Sluigi				break;
182198943Sluigi			case O_XMIT:
182298943Sluigi			case O_RECV:
1823140423Sglebius			case O_VIA:
1824140423Sglebius			    {
1825117469Sluigi				char const *s;
182698943Sluigi				ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd;
182798943Sluigi
182898943Sluigi				if (cmd->opcode == O_XMIT)
182998943Sluigi					s = "xmit";
183098943Sluigi				else if (cmd->opcode == O_RECV)
183198943Sluigi					s = "recv";
1832117469Sluigi				else /* if (cmd->opcode == O_VIA) */
183398943Sluigi					s = "via";
183498943Sluigi				if (cmdif->name[0] == '\0')
183599475Sluigi					printf(" %s %s", s,
183699475Sluigi					    inet_ntoa(cmdif->p.ip));
1837140423Sglebius				else
1838140423Sglebius					printf(" %s %s", s, cmdif->name);
1839140423Sglebius
184098943Sluigi				break;
1841140423Sglebius			    }
184298943Sluigi			case O_IPID:
1843116690Sluigi				if (F_LEN(cmd) == 1)
1844116690Sluigi				    printf(" ipid %u", cmd->arg1 );
1845116690Sluigi				else
1846116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1847116690Sluigi					O_IPID);
184898943Sluigi				break;
184998943Sluigi
185098943Sluigi			case O_IPTTL:
1851116690Sluigi				if (F_LEN(cmd) == 1)
1852116690Sluigi				    printf(" ipttl %u", cmd->arg1 );
1853116690Sluigi				else
1854116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1855116690Sluigi					O_IPTTL);
185698943Sluigi				break;
185798943Sluigi
185898943Sluigi			case O_IPVER:
185998943Sluigi				printf(" ipver %u", cmd->arg1 );
186098943Sluigi				break;
186198943Sluigi
186299475Sluigi			case O_IPPRECEDENCE:
186399475Sluigi				printf(" ipprecedence %u", (cmd->arg1) >> 5 );
186499475Sluigi				break;
186599475Sluigi
186698943Sluigi			case O_IPLEN:
1867116690Sluigi				if (F_LEN(cmd) == 1)
1868116690Sluigi				    printf(" iplen %u", cmd->arg1 );
1869116690Sluigi				else
1870116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1871116690Sluigi					O_IPLEN);
187298943Sluigi				break;
187398943Sluigi
1874101116Sluigi			case O_IPOPT:
187598943Sluigi				print_flags("ipoptions", cmd, f_ipopts);
187698943Sluigi				break;
187798943Sluigi
187899475Sluigi			case O_IPTOS:
187999475Sluigi				print_flags("iptos", cmd, f_iptos);
188099475Sluigi				break;
188199475Sluigi
188299475Sluigi			case O_ICMPTYPE:
188399475Sluigi				print_icmptypes((ipfw_insn_u32 *)cmd);
188499475Sluigi				break;
188599475Sluigi
188698943Sluigi			case O_ESTAB:
188798943Sluigi				printf(" established");
188898943Sluigi				break;
188998943Sluigi
1890136075Sgreen			case O_TCPDATALEN:
1891136075Sgreen				if (F_LEN(cmd) == 1)
1892136075Sgreen				    printf(" tcpdatalen %u", cmd->arg1 );
1893136075Sgreen				else
1894136075Sgreen				    print_newports((ipfw_insn_u16 *)cmd, 0,
1895136075Sgreen					O_TCPDATALEN);
1896136075Sgreen				break;
1897136075Sgreen
189898943Sluigi			case O_TCPFLAGS:
189998943Sluigi				print_flags("tcpflags", cmd, f_tcpflags);
190098943Sluigi				break;
190198943Sluigi
190298943Sluigi			case O_TCPOPTS:
190398943Sluigi				print_flags("tcpoptions", cmd, f_tcpopts);
190498943Sluigi				break;
190598943Sluigi
190698943Sluigi			case O_TCPWIN:
190798943Sluigi				printf(" tcpwin %d", ntohs(cmd->arg1));
190898943Sluigi				break;
190998943Sluigi
191098943Sluigi			case O_TCPACK:
191198943Sluigi				printf(" tcpack %d", ntohl(cmd32->d[0]));
191298943Sluigi				break;
191398943Sluigi
191498943Sluigi			case O_TCPSEQ:
191598943Sluigi				printf(" tcpseq %d", ntohl(cmd32->d[0]));
191698943Sluigi				break;
191798943Sluigi
191898943Sluigi			case O_UID:
191998943Sluigi			    {
192098943Sluigi				struct passwd *pwd = getpwuid(cmd32->d[0]);
192198943Sluigi
192298943Sluigi				if (pwd)
192398943Sluigi					printf(" uid %s", pwd->pw_name);
192498943Sluigi				else
192598943Sluigi					printf(" uid %u", cmd32->d[0]);
192698943Sluigi			    }
192798943Sluigi				break;
192898943Sluigi
192998943Sluigi			case O_GID:
193098943Sluigi			    {
193198943Sluigi				struct group *grp = getgrgid(cmd32->d[0]);
193298943Sluigi
193398943Sluigi				if (grp)
193498943Sluigi					printf(" gid %s", grp->gr_name);
193598943Sluigi				else
193698943Sluigi					printf(" gid %u", cmd32->d[0]);
193798943Sluigi			    }
193898943Sluigi				break;
193998943Sluigi
1940133600Scsjp			case O_JAIL:
1941133600Scsjp				printf(" jail %d", cmd32->d[0]);
1942133600Scsjp				break;
1943133600Scsjp
1944112250Scjc			case O_VERREVPATH:
1945112250Scjc				printf(" verrevpath");
1946112250Scjc				break;
1947116919Sluigi
1948128575Sandre			case O_VERSRCREACH:
1949128575Sandre				printf(" versrcreach");
1950128575Sandre				break;
1951128575Sandre
1952133387Sandre			case O_ANTISPOOF:
1953133387Sandre				printf(" antispoof");
1954133387Sandre				break;
1955133387Sandre
1956117241Sluigi			case O_IPSEC:
1957117241Sluigi				printf(" ipsec");
1958117241Sluigi				break;
1959117241Sluigi
1960117469Sluigi			case O_NOP:
1961117626Sluigi				comment = (char *)(cmd + 1);
1962117469Sluigi				break;
1963117469Sluigi
196498943Sluigi			case O_KEEP_STATE:
196598943Sluigi				printf(" keep-state");
196698943Sluigi				break;
196798943Sluigi
1968159636Soleg			case O_LIMIT: {
196998943Sluigi				struct _s_x *p = limit_masks;
197098943Sluigi				ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
1971117328Sluigi				uint8_t x = c->limit_mask;
1972117469Sluigi				char const *comma = " ";
197398943Sluigi
197498943Sluigi				printf(" limit");
1975117577Sluigi				for (; p->x != 0 ; p++)
197699909Sluigi					if ((x & p->x) == p->x) {
197798943Sluigi						x &= ~p->x;
197898943Sluigi						printf("%s%s", comma, p->s);
197998943Sluigi						comma = ",";
198098943Sluigi					}
1981159636Soleg				PRINT_UINT_ARG(" ", c->conn_limit);
198298943Sluigi				break;
1983159636Soleg			}
198498943Sluigi
1985146894Smlaier			case O_IP6:
1986152923Sume				printf(" ip6");
1987145246Sbrooks				break;
1988145246Sbrooks
1989146894Smlaier			case O_IP4:
1990152923Sume				printf(" ip4");
1991146894Smlaier				break;
1992146894Smlaier
1993145246Sbrooks			case O_ICMP6TYPE:
1994145246Sbrooks				print_icmp6types((ipfw_insn_u32 *)cmd);
1995145246Sbrooks				break;
1996145246Sbrooks
1997145246Sbrooks			case O_EXT_HDR:
1998145246Sbrooks				print_ext6hdr( (ipfw_insn *) cmd );
1999145246Sbrooks				break;
2000145246Sbrooks
2001158879Soleg			case O_TAGGED:
2002158879Soleg				if (F_LEN(cmd) == 1)
2003159636Soleg					PRINT_UINT_ARG(" tagged ", cmd->arg1);
2004158879Soleg				else
2005159636Soleg					print_newports((ipfw_insn_u16 *)cmd, 0,
2006159636Soleg					    O_TAGGED);
2007158879Soleg				break;
2008158879Soleg
200998943Sluigi			default:
201098943Sluigi				printf(" [opcode %d len %d]",
201198943Sluigi				    cmd->opcode, cmd->len);
201298943Sluigi			}
201398943Sluigi		}
201498943Sluigi		if (cmd->len & F_OR) {
201598943Sluigi			printf(" or");
201698943Sluigi			or_block = 1;
201798943Sluigi		} else if (or_block) {
201898943Sluigi			printf(" }");
201998943Sluigi			or_block = 0;
202098943Sluigi		}
202198943Sluigi	}
2022102087Sluigi	show_prerequisites(&flags, HAVE_IP, 0);
2023117626Sluigi	if (comment)
2024117626Sluigi		printf(" // %s", comment);
202598943Sluigi	printf("\n");
202698943Sluigi}
202798943Sluigi
202898943Sluigistatic void
2029112189Smaximshow_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth)
203098943Sluigi{
203198943Sluigi	struct protoent *pe;
203298943Sluigi	struct in_addr a;
2033115793Sticso	uint16_t rulenum;
2034159160Smlaier	char buf[INET6_ADDRSTRLEN];
203598943Sluigi
203698943Sluigi	if (!do_expired) {
203798943Sluigi		if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT))
203898943Sluigi			return;
203998943Sluigi	}
2040115793Sticso	bcopy(&d->rule, &rulenum, sizeof(rulenum));
2041117328Sluigi	printf("%05d", rulenum);
2042117469Sluigi	if (pcwidth>0 || bcwidth>0)
2043117328Sluigi	    printf(" %*llu %*llu (%ds)", pcwidth,
2044117328Sluigi		align_uint64(&d->pcnt), bcwidth,
2045117328Sluigi		align_uint64(&d->bcnt), d->expire);
204698943Sluigi	switch (d->dyn_type) {
204798943Sluigi	case O_LIMIT_PARENT:
204898943Sluigi		printf(" PARENT %d", d->count);
204998943Sluigi		break;
205098943Sluigi	case O_LIMIT:
205198943Sluigi		printf(" LIMIT");
205298943Sluigi		break;
205398943Sluigi	case O_KEEP_STATE: /* bidir, no mask */
2054106505Smaxim		printf(" STATE");
205598943Sluigi		break;
205698943Sluigi	}
205798943Sluigi
205898943Sluigi	if ((pe = getprotobynumber(d->id.proto)) != NULL)
205998943Sluigi		printf(" %s", pe->p_name);
206098943Sluigi	else
206198943Sluigi		printf(" proto %u", d->id.proto);
206298943Sluigi
2063159160Smlaier	if (d->id.addr_type == 4) {
2064159160Smlaier		a.s_addr = htonl(d->id.src_ip);
2065159160Smlaier		printf(" %s %d", inet_ntoa(a), d->id.src_port);
206698943Sluigi
2067159160Smlaier		a.s_addr = htonl(d->id.dst_ip);
2068159160Smlaier		printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port);
2069159160Smlaier	} else if (d->id.addr_type == 6) {
2070159160Smlaier		printf(" %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf,
2071159160Smlaier		    sizeof(buf)), d->id.src_port);
2072159160Smlaier		printf(" <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf,
2073159160Smlaier		    sizeof(buf)), d->id.dst_port);
2074159160Smlaier	} else
2075159160Smlaier		printf(" UNKNOWN <-> UNKNOWN\n");
2076159160Smlaier
207798943Sluigi	printf("\n");
207898943Sluigi}
207998943Sluigi
2080117469Sluigistatic int
208198943Sluigisort_q(const void *pa, const void *pb)
208298943Sluigi{
208398943Sluigi	int rev = (do_sort < 0);
208498943Sluigi	int field = rev ? -do_sort : do_sort;
208598943Sluigi	long long res = 0;
208698943Sluigi	const struct dn_flow_queue *a = pa;
208798943Sluigi	const struct dn_flow_queue *b = pb;
208898943Sluigi
208998943Sluigi	switch (field) {
209098943Sluigi	case 1: /* pkts */
209198943Sluigi		res = a->len - b->len;
209298943Sluigi		break;
209398943Sluigi	case 2: /* bytes */
209498943Sluigi		res = a->len_bytes - b->len_bytes;
209598943Sluigi		break;
209698943Sluigi
209798943Sluigi	case 3: /* tot pkts */
209898943Sluigi		res = a->tot_pkts - b->tot_pkts;
209998943Sluigi		break;
210098943Sluigi
210198943Sluigi	case 4: /* tot bytes */
210298943Sluigi		res = a->tot_bytes - b->tot_bytes;
210398943Sluigi		break;
210498943Sluigi	}
210598943Sluigi	if (res < 0)
210698943Sluigi		res = -1;
210798943Sluigi	if (res > 0)
210898943Sluigi		res = 1;
210998943Sluigi	return (int)(rev ? res : -res);
211098943Sluigi}
211198943Sluigi
211298943Sluigistatic void
211398943Sluigilist_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
211498943Sluigi{
211598943Sluigi	int l;
2116145246Sbrooks	int index_printed, indexes = 0;
2117145246Sbrooks	char buff[255];
2118145246Sbrooks	struct protoent *pe;
211998943Sluigi
212098943Sluigi	if (fs->rq_elements == 0)
212198943Sluigi		return;
212298943Sluigi
212398943Sluigi	if (do_sort != 0)
212498943Sluigi		heapsort(q, fs->rq_elements, sizeof *q, sort_q);
2125145246Sbrooks
2126145246Sbrooks	/* Print IPv4 flows */
2127145246Sbrooks	index_printed = 0;
212898943Sluigi	for (l = 0; l < fs->rq_elements; l++) {
212998943Sluigi		struct in_addr ina;
213098943Sluigi
2131145246Sbrooks		/* XXX: Should check for IPv4 flows */
2132145246Sbrooks		if (IS_IP6_FLOW_ID(&(q[l].id)))
2133145246Sbrooks			continue;
2134145246Sbrooks
2135145246Sbrooks		if (!index_printed) {
2136145246Sbrooks			index_printed = 1;
2137145246Sbrooks			if (indexes > 0)	/* currently a no-op */
2138145246Sbrooks				printf("\n");
2139145246Sbrooks			indexes++;
2140145246Sbrooks			printf("    "
2141145246Sbrooks			    "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
2142145246Sbrooks			    fs->flow_mask.proto,
2143145246Sbrooks			    fs->flow_mask.src_ip, fs->flow_mask.src_port,
2144145246Sbrooks			    fs->flow_mask.dst_ip, fs->flow_mask.dst_port);
2145145246Sbrooks
2146145246Sbrooks			printf("BKT Prot ___Source IP/port____ "
2147145246Sbrooks			    "____Dest. IP/port____ "
2148145246Sbrooks			    "Tot_pkt/bytes Pkt/Byte Drp\n");
2149145246Sbrooks		}
2150145246Sbrooks
215198943Sluigi		printf("%3d ", q[l].hash_slot);
215298943Sluigi		pe = getprotobynumber(q[l].id.proto);
215398943Sluigi		if (pe)
215498943Sluigi			printf("%-4s ", pe->p_name);
215598943Sluigi		else
215698943Sluigi			printf("%4u ", q[l].id.proto);
2157145246Sbrooks		ina.s_addr = htonl(q[l].id.src_ip);
215898943Sluigi		printf("%15s/%-5d ",
215998943Sluigi		    inet_ntoa(ina), q[l].id.src_port);
216098943Sluigi		ina.s_addr = htonl(q[l].id.dst_ip);
216198943Sluigi		printf("%15s/%-5d ",
216298943Sluigi		    inet_ntoa(ina), q[l].id.dst_port);
216398943Sluigi		printf("%4qu %8qu %2u %4u %3u\n",
216498943Sluigi		    q[l].tot_pkts, q[l].tot_bytes,
216598943Sluigi		    q[l].len, q[l].len_bytes, q[l].drops);
216698943Sluigi		if (verbose)
216798943Sluigi			printf("   S %20qd  F %20qd\n",
216898943Sluigi			    q[l].S, q[l].F);
216998943Sluigi	}
2170145246Sbrooks
2171145246Sbrooks	/* Print IPv6 flows */
2172145246Sbrooks	index_printed = 0;
2173145246Sbrooks	for (l = 0; l < fs->rq_elements; l++) {
2174145246Sbrooks		if (!IS_IP6_FLOW_ID(&(q[l].id)))
2175145246Sbrooks			continue;
2176145246Sbrooks
2177145246Sbrooks		if (!index_printed) {
2178145246Sbrooks			index_printed = 1;
2179145246Sbrooks			if (indexes > 0)
2180145246Sbrooks				printf("\n");
2181145246Sbrooks			indexes++;
2182145246Sbrooks			printf("\n        mask: proto: 0x%02x, flow_id: 0x%08x,  ",
2183145246Sbrooks			    fs->flow_mask.proto, fs->flow_mask.flow_id6);
2184145246Sbrooks			inet_ntop(AF_INET6, &(fs->flow_mask.src_ip6),
2185145246Sbrooks			    buff, sizeof(buff));
2186145246Sbrooks			printf("%s/0x%04x -> ", buff, fs->flow_mask.src_port);
2187145246Sbrooks			inet_ntop( AF_INET6, &(fs->flow_mask.dst_ip6),
2188145246Sbrooks			    buff, sizeof(buff) );
2189145246Sbrooks			printf("%s/0x%04x\n", buff, fs->flow_mask.dst_port);
2190145246Sbrooks
2191145246Sbrooks			printf("BKT ___Prot___ _flow-id_ "
2192145246Sbrooks			    "______________Source IPv6/port_______________ "
2193145246Sbrooks			    "_______________Dest. IPv6/port_______________ "
2194145246Sbrooks			    "Tot_pkt/bytes Pkt/Byte Drp\n");
2195145246Sbrooks		}
2196145246Sbrooks		printf("%3d ", q[l].hash_slot);
2197145246Sbrooks		pe = getprotobynumber(q[l].id.proto);
2198145246Sbrooks		if (pe != NULL)
2199145246Sbrooks			printf("%9s ", pe->p_name);
2200145246Sbrooks		else
2201145246Sbrooks			printf("%9u ", q[l].id.proto);
2202145246Sbrooks		printf("%7d  %39s/%-5d ", q[l].id.flow_id6,
2203145246Sbrooks		    inet_ntop(AF_INET6, &(q[l].id.src_ip6), buff, sizeof(buff)),
2204145246Sbrooks		    q[l].id.src_port);
2205145246Sbrooks		printf(" %39s/%-5d ",
2206145246Sbrooks		    inet_ntop(AF_INET6, &(q[l].id.dst_ip6), buff, sizeof(buff)),
2207145246Sbrooks		    q[l].id.dst_port);
2208145246Sbrooks		printf(" %4qu %8qu %2u %4u %3u\n",
2209145246Sbrooks		    q[l].tot_pkts, q[l].tot_bytes,
2210145246Sbrooks		    q[l].len, q[l].len_bytes, q[l].drops);
2211145246Sbrooks		if (verbose)
2212145246Sbrooks			printf("   S %20qd  F %20qd\n", q[l].S, q[l].F);
2213145246Sbrooks	}
221498943Sluigi}
221598943Sluigi
221698943Sluigistatic void
221798943Sluigiprint_flowset_parms(struct dn_flow_set *fs, char *prefix)
221898943Sluigi{
221998943Sluigi	int l;
222098943Sluigi	char qs[30];
222198943Sluigi	char plr[30];
222298943Sluigi	char red[90];	/* Display RED parameters */
222398943Sluigi
222498943Sluigi	l = fs->qsize;
222598943Sluigi	if (fs->flags_fs & DN_QSIZE_IS_BYTES) {
222698943Sluigi		if (l >= 8192)
222798943Sluigi			sprintf(qs, "%d KB", l / 1024);
222898943Sluigi		else
222998943Sluigi			sprintf(qs, "%d B", l);
223098943Sluigi	} else
223198943Sluigi		sprintf(qs, "%3d sl.", l);
223298943Sluigi	if (fs->plr)
223398943Sluigi		sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
223498943Sluigi	else
223598943Sluigi		plr[0] = '\0';
223698943Sluigi	if (fs->flags_fs & DN_IS_RED)	/* RED parameters */
223798943Sluigi		sprintf(red,
223898943Sluigi		    "\n\t  %cRED w_q %f min_th %d max_th %d max_p %f",
223998943Sluigi		    (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ',
224098943Sluigi		    1.0 * fs->w_q / (double)(1 << SCALE_RED),
224198943Sluigi		    SCALE_VAL(fs->min_th),
224298943Sluigi		    SCALE_VAL(fs->max_th),
224398943Sluigi		    1.0 * fs->max_p / (double)(1 << SCALE_RED));
224498943Sluigi	else
224598943Sluigi		sprintf(red, "droptail");
224698943Sluigi
224798943Sluigi	printf("%s %s%s %d queues (%d buckets) %s\n",
224898943Sluigi	    prefix, qs, plr, fs->rq_elements, fs->rq_size, red);
224998943Sluigi}
225098943Sluigi
225198943Sluigistatic void
2252117469Sluigilist_pipes(void *data, uint nbytes, int ac, char *av[])
225398943Sluigi{
2254117469Sluigi	int rulenum;
225598943Sluigi	void *next = data;
225698943Sluigi	struct dn_pipe *p = (struct dn_pipe *) data;
225798943Sluigi	struct dn_flow_set *fs;
225898943Sluigi	struct dn_flow_queue *q;
225998943Sluigi	int l;
226098943Sluigi
226198943Sluigi	if (ac > 0)
226298943Sluigi		rulenum = strtoul(*av++, NULL, 10);
226398943Sluigi	else
226498943Sluigi		rulenum = 0;
226598943Sluigi	for (; nbytes >= sizeof *p; p = (struct dn_pipe *)next) {
226698943Sluigi		double b = p->bandwidth;
226798943Sluigi		char buf[30];
226898943Sluigi		char prefix[80];
226998943Sluigi
2270161001Sstefanf		if (SLIST_NEXT(p, next) != (struct dn_pipe *)DN_IS_PIPE)
227198943Sluigi			break;	/* done with pipes, now queues */
227298943Sluigi
227398943Sluigi		/*
227498943Sluigi		 * compute length, as pipe have variable size
227598943Sluigi		 */
227698943Sluigi		l = sizeof(*p) + p->fs.rq_elements * sizeof(*q);
2277117469Sluigi		next = (char *)p + l;
227898943Sluigi		nbytes -= l;
227998943Sluigi
2280134225Spjd		if ((rulenum != 0 && rulenum != p->pipe_nr) || do_pipe == 2)
228198943Sluigi			continue;
228298943Sluigi
228398943Sluigi		/*
228498943Sluigi		 * Print rate (or clocking interface)
228598943Sluigi		 */
228698943Sluigi		if (p->if_name[0] != '\0')
228798943Sluigi			sprintf(buf, "%s", p->if_name);
228898943Sluigi		else if (b == 0)
228998943Sluigi			sprintf(buf, "unlimited");
229098943Sluigi		else if (b >= 1000000)
229198943Sluigi			sprintf(buf, "%7.3f Mbit/s", b/1000000);
229298943Sluigi		else if (b >= 1000)
229398943Sluigi			sprintf(buf, "%7.3f Kbit/s", b/1000);
229498943Sluigi		else
229598943Sluigi			sprintf(buf, "%7.3f bit/s ", b);
229698943Sluigi
229798943Sluigi		sprintf(prefix, "%05d: %s %4d ms ",
229898943Sluigi		    p->pipe_nr, buf, p->delay);
229998943Sluigi		print_flowset_parms(&(p->fs), prefix);
230098943Sluigi		if (verbose)
230198943Sluigi			printf("   V %20qd\n", p->V >> MY_M);
2302106505Smaxim
230398943Sluigi		q = (struct dn_flow_queue *)(p+1);
230498943Sluigi		list_queues(&(p->fs), q);
230598943Sluigi	}
230698943Sluigi	for (fs = next; nbytes >= sizeof *fs; fs = next) {
230798943Sluigi		char prefix[80];
230898943Sluigi
2309161001Sstefanf		if (SLIST_NEXT(fs, next) != (struct dn_flow_set *)DN_IS_QUEUE)
231098943Sluigi			break;
231198943Sluigi		l = sizeof(*fs) + fs->rq_elements * sizeof(*q);
2312117469Sluigi		next = (char *)fs + l;
231398943Sluigi		nbytes -= l;
2314134225Spjd
2315134225Spjd		if (rulenum != 0 && ((rulenum != fs->fs_nr && do_pipe == 2) ||
2316134225Spjd		    (rulenum != fs->parent_nr && do_pipe == 1))) {
2317134225Spjd			continue;
2318134225Spjd		}
2319134225Spjd
232098943Sluigi		q = (struct dn_flow_queue *)(fs+1);
232198943Sluigi		sprintf(prefix, "q%05d: weight %d pipe %d ",
232298943Sluigi		    fs->fs_nr, fs->weight, fs->parent_nr);
232398943Sluigi		print_flowset_parms(fs, prefix);
232498943Sluigi		list_queues(fs, q);
232598943Sluigi	}
232698943Sluigi}
232798943Sluigi
2328101978Sluigi/*
2329101978Sluigi * This one handles all set-related commands
2330101978Sluigi * 	ipfw set { show | enable | disable }
2331101978Sluigi * 	ipfw set swap X Y
2332101978Sluigi * 	ipfw set move X to Y
2333101978Sluigi * 	ipfw set move rule X to Y
2334101978Sluigi */
233598943Sluigistatic void
2336101978Sluigisets_handler(int ac, char *av[])
2337101978Sluigi{
2338117328Sluigi	uint32_t set_disable, masks[2];
2339101978Sluigi	int i, nbytes;
2340117328Sluigi	uint16_t rulenum;
2341117328Sluigi	uint8_t cmd, new_set;
2342101978Sluigi
2343101978Sluigi	ac--;
2344101978Sluigi	av++;
2345101978Sluigi
2346101978Sluigi	if (!ac)
2347101978Sluigi		errx(EX_USAGE, "set needs command");
2348140271Sbrooks	if (_substrcmp(*av, "show") == 0) {
2349101978Sluigi		void *data;
2350117469Sluigi		char const *msg;
2351101978Sluigi
2352101978Sluigi		nbytes = sizeof(struct ip_fw);
2353117328Sluigi		if ((data = calloc(1, nbytes)) == NULL)
2354117328Sluigi			err(EX_OSERR, "calloc");
2355119740Stmm		if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0)
2356101978Sluigi			err(EX_OSERR, "getsockopt(IP_FW_GET)");
2357115793Sticso		bcopy(&((struct ip_fw *)data)->next_rule,
2358115793Sticso			&set_disable, sizeof(set_disable));
2359101978Sluigi
2360117655Sluigi		for (i = 0, msg = "disable" ; i < RESVD_SET; i++)
2361117577Sluigi			if ((set_disable & (1<<i))) {
2362101978Sluigi				printf("%s %d", msg, i);
2363101978Sluigi				msg = "";
2364101978Sluigi			}
2365101978Sluigi		msg = (set_disable) ? " enable" : "enable";
2366117655Sluigi		for (i = 0; i < RESVD_SET; i++)
2367117577Sluigi			if (!(set_disable & (1<<i))) {
2368101978Sluigi				printf("%s %d", msg, i);
2369101978Sluigi				msg = "";
2370101978Sluigi			}
2371101978Sluigi		printf("\n");
2372140271Sbrooks	} else if (_substrcmp(*av, "swap") == 0) {
2373101978Sluigi		ac--; av++;
2374101978Sluigi		if (ac != 2)
2375101978Sluigi			errx(EX_USAGE, "set swap needs 2 set numbers\n");
2376101978Sluigi		rulenum = atoi(av[0]);
2377101978Sluigi		new_set = atoi(av[1]);
2378117655Sluigi		if (!isdigit(*(av[0])) || rulenum > RESVD_SET)
2379101978Sluigi			errx(EX_DATAERR, "invalid set number %s\n", av[0]);
2380117655Sluigi		if (!isdigit(*(av[1])) || new_set > RESVD_SET)
2381101978Sluigi			errx(EX_DATAERR, "invalid set number %s\n", av[1]);
2382101978Sluigi		masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
2383117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
2384140271Sbrooks	} else if (_substrcmp(*av, "move") == 0) {
2385101978Sluigi		ac--; av++;
2386140271Sbrooks		if (ac && _substrcmp(*av, "rule") == 0) {
2387101978Sluigi			cmd = 2;
2388101978Sluigi			ac--; av++;
2389101978Sluigi		} else
2390101978Sluigi			cmd = 3;
2391140271Sbrooks		if (ac != 3 || _substrcmp(av[1], "to") != 0)
2392101978Sluigi			errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
2393101978Sluigi		rulenum = atoi(av[0]);
2394101978Sluigi		new_set = atoi(av[2]);
2395117655Sluigi		if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) ||
2396101978Sluigi			(cmd == 2 && rulenum == 65535) )
2397101978Sluigi			errx(EX_DATAERR, "invalid source number %s\n", av[0]);
2398117655Sluigi		if (!isdigit(*(av[2])) || new_set > RESVD_SET)
2399101978Sluigi			errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
2400101978Sluigi		masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
2401117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
2402140271Sbrooks	} else if (_substrcmp(*av, "disable") == 0 ||
2403140271Sbrooks		   _substrcmp(*av, "enable") == 0 ) {
2404140271Sbrooks		int which = _substrcmp(*av, "enable") == 0 ? 1 : 0;
2405101978Sluigi
2406101978Sluigi		ac--; av++;
2407101978Sluigi		masks[0] = masks[1] = 0;
2408101978Sluigi
2409101978Sluigi		while (ac) {
2410101978Sluigi			if (isdigit(**av)) {
2411101978Sluigi				i = atoi(*av);
2412117655Sluigi				if (i < 0 || i > RESVD_SET)
2413101978Sluigi					errx(EX_DATAERR,
2414101978Sluigi					    "invalid set number %d\n", i);
2415101978Sluigi				masks[which] |= (1<<i);
2416140271Sbrooks			} else if (_substrcmp(*av, "disable") == 0)
2417101978Sluigi				which = 0;
2418140271Sbrooks			else if (_substrcmp(*av, "enable") == 0)
2419101978Sluigi				which = 1;
2420101978Sluigi			else
2421101978Sluigi				errx(EX_DATAERR,
2422101978Sluigi					"invalid set command %s\n", *av);
2423101978Sluigi			av++; ac--;
2424101978Sluigi		}
2425101978Sluigi		if ( (masks[0] & masks[1]) != 0 )
2426101978Sluigi			errx(EX_DATAERR,
2427101978Sluigi			    "cannot enable and disable the same set\n");
2428101978Sluigi
2429117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(masks));
2430101978Sluigi		if (i)
2431101978Sluigi			warn("set enable/disable: setsockopt(IP_FW_DEL)");
2432101978Sluigi	} else
2433101978Sluigi		errx(EX_USAGE, "invalid set command %s\n", *av);
2434101978Sluigi}
2435101978Sluigi
2436101978Sluigistatic void
2437109126Sdillonsysctl_handler(int ac, char *av[], int which)
2438109126Sdillon{
2439109126Sdillon	ac--;
2440109126Sdillon	av++;
2441109126Sdillon
2442119668Smaxim	if (ac == 0) {
2443109126Sdillon		warnx("missing keyword to enable/disable\n");
2444140271Sbrooks	} else if (_substrcmp(*av, "firewall") == 0) {
2445116770Sluigi		sysctlbyname("net.inet.ip.fw.enable", NULL, 0,
2446116770Sluigi		    &which, sizeof(which));
2447140271Sbrooks	} else if (_substrcmp(*av, "one_pass") == 0) {
2448116770Sluigi		sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0,
2449116770Sluigi		    &which, sizeof(which));
2450140271Sbrooks	} else if (_substrcmp(*av, "debug") == 0) {
2451116770Sluigi		sysctlbyname("net.inet.ip.fw.debug", NULL, 0,
2452116770Sluigi		    &which, sizeof(which));
2453140271Sbrooks	} else if (_substrcmp(*av, "verbose") == 0) {
2454116770Sluigi		sysctlbyname("net.inet.ip.fw.verbose", NULL, 0,
2455116770Sluigi		    &which, sizeof(which));
2456140271Sbrooks	} else if (_substrcmp(*av, "dyn_keepalive") == 0) {
2457116770Sluigi		sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0,
2458116770Sluigi		    &which, sizeof(which));
2459140271Sbrooks	} else if (_substrcmp(*av, "altq") == 0) {
2460136071Sgreen		altq_set_enabled(which);
2461109126Sdillon	} else {
2462109126Sdillon		warnx("unrecognize enable/disable keyword: %s\n", *av);
2463109126Sdillon	}
2464109126Sdillon}
2465109126Sdillon
2466109126Sdillonstatic void
2467117469Sluigilist(int ac, char *av[], int show_counters)
246898943Sluigi{
246998943Sluigi	struct ip_fw *r;
247098943Sluigi	ipfw_dyn_rule *dynrules, *d;
247198943Sluigi
2472117469Sluigi#define NEXT(r)	((struct ip_fw *)((char *)r + RULESIZE(r)))
2473117469Sluigi	char *lim;
2474117469Sluigi	void *data = NULL;
2475112189Smaxim	int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width;
247698943Sluigi	int exitval = EX_OK;
247798943Sluigi	int lac;
247898943Sluigi	char **lav;
2479117469Sluigi	u_long rnum, last;
248098943Sluigi	char *endptr;
248198943Sluigi	int seen = 0;
248298943Sluigi
248398943Sluigi	const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
248498943Sluigi	int nalloc = 1024;	/* start somewhere... */
248598943Sluigi
2486135036Smaxim	last = 0;
2487135036Smaxim
2488117328Sluigi	if (test_only) {
2489117328Sluigi		fprintf(stderr, "Testing only, list disabled\n");
2490117328Sluigi		return;
2491117328Sluigi	}
2492117328Sluigi
249398943Sluigi	ac--;
249498943Sluigi	av++;
249598943Sluigi
249698943Sluigi	/* get rules or pipes from kernel, resizing array as necessary */
249798943Sluigi	nbytes = nalloc;
249898943Sluigi
249998943Sluigi	while (nbytes >= nalloc) {
250098943Sluigi		nalloc = nalloc * 2 + 200;
250198943Sluigi		nbytes = nalloc;
250298943Sluigi		if ((data = realloc(data, nbytes)) == NULL)
250398943Sluigi			err(EX_OSERR, "realloc");
2504119740Stmm		if (do_cmd(ocmd, data, (uintptr_t)&nbytes) < 0)
250598943Sluigi			err(EX_OSERR, "getsockopt(IP_%s_GET)",
250698943Sluigi				do_pipe ? "DUMMYNET" : "FW");
250798943Sluigi	}
250898943Sluigi
250998943Sluigi	if (do_pipe) {
251098943Sluigi		list_pipes(data, nbytes, ac, av);
251198943Sluigi		goto done;
251298943Sluigi	}
251398943Sluigi
251498943Sluigi	/*
251598943Sluigi	 * Count static rules. They have variable size so we
251698943Sluigi	 * need to scan the list to count them.
251798943Sluigi	 */
2518117469Sluigi	for (nstat = 1, r = data, lim = (char *)data + nbytes;
2519117469Sluigi		    r->rulenum < 65535 && (char *)r < lim;
2520117469Sluigi		    ++nstat, r = NEXT(r) )
252198943Sluigi		; /* nothing */
252298943Sluigi
252398943Sluigi	/*
252498943Sluigi	 * Count dynamic rules. This is easier as they have
252598943Sluigi	 * fixed size.
252698943Sluigi	 */
2527117469Sluigi	r = NEXT(r);
252898943Sluigi	dynrules = (ipfw_dyn_rule *)r ;
2529117469Sluigi	n = (char *)r - (char *)data;
253098943Sluigi	ndyn = (nbytes - n) / sizeof *dynrules;
253198943Sluigi
2532112189Smaxim	/* if showing stats, figure out column widths ahead of time */
2533112189Smaxim	bcwidth = pcwidth = 0;
2534117469Sluigi	if (show_counters) {
2535117469Sluigi		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
2536112189Smaxim			/* packet counter */
2537115793Sticso			width = snprintf(NULL, 0, "%llu",
2538115793Sticso			    align_uint64(&r->pcnt));
2539112189Smaxim			if (width > pcwidth)
2540112189Smaxim				pcwidth = width;
2541112189Smaxim
2542112189Smaxim			/* byte counter */
2543115793Sticso			width = snprintf(NULL, 0, "%llu",
2544115793Sticso			    align_uint64(&r->bcnt));
2545112189Smaxim			if (width > bcwidth)
2546112189Smaxim				bcwidth = width;
2547112189Smaxim		}
2548112189Smaxim	}
2549112189Smaxim	if (do_dynamic && ndyn) {
2550112189Smaxim		for (n = 0, d = dynrules; n < ndyn; n++, d++) {
2551115793Sticso			width = snprintf(NULL, 0, "%llu",
2552115793Sticso			    align_uint64(&d->pcnt));
2553112189Smaxim			if (width > pcwidth)
2554112189Smaxim				pcwidth = width;
2555112189Smaxim
2556115793Sticso			width = snprintf(NULL, 0, "%llu",
2557115793Sticso			    align_uint64(&d->bcnt));
2558112189Smaxim			if (width > bcwidth)
2559112189Smaxim				bcwidth = width;
2560112189Smaxim		}
2561112189Smaxim	}
256298943Sluigi	/* if no rule numbers were specified, list all rules */
256398943Sluigi	if (ac == 0) {
2564117469Sluigi		for (n = 0, r = data; n < nstat; n++, r = NEXT(r) )
2565112189Smaxim			show_ipfw(r, pcwidth, bcwidth);
256698943Sluigi
256798943Sluigi		if (do_dynamic && ndyn) {
256898943Sluigi			printf("## Dynamic rules (%d):\n", ndyn);
256998943Sluigi			for (n = 0, d = dynrules; n < ndyn; n++, d++)
2570112189Smaxim				show_dyn_ipfw(d, pcwidth, bcwidth);
257198943Sluigi		}
257298943Sluigi		goto done;
257398943Sluigi	}
257498943Sluigi
257598943Sluigi	/* display specific rules requested on command line */
257698943Sluigi
257798943Sluigi	for (lac = ac, lav = av; lac != 0; lac--) {
257898943Sluigi		/* convert command line rule # */
2579117469Sluigi		last = rnum = strtoul(*lav++, &endptr, 10);
2580117469Sluigi		if (*endptr == '-')
2581117469Sluigi			last = strtoul(endptr+1, &endptr, 10);
258298943Sluigi		if (*endptr) {
258398943Sluigi			exitval = EX_USAGE;
258498943Sluigi			warnx("invalid rule number: %s", *(lav - 1));
258598943Sluigi			continue;
258698943Sluigi		}
2587117469Sluigi		for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) {
2588117469Sluigi			if (r->rulenum > last)
258998943Sluigi				break;
2590117469Sluigi			if (r->rulenum >= rnum && r->rulenum <= last) {
2591112189Smaxim				show_ipfw(r, pcwidth, bcwidth);
259298943Sluigi				seen = 1;
259398943Sluigi			}
259498943Sluigi		}
259598943Sluigi		if (!seen) {
259698943Sluigi			/* give precedence to other error(s) */
259798943Sluigi			if (exitval == EX_OK)
259898943Sluigi				exitval = EX_UNAVAILABLE;
259998943Sluigi			warnx("rule %lu does not exist", rnum);
260098943Sluigi		}
260198943Sluigi	}
260298943Sluigi
260398943Sluigi	if (do_dynamic && ndyn) {
260498943Sluigi		printf("## Dynamic rules:\n");
260598943Sluigi		for (lac = ac, lav = av; lac != 0; lac--) {
2606145246Sbrooks			last = rnum = strtoul(*lav++, &endptr, 10);
2607117469Sluigi			if (*endptr == '-')
2608117469Sluigi				last = strtoul(endptr+1, &endptr, 10);
260998943Sluigi			if (*endptr)
261098943Sluigi				/* already warned */
261198943Sluigi				continue;
261298943Sluigi			for (n = 0, d = dynrules; n < ndyn; n++, d++) {
2613115793Sticso				uint16_t rulenum;
2614115793Sticso
2615115793Sticso				bcopy(&d->rule, &rulenum, sizeof(rulenum));
2616115793Sticso				if (rulenum > rnum)
261798943Sluigi					break;
2618117469Sluigi				if (r->rulenum >= rnum && r->rulenum <= last)
2619112189Smaxim					show_dyn_ipfw(d, pcwidth, bcwidth);
262098943Sluigi			}
262198943Sluigi		}
262298943Sluigi	}
262398943Sluigi
262498943Sluigi	ac = 0;
262598943Sluigi
262698943Sluigidone:
262798943Sluigi	free(data);
262898943Sluigi
262998943Sluigi	if (exitval != EX_OK)
263098943Sluigi		exit(exitval);
2631117469Sluigi#undef NEXT
263298943Sluigi}
263398943Sluigi
263498943Sluigistatic void
263598943Sluigishow_usage(void)
263698943Sluigi{
263798943Sluigi	fprintf(stderr, "usage: ipfw [options]\n"
263898943Sluigi"do \"ipfw -h\" or see ipfw manpage for details\n"
263998943Sluigi);
264098943Sluigi	exit(EX_USAGE);
264198943Sluigi}
264298943Sluigi
264398943Sluigistatic void
264498943Sluigihelp(void)
264598943Sluigi{
2646117328Sluigi	fprintf(stderr,
2647117328Sluigi"ipfw syntax summary (but please do read the ipfw(8) manpage):\n"
2648123495Sluigi"ipfw [-abcdefhnNqStTv] <command> where <command> is one of:\n"
2649117328Sluigi"add [num] [set N] [prob x] RULE-BODY\n"
2650117328Sluigi"{pipe|queue} N config PIPE-BODY\n"
2651117328Sluigi"[pipe|queue] {zero|delete|show} [N{,N}]\n"
2652165648Spiso"nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|reset|\n"
2653165648Spiso"		reverse|proxy_only|redirect_addr linkspec|\n"
2654165648Spiso"		redirect_port linkspec|redirect_proto linkspec}\n"
2655117328Sluigi"set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n"
2656130281Sru"table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n"
265798943Sluigi"\n"
2658136071Sgreen"RULE-BODY:	check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n"
2659149020Sbz"ACTION:	check-state | allow | count | deny | unreach{,6} CODE |\n"
2660149020Sbz"               skipto N | {divert|tee} PORT | forward ADDR |\n"
2661165648Spiso"               pipe N | queue N | nat N\n"
2662136071Sgreen"PARAMS: 	[log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n"
266398943Sluigi"ADDR:		[ MAC dst src ether_type ] \n"
2664145246Sbrooks"		[ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
2665145246Sbrooks"		[ ipv6|ip6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n"
2666130281Sru"IPADDR:	[not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n"
2667145246Sbrooks"IP6ADDR:	[not] { any | me | me6 | ip6/bits | IP6LIST }\n"
2668145246Sbrooks"IP6LIST:	{ ip6 | ip6/bits }[,IP6LIST]\n"
2669117544Sluigi"IPLIST:	{ ip | ip/bits | ip:mask }[,IPLIST]\n"
2670117544Sluigi"OPTION_LIST:	OPTION [OPTION_LIST]\n"
2671136247Sgreen"OPTION:	bridged | diverted | diverted-loopback | diverted-output |\n"
2672145246Sbrooks"	{dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n"
2673145246Sbrooks"	{dst-port|src-port} LIST |\n"
2674117328Sluigi"	estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n"
2675117328Sluigi"	iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n"
2676117328Sluigi"	ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n"
2677145246Sbrooks"	icmp6types LIST | ext6hdr LIST | flow-id N[,N] |\n"
2678117328Sluigi"	mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n"
2679117328Sluigi"	setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n"
2680136075Sgreen"	tcpdatalen LIST | verrevpath | versrcreach | antispoof\n"
268198943Sluigi);
268298943Sluigiexit(0);
268398943Sluigi}
268498943Sluigi
268598943Sluigi
268698943Sluigistatic int
268798943Sluigilookup_host (char *host, struct in_addr *ipaddr)
268898943Sluigi{
268998943Sluigi	struct hostent *he;
269098943Sluigi
269198943Sluigi	if (!inet_aton(host, ipaddr)) {
269298943Sluigi		if ((he = gethostbyname(host)) == NULL)
269398943Sluigi			return(-1);
269498943Sluigi		*ipaddr = *(struct in_addr *)he->h_addr_list[0];
269598943Sluigi	}
269698943Sluigi	return(0);
269798943Sluigi}
269898943Sluigi
269998943Sluigi/*
270098943Sluigi * fills the addr and mask fields in the instruction as appropriate from av.
270198943Sluigi * Update length as appropriate.
270298943Sluigi * The following formats are allowed:
270398943Sluigi *	me	returns O_IP_*_ME
270498943Sluigi *	1.2.3.4		single IP address
270598943Sluigi *	1.2.3.4:5.6.7.8	address:mask
270698943Sluigi *	1.2.3.4/24	address/mask
270798943Sluigi *	1.2.3.4/26{1,6,5,4,23}	set of addresses in a subnet
2708117328Sluigi * We can have multiple comma-separated address/mask entries.
270998943Sluigi */
271098943Sluigistatic void
271198943Sluigifill_ip(ipfw_insn_ip *cmd, char *av)
271298943Sluigi{
2713117328Sluigi	int len = 0;
2714117328Sluigi	uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
271598943Sluigi
271698943Sluigi	cmd->o.len &= ~F_LEN_MASK;	/* zero len */
271798943Sluigi
2718140271Sbrooks	if (_substrcmp(av, "any") == 0)
271998943Sluigi		return;
272098943Sluigi
2721140271Sbrooks	if (_substrcmp(av, "me") == 0) {
272298943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn);
272398943Sluigi		return;
272498943Sluigi	}
272598943Sluigi
2726140271Sbrooks	if (strncmp(av, "table(", 6) == 0) {
2727130281Sru		char *p = strchr(av + 6, ',');
2728130281Sru
2729130281Sru		if (p)
2730130281Sru			*p++ = '\0';
2731130281Sru		cmd->o.opcode = O_IP_DST_LOOKUP;
2732130281Sru		cmd->o.arg1 = strtoul(av + 6, NULL, 0);
2733130281Sru		if (p) {
2734130281Sru			cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2735130281Sru			d[0] = strtoul(p, NULL, 0);
2736130281Sru		} else
2737130281Sru			cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2738130281Sru		return;
2739130281Sru	}
2740130281Sru
2741117328Sluigi    while (av) {
2742117328Sluigi	/*
2743117328Sluigi	 * After the address we can have '/' or ':' indicating a mask,
2744117328Sluigi	 * ',' indicating another address follows, '{' indicating a
2745117328Sluigi	 * set of addresses of unspecified size.
2746117328Sluigi	 */
2747165851Smlaier	char *t = NULL, *p = strpbrk(av, "/:,{");
2748117328Sluigi	int masklen;
2749165851Smlaier	char md, nd;
2750117328Sluigi
275198943Sluigi	if (p) {
275298943Sluigi		md = *p;
275398943Sluigi		*p++ = '\0';
2754165851Smlaier		if ((t = strpbrk(p, ",{")) != NULL) {
2755165851Smlaier			nd = *t;
2756165851Smlaier			*t = '\0';
2757165851Smlaier		}
2758117328Sluigi	} else
2759117328Sluigi		md = '\0';
276098943Sluigi
2761117328Sluigi	if (lookup_host(av, (struct in_addr *)&d[0]) != 0)
276298943Sluigi		errx(EX_NOHOST, "hostname ``%s'' unknown", av);
276398943Sluigi	switch (md) {
276498943Sluigi	case ':':
2765117328Sluigi		if (!inet_aton(p, (struct in_addr *)&d[1]))
276698943Sluigi			errx(EX_DATAERR, "bad netmask ``%s''", p);
276798943Sluigi		break;
276898943Sluigi	case '/':
2769117328Sluigi		masklen = atoi(p);
2770117328Sluigi		if (masklen == 0)
2771117328Sluigi			d[1] = htonl(0);	/* mask */
2772117328Sluigi		else if (masklen > 32)
277398943Sluigi			errx(EX_DATAERR, "bad width ``%s''", p);
277498943Sluigi		else
2775117328Sluigi			d[1] = htonl(~0 << (32 - masklen));
277698943Sluigi		break;
2777117328Sluigi	case '{':	/* no mask, assume /24 and put back the '{' */
2778117328Sluigi		d[1] = htonl(~0 << (32 - 24));
2779117328Sluigi		*(--p) = md;
2780117328Sluigi		break;
2781117328Sluigi
2782117328Sluigi	case ',':	/* single address plus continuation */
2783117328Sluigi		*(--p) = md;
2784117328Sluigi		/* FALLTHROUGH */
2785117328Sluigi	case 0:		/* initialization value */
278698943Sluigi	default:
2787117328Sluigi		d[1] = htonl(~0);	/* force /32 */
278898943Sluigi		break;
278998943Sluigi	}
2790117328Sluigi	d[0] &= d[1];		/* mask base address with mask */
2791165851Smlaier	if (t)
2792165851Smlaier		*t = nd;
2793117328Sluigi	/* find next separator */
279498943Sluigi	if (p)
2795117328Sluigi		p = strpbrk(p, ",{");
2796117328Sluigi	if (p && *p == '{') {
2797117328Sluigi		/*
2798117328Sluigi		 * We have a set of addresses. They are stored as follows:
2799117328Sluigi		 *   arg1	is the set size (powers of 2, 2..256)
2800117328Sluigi		 *   addr	is the base address IN HOST FORMAT
2801117328Sluigi		 *   mask..	is an array of arg1 bits (rounded up to
2802117328Sluigi		 *		the next multiple of 32) with bits set
2803117328Sluigi		 *		for each host in the map.
2804117328Sluigi		 */
2805117328Sluigi		uint32_t *map = (uint32_t *)&cmd->mask;
280698943Sluigi		int low, high;
2807117577Sluigi		int i = contigmask((uint8_t *)&(d[1]), 32);
280898943Sluigi
2809117328Sluigi		if (len > 0)
2810117328Sluigi			errx(EX_DATAERR, "address set cannot be in a list");
2811117328Sluigi		if (i < 24 || i > 31)
2812117328Sluigi			errx(EX_DATAERR, "invalid set with mask %d\n", i);
2813117328Sluigi		cmd->o.arg1 = 1<<(32-i);	/* map length		*/
2814117328Sluigi		d[0] = ntohl(d[0]);		/* base addr in host format */
281598943Sluigi		cmd->o.opcode = O_IP_DST_SET;	/* default */
281698943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32;
2817101117Sluigi		for (i = 0; i < (cmd->o.arg1+31)/32 ; i++)
2818117328Sluigi			map[i] = 0;	/* clear map */
281998943Sluigi
2820117328Sluigi		av = p + 1;
2821117328Sluigi		low = d[0] & 0xff;
282298943Sluigi		high = low + cmd->o.arg1 - 1;
2823117328Sluigi		/*
2824117328Sluigi		 * Here, i stores the previous value when we specify a range
2825117328Sluigi		 * of addresses within a mask, e.g. 45-63. i = -1 means we
2826117328Sluigi		 * have no previous value.
2827117328Sluigi		 */
2828116716Sluigi		i = -1;	/* previous value in a range */
282998943Sluigi		while (isdigit(*av)) {
283098943Sluigi			char *s;
2831117328Sluigi			int a = strtol(av, &s, 0);
283298943Sluigi
2833117328Sluigi			if (s == av) { /* no parameter */
2834117328Sluigi			    if (*av != '}')
2835117328Sluigi				errx(EX_DATAERR, "set not closed\n");
2836117328Sluigi			    if (i != -1)
2837117328Sluigi				errx(EX_DATAERR, "incomplete range %d-", i);
2838117328Sluigi			    break;
2839117328Sluigi			}
2840117328Sluigi			if (a < low || a > high)
2841117328Sluigi			    errx(EX_DATAERR, "addr %d out of range [%d-%d]\n",
284298943Sluigi				a, low, high);
284398943Sluigi			a -= low;
2844116716Sluigi			if (i == -1)	/* no previous in range */
2845116716Sluigi			    i = a;
2846116716Sluigi			else {		/* check that range is valid */
2847116716Sluigi			    if (i > a)
2848116716Sluigi				errx(EX_DATAERR, "invalid range %d-%d",
2849116716Sluigi					i+low, a+low);
2850116716Sluigi			    if (*s == '-')
2851116716Sluigi				errx(EX_DATAERR, "double '-' in range");
2852116716Sluigi			}
2853116716Sluigi			for (; i <= a; i++)
2854117328Sluigi			    map[i/32] |= 1<<(i & 31);
2855116716Sluigi			i = -1;
2856116716Sluigi			if (*s == '-')
2857116716Sluigi			    i = a;
2858117328Sluigi			else if (*s == '}')
2859116716Sluigi			    break;
286098943Sluigi			av = s+1;
286198943Sluigi		}
286298943Sluigi		return;
286398943Sluigi	}
2864117328Sluigi	av = p;
2865117328Sluigi	if (av)			/* then *av must be a ',' */
2866117328Sluigi		av++;
286798943Sluigi
2868117328Sluigi	/* Check this entry */
2869117328Sluigi	if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */
2870117328Sluigi		/*
2871117328Sluigi		 * 'any' turns the entire list into a NOP.
2872117328Sluigi		 * 'not any' never matches, so it is removed from the
2873117328Sluigi		 * list unless it is the only item, in which case we
2874117328Sluigi		 * report an error.
2875117328Sluigi		 */
2876117328Sluigi		if (cmd->o.len & F_NOT) {	/* "not any" never matches */
2877117328Sluigi			if (av == NULL && len == 0) /* only this entry */
2878117328Sluigi				errx(EX_DATAERR, "not any never matches");
2879117328Sluigi		}
2880117328Sluigi		/* else do nothing and skip this entry */
2881128067Smaxim		return;
2882117328Sluigi	}
2883117328Sluigi	/* A single IP can be stored in an optimized format */
2884117328Sluigi	if (d[1] == IP_MASK_ALL && av == NULL && len == 0) {
288598943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2886117328Sluigi		return;
2887117328Sluigi	}
2888117328Sluigi	len += 2;	/* two words... */
2889117328Sluigi	d += 2;
2890117328Sluigi    } /* end while */
2891162363Sjhay    if (len + 1 > F_LEN_MASK)
2892162363Sjhay	errx(EX_DATAERR, "address list too long");
2893117328Sluigi    cmd->o.len |= len+1;
289498943Sluigi}
289598943Sluigi
289698943Sluigi
2897145246Sbrooks/* Try to find ipv6 address by hostname */
2898145246Sbrooksstatic int
2899145246Sbrookslookup_host6 (char *host, struct in6_addr *ip6addr)
2900145246Sbrooks{
2901145246Sbrooks	struct hostent *he;
2902145246Sbrooks
2903145246Sbrooks	if (!inet_pton(AF_INET6, host, ip6addr)) {
2904145246Sbrooks		if ((he = gethostbyname2(host, AF_INET6)) == NULL)
2905145246Sbrooks			return(-1);
2906145246Sbrooks		memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr));
2907145246Sbrooks	}
2908145246Sbrooks	return(0);
2909145246Sbrooks}
2910145246Sbrooks
2911145246Sbrooks
2912145246Sbrooks/* n2mask sets n bits of the mask */
2913145246Sbrooksstatic void
2914145246Sbrooksn2mask(struct in6_addr *mask, int n)
2915145246Sbrooks{
2916145246Sbrooks	static int	minimask[9] =
2917145246Sbrooks	    { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
2918145246Sbrooks	u_char		*p;
2919145246Sbrooks
2920145246Sbrooks	memset(mask, 0, sizeof(struct in6_addr));
2921145246Sbrooks	p = (u_char *) mask;
2922145246Sbrooks	for (; n > 0; p++, n -= 8) {
2923145246Sbrooks		if (n >= 8)
2924145246Sbrooks			*p = 0xff;
2925145246Sbrooks		else
2926145246Sbrooks			*p = minimask[n];
2927145246Sbrooks	}
2928145246Sbrooks	return;
2929145246Sbrooks}
2930145246Sbrooks
2931145246Sbrooks
293298943Sluigi/*
2933145246Sbrooks * fill the addr and mask fields in the instruction as appropriate from av.
2934145246Sbrooks * Update length as appropriate.
2935145246Sbrooks * The following formats are allowed:
2936145246Sbrooks *     any     matches any IP6. Actually returns an empty instruction.
2937145246Sbrooks *     me      returns O_IP6_*_ME
2938145246Sbrooks *
2939145246Sbrooks *     03f1::234:123:0342                single IP6 addres
2940145246Sbrooks *     03f1::234:123:0342/24            address/mask
2941145246Sbrooks *     03f1::234:123:0342/24,03f1::234:123:0343/               List of address
2942145246Sbrooks *
2943145246Sbrooks * Set of address (as in ipv6) not supported because ipv6 address
2944145246Sbrooks * are typically random past the initial prefix.
2945145246Sbrooks * Return 1 on success, 0 on failure.
2946145246Sbrooks */
2947145246Sbrooksstatic int
2948145246Sbrooksfill_ip6(ipfw_insn_ip6 *cmd, char *av)
2949145246Sbrooks{
2950145246Sbrooks	int len = 0;
2951145246Sbrooks	struct in6_addr *d = &(cmd->addr6);
2952145246Sbrooks	/*
2953145246Sbrooks	 * Needed for multiple address.
2954145246Sbrooks	 * Note d[1] points to struct in6_add r mask6 of cmd
2955145246Sbrooks	 */
2956145246Sbrooks
2957145246Sbrooks       cmd->o.len &= ~F_LEN_MASK;	/* zero len */
2958145246Sbrooks
2959145246Sbrooks       if (strcmp(av, "any") == 0)
2960145246Sbrooks	       return (1);
2961145246Sbrooks
2962145246Sbrooks
2963145246Sbrooks       if (strcmp(av, "me") == 0) {	/* Set the data for "me" opt*/
2964145246Sbrooks	       cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2965145246Sbrooks	       return (1);
2966145246Sbrooks       }
2967145246Sbrooks
2968145246Sbrooks       if (strcmp(av, "me6") == 0) {	/* Set the data for "me" opt*/
2969145246Sbrooks	       cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2970145246Sbrooks	       return (1);
2971145246Sbrooks       }
2972145246Sbrooks
2973145246Sbrooks       av = strdup(av);
2974145246Sbrooks       while (av) {
2975145246Sbrooks		/*
2976145246Sbrooks		 * After the address we can have '/' indicating a mask,
2977145246Sbrooks		 * or ',' indicating another address follows.
2978145246Sbrooks		 */
2979145246Sbrooks
2980145246Sbrooks		char *p;
2981145246Sbrooks		int masklen;
2982145246Sbrooks		char md = '\0';
2983145246Sbrooks
2984145246Sbrooks		if ((p = strpbrk(av, "/,")) ) {
2985145246Sbrooks			md = *p;	/* save the separator */
2986145246Sbrooks			*p = '\0';	/* terminate address string */
2987145246Sbrooks			p++;		/* and skip past it */
2988145246Sbrooks		}
2989145246Sbrooks		/* now p points to NULL, mask or next entry */
2990145246Sbrooks
2991145246Sbrooks		/* lookup stores address in *d as a side effect */
2992145246Sbrooks		if (lookup_host6(av, d) != 0) {
2993145246Sbrooks			/* XXX: failed. Free memory and go */
2994145246Sbrooks			errx(EX_DATAERR, "bad address \"%s\"", av);
2995145246Sbrooks		}
2996145246Sbrooks		/* next, look at the mask, if any */
2997145246Sbrooks		masklen = (md == '/') ? atoi(p) : 128;
2998145246Sbrooks		if (masklen > 128 || masklen < 0)
2999145246Sbrooks			errx(EX_DATAERR, "bad width \"%s\''", p);
3000145246Sbrooks		else
3001145246Sbrooks			n2mask(&d[1], masklen);
3002145246Sbrooks
3003145246Sbrooks		APPLY_MASK(d, &d[1])   /* mask base address with mask */
3004145246Sbrooks
3005145246Sbrooks		/* find next separator */
3006145246Sbrooks
3007145246Sbrooks		if (md == '/') {	/* find separator past the mask */
3008145246Sbrooks			p = strpbrk(p, ",");
3009145246Sbrooks			if (p != NULL)
3010145246Sbrooks				p++;
3011145246Sbrooks		}
3012145246Sbrooks		av = p;
3013145246Sbrooks
3014145246Sbrooks		/* Check this entry */
3015145246Sbrooks		if (masklen == 0) {
3016145246Sbrooks			/*
3017145246Sbrooks			 * 'any' turns the entire list into a NOP.
3018145246Sbrooks			 * 'not any' never matches, so it is removed from the
3019145246Sbrooks			 * list unless it is the only item, in which case we
3020145246Sbrooks			 * report an error.
3021145246Sbrooks			 */
3022145246Sbrooks			if (cmd->o.len & F_NOT && av == NULL && len == 0)
3023145246Sbrooks				errx(EX_DATAERR, "not any never matches");
3024145246Sbrooks			continue;
3025145246Sbrooks		}
3026145246Sbrooks
3027145246Sbrooks		/*
3028145246Sbrooks		 * A single IP can be stored alone
3029145246Sbrooks		 */
3030145246Sbrooks		if (masklen == 128 && av == NULL && len == 0) {
3031145246Sbrooks			len = F_INSN_SIZE(struct in6_addr);
3032145246Sbrooks			break;
3033145246Sbrooks		}
3034145246Sbrooks
3035145246Sbrooks		/* Update length and pointer to arguments */
3036145246Sbrooks		len += F_INSN_SIZE(struct in6_addr)*2;
3037145246Sbrooks		d += 2;
3038145246Sbrooks	} /* end while */
3039145246Sbrooks
3040145246Sbrooks	/*
3041145246Sbrooks	 * Total length of the command, remember that 1 is the size of
3042145246Sbrooks	 * the base command.
3043145246Sbrooks	 */
3044162363Sjhay	if (len + 1 > F_LEN_MASK)
3045162363Sjhay		errx(EX_DATAERR, "address list too long");
3046145246Sbrooks	cmd->o.len |= len+1;
3047145246Sbrooks	free(av);
3048145246Sbrooks	return (1);
3049145246Sbrooks}
3050145246Sbrooks
3051145246Sbrooks/*
3052145246Sbrooks * fills command for ipv6 flow-id filtering
3053145246Sbrooks * note that the 20 bit flow number is stored in a array of u_int32_t
3054145246Sbrooks * it's supported lists of flow-id, so in the o.arg1 we store how many
3055145246Sbrooks * additional flow-id we want to filter, the basic is 1
3056145246Sbrooks */
3057145246Sbrooksvoid
3058145246Sbrooksfill_flow6( ipfw_insn_u32 *cmd, char *av )
3059145246Sbrooks{
3060145246Sbrooks	u_int32_t type;	 /* Current flow number */
3061145246Sbrooks	u_int16_t nflow = 0;    /* Current flow index */
3062145246Sbrooks	char *s = av;
3063145246Sbrooks	cmd->d[0] = 0;	  /* Initializing the base number*/
3064145246Sbrooks
3065145246Sbrooks	while (s) {
3066145246Sbrooks		av = strsep( &s, ",") ;
3067145246Sbrooks		type = strtoul(av, &av, 0);
3068145246Sbrooks		if (*av != ',' && *av != '\0')
3069145246Sbrooks			errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
3070145246Sbrooks		if (type > 0xfffff)
3071145246Sbrooks			errx(EX_DATAERR, "flow number out of range %s", av);
3072145246Sbrooks		cmd->d[nflow] |= type;
3073145246Sbrooks		nflow++;
3074145246Sbrooks	}
3075145246Sbrooks	if( nflow > 0 ) {
3076145246Sbrooks		cmd->o.opcode = O_FLOW6ID;
3077145246Sbrooks		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow;
3078145246Sbrooks		cmd->o.arg1 = nflow;
3079145246Sbrooks	}
3080145246Sbrooks	else {
3081145246Sbrooks		errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
3082145246Sbrooks	}
3083145246Sbrooks}
3084145246Sbrooks
3085145246Sbrooksstatic ipfw_insn *
3086145246Sbrooksadd_srcip6(ipfw_insn *cmd, char *av)
3087145246Sbrooks{
3088145246Sbrooks
3089145246Sbrooks	fill_ip6((ipfw_insn_ip6 *)cmd, av);
3090145246Sbrooks	if (F_LEN(cmd) == 0)				/* any */
3091145246Sbrooks		;
3092145246Sbrooks	if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) {	/* "me" */
3093145246Sbrooks		cmd->opcode = O_IP6_SRC_ME;
3094145246Sbrooks	} else if (F_LEN(cmd) ==
3095145246Sbrooks	    (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
3096145246Sbrooks		/* single IP, no mask*/
3097145246Sbrooks		cmd->opcode = O_IP6_SRC;
3098145246Sbrooks	} else {					/* addr/mask opt */
3099145246Sbrooks		cmd->opcode = O_IP6_SRC_MASK;
3100145246Sbrooks	}
3101145246Sbrooks	return cmd;
3102145246Sbrooks}
3103145246Sbrooks
3104145246Sbrooksstatic ipfw_insn *
3105145246Sbrooksadd_dstip6(ipfw_insn *cmd, char *av)
3106145246Sbrooks{
3107145246Sbrooks
3108145246Sbrooks	fill_ip6((ipfw_insn_ip6 *)cmd, av);
3109145246Sbrooks	if (F_LEN(cmd) == 0)				/* any */
3110145246Sbrooks		;
3111145246Sbrooks	if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) {	/* "me" */
3112145246Sbrooks		cmd->opcode = O_IP6_DST_ME;
3113145246Sbrooks	} else if (F_LEN(cmd) ==
3114145246Sbrooks	    (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
3115145246Sbrooks		/* single IP, no mask*/
3116145246Sbrooks		cmd->opcode = O_IP6_DST;
3117145246Sbrooks	} else {					/* addr/mask opt */
3118145246Sbrooks		cmd->opcode = O_IP6_DST_MASK;
3119145246Sbrooks	}
3120145246Sbrooks	return cmd;
3121145246Sbrooks}
3122145246Sbrooks
3123145246Sbrooks
3124145246Sbrooks/*
312598943Sluigi * helper function to process a set of flags and set bits in the
312698943Sluigi * appropriate masks.
312798943Sluigi */
312898943Sluigistatic void
312998943Sluigifill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
313098943Sluigi	struct _s_x *flags, char *p)
313198943Sluigi{
3132117328Sluigi	uint8_t set=0, clear=0;
313398943Sluigi
313498943Sluigi	while (p && *p) {
313598943Sluigi		char *q;	/* points to the separator */
313698943Sluigi		int val;
3137117328Sluigi		uint8_t *which;	/* mask we are working on */
313898943Sluigi
313998943Sluigi		if (*p == '!') {
314098943Sluigi			p++;
314198943Sluigi			which = &clear;
314298943Sluigi		} else
314398943Sluigi			which = &set;
314498943Sluigi		q = strchr(p, ',');
314598943Sluigi		if (q)
314698943Sluigi			*q++ = '\0';
314798943Sluigi		val = match_token(flags, p);
314898943Sluigi		if (val <= 0)
314998943Sluigi			errx(EX_DATAERR, "invalid flag %s", p);
3150117328Sluigi		*which |= (uint8_t)val;
315198943Sluigi		p = q;
315298943Sluigi	}
315398943Sluigi        cmd->opcode = opcode;
315498943Sluigi        cmd->len =  (cmd->len & (F_NOT | F_OR)) | 1;
315598943Sluigi        cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
315698943Sluigi}
315798943Sluigi
315898943Sluigi
315998943Sluigistatic void
316098943Sluigidelete(int ac, char *av[])
316198943Sluigi{
3162117328Sluigi	uint32_t rulenum;
3163117469Sluigi	struct dn_pipe p;
316498943Sluigi	int i;
316598943Sluigi	int exitval = EX_OK;
3166101628Sluigi	int do_set = 0;
316798943Sluigi
3168117469Sluigi	memset(&p, 0, sizeof p);
316998943Sluigi
317098943Sluigi	av++; ac--;
3171130013Scsjp	NEED1("missing rule specification");
3172140271Sbrooks	if (ac > 0 && _substrcmp(*av, "set") == 0) {
3173101978Sluigi		do_set = 1;	/* delete set */
3174101628Sluigi		ac--; av++;
3175101978Sluigi	}
317698943Sluigi
317798943Sluigi	/* Rule number */
317898943Sluigi	while (ac && isdigit(**av)) {
317998943Sluigi		i = atoi(*av); av++; ac--;
3180165648Spiso		if (do_nat) {
3181165648Spiso			exitval = do_cmd(IP_FW_NAT_DEL, &i, sizeof i);
3182165648Spiso			if (exitval) {
3183165648Spiso				exitval = EX_UNAVAILABLE;
3184165648Spiso				warn("rule %u not available", i);
3185165648Spiso			}
3186165648Spiso 		} else if (do_pipe) {
318798943Sluigi			if (do_pipe == 1)
3188117469Sluigi				p.pipe_nr = i;
318998943Sluigi			else
3190117469Sluigi				p.fs.fs_nr = i;
3191117469Sluigi			i = do_cmd(IP_DUMMYNET_DEL, &p, sizeof p);
319298943Sluigi			if (i) {
319398943Sluigi				exitval = 1;
319498943Sluigi				warn("rule %u: setsockopt(IP_DUMMYNET_DEL)",
3195117469Sluigi				    do_pipe == 1 ? p.pipe_nr : p.fs.fs_nr);
319698943Sluigi			}
319798943Sluigi		} else {
3198101978Sluigi			rulenum =  (i & 0xffff) | (do_set << 24);
3199117328Sluigi			i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum);
320098943Sluigi			if (i) {
320198943Sluigi				exitval = EX_UNAVAILABLE;
320298943Sluigi				warn("rule %u: setsockopt(IP_FW_DEL)",
320398943Sluigi				    rulenum);
320498943Sluigi			}
320598943Sluigi		}
320698943Sluigi	}
320798943Sluigi	if (exitval != EX_OK)
320898943Sluigi		exit(exitval);
320998943Sluigi}
321098943Sluigi
321198943Sluigi
321298943Sluigi/*
321398943Sluigi * fill the interface structure. We do not check the name as we can
321498943Sluigi * create interfaces dynamically, so checking them at insert time
321598943Sluigi * makes relatively little sense.
3216121816Sbrooks * Interface names containing '*', '?', or '[' are assumed to be shell
3217121816Sbrooks * patterns which match interfaces.
321898943Sluigi */
321998943Sluigistatic void
322098943Sluigifill_iface(ipfw_insn_if *cmd, char *arg)
322198943Sluigi{
322298943Sluigi	cmd->name[0] = '\0';
322398943Sluigi	cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
322498943Sluigi
322598943Sluigi	/* Parse the interface or address */
3226140271Sbrooks	if (strcmp(arg, "any") == 0)
322798943Sluigi		cmd->o.len = 0;		/* effectively ignore this command */
322898943Sluigi	else if (!isdigit(*arg)) {
3229121816Sbrooks		strlcpy(cmd->name, arg, sizeof(cmd->name));
3230121816Sbrooks		cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
323198943Sluigi	} else if (!inet_aton(arg, &cmd->p.ip))
323298943Sluigi		errx(EX_DATAERR, "bad ip address ``%s''", arg);
323398943Sluigi}
323498943Sluigi
3235165648Spiso/*
3236165648Spiso * Search for interface with name "ifn", and fill n accordingly:
3237165648Spiso *
3238165648Spiso * n->ip        ip address of interface "ifn"
3239165648Spiso * n->if_name   copy of interface name "ifn"
3240165648Spiso */
324198943Sluigistatic void
3242165648Spisoset_addr_dynamic(const char *ifn, struct cfg_nat *n)
3243165648Spiso{
3244165648Spiso	size_t needed;
3245165648Spiso	int mib[6];
3246165648Spiso	char *buf, *lim, *next;
3247165648Spiso	struct if_msghdr *ifm;
3248165648Spiso	struct ifa_msghdr *ifam;
3249165648Spiso	struct sockaddr_dl *sdl;
3250165648Spiso	struct sockaddr_in *sin;
3251165648Spiso	int ifIndex, ifMTU;
3252165648Spiso
3253165648Spiso	mib[0] = CTL_NET;
3254165648Spiso	mib[1] = PF_ROUTE;
3255165648Spiso	mib[2] = 0;
3256165648Spiso	mib[3] = AF_INET;
3257165648Spiso	mib[4] = NET_RT_IFLIST;
3258165648Spiso	mib[5] = 0;
3259165648Spiso/*
3260165648Spiso * Get interface data.
3261165648Spiso */
3262165648Spiso	if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
3263165648Spiso		err(1, "iflist-sysctl-estimate");
3264165648Spiso	if ((buf = malloc(needed)) == NULL)
3265165648Spiso		errx(1, "malloc failed");
3266165648Spiso	if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
3267165648Spiso		err(1, "iflist-sysctl-get");
3268165648Spiso	lim = buf + needed;
3269165648Spiso/*
3270165648Spiso * Loop through interfaces until one with
3271165648Spiso * given name is found. This is done to
3272165648Spiso * find correct interface index for routing
3273165648Spiso * message processing.
3274165648Spiso */
3275165648Spiso	ifIndex	= 0;
3276165648Spiso	next = buf;
3277165648Spiso	while (next < lim) {
3278165648Spiso		ifm = (struct if_msghdr *)next;
3279165648Spiso		next += ifm->ifm_msglen;
3280165648Spiso		if (ifm->ifm_version != RTM_VERSION) {
3281165648Spiso			if (verbose)
3282165648Spiso				warnx("routing message version %d "
3283165648Spiso				    "not understood", ifm->ifm_version);
3284165648Spiso			continue;
3285165648Spiso		}
3286165648Spiso		if (ifm->ifm_type == RTM_IFINFO) {
3287165648Spiso			sdl = (struct sockaddr_dl *)(ifm + 1);
3288165648Spiso			if (strlen(ifn) == sdl->sdl_nlen &&
3289165648Spiso			    strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
3290165648Spiso				ifIndex = ifm->ifm_index;
3291165648Spiso				ifMTU = ifm->ifm_data.ifi_mtu;
3292165648Spiso				break;
3293165648Spiso			}
3294165648Spiso		}
3295165648Spiso	}
3296165648Spiso	if (!ifIndex)
3297165648Spiso		errx(1, "unknown interface name %s", ifn);
3298165648Spiso/*
3299165648Spiso * Get interface address.
3300165648Spiso */
3301165648Spiso	sin = NULL;
3302165648Spiso	while (next < lim) {
3303165648Spiso		ifam = (struct ifa_msghdr *)next;
3304165648Spiso		next += ifam->ifam_msglen;
3305165648Spiso		if (ifam->ifam_version != RTM_VERSION) {
3306165648Spiso			if (verbose)
3307165648Spiso				warnx("routing message version %d "
3308165648Spiso				    "not understood", ifam->ifam_version);
3309165648Spiso			continue;
3310165648Spiso		}
3311165648Spiso		if (ifam->ifam_type != RTM_NEWADDR)
3312165648Spiso			break;
3313165648Spiso		if (ifam->ifam_addrs & RTA_IFA) {
3314165648Spiso			int i;
3315165648Spiso			char *cp = (char *)(ifam + 1);
3316165648Spiso
3317165648Spiso			for (i = 1; i < RTA_IFA; i <<= 1) {
3318165648Spiso				if (ifam->ifam_addrs & i)
3319165648Spiso					cp += SA_SIZE((struct sockaddr *)cp);
3320165648Spiso			}
3321165648Spiso			if (((struct sockaddr *)cp)->sa_family == AF_INET) {
3322165648Spiso				sin = (struct sockaddr_in *)cp;
3323165648Spiso				break;
3324165648Spiso			}
3325165648Spiso		}
3326165648Spiso	}
3327165648Spiso	if (sin == NULL)
3328165648Spiso		errx(1, "%s: cannot get interface address", ifn);
3329165648Spiso
3330165648Spiso	n->ip = sin->sin_addr;
3331165648Spiso	strncpy(n->if_name, ifn, IF_NAMESIZE);
3332165648Spiso
3333165648Spiso	free(buf);
3334165648Spiso}
3335165648Spiso
3336165648Spiso/*
3337165648Spiso * XXX - The following functions, macros and definitions come from natd.c:
3338165648Spiso * it would be better to move them outside natd.c, in a file
3339165648Spiso * (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live
3340165648Spiso * with it.
3341165648Spiso */
3342165648Spiso
3343165648Spiso/*
3344165648Spiso * Definition of a port range, and macros to deal with values.
3345165648Spiso * FORMAT:  HI 16-bits == first port in range, 0 == all ports.
3346165648Spiso *          LO 16-bits == number of ports in range
3347165648Spiso * NOTES:   - Port values are not stored in network byte order.
3348165648Spiso */
3349165648Spiso
3350165648Spiso#define port_range u_long
3351165648Spiso
3352165648Spiso#define GETLOPORT(x)     ((x) >> 0x10)
3353165648Spiso#define GETNUMPORTS(x)   ((x) & 0x0000ffff)
3354165648Spiso#define GETHIPORT(x)     (GETLOPORT((x)) + GETNUMPORTS((x)))
3355165648Spiso
3356165648Spiso/* Set y to be the low-port value in port_range variable x. */
3357165648Spiso#define SETLOPORT(x,y)   ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
3358165648Spiso
3359165648Spiso/* Set y to be the number of ports in port_range variable x. */
3360165648Spiso#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
3361165648Spiso
3362165648Spisostatic void
3363165648SpisoStrToAddr (const char* str, struct in_addr* addr)
3364165648Spiso{
3365165648Spiso	struct hostent* hp;
3366165648Spiso
3367165648Spiso	if (inet_aton (str, addr))
3368165648Spiso		return;
3369165648Spiso
3370165648Spiso	hp = gethostbyname (str);
3371165648Spiso	if (!hp)
3372165648Spiso		errx (1, "unknown host %s", str);
3373165648Spiso
3374165648Spiso	memcpy (addr, hp->h_addr, sizeof (struct in_addr));
3375165648Spiso}
3376165648Spiso
3377165648Spisostatic int
3378165648SpisoStrToPortRange (const char* str, const char* proto, port_range *portRange)
3379165648Spiso{
3380165648Spiso	char*           sep;
3381165648Spiso	struct servent*	sp;
3382165648Spiso	char*		end;
3383165648Spiso	u_short         loPort;
3384165648Spiso	u_short         hiPort;
3385165648Spiso
3386165648Spiso	/* First see if this is a service, return corresponding port if so. */
3387165648Spiso	sp = getservbyname (str,proto);
3388165648Spiso	if (sp) {
3389165648Spiso	        SETLOPORT(*portRange, ntohs(sp->s_port));
3390165648Spiso		SETNUMPORTS(*portRange, 1);
3391165648Spiso		return 0;
3392165648Spiso	}
3393165648Spiso
3394165648Spiso	/* Not a service, see if it's a single port or port range. */
3395165648Spiso	sep = strchr (str, '-');
3396165648Spiso	if (sep == NULL) {
3397165648Spiso	        SETLOPORT(*portRange, strtol(str, &end, 10));
3398165648Spiso		if (end != str) {
3399165648Spiso		        /* Single port. */
3400165648Spiso		        SETNUMPORTS(*portRange, 1);
3401165648Spiso			return 0;
3402165648Spiso		}
3403165648Spiso
3404165648Spiso		/* Error in port range field. */
3405165648Spiso		errx (EX_DATAERR, "%s/%s: unknown service", str, proto);
3406165648Spiso	}
3407165648Spiso
3408165648Spiso	/* Port range, get the values and sanity check. */
3409165648Spiso	sscanf (str, "%hu-%hu", &loPort, &hiPort);
3410165648Spiso	SETLOPORT(*portRange, loPort);
3411165648Spiso	SETNUMPORTS(*portRange, 0);	/* Error by default */
3412165648Spiso	if (loPort <= hiPort)
3413165648Spiso	        SETNUMPORTS(*portRange, hiPort - loPort + 1);
3414165648Spiso
3415165648Spiso	if (GETNUMPORTS(*portRange) == 0)
3416165648Spiso	        errx (EX_DATAERR, "invalid port range %s", str);
3417165648Spiso
3418165648Spiso	return 0;
3419165648Spiso}
3420165648Spiso
3421165648Spisostatic int
3422165648SpisoStrToProto (const char* str)
3423165648Spiso{
3424165648Spiso	if (!strcmp (str, "tcp"))
3425165648Spiso		return IPPROTO_TCP;
3426165648Spiso
3427165648Spiso	if (!strcmp (str, "udp"))
3428165648Spiso		return IPPROTO_UDP;
3429165648Spiso
3430165648Spiso	errx (EX_DATAERR, "unknown protocol %s. Expected tcp or udp", str);
3431165648Spiso}
3432165648Spiso
3433165648Spisostatic int
3434165648SpisoStrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
3435165648Spiso		       port_range *portRange)
3436165648Spiso{
3437165648Spiso	char*	ptr;
3438165648Spiso
3439165648Spiso	ptr = strchr (str, ':');
3440165648Spiso	if (!ptr)
3441165648Spiso		errx (EX_DATAERR, "%s is missing port number", str);
3442165648Spiso
3443165648Spiso	*ptr = '\0';
3444165648Spiso	++ptr;
3445165648Spiso
3446165648Spiso	StrToAddr (str, addr);
3447165648Spiso	return StrToPortRange (ptr, proto, portRange);
3448165648Spiso}
3449165648Spiso
3450165648Spiso/* End of stuff taken from natd.c. */
3451165648Spiso
3452165648Spiso#define INC_ARGCV() do {        \
3453165648Spiso	(*_av)++;               \
3454165648Spiso	(*_ac)--;               \
3455165648Spiso	av = *_av;              \
3456165648Spiso	ac = *_ac;              \
3457165648Spiso} while(0)
3458165648Spiso
3459165648Spiso/*
3460165648Spiso * The next 3 functions add support for the addr, port and proto redirect and
3461165648Spiso * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect()
3462165648Spiso * and SetupProtoRedirect() from natd.c.
3463165648Spiso *
3464165648Spiso * Every setup_* function fills at least one redirect entry
3465165648Spiso * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool)
3466165648Spiso * in buf.
3467165648Spiso *
3468165648Spiso * The format of data in buf is:
3469165648Spiso *
3470165648Spiso *
3471165648Spiso *     cfg_nat    cfg_redir    cfg_spool    ......  cfg_spool
3472165648Spiso *
3473165648Spiso *    -------------------------------------        ------------
3474165648Spiso *   |          | .....X ... |          |         |           |  .....
3475165648Spiso *    ------------------------------------- ...... ------------
3476165648Spiso *                     ^
3477165648Spiso *                spool_cnt       n=0       ......   n=(X-1)
3478165648Spiso *
3479165648Spiso * len points to the amount of available space in buf
3480165648Spiso * space counts the memory consumed by every function
3481165648Spiso *
3482165648Spiso * XXX - Every function get all the argv params so it
3483165648Spiso * has to check, in optional parameters, that the next
3484165648Spiso * args is a valid option for the redir entry and not
3485165648Spiso * another token. Only redir_port and redir_proto are
3486165648Spiso * affected by this.
3487165648Spiso */
3488165648Spiso
3489165648Spisostatic int
3490165648Spisosetup_redir_addr(char *spool_buf, int len,
3491165648Spiso		 int *_ac, char ***_av)
3492165648Spiso{
3493165648Spiso	char **av, *sep; /* Token separator. */
3494165648Spiso	/* Temporary buffer used to hold server pool ip's. */
3495165648Spiso	char tmp_spool_buf[NAT_BUF_LEN];
3496165648Spiso	int ac, i, space, lsnat;
3497165648Spiso	struct cfg_redir *r;
3498165648Spiso	struct cfg_spool *tmp;
3499165648Spiso
3500165648Spiso	av = *_av;
3501165648Spiso	ac = *_ac;
3502165648Spiso	space = 0;
3503165648Spiso	lsnat = 0;
3504165648Spiso	if (len >= SOF_REDIR) {
3505165648Spiso		r = (struct cfg_redir *)spool_buf;
3506165648Spiso		/* Skip cfg_redir at beginning of buf. */
3507165648Spiso		spool_buf = &spool_buf[SOF_REDIR];
3508165648Spiso		space = SOF_REDIR;
3509165648Spiso		len -= SOF_REDIR;
3510165648Spiso	} else
3511165648Spiso		goto nospace;
3512165648Spiso	r->mode = REDIR_ADDR;
3513165648Spiso	/* Extract local address. */
3514165648Spiso	if (ac == 0)
3515165648Spiso		errx(EX_DATAERR, "redirect_addr: missing local address");
3516165648Spiso	sep = strchr(*av, ',');
3517165648Spiso	if (sep) {		/* LSNAT redirection syntax. */
3518165648Spiso		r->laddr.s_addr = INADDR_NONE;
3519165648Spiso		/* Preserve av, copy spool servers to tmp_spool_buf. */
3520165648Spiso		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
3521165648Spiso		lsnat = 1;
3522165648Spiso	} else
3523165648Spiso		StrToAddr(*av, &r->laddr);
3524165648Spiso	INC_ARGCV();
3525165648Spiso
3526165648Spiso	/* Extract public address. */
3527165648Spiso	if (ac == 0)
3528165648Spiso		errx(EX_DATAERR, "redirect_addr: missing public address");
3529165648Spiso	StrToAddr(*av, &r->paddr);
3530165648Spiso	INC_ARGCV();
3531165648Spiso
3532165648Spiso	/* Setup LSNAT server pool. */
3533165648Spiso	if (sep) {
3534165648Spiso		sep = strtok(tmp_spool_buf, ",");
3535165648Spiso		while (sep != NULL) {
3536165648Spiso			tmp = (struct cfg_spool *)spool_buf;
3537165648Spiso			if (len < SOF_SPOOL)
3538165648Spiso				goto nospace;
3539165648Spiso			len -= SOF_SPOOL;
3540165648Spiso			space += SOF_SPOOL;
3541165648Spiso			StrToAddr(sep, &tmp->addr);
3542165648Spiso			tmp->port = ~0;
3543165648Spiso			r->spool_cnt++;
3544165648Spiso			/* Point to the next possible cfg_spool. */
3545165648Spiso			spool_buf = &spool_buf[SOF_SPOOL];
3546165648Spiso			sep = strtok(NULL, ",");
3547165648Spiso		}
3548165648Spiso	}
3549165648Spiso	return(space);
3550165648Spisonospace:
3551165648Spiso	errx(EX_DATAERR, "redirect_addr: buf is too small\n");
3552165648Spiso}
3553165648Spiso
3554165648Spisostatic int
3555165648Spisosetup_redir_port(char *spool_buf, int len,
3556165648Spiso		 int *_ac, char ***_av)
3557165648Spiso{
3558165648Spiso	char **av, *sep, *protoName;
3559165648Spiso	char tmp_spool_buf[NAT_BUF_LEN];
3560165648Spiso	int ac, space, lsnat;
3561165648Spiso	struct cfg_redir *r;
3562165648Spiso	struct cfg_spool *tmp;
3563165648Spiso	u_short numLocalPorts;
3564165648Spiso	port_range portRange;
3565165648Spiso
3566165648Spiso	av = *_av;
3567165648Spiso	ac = *_ac;
3568165648Spiso	space = 0;
3569165648Spiso	lsnat = 0;
3570165648Spiso	numLocalPorts = 0;
3571165648Spiso
3572165648Spiso	if (len >= SOF_REDIR) {
3573165648Spiso		r = (struct cfg_redir *)spool_buf;
3574165648Spiso		/* Skip cfg_redir at beginning of buf. */
3575165648Spiso		spool_buf = &spool_buf[SOF_REDIR];
3576165648Spiso		space = SOF_REDIR;
3577165648Spiso		len -= SOF_REDIR;
3578165648Spiso	} else
3579165648Spiso		goto nospace;
3580165648Spiso	r->mode = REDIR_PORT;
3581165648Spiso	/*
3582165648Spiso	 * Extract protocol.
3583165648Spiso	 */
3584165648Spiso	if (ac == 0)
3585165648Spiso		errx (EX_DATAERR, "redirect_port: missing protocol");
3586165648Spiso	r->proto = StrToProto(*av);
3587165648Spiso	protoName = *av;
3588165648Spiso	INC_ARGCV();
3589165648Spiso
3590165648Spiso	/*
3591165648Spiso	 * Extract local address.
3592165648Spiso	 */
3593165648Spiso	if (ac == 0)
3594165648Spiso		errx (EX_DATAERR, "redirect_port: missing local address");
3595165648Spiso
3596165648Spiso	sep = strchr(*av, ',');
3597165648Spiso	/* LSNAT redirection syntax. */
3598165648Spiso	if (sep) {
3599165648Spiso		r->laddr.s_addr = INADDR_NONE;
3600165648Spiso		r->lport = ~0;
3601165648Spiso		numLocalPorts = 1;
3602165648Spiso		/* Preserve av, copy spool servers to tmp_spool_buf. */
3603165648Spiso		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
3604165648Spiso		lsnat = 1;
3605165648Spiso	} else {
3606165648Spiso		if (StrToAddrAndPortRange (*av, &r->laddr, protoName,
3607165648Spiso		    &portRange) != 0)
3608165648Spiso			errx(EX_DATAERR, "redirect_port:"
3609165648Spiso			    "invalid local port range");
3610165648Spiso
3611165648Spiso		r->lport = GETLOPORT(portRange);
3612165648Spiso		numLocalPorts = GETNUMPORTS(portRange);
3613165648Spiso	}
3614165648Spiso	INC_ARGCV();
3615165648Spiso
3616165648Spiso	/*
3617165648Spiso	 * Extract public port and optionally address.
3618165648Spiso	 */
3619165648Spiso	if (ac == 0)
3620165648Spiso		errx (EX_DATAERR, "redirect_port: missing public port");
3621165648Spiso
3622165648Spiso	sep = strchr (*av, ':');
3623165648Spiso	if (sep) {
3624165648Spiso	        if (StrToAddrAndPortRange (*av, &r->paddr, protoName,
3625165648Spiso		    &portRange) != 0)
3626165648Spiso		        errx(EX_DATAERR, "redirect_port:"
3627165648Spiso			    "invalid public port range");
3628165648Spiso	} else {
3629165648Spiso		r->paddr.s_addr = INADDR_ANY;
3630165648Spiso		if (StrToPortRange (*av, protoName, &portRange) != 0)
3631165648Spiso		        errx(EX_DATAERR, "redirect_port:"
3632165648Spiso			    "invalid public port range");
3633165648Spiso	}
3634165648Spiso
3635165648Spiso	r->pport = GETLOPORT(portRange);
3636165648Spiso	r->pport_cnt = GETNUMPORTS(portRange);
3637165648Spiso	INC_ARGCV();
3638165648Spiso
3639165648Spiso	/*
3640165648Spiso	 * Extract remote address and optionally port.
3641165648Spiso	 */
3642165648Spiso	/*
3643165648Spiso	 * NB: isalpha(**av) => we've to check that next parameter is really an
3644165648Spiso	 * option for this redirect entry, else stop here processing arg[cv].
3645165648Spiso	 */
3646165648Spiso	if (ac != 0 && !isalpha(**av)) {
3647165648Spiso		sep = strchr (*av, ':');
3648165648Spiso		if (sep) {
3649165648Spiso		        if (StrToAddrAndPortRange (*av, &r->raddr, protoName,
3650165648Spiso			    &portRange) != 0)
3651165648Spiso				errx(EX_DATAERR, "redirect_port:"
3652165648Spiso				    "invalid remote port range");
3653165648Spiso		} else {
3654165648Spiso		        SETLOPORT(portRange, 0);
3655165648Spiso			SETNUMPORTS(portRange, 1);
3656165648Spiso			StrToAddr (*av, &r->raddr);
3657165648Spiso		}
3658165648Spiso		INC_ARGCV();
3659165648Spiso	} else {
3660165648Spiso		SETLOPORT(portRange, 0);
3661165648Spiso		SETNUMPORTS(portRange, 1);
3662165648Spiso		r->raddr.s_addr = INADDR_ANY;
3663165648Spiso	}
3664165648Spiso	r->rport = GETLOPORT(portRange);
3665165648Spiso	r->rport_cnt = GETNUMPORTS(portRange);
3666165648Spiso
3667165648Spiso	/*
3668165648Spiso	 * Make sure port ranges match up, then add the redirect ports.
3669165648Spiso	 */
3670165648Spiso	if (numLocalPorts != r->pport_cnt)
3671165648Spiso	        errx(EX_DATAERR, "redirect_port:"
3672165648Spiso		    "port ranges must be equal in size");
3673165648Spiso
3674165648Spiso	/* Remote port range is allowed to be '0' which means all ports. */
3675165648Spiso	if (r->rport_cnt != numLocalPorts &&
3676165648Spiso	    (r->rport_cnt != 1 || r->rport != 0))
3677165648Spiso	        errx(EX_DATAERR, "redirect_port: remote port must"
3678165648Spiso		    "be 0 or equal to local port range in size");
3679165648Spiso
3680165648Spiso	/*
3681165648Spiso	 * Setup LSNAT server pool.
3682165648Spiso	 */
3683165648Spiso	if (lsnat) {
3684165648Spiso		sep = strtok(tmp_spool_buf, ",");
3685165648Spiso		while (sep != NULL) {
3686165648Spiso			tmp = (struct cfg_spool *)spool_buf;
3687165648Spiso			if (len < SOF_SPOOL)
3688165648Spiso				goto nospace;
3689165648Spiso			len -= SOF_SPOOL;
3690165648Spiso			space += SOF_SPOOL;
3691165648Spiso			if (StrToAddrAndPortRange(sep, &tmp->addr, protoName,
3692165648Spiso			    &portRange) != 0)
3693165648Spiso				errx(EX_DATAERR, "redirect_port:"
3694165648Spiso				    "invalid local port range");
3695165648Spiso			if (GETNUMPORTS(portRange) != 1)
3696165648Spiso				errx(EX_DATAERR, "redirect_port: local port"
3697165648Spiso				    "must be single in this context");
3698165648Spiso			tmp->port = GETLOPORT(portRange);
3699165648Spiso			r->spool_cnt++;
3700165648Spiso			/* Point to the next possible cfg_spool. */
3701165648Spiso			spool_buf = &spool_buf[SOF_SPOOL];
3702165648Spiso			sep = strtok(NULL, ",");
3703165648Spiso		}
3704165648Spiso	}
3705165648Spiso	return (space);
3706165648Spisonospace:
3707165648Spiso	errx(EX_DATAERR, "redirect_port: buf is too small\n");
3708165648Spiso}
3709165648Spiso
3710165648Spisostatic int
3711165648Spisosetup_redir_proto(char *spool_buf, int len,
3712165648Spiso		 int *_ac, char ***_av)
3713165648Spiso{
3714165648Spiso	char **av;
3715165648Spiso	int ac, i, space;
3716165648Spiso	struct protoent *protoent;
3717165648Spiso	struct cfg_redir *r;
3718165648Spiso
3719165648Spiso	av = *_av;
3720165648Spiso	ac = *_ac;
3721165648Spiso	if (len >= SOF_REDIR) {
3722165648Spiso		r = (struct cfg_redir *)spool_buf;
3723165648Spiso		/* Skip cfg_redir at beginning of buf. */
3724165648Spiso		spool_buf = &spool_buf[SOF_REDIR];
3725165648Spiso		space = SOF_REDIR;
3726165648Spiso		len -= SOF_REDIR;
3727165648Spiso	} else
3728165648Spiso		goto nospace;
3729165648Spiso	r->mode = REDIR_PROTO;
3730165648Spiso	/*
3731165648Spiso	 * Extract protocol.
3732165648Spiso	 */
3733165648Spiso	if (ac == 0)
3734165648Spiso		errx(EX_DATAERR, "redirect_proto: missing protocol");
3735165648Spiso
3736165648Spiso	protoent = getprotobyname(*av);
3737165648Spiso	if (protoent == NULL)
3738165648Spiso		errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av);
3739165648Spiso	else
3740165648Spiso		r->proto = protoent->p_proto;
3741165648Spiso
3742165648Spiso	INC_ARGCV();
3743165648Spiso
3744165648Spiso	/*
3745165648Spiso	 * Extract local address.
3746165648Spiso	 */
3747165648Spiso	if (ac == 0)
3748165648Spiso		errx(EX_DATAERR, "redirect_proto: missing local address");
3749165648Spiso	else
3750165648Spiso		StrToAddr(*av, &r->laddr);
3751165648Spiso
3752165648Spiso	INC_ARGCV();
3753165648Spiso
3754165648Spiso	/*
3755165648Spiso	 * Extract optional public address.
3756165648Spiso	 */
3757165648Spiso	if (ac == 0) {
3758165648Spiso		r->paddr.s_addr = INADDR_ANY;
3759165648Spiso		r->raddr.s_addr = INADDR_ANY;
3760165648Spiso	} else {
3761165648Spiso		/* see above in setup_redir_port() */
3762165648Spiso		if (!isalpha(**av)) {
3763165648Spiso			StrToAddr(*av, &r->paddr);
3764165648Spiso			INC_ARGCV();
3765165648Spiso
3766165648Spiso			/*
3767165648Spiso			 * Extract optional remote address.
3768165648Spiso			 */
3769165648Spiso			/* see above in setup_redir_port() */
3770165648Spiso			if (ac!=0 && !isalpha(**av)) {
3771165648Spiso				StrToAddr(*av, &r->raddr);
3772165648Spiso				INC_ARGCV();
3773165648Spiso			}
3774165648Spiso		}
3775165648Spiso	}
3776165648Spiso	return (space);
3777165648Spisonospace:
3778165648Spiso	errx(EX_DATAERR, "redirect_proto: buf is too small\n");
3779165648Spiso}
3780165648Spiso
3781165648Spisostatic void
3782165648Spisoshow_nat(int ac, char **av);
3783165648Spiso
3784165648Spisostatic void
3785165648Spisoprint_nat_config(char *buf) {
3786165648Spiso	struct cfg_nat *n;
3787165648Spiso	int i, cnt, flag, off;
3788165648Spiso	struct cfg_redir *t;
3789165648Spiso	struct cfg_spool *s;
3790165648Spiso	struct protoent *p;
3791165648Spiso
3792165648Spiso	n = (struct cfg_nat *)buf;
3793165648Spiso	flag = 1;
3794165648Spiso	off  = sizeof(*n);
3795165648Spiso	printf("ipfw nat %u config", n->id);
3796165648Spiso	if (strlen(n->if_name) != 0)
3797165648Spiso		printf(" if %s", n->if_name);
3798165648Spiso	else if (n->ip.s_addr != 0)
3799165648Spiso		printf(" ip %s", inet_ntoa(n->ip));
3800165648Spiso	while (n->mode != 0) {
3801165648Spiso		if (n->mode & PKT_ALIAS_LOG) {
3802165648Spiso			printf(" log");
3803165648Spiso			n->mode &= ~PKT_ALIAS_LOG;
3804165648Spiso		} else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
3805165648Spiso			printf(" deny_in");
3806165648Spiso			n->mode &= ~PKT_ALIAS_DENY_INCOMING;
3807165648Spiso		} else if (n->mode & PKT_ALIAS_SAME_PORTS) {
3808165648Spiso			printf(" same_ports");
3809165648Spiso			n->mode &= ~PKT_ALIAS_SAME_PORTS;
3810165648Spiso		} else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
3811165648Spiso			printf(" unreg_only");
3812165648Spiso			n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
3813165648Spiso		} else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
3814165648Spiso			printf(" reset");
3815165648Spiso			n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
3816165648Spiso		} else if (n->mode & PKT_ALIAS_REVERSE) {
3817165648Spiso			printf(" reverse");
3818165648Spiso			n->mode &= ~PKT_ALIAS_REVERSE;
3819165648Spiso		} else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
3820165648Spiso			printf(" proxy_only");
3821165648Spiso			n->mode &= ~PKT_ALIAS_PROXY_ONLY;
3822165648Spiso		}
3823165648Spiso	}
3824165648Spiso	/* Print all the redirect's data configuration. */
3825165648Spiso	for (cnt = 0; cnt < n->redir_cnt; cnt++) {
3826165648Spiso		t = (struct cfg_redir *)&buf[off];
3827165648Spiso		off += SOF_REDIR;
3828165648Spiso		switch (t->mode) {
3829165648Spiso		case REDIR_ADDR:
3830165648Spiso			printf(" redirect_addr");
3831165648Spiso			if (t->spool_cnt == 0)
3832165648Spiso				printf(" %s", inet_ntoa(t->laddr));
3833165648Spiso			else
3834165648Spiso				for (i = 0; i < t->spool_cnt; i++) {
3835165648Spiso					s = (struct cfg_spool *)&buf[off];
3836165648Spiso					if (i)
3837165648Spiso						printf(",");
3838165648Spiso					else
3839165648Spiso						printf(" ");
3840165648Spiso					printf("%s", inet_ntoa(s->addr));
3841165648Spiso					off += SOF_SPOOL;
3842165648Spiso				}
3843165648Spiso			printf(" %s", inet_ntoa(t->paddr));
3844165648Spiso			break;
3845165648Spiso		case REDIR_PORT:
3846165648Spiso			p = getprotobynumber(t->proto);
3847165648Spiso			printf(" redirect_port %s ", p->p_name);
3848165648Spiso			if (!t->spool_cnt) {
3849165648Spiso				printf("%s:%u", inet_ntoa(t->laddr), t->lport);
3850165648Spiso				if (t->pport_cnt > 1)
3851165648Spiso					printf("-%u", t->lport +
3852165648Spiso					    t->pport_cnt - 1);
3853165648Spiso			} else
3854165648Spiso				for (i=0; i < t->spool_cnt; i++) {
3855165648Spiso					s = (struct cfg_spool *)&buf[off];
3856165648Spiso					if (i)
3857165648Spiso						printf(",");
3858165648Spiso					printf("%s:%u", inet_ntoa(s->addr),
3859165648Spiso					    s->port);
3860165648Spiso					off += SOF_SPOOL;
3861165648Spiso				}
3862165648Spiso
3863165648Spiso			printf(" ");
3864165648Spiso			if (t->paddr.s_addr)
3865165648Spiso				printf("%s:", inet_ntoa(t->paddr));
3866165648Spiso			printf("%u", t->pport);
3867165648Spiso			if (!t->spool_cnt && t->pport_cnt > 1)
3868165648Spiso				printf("-%u", t->pport + t->pport_cnt - 1);
3869165648Spiso
3870165648Spiso			if (t->raddr.s_addr) {
3871165648Spiso				printf(" %s", inet_ntoa(t->raddr));
3872165648Spiso				if (t->rport) {
3873165648Spiso					printf(":%u", t->rport);
3874165648Spiso					if (!t->spool_cnt && t->rport_cnt > 1)
3875165648Spiso						printf("-%u", t->rport +
3876165648Spiso						    t->rport_cnt - 1);
3877165648Spiso				}
3878165648Spiso			}
3879165648Spiso			break;
3880165648Spiso		case REDIR_PROTO:
3881165648Spiso			p = getprotobynumber(t->proto);
3882165648Spiso			printf(" redirect_proto %s %s", p->p_name,
3883165648Spiso			    inet_ntoa(t->laddr));
3884165648Spiso			if (t->paddr.s_addr != 0) {
3885165648Spiso				printf(" %s", inet_ntoa(t->paddr));
3886165648Spiso				if (t->raddr.s_addr)
3887165648Spiso					printf(" %s", inet_ntoa(t->raddr));
3888165648Spiso			}
3889165648Spiso			break;
3890165648Spiso		default:
3891165648Spiso			errx(EX_DATAERR, "unknown redir mode");
3892165648Spiso			break;
3893165648Spiso		}
3894165648Spiso	}
3895165648Spiso	printf("\n");
3896165648Spiso}
3897165648Spiso
3898165648Spisostatic void
3899165648Spisoconfig_nat(int ac, char **av)
3900165648Spiso{
3901165648Spiso	struct cfg_nat *n;              /* Nat instance configuration. */
3902165648Spiso	struct in_addr ip;
3903165648Spiso	int i, len, off, tok;
3904165648Spiso	char *id, buf[NAT_BUF_LEN]; 	/* Buffer for serialized data. */
3905165648Spiso
3906165648Spiso	len = NAT_BUF_LEN;
3907165648Spiso	/* Offset in buf: save space for n at the beginning. */
3908165648Spiso	off = sizeof(*n);
3909165648Spiso	memset(buf, 0, sizeof(buf));
3910165648Spiso	n = (struct cfg_nat *)buf;
3911165648Spiso
3912165648Spiso	av++; ac--;
3913165648Spiso	/* Nat id. */
3914165648Spiso	if (ac && isdigit(**av)) {
3915165648Spiso		id = *av;
3916165648Spiso		i = atoi(*av);
3917165648Spiso		ac--; av++;
3918165648Spiso		n->id = i;
3919165648Spiso	} else
3920165648Spiso		errx(EX_DATAERR, "missing nat id");
3921165648Spiso	if (ac == 0)
3922165648Spiso		errx(EX_DATAERR, "missing option");
3923165648Spiso
3924165648Spiso	while (ac > 0) {
3925165648Spiso		tok = match_token(nat_params, *av);
3926165648Spiso		ac--; av++;
3927165648Spiso		switch (tok) {
3928165648Spiso		case TOK_IP:
3929165648Spiso			if (ac == 0)
3930165648Spiso				errx(EX_DATAERR, "missing option");
3931165648Spiso			if (!inet_aton(av[0], &(n->ip)))
3932165648Spiso				errx(EX_DATAERR, "bad ip address ``%s''",
3933165648Spiso				    av[0]);
3934165648Spiso			ac--; av++;
3935165648Spiso			break;
3936165648Spiso		case TOK_IF:
3937165648Spiso			set_addr_dynamic(av[0], n);
3938165648Spiso			ac--; av++;
3939165648Spiso			break;
3940165648Spiso		case TOK_ALOG:
3941165648Spiso			n->mode |= PKT_ALIAS_LOG;
3942165648Spiso			break;
3943165648Spiso		case TOK_DENY_INC:
3944165648Spiso			n->mode |= PKT_ALIAS_DENY_INCOMING;
3945165648Spiso			break;
3946165648Spiso		case TOK_SAME_PORTS:
3947165648Spiso			n->mode |= PKT_ALIAS_SAME_PORTS;
3948165648Spiso			break;
3949165648Spiso		case TOK_UNREG_ONLY:
3950165648Spiso			n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
3951165648Spiso			break;
3952165648Spiso		case TOK_RESET_ADDR:
3953165648Spiso			n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
3954165648Spiso			break;
3955165648Spiso		case TOK_ALIAS_REV:
3956165648Spiso			n->mode |= PKT_ALIAS_REVERSE;
3957165648Spiso			break;
3958165648Spiso		case TOK_PROXY_ONLY:
3959165648Spiso			n->mode |= PKT_ALIAS_PROXY_ONLY;
3960165648Spiso			break;
3961165648Spiso			/*
3962165648Spiso			 * All the setup_redir_* functions work directly in the final
3963165648Spiso			 * buffer, see above for details.
3964165648Spiso			 */
3965165648Spiso		case TOK_REDIR_ADDR:
3966165648Spiso		case TOK_REDIR_PORT:
3967165648Spiso		case TOK_REDIR_PROTO:
3968165648Spiso			switch (tok) {
3969165648Spiso			case TOK_REDIR_ADDR:
3970165648Spiso				i = setup_redir_addr(&buf[off], len, &ac, &av);
3971165648Spiso				break;
3972165648Spiso			case TOK_REDIR_PORT:
3973165648Spiso				i = setup_redir_port(&buf[off], len, &ac, &av);
3974165648Spiso				break;
3975165648Spiso			case TOK_REDIR_PROTO:
3976165648Spiso				i = setup_redir_proto(&buf[off], len, &ac, &av);
3977165648Spiso				break;
3978165648Spiso			}
3979165648Spiso			n->redir_cnt++;
3980165648Spiso			off += i;
3981165648Spiso			len -= i;
3982165648Spiso			break;
3983165648Spiso		default:
3984165648Spiso			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
3985165648Spiso		}
3986165648Spiso	}
3987165648Spiso
3988165648Spiso	i = do_cmd(IP_FW_NAT_CFG, buf, off);
3989165648Spiso	if (i)
3990165648Spiso		err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
3991165648Spiso
3992165648Spiso	/* After every modification, we show the resultant rule. */
3993165648Spiso	int _ac = 3;
3994165648Spiso	char *_av[] = {"show", "config", id};
3995165648Spiso	show_nat(_ac, _av);
3996165648Spiso}
3997165648Spiso
3998165648Spisostatic void
399998943Sluigiconfig_pipe(int ac, char **av)
400098943Sluigi{
4001117469Sluigi	struct dn_pipe p;
400298943Sluigi	int i;
400398943Sluigi	char *end;
400498943Sluigi	void *par = NULL;
400598943Sluigi
4006117469Sluigi	memset(&p, 0, sizeof p);
400798943Sluigi
400898943Sluigi	av++; ac--;
400998943Sluigi	/* Pipe number */
401098943Sluigi	if (ac && isdigit(**av)) {
401198943Sluigi		i = atoi(*av); av++; ac--;
401298943Sluigi		if (do_pipe == 1)
4013117469Sluigi			p.pipe_nr = i;
401498943Sluigi		else
4015117469Sluigi			p.fs.fs_nr = i;
401698943Sluigi	}
401799475Sluigi	while (ac > 0) {
401898943Sluigi		double d;
401998943Sluigi		int tok = match_token(dummynet_params, *av);
402098943Sluigi		ac--; av++;
402198943Sluigi
402298943Sluigi		switch(tok) {
4023101978Sluigi		case TOK_NOERROR:
4024117469Sluigi			p.fs.flags_fs |= DN_NOERROR;
4025101978Sluigi			break;
4026101978Sluigi
402798943Sluigi		case TOK_PLR:
402898943Sluigi			NEED1("plr needs argument 0..1\n");
402998943Sluigi			d = strtod(av[0], NULL);
403098943Sluigi			if (d > 1)
403198943Sluigi				d = 1;
403298943Sluigi			else if (d < 0)
403398943Sluigi				d = 0;
4034117469Sluigi			p.fs.plr = (int)(d*0x7fffffff);
403598943Sluigi			ac--; av++;
403698943Sluigi			break;
403798943Sluigi
403898943Sluigi		case TOK_QUEUE:
403998943Sluigi			NEED1("queue needs queue size\n");
404098943Sluigi			end = NULL;
4041117469Sluigi			p.fs.qsize = strtoul(av[0], &end, 0);
404298943Sluigi			if (*end == 'K' || *end == 'k') {
4043117469Sluigi				p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
4044117469Sluigi				p.fs.qsize *= 1024;
4045140271Sbrooks			} else if (*end == 'B' ||
4046140271Sbrooks			    _substrcmp2(end, "by", "bytes") == 0) {
4047117469Sluigi				p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
404898943Sluigi			}
404998943Sluigi			ac--; av++;
405098943Sluigi			break;
405198943Sluigi
405298943Sluigi		case TOK_BUCKETS:
405398943Sluigi			NEED1("buckets needs argument\n");
4054117469Sluigi			p.fs.rq_size = strtoul(av[0], NULL, 0);
405598943Sluigi			ac--; av++;
405698943Sluigi			break;
405798943Sluigi
405898943Sluigi		case TOK_MASK:
405998943Sluigi			NEED1("mask needs mask specifier\n");
406098943Sluigi			/*
406198943Sluigi			 * per-flow queue, mask is dst_ip, dst_port,
406298943Sluigi			 * src_ip, src_port, proto measured in bits
406398943Sluigi			 */
406498943Sluigi			par = NULL;
406598943Sluigi
4066145246Sbrooks			bzero(&p.fs.flow_mask, sizeof(p.fs.flow_mask));
406798943Sluigi			end = NULL;
406898943Sluigi
406998943Sluigi			while (ac >= 1) {
4070117328Sluigi			    uint32_t *p32 = NULL;
4071117328Sluigi			    uint16_t *p16 = NULL;
4072145246Sbrooks			    uint32_t *p20 = NULL;
4073145246Sbrooks			    struct in6_addr *pa6 = NULL;
4074145246Sbrooks			    uint32_t a;
407598943Sluigi
407698943Sluigi			    tok = match_token(dummynet_params, *av);
407798943Sluigi			    ac--; av++;
407898943Sluigi			    switch(tok) {
407998943Sluigi			    case TOK_ALL:
408098943Sluigi				    /*
408198943Sluigi				     * special case, all bits significant
408298943Sluigi				     */
4083117469Sluigi				    p.fs.flow_mask.dst_ip = ~0;
4084117469Sluigi				    p.fs.flow_mask.src_ip = ~0;
4085117469Sluigi				    p.fs.flow_mask.dst_port = ~0;
4086117469Sluigi				    p.fs.flow_mask.src_port = ~0;
4087117469Sluigi				    p.fs.flow_mask.proto = ~0;
4088145246Sbrooks				    n2mask(&(p.fs.flow_mask.dst_ip6), 128);
4089145246Sbrooks				    n2mask(&(p.fs.flow_mask.src_ip6), 128);
4090145246Sbrooks				    p.fs.flow_mask.flow_id6 = ~0;
4091117469Sluigi				    p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
409298943Sluigi				    goto end_mask;
409398943Sluigi
409498943Sluigi			    case TOK_DSTIP:
4095117469Sluigi				    p32 = &p.fs.flow_mask.dst_ip;
409698943Sluigi				    break;
409798943Sluigi
409898943Sluigi			    case TOK_SRCIP:
4099117469Sluigi				    p32 = &p.fs.flow_mask.src_ip;
410098943Sluigi				    break;
410198943Sluigi
4102145246Sbrooks			    case TOK_DSTIP6:
4103145246Sbrooks				    pa6 = &(p.fs.flow_mask.dst_ip6);
4104145246Sbrooks				    break;
4105145246Sbrooks
4106145246Sbrooks			    case TOK_SRCIP6:
4107145246Sbrooks				    pa6 = &(p.fs.flow_mask.src_ip6);
4108145246Sbrooks				    break;
4109145246Sbrooks
4110145246Sbrooks			    case TOK_FLOWID:
4111145246Sbrooks				    p20 = &p.fs.flow_mask.flow_id6;
4112145246Sbrooks				    break;
4113145246Sbrooks
411498943Sluigi			    case TOK_DSTPORT:
4115117469Sluigi				    p16 = &p.fs.flow_mask.dst_port;
411698943Sluigi				    break;
411798943Sluigi
411898943Sluigi			    case TOK_SRCPORT:
4119117469Sluigi				    p16 = &p.fs.flow_mask.src_port;
412098943Sluigi				    break;
412198943Sluigi
412298943Sluigi			    case TOK_PROTO:
412398943Sluigi				    break;
412498943Sluigi
412598943Sluigi			    default:
412698943Sluigi				    ac++; av--; /* backtrack */
412798943Sluigi				    goto end_mask;
412898943Sluigi			    }
412998943Sluigi			    if (ac < 1)
413098943Sluigi				    errx(EX_USAGE, "mask: value missing");
413198943Sluigi			    if (*av[0] == '/') {
413298943Sluigi				    a = strtoul(av[0]+1, &end, 0);
4133145246Sbrooks				    if (pa6 == NULL)
4134145246Sbrooks					    a = (a == 32) ? ~0 : (1 << a) - 1;
4135106505Smaxim			    } else
413699909Sluigi				    a = strtoul(av[0], &end, 0);
413798943Sluigi			    if (p32 != NULL)
413898943Sluigi				    *p32 = a;
413998943Sluigi			    else if (p16 != NULL) {
4140139821Sbrooks				    if (a > 0xFFFF)
414198943Sluigi					    errx(EX_DATAERR,
4142144687Sbrooks						"port mask must be 16 bit");
4143117328Sluigi				    *p16 = (uint16_t)a;
4144145246Sbrooks			    } else if (p20 != NULL) {
4145145246Sbrooks				    if (a > 0xfffff)
4146145246Sbrooks					errx(EX_DATAERR,
4147145246Sbrooks					    "flow_id mask must be 20 bit");
4148145246Sbrooks				    *p20 = (uint32_t)a;
4149145246Sbrooks			    } else if (pa6 != NULL) {
4150145246Sbrooks				    if (a < 0 || a > 128)
4151145246Sbrooks					errx(EX_DATAERR,
4152145246Sbrooks					    "in6addr invalid mask len");
4153145246Sbrooks				    else
4154145246Sbrooks					n2mask(pa6, a);
415598943Sluigi			    } else {
4156139821Sbrooks				    if (a > 0xFF)
415798943Sluigi					    errx(EX_DATAERR,
4158144687Sbrooks						"proto mask must be 8 bit");
4159117469Sluigi				    p.fs.flow_mask.proto = (uint8_t)a;
416098943Sluigi			    }
416198943Sluigi			    if (a != 0)
4162117469Sluigi				    p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
416398943Sluigi			    ac--; av++;
416498943Sluigi			} /* end while, config masks */
416598943Sluigiend_mask:
416698943Sluigi			break;
416798943Sluigi
416898943Sluigi		case TOK_RED:
416998943Sluigi		case TOK_GRED:
417098943Sluigi			NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
4171117469Sluigi			p.fs.flags_fs |= DN_IS_RED;
417298943Sluigi			if (tok == TOK_GRED)
4173117469Sluigi				p.fs.flags_fs |= DN_IS_GENTLE_RED;
417498943Sluigi			/*
417598943Sluigi			 * the format for parameters is w_q/min_th/max_th/max_p
417698943Sluigi			 */
417798943Sluigi			if ((end = strsep(&av[0], "/"))) {
417898943Sluigi			    double w_q = strtod(end, NULL);
417998943Sluigi			    if (w_q > 1 || w_q <= 0)
418098943Sluigi				errx(EX_DATAERR, "0 < w_q <= 1");
4181117469Sluigi			    p.fs.w_q = (int) (w_q * (1 << SCALE_RED));
418298943Sluigi			}
418398943Sluigi			if ((end = strsep(&av[0], "/"))) {
4184117469Sluigi			    p.fs.min_th = strtoul(end, &end, 0);
418598943Sluigi			    if (*end == 'K' || *end == 'k')
4186117469Sluigi				p.fs.min_th *= 1024;
418798943Sluigi			}
418898943Sluigi			if ((end = strsep(&av[0], "/"))) {
4189117469Sluigi			    p.fs.max_th = strtoul(end, &end, 0);
419098943Sluigi			    if (*end == 'K' || *end == 'k')
4191117469Sluigi				p.fs.max_th *= 1024;
419298943Sluigi			}
419398943Sluigi			if ((end = strsep(&av[0], "/"))) {
419498943Sluigi			    double max_p = strtod(end, NULL);
419598943Sluigi			    if (max_p > 1 || max_p <= 0)
419698943Sluigi				errx(EX_DATAERR, "0 < max_p <= 1");
4197117469Sluigi			    p.fs.max_p = (int)(max_p * (1 << SCALE_RED));
419898943Sluigi			}
419998943Sluigi			ac--; av++;
420098943Sluigi			break;
420198943Sluigi
420298943Sluigi		case TOK_DROPTAIL:
4203117469Sluigi			p.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
420498943Sluigi			break;
4205106505Smaxim
420698943Sluigi		case TOK_BW:
420798943Sluigi			NEED1("bw needs bandwidth or interface\n");
420898943Sluigi			if (do_pipe != 1)
420998943Sluigi			    errx(EX_DATAERR, "bandwidth only valid for pipes");
421098943Sluigi			/*
421198943Sluigi			 * set clocking interface or bandwidth value
421298943Sluigi			 */
421398943Sluigi			if (av[0][0] >= 'a' && av[0][0] <= 'z') {
4214117469Sluigi			    int l = sizeof(p.if_name)-1;
421598943Sluigi			    /* interface name */
4216117469Sluigi			    strncpy(p.if_name, av[0], l);
4217117469Sluigi			    p.if_name[l] = '\0';
4218117469Sluigi			    p.bandwidth = 0;
421998943Sluigi			} else {
4220117469Sluigi			    p.if_name[0] = '\0';
4221117469Sluigi			    p.bandwidth = strtoul(av[0], &end, 0);
422298943Sluigi			    if (*end == 'K' || *end == 'k') {
422398943Sluigi				end++;
4224117469Sluigi				p.bandwidth *= 1000;
422598943Sluigi			    } else if (*end == 'M') {
422698943Sluigi				end++;
4227117469Sluigi				p.bandwidth *= 1000000;
422898943Sluigi			    }
4229161550Sdwmalone			    if ((*end == 'B' &&
4230161550Sdwmalone				  _substrcmp2(end, "Bi", "Bit/s") != 0) ||
4231140271Sbrooks			        _substrcmp2(end, "by", "bytes") == 0)
4232117469Sluigi				p.bandwidth *= 8;
4233117469Sluigi			    if (p.bandwidth < 0)
423498943Sluigi				errx(EX_DATAERR, "bandwidth too large");
423598943Sluigi			}
423698943Sluigi			ac--; av++;
423798943Sluigi			break;
423898943Sluigi
423998943Sluigi		case TOK_DELAY:
424098943Sluigi			if (do_pipe != 1)
424198943Sluigi				errx(EX_DATAERR, "delay only valid for pipes");
424298943Sluigi			NEED1("delay needs argument 0..10000ms\n");
4243117469Sluigi			p.delay = strtoul(av[0], NULL, 0);
424498943Sluigi			ac--; av++;
424598943Sluigi			break;
424698943Sluigi
424798943Sluigi		case TOK_WEIGHT:
424898943Sluigi			if (do_pipe == 1)
424998943Sluigi				errx(EX_DATAERR,"weight only valid for queues");
425098943Sluigi			NEED1("weight needs argument 0..100\n");
4251117469Sluigi			p.fs.weight = strtoul(av[0], &end, 0);
425298943Sluigi			ac--; av++;
425398943Sluigi			break;
425498943Sluigi
425598943Sluigi		case TOK_PIPE:
425698943Sluigi			if (do_pipe == 1)
425798943Sluigi				errx(EX_DATAERR,"pipe only valid for queues");
425898943Sluigi			NEED1("pipe needs pipe_number\n");
4259117469Sluigi			p.fs.parent_nr = strtoul(av[0], &end, 0);
426098943Sluigi			ac--; av++;
426198943Sluigi			break;
426298943Sluigi
426398943Sluigi		default:
4264124924Smaxim			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
426598943Sluigi		}
426698943Sluigi	}
426798943Sluigi	if (do_pipe == 1) {
4268117469Sluigi		if (p.pipe_nr == 0)
426998943Sluigi			errx(EX_DATAERR, "pipe_nr must be > 0");
4270117469Sluigi		if (p.delay > 10000)
427198943Sluigi			errx(EX_DATAERR, "delay must be < 10000");
427298943Sluigi	} else { /* do_pipe == 2, queue */
4273117469Sluigi		if (p.fs.parent_nr == 0)
427498943Sluigi			errx(EX_DATAERR, "pipe must be > 0");
4275117469Sluigi		if (p.fs.weight >100)
427698943Sluigi			errx(EX_DATAERR, "weight must be <= 100");
427798943Sluigi	}
4278117469Sluigi	if (p.fs.flags_fs & DN_QSIZE_IS_BYTES) {
4279117469Sluigi		if (p.fs.qsize > 1024*1024)
428098943Sluigi			errx(EX_DATAERR, "queue size must be < 1MB");
428198943Sluigi	} else {
4282117469Sluigi		if (p.fs.qsize > 100)
428398943Sluigi			errx(EX_DATAERR, "2 <= queue size <= 100");
428498943Sluigi	}
4285117469Sluigi	if (p.fs.flags_fs & DN_IS_RED) {
428698943Sluigi		size_t len;
428798943Sluigi		int lookup_depth, avg_pkt_size;
428898943Sluigi		double s, idle, weight, w_q;
4289117469Sluigi		struct clockinfo ck;
429098943Sluigi		int t;
429198943Sluigi
4292117469Sluigi		if (p.fs.min_th >= p.fs.max_th)
429398943Sluigi		    errx(EX_DATAERR, "min_th %d must be < than max_th %d",
4294117469Sluigi			p.fs.min_th, p.fs.max_th);
4295117469Sluigi		if (p.fs.max_th == 0)
429698943Sluigi		    errx(EX_DATAERR, "max_th must be > 0");
429798943Sluigi
429898943Sluigi		len = sizeof(int);
429998943Sluigi		if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
430098943Sluigi			&lookup_depth, &len, NULL, 0) == -1)
430198943Sluigi
430298943Sluigi		    errx(1, "sysctlbyname(\"%s\")",
430398943Sluigi			"net.inet.ip.dummynet.red_lookup_depth");
430498943Sluigi		if (lookup_depth == 0)
430598943Sluigi		    errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth"
430698943Sluigi			" must be greater than zero");
430798943Sluigi
430898943Sluigi		len = sizeof(int);
430998943Sluigi		if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
431098943Sluigi			&avg_pkt_size, &len, NULL, 0) == -1)
431198943Sluigi
431298943Sluigi		    errx(1, "sysctlbyname(\"%s\")",
431398943Sluigi			"net.inet.ip.dummynet.red_avg_pkt_size");
431498943Sluigi		if (avg_pkt_size == 0)
431598943Sluigi			errx(EX_DATAERR,
431698943Sluigi			    "net.inet.ip.dummynet.red_avg_pkt_size must"
431798943Sluigi			    " be greater than zero");
431898943Sluigi
431998943Sluigi		len = sizeof(struct clockinfo);
4320117469Sluigi		if (sysctlbyname("kern.clockrate", &ck, &len, NULL, 0) == -1)
432198943Sluigi			errx(1, "sysctlbyname(\"%s\")", "kern.clockrate");
432298943Sluigi
432398943Sluigi		/*
432498943Sluigi		 * Ticks needed for sending a medium-sized packet.
432598943Sluigi		 * Unfortunately, when we are configuring a WF2Q+ queue, we
432698943Sluigi		 * do not have bandwidth information, because that is stored
432798943Sluigi		 * in the parent pipe, and also we have multiple queues
432898943Sluigi		 * competing for it. So we set s=0, which is not very
432998943Sluigi		 * correct. But on the other hand, why do we want RED with
433098943Sluigi		 * WF2Q+ ?
433198943Sluigi		 */
4332117469Sluigi		if (p.bandwidth==0) /* this is a WF2Q+ queue */
433398943Sluigi			s = 0;
433498943Sluigi		else
4335117469Sluigi			s = ck.hz * avg_pkt_size * 8 / p.bandwidth;
433698943Sluigi
433798943Sluigi		/*
433898943Sluigi		 * max idle time (in ticks) before avg queue size becomes 0.
433998943Sluigi		 * NOTA:  (3/w_q) is approx the value x so that
434098943Sluigi		 * (1-w_q)^x < 10^-3.
434198943Sluigi		 */
4342117469Sluigi		w_q = ((double)p.fs.w_q) / (1 << SCALE_RED);
434398943Sluigi		idle = s * 3. / w_q;
4344117469Sluigi		p.fs.lookup_step = (int)idle / lookup_depth;
4345117469Sluigi		if (!p.fs.lookup_step)
4346117469Sluigi			p.fs.lookup_step = 1;
434798943Sluigi		weight = 1 - w_q;
4348117469Sluigi		for (t = p.fs.lookup_step; t > 0; --t)
434998943Sluigi			weight *= weight;
4350117469Sluigi		p.fs.lookup_weight = (int)(weight * (1 << SCALE_RED));
435198943Sluigi	}
4352117469Sluigi	i = do_cmd(IP_DUMMYNET_CONFIGURE, &p, sizeof p);
435398943Sluigi	if (i)
435498943Sluigi		err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
435598943Sluigi}
435698943Sluigi
435798943Sluigistatic void
4358117577Sluigiget_mac_addr_mask(char *p, uint8_t *addr, uint8_t *mask)
435998943Sluigi{
436098943Sluigi	int i, l;
436198943Sluigi
436298943Sluigi	for (i=0; i<6; i++)
436398943Sluigi		addr[i] = mask[i] = 0;
4364140271Sbrooks	if (strcmp(p, "any") == 0)
436598943Sluigi		return;
436698943Sluigi
436798943Sluigi	for (i=0; *p && i<6;i++, p++) {
436898943Sluigi		addr[i] = strtol(p, &p, 16);
436998943Sluigi		if (*p != ':') /* we start with the mask */
437098943Sluigi			break;
437198943Sluigi	}
437298943Sluigi	if (*p == '/') { /* mask len */
437398943Sluigi		l = strtol(p+1, &p, 0);
437498943Sluigi		for (i=0; l>0; l -=8, i++)
437598943Sluigi			mask[i] = (l >=8) ? 0xff : (~0) << (8-l);
437698943Sluigi	} else if (*p == '&') { /* mask */
437798943Sluigi		for (i=0, p++; *p && i<6;i++, p++) {
437898943Sluigi			mask[i] = strtol(p, &p, 16);
437998943Sluigi			if (*p != ':')
438098943Sluigi				break;
438198943Sluigi		}
438298943Sluigi	} else if (*p == '\0') {
438398943Sluigi		for (i=0; i<6; i++)
438498943Sluigi			mask[i] = 0xff;
438598943Sluigi	}
438698943Sluigi	for (i=0; i<6; i++)
438798943Sluigi		addr[i] &= mask[i];
438898943Sluigi}
438998943Sluigi
439098943Sluigi/*
439198943Sluigi * helper function, updates the pointer to cmd with the length
439298943Sluigi * of the current command, and also cleans up the first word of
439398943Sluigi * the new command in case it has been clobbered before.
439498943Sluigi */
439598943Sluigistatic ipfw_insn *
439698943Sluiginext_cmd(ipfw_insn *cmd)
439798943Sluigi{
439898943Sluigi	cmd += F_LEN(cmd);
439998943Sluigi	bzero(cmd, sizeof(*cmd));
440098943Sluigi	return cmd;
440198943Sluigi}
440298943Sluigi
440398943Sluigi/*
4404117469Sluigi * Takes arguments and copies them into a comment
4405117469Sluigi */
4406117469Sluigistatic void
4407117469Sluigifill_comment(ipfw_insn *cmd, int ac, char **av)
4408117469Sluigi{
4409117469Sluigi	int i, l;
4410117469Sluigi	char *p = (char *)(cmd + 1);
4411117577Sluigi
4412117469Sluigi	cmd->opcode = O_NOP;
4413117469Sluigi	cmd->len =  (cmd->len & (F_NOT | F_OR));
4414117469Sluigi
4415117469Sluigi	/* Compute length of comment string. */
4416117469Sluigi	for (i = 0, l = 0; i < ac; i++)
4417117469Sluigi		l += strlen(av[i]) + 1;
4418117469Sluigi	if (l == 0)
4419117469Sluigi		return;
4420117469Sluigi	if (l > 84)
4421117469Sluigi		errx(EX_DATAERR,
4422117469Sluigi		    "comment too long (max 80 chars)");
4423117469Sluigi	l = 1 + (l+3)/4;
4424117469Sluigi	cmd->len =  (cmd->len & (F_NOT | F_OR)) | l;
4425117469Sluigi	for (i = 0; i < ac; i++) {
4426117469Sluigi		strcpy(p, av[i]);
4427117469Sluigi		p += strlen(av[i]);
4428117469Sluigi		*p++ = ' ';
4429117469Sluigi	}
4430117469Sluigi	*(--p) = '\0';
4431117469Sluigi}
4432117577Sluigi
4433117469Sluigi/*
443498943Sluigi * A function to fill simple commands of size 1.
443598943Sluigi * Existing flags are preserved.
443698943Sluigi */
443798943Sluigistatic void
4438117328Sluigifill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg)
443998943Sluigi{
444098943Sluigi	cmd->opcode = opcode;
444198943Sluigi	cmd->len =  ((cmd->len | flags) & (F_NOT | F_OR)) | 1;
444298943Sluigi	cmd->arg1 = arg;
444398943Sluigi}
444498943Sluigi
444598943Sluigi/*
444698943Sluigi * Fetch and add the MAC address and type, with masks. This generates one or
444798943Sluigi * two microinstructions, and returns the pointer to the last one.
444898943Sluigi */
444998943Sluigistatic ipfw_insn *
445098943Sluigiadd_mac(ipfw_insn *cmd, int ac, char *av[])
445198943Sluigi{
4452102087Sluigi	ipfw_insn_mac *mac;
445398943Sluigi
4454102087Sluigi	if (ac < 2)
4455102098Sluigi		errx(EX_DATAERR, "MAC dst src");
445698943Sluigi
445798943Sluigi	cmd->opcode = O_MACADDR2;
445898943Sluigi	cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
445998943Sluigi
446098943Sluigi	mac = (ipfw_insn_mac *)cmd;
4461101978Sluigi	get_mac_addr_mask(av[0], mac->addr, mac->mask);	/* dst */
446298943Sluigi	get_mac_addr_mask(av[1], &(mac->addr[6]), &(mac->mask[6])); /* src */
4463102087Sluigi	return cmd;
4464102087Sluigi}
446598943Sluigi
4466102087Sluigistatic ipfw_insn *
4467102087Sluigiadd_mactype(ipfw_insn *cmd, int ac, char *av)
4468102087Sluigi{
4469102087Sluigi	if (ac < 1)
4470102087Sluigi		errx(EX_DATAERR, "missing MAC type");
4471102087Sluigi	if (strcmp(av, "any") != 0) { /* we have a non-null type */
4472102087Sluigi		fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE);
447398943Sluigi		cmd->opcode = O_MAC_TYPE;
4474102087Sluigi		return cmd;
4475102087Sluigi	} else
4476102087Sluigi		return NULL;
4477102087Sluigi}
447898943Sluigi
4479102087Sluigistatic ipfw_insn *
4480152923Sumeadd_proto0(ipfw_insn *cmd, char *av, u_char *protop)
4481102087Sluigi{
4482102087Sluigi	struct protoent *pe;
4483152923Sume	char *ep;
4484152923Sume	int proto;
4485102087Sluigi
4486156315Sume	proto = strtol(av, &ep, 10);
4487156315Sume	if (*ep != '\0' || proto <= 0) {
4488152923Sume		if ((pe = getprotobyname(av)) == NULL)
4489152923Sume			return NULL;
4490152923Sume		proto = pe->p_proto;
4491152923Sume	}
4492145246Sbrooks
4493152923Sume	fill_cmd(cmd, O_PROTO, 0, proto);
4494152923Sume	*protop = proto;
4495152923Sume	return cmd;
4496152923Sume}
4497152923Sume
4498152923Sumestatic ipfw_insn *
4499152923Sumeadd_proto(ipfw_insn *cmd, char *av, u_char *protop)
4500152923Sume{
4501152923Sume	u_char proto = IPPROTO_IP;
4502152923Sume
4503156315Sume	if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
4504146894Smlaier		; /* do not set O_IP4 nor O_IP6 */
4505152923Sume	else if (strcmp(av, "ip4") == 0)
4506152923Sume		/* explicit "just IPv4" rule */
4507152923Sume		fill_cmd(cmd, O_IP4, 0, 0);
4508152923Sume	else if (strcmp(av, "ip6") == 0) {
4509152923Sume		/* explicit "just IPv6" rule */
4510152923Sume		proto = IPPROTO_IPV6;
4511152923Sume		fill_cmd(cmd, O_IP6, 0, 0);
4512152923Sume	} else
4513152923Sume		return add_proto0(cmd, av, protop);
4514152923Sume
4515152923Sume	*protop = proto;
4516152923Sume	return cmd;
4517152923Sume}
4518152923Sume
4519152923Sumestatic ipfw_insn *
4520152923Sumeadd_proto_compat(ipfw_insn *cmd, char *av, u_char *protop)
4521152923Sume{
4522152923Sume	u_char proto = IPPROTO_IP;
4523152923Sume
4524152923Sume	if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
4525152923Sume		; /* do not set O_IP4 nor O_IP6 */
4526146894Smlaier	else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0)
4527146894Smlaier		/* explicit "just IPv4" rule */
4528146894Smlaier		fill_cmd(cmd, O_IP4, 0, 0);
4529146894Smlaier	else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) {
4530146894Smlaier		/* explicit "just IPv6" rule */
4531152923Sume		proto = IPPROTO_IPV6;
4532146894Smlaier		fill_cmd(cmd, O_IP6, 0, 0);
4533152923Sume	} else
4534152923Sume		return add_proto0(cmd, av, protop);
4535145246Sbrooks
4536152923Sume	*protop = proto;
453798943Sluigi	return cmd;
453898943Sluigi}
453998943Sluigi
4540102087Sluigistatic ipfw_insn *
4541102087Sluigiadd_srcip(ipfw_insn *cmd, char *av)
4542102087Sluigi{
4543102087Sluigi	fill_ip((ipfw_insn_ip *)cmd, av);
4544102087Sluigi	if (cmd->opcode == O_IP_DST_SET)			/* set */
4545102087Sluigi		cmd->opcode = O_IP_SRC_SET;
4546130281Sru	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
4547130281Sru		cmd->opcode = O_IP_SRC_LOOKUP;
4548102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
4549102087Sluigi		cmd->opcode = O_IP_SRC_ME;
4550102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
4551102087Sluigi		cmd->opcode = O_IP_SRC;
4552117328Sluigi	else							/* addr/mask */
4553102087Sluigi		cmd->opcode = O_IP_SRC_MASK;
4554102087Sluigi	return cmd;
4555102087Sluigi}
4556102087Sluigi
4557102087Sluigistatic ipfw_insn *
4558102087Sluigiadd_dstip(ipfw_insn *cmd, char *av)
4559102087Sluigi{
4560102087Sluigi	fill_ip((ipfw_insn_ip *)cmd, av);
4561102087Sluigi	if (cmd->opcode == O_IP_DST_SET)			/* set */
4562102087Sluigi		;
4563130281Sru	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
4564130281Sru		;
4565102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
4566102087Sluigi		cmd->opcode = O_IP_DST_ME;
4567102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
4568102087Sluigi		cmd->opcode = O_IP_DST;
4569117328Sluigi	else							/* addr/mask */
4570102087Sluigi		cmd->opcode = O_IP_DST_MASK;
4571102087Sluigi	return cmd;
4572102087Sluigi}
4573102087Sluigi
4574102087Sluigistatic ipfw_insn *
4575102087Sluigiadd_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
4576102087Sluigi{
4577140271Sbrooks	if (_substrcmp(av, "any") == 0) {
4578102087Sluigi		return NULL;
4579102087Sluigi	} else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) {
4580102087Sluigi		/* XXX todo: check that we have a protocol with ports */
4581102087Sluigi		cmd->opcode = opcode;
4582102087Sluigi		return cmd;
4583102087Sluigi	}
4584102087Sluigi	return NULL;
4585102087Sluigi}
4586102087Sluigi
4587145246Sbrooksstatic ipfw_insn *
4588145246Sbrooksadd_src(ipfw_insn *cmd, char *av, u_char proto)
4589145246Sbrooks{
4590145246Sbrooks	struct in6_addr a;
4591158553Smlaier	char *host, *ch;
4592158553Smlaier	ipfw_insn *ret = NULL;
4593145246Sbrooks
4594158553Smlaier	if ((host = strdup(av)) == NULL)
4595158553Smlaier		return NULL;
4596158553Smlaier	if ((ch = strrchr(host, '/')) != NULL)
4597158553Smlaier		*ch = '\0';
4598158553Smlaier
4599145246Sbrooks	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
4600158553Smlaier	    inet_pton(AF_INET6, host, &a))
4601158553Smlaier		ret = add_srcip6(cmd, av);
4602145246Sbrooks	/* XXX: should check for IPv4, not !IPv6 */
4603161483Sdwmalone	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
4604161483Sdwmalone	    !inet_pton(AF_INET6, host, &a)))
4605158553Smlaier		ret = add_srcip(cmd, av);
4606161483Sdwmalone	if (ret == NULL && strcmp(av, "any") != 0)
4607158553Smlaier		ret = cmd;
4608145246Sbrooks
4609158553Smlaier	free(host);
4610158553Smlaier	return ret;
4611145246Sbrooks}
4612145246Sbrooks
4613145246Sbrooksstatic ipfw_insn *
4614145246Sbrooksadd_dst(ipfw_insn *cmd, char *av, u_char proto)
4615145246Sbrooks{
4616145246Sbrooks	struct in6_addr a;
4617158553Smlaier	char *host, *ch;
4618158553Smlaier	ipfw_insn *ret = NULL;
4619145246Sbrooks
4620158553Smlaier	if ((host = strdup(av)) == NULL)
4621158553Smlaier		return NULL;
4622158553Smlaier	if ((ch = strrchr(host, '/')) != NULL)
4623158553Smlaier		*ch = '\0';
4624158553Smlaier
4625145246Sbrooks	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
4626158553Smlaier	    inet_pton(AF_INET6, host, &a))
4627158553Smlaier		ret = add_dstip6(cmd, av);
4628145246Sbrooks	/* XXX: should check for IPv4, not !IPv6 */
4629161483Sdwmalone	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
4630161483Sdwmalone	    !inet_pton(AF_INET6, host, &a)))
4631158553Smlaier		ret = add_dstip(cmd, av);
4632161483Sdwmalone	if (ret == NULL && strcmp(av, "any") != 0)
4633158553Smlaier		ret = cmd;
4634145246Sbrooks
4635158553Smlaier	free(host);
4636158553Smlaier	return ret;
4637145246Sbrooks}
4638145246Sbrooks
463998943Sluigi/*
464098943Sluigi * Parse arguments and assemble the microinstructions which make up a rule.
464198943Sluigi * Rules are added into the 'rulebuf' and then copied in the correct order
464298943Sluigi * into the actual rule.
464398943Sluigi *
4644136071Sgreen * The syntax for a rule starts with the action, followed by
4645136071Sgreen * optional action parameters, and the various match patterns.
4646108533Sschweikh * In the assembled microcode, the first opcode must be an O_PROBE_STATE
464798943Sluigi * (generated if the rule includes a keep-state option), then the
4648136071Sgreen * various match patterns, log/altq actions, and the actual action.
4649106505Smaxim *
465098943Sluigi */
465198943Sluigistatic void
465298943Sluigiadd(int ac, char *av[])
465398943Sluigi{
465498943Sluigi	/*
465598943Sluigi	 * rules are added into the 'rulebuf' and then copied in
465698943Sluigi	 * the correct order into the actual rule.
465798943Sluigi	 * Some things that need to go out of order (prob, action etc.)
465898943Sluigi	 * go into actbuf[].
465998943Sluigi	 */
4660117328Sluigi	static uint32_t rulebuf[255], actbuf[255], cmdbuf[255];
466198943Sluigi
4662117469Sluigi	ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
4663102087Sluigi	ipfw_insn *first_cmd;	/* first match pattern */
466498943Sluigi
466598943Sluigi	struct ip_fw *rule;
466698943Sluigi
466798943Sluigi	/*
466898943Sluigi	 * various flags used to record that we entered some fields.
466998943Sluigi	 */
4670101116Sluigi	ipfw_insn *have_state = NULL;	/* check-state or keep-state */
4671158879Soleg	ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL;
4672134475Smaxim	size_t len;
467398943Sluigi
467498943Sluigi	int i;
467598943Sluigi
467698943Sluigi	int open_par = 0;	/* open parenthesis ( */
467798943Sluigi
467898943Sluigi	/* proto is here because it is used to fetch ports */
467998943Sluigi	u_char proto = IPPROTO_IP;	/* default protocol */
468098943Sluigi
4681107289Sluigi	double match_prob = 1; /* match probability, default is always match */
4682107289Sluigi
468398943Sluigi	bzero(actbuf, sizeof(actbuf));		/* actions go here */
468498943Sluigi	bzero(cmdbuf, sizeof(cmdbuf));
468598943Sluigi	bzero(rulebuf, sizeof(rulebuf));
468698943Sluigi
468798943Sluigi	rule = (struct ip_fw *)rulebuf;
468898943Sluigi	cmd = (ipfw_insn *)cmdbuf;
468998943Sluigi	action = (ipfw_insn *)actbuf;
469098943Sluigi
469198943Sluigi	av++; ac--;
469298943Sluigi
469398943Sluigi	/* [rule N]	-- Rule number optional */
469498943Sluigi	if (ac && isdigit(**av)) {
469598943Sluigi		rule->rulenum = atoi(*av);
469698943Sluigi		av++;
469798943Sluigi		ac--;
469898943Sluigi	}
469998943Sluigi
4700117655Sluigi	/* [set N]	-- set number (0..RESVD_SET), optional */
4701140271Sbrooks	if (ac > 1 && _substrcmp(*av, "set") == 0) {
4702101628Sluigi		int set = strtoul(av[1], NULL, 10);
4703117655Sluigi		if (set < 0 || set > RESVD_SET)
4704101628Sluigi			errx(EX_DATAERR, "illegal set %s", av[1]);
4705101628Sluigi		rule->set = set;
4706101628Sluigi		av += 2; ac -= 2;
4707101628Sluigi	}
4708101628Sluigi
470998943Sluigi	/* [prob D]	-- match probability, optional */
4710140271Sbrooks	if (ac > 1 && _substrcmp(*av, "prob") == 0) {
4711107289Sluigi		match_prob = strtod(av[1], NULL);
471298943Sluigi
4713107289Sluigi		if (match_prob <= 0 || match_prob > 1)
471498943Sluigi			errx(EX_DATAERR, "illegal match prob. %s", av[1]);
471598943Sluigi		av += 2; ac -= 2;
471698943Sluigi	}
471798943Sluigi
471898943Sluigi	/* action	-- mandatory */
471998943Sluigi	NEED1("missing action");
472098943Sluigi	i = match_token(rule_actions, *av);
472198943Sluigi	ac--; av++;
472298943Sluigi	action->len = 1;	/* default */
472398943Sluigi	switch(i) {
472498943Sluigi	case TOK_CHECKSTATE:
4725101116Sluigi		have_state = action;
472698943Sluigi		action->opcode = O_CHECK_STATE;
472798943Sluigi		break;
472898943Sluigi
472998943Sluigi	case TOK_ACCEPT:
473098943Sluigi		action->opcode = O_ACCEPT;
473198943Sluigi		break;
473298943Sluigi
473398943Sluigi	case TOK_DENY:
473498943Sluigi		action->opcode = O_DENY;
473599475Sluigi		action->arg1 = 0;
473698943Sluigi		break;
473798943Sluigi
473899475Sluigi	case TOK_REJECT:
473999475Sluigi		action->opcode = O_REJECT;
474099475Sluigi		action->arg1 = ICMP_UNREACH_HOST;
474199475Sluigi		break;
474299475Sluigi
474399475Sluigi	case TOK_RESET:
474499475Sluigi		action->opcode = O_REJECT;
474599475Sluigi		action->arg1 = ICMP_REJECT_RST;
474699475Sluigi		break;
474799475Sluigi
4748149020Sbz	case TOK_RESET6:
4749149020Sbz		action->opcode = O_UNREACH6;
4750149020Sbz		action->arg1 = ICMP6_UNREACH_RST;
4751149020Sbz		break;
4752149020Sbz
475399475Sluigi	case TOK_UNREACH:
475499475Sluigi		action->opcode = O_REJECT;
475599475Sluigi		NEED1("missing reject code");
475699475Sluigi		fill_reject_code(&action->arg1, *av);
475799475Sluigi		ac--; av++;
475899475Sluigi		break;
475999475Sluigi
4760149020Sbz	case TOK_UNREACH6:
4761149020Sbz		action->opcode = O_UNREACH6;
4762149020Sbz		NEED1("missing unreach code");
4763149020Sbz		fill_unreach6_code(&action->arg1, *av);
4764149020Sbz		ac--; av++;
4765149020Sbz		break;
4766149020Sbz
476798943Sluigi	case TOK_COUNT:
476898943Sluigi		action->opcode = O_COUNT;
476998943Sluigi		break;
477098943Sluigi
477198943Sluigi	case TOK_QUEUE:
4772153374Sglebius		action->opcode = O_QUEUE;
4773153374Sglebius		goto chkarg;
477498943Sluigi	case TOK_PIPE:
4775153374Sglebius		action->opcode = O_PIPE;
4776153374Sglebius		goto chkarg;
477798943Sluigi	case TOK_SKIPTO:
4778153374Sglebius		action->opcode = O_SKIPTO;
4779153374Sglebius		goto chkarg;
4780153374Sglebius	case TOK_NETGRAPH:
4781153374Sglebius		action->opcode = O_NETGRAPH;
4782153374Sglebius		goto chkarg;
4783153374Sglebius	case TOK_NGTEE:
4784153374Sglebius		action->opcode = O_NGTEE;
4785153374Sglebius		goto chkarg;
478698943Sluigi	case TOK_DIVERT:
4787153374Sglebius		action->opcode = O_DIVERT;
4788153374Sglebius		goto chkarg;
478998943Sluigi	case TOK_TEE:
4790153374Sglebius		action->opcode = O_TEE;
4791153374Sglebiuschkarg:
4792153374Sglebius		if (!ac)
4793153374Sglebius			errx(EX_USAGE, "missing argument for %s", *(av - 1));
4794153374Sglebius		if (isdigit(**av)) {
4795153374Sglebius			action->arg1 = strtoul(*av, NULL, 10);
4796153374Sglebius			if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG)
4797153374Sglebius				errx(EX_DATAERR, "illegal argument for %s",
4798153374Sglebius				    *(av - 1));
4799153374Sglebius		} else if (_substrcmp(*av, TABLEARG) == 0) {
4800153374Sglebius			action->arg1 = IP_FW_TABLEARG;
4801153374Sglebius		} else if (i == TOK_DIVERT || i == TOK_TEE) {
480298943Sluigi			struct servent *s;
480398943Sluigi			setservent(1);
480498943Sluigi			s = getservbyname(av[0], "divert");
480598943Sluigi			if (s != NULL)
480698943Sluigi				action->arg1 = ntohs(s->s_port);
480798943Sluigi			else
480898943Sluigi				errx(EX_DATAERR, "illegal divert/tee port");
4809153374Sglebius		} else
4810153374Sglebius			errx(EX_DATAERR, "illegal argument for %s", *(av - 1));
481198943Sluigi		ac--; av++;
481298943Sluigi		break;
481398943Sluigi
481498943Sluigi	case TOK_FORWARD: {
481598943Sluigi		ipfw_insn_sa *p = (ipfw_insn_sa *)action;
481698943Sluigi		char *s, *end;
481798943Sluigi
481898943Sluigi		NEED1("missing forward address[:port]");
481998943Sluigi
482098943Sluigi		action->opcode = O_FORWARD_IP;
482198943Sluigi		action->len = F_INSN_SIZE(ipfw_insn_sa);
482298943Sluigi
482398943Sluigi		p->sa.sin_len = sizeof(struct sockaddr_in);
482498943Sluigi		p->sa.sin_family = AF_INET;
482598943Sluigi		p->sa.sin_port = 0;
482698943Sluigi		/*
482798943Sluigi		 * locate the address-port separator (':' or ',')
482898943Sluigi		 */
482998943Sluigi		s = strchr(*av, ':');
483098943Sluigi		if (s == NULL)
483198943Sluigi			s = strchr(*av, ',');
483298943Sluigi		if (s != NULL) {
483398943Sluigi			*(s++) = '\0';
483498943Sluigi			i = strtoport(s, &end, 0 /* base */, 0 /* proto */);
483598943Sluigi			if (s == end)
483698943Sluigi				errx(EX_DATAERR,
483798943Sluigi				    "illegal forwarding port ``%s''", s);
4838103241Sluigi			p->sa.sin_port = (u_short)i;
483998943Sluigi		}
4840161456Sjulian		if (_substrcmp(*av, "tablearg") == 0)
4841161456Sjulian			p->sa.sin_addr.s_addr = INADDR_ANY;
4842161456Sjulian		else
4843161424Sjulian			lookup_host(*av, &(p->sa.sin_addr));
484498943Sluigi		ac--; av++;
484598943Sluigi		break;
4846161424Sjulian	    }
4847117469Sluigi	case TOK_COMMENT:
4848117469Sluigi		/* pretend it is a 'count' rule followed by the comment */
4849117469Sluigi		action->opcode = O_COUNT;
4850117469Sluigi		ac++; av--;	/* go back... */
4851117469Sluigi		break;
4852117469Sluigi
4853165648Spiso	case TOK_NAT:
4854165648Spiso 		action->opcode = O_NAT;
4855165648Spiso 		action->len = F_INSN_SIZE(ipfw_insn_nat);
4856165648Spiso 		NEED1("missing nat number");
4857165648Spiso 	        action->arg1 = strtoul(*av, NULL, 10);
4858165648Spiso 		ac--; av++;
4859165648Spiso 		break;
4860165648Spiso
486198943Sluigi	default:
4862102087Sluigi		errx(EX_DATAERR, "invalid action %s\n", av[-1]);
486398943Sluigi	}
486498943Sluigi	action = next_cmd(action);
486598943Sluigi
486698943Sluigi	/*
4867136071Sgreen	 * [altq queuename] -- altq tag, optional
486898943Sluigi	 * [log [logamount N]]	-- log, optional
486998943Sluigi	 *
4870136071Sgreen	 * If they exist, it go first in the cmdbuf, but then it is
487198943Sluigi	 * skipped in the copy section to the end of the buffer.
487298943Sluigi	 */
4873136071Sgreen	while (ac != 0 && (i = match_token(rule_action_params, *av)) != -1) {
4874136071Sgreen		ac--; av++;
4875136071Sgreen		switch (i) {
4876136071Sgreen		case TOK_LOG:
4877136071Sgreen		    {
4878136071Sgreen			ipfw_insn_log *c = (ipfw_insn_log *)cmd;
4879136071Sgreen			int l;
488098943Sluigi
4881136071Sgreen			if (have_log)
4882136071Sgreen				errx(EX_DATAERR,
4883136071Sgreen				    "log cannot be specified more than once");
4884136071Sgreen			have_log = (ipfw_insn *)c;
4885136071Sgreen			cmd->len = F_INSN_SIZE(ipfw_insn_log);
4886136071Sgreen			cmd->opcode = O_LOG;
4887140271Sbrooks			if (ac && _substrcmp(*av, "logamount") == 0) {
4888136071Sgreen				ac--; av++;
4889136071Sgreen				NEED1("logamount requires argument");
4890136071Sgreen				l = atoi(*av);
4891136071Sgreen				if (l < 0)
4892136071Sgreen					errx(EX_DATAERR,
4893136071Sgreen					    "logamount must be positive");
4894136071Sgreen				c->max_log = l;
4895136071Sgreen				ac--; av++;
4896136071Sgreen			} else {
4897136071Sgreen				len = sizeof(c->max_log);
4898136071Sgreen				if (sysctlbyname("net.inet.ip.fw.verbose_limit",
4899136071Sgreen				    &c->max_log, &len, NULL, 0) == -1)
4900136071Sgreen					errx(1, "sysctlbyname(\"%s\")",
4901136071Sgreen					    "net.inet.ip.fw.verbose_limit");
4902136071Sgreen			}
4903136071Sgreen		    }
4904136071Sgreen			break;
4905136071Sgreen
4906136071Sgreen		case TOK_ALTQ:
4907136071Sgreen		    {
4908136071Sgreen			ipfw_insn_altq *a = (ipfw_insn_altq *)cmd;
4909136071Sgreen
4910136071Sgreen			NEED1("missing altq queue name");
4911136071Sgreen			if (have_altq)
4912136071Sgreen				errx(EX_DATAERR,
4913136071Sgreen				    "altq cannot be specified more than once");
4914136071Sgreen			have_altq = (ipfw_insn *)a;
4915136071Sgreen			cmd->len = F_INSN_SIZE(ipfw_insn_altq);
4916136071Sgreen			cmd->opcode = O_ALTQ;
4917136071Sgreen			fill_altq_qid(&a->qid, *av);
491898943Sluigi			ac--; av++;
4919136071Sgreen		    }
4920136071Sgreen			break;
4921136071Sgreen
4922158879Soleg		case TOK_TAG:
4923159636Soleg		case TOK_UNTAG: {
4924159636Soleg			uint16_t tag;
4925159636Soleg
4926158879Soleg			if (have_tag)
4927159636Soleg				errx(EX_USAGE, "tag and untag cannot be "
4928159636Soleg				    "specified more than once");
4929159636Soleg			GET_UINT_ARG(tag, 1, 65534, i, rule_action_params);
4930158879Soleg			have_tag = cmd;
4931159636Soleg			fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag);
4932158879Soleg			ac--; av++;
4933158879Soleg			break;
4934159636Soleg		}
4935158879Soleg
4936136071Sgreen		default:
4937136071Sgreen			abort();
493898943Sluigi		}
493998943Sluigi		cmd = next_cmd(cmd);
494098943Sluigi	}
494198943Sluigi
4942101116Sluigi	if (have_state)	/* must be a check-state, we are done */
494398943Sluigi		goto done;
494498943Sluigi
494598943Sluigi#define OR_START(target)					\
494698943Sluigi	if (ac && (*av[0] == '(' || *av[0] == '{')) {		\
494798943Sluigi		if (open_par)					\
494898943Sluigi			errx(EX_USAGE, "nested \"(\" not allowed\n"); \
4949101641Sluigi		prev = NULL;					\
495098943Sluigi		open_par = 1;					\
495198943Sluigi		if ( (av[0])[1] == '\0') {			\
495298943Sluigi			ac--; av++;				\
495398943Sluigi		} else						\
495498943Sluigi			(*av)++;				\
495598943Sluigi	}							\
495698943Sluigi	target:							\
495798943Sluigi
495898943Sluigi
495998943Sluigi#define	CLOSE_PAR						\
496098943Sluigi	if (open_par) {						\
496198943Sluigi		if (ac && (					\
4962140271Sbrooks		    strcmp(*av, ")") == 0 ||			\
4963140271Sbrooks		    strcmp(*av, "}") == 0)) {			\
4964101641Sluigi			prev = NULL;				\
496598943Sluigi			open_par = 0;				\
496698943Sluigi			ac--; av++;				\
496798943Sluigi		} else						\
496898943Sluigi			errx(EX_USAGE, "missing \")\"\n");	\
496998943Sluigi	}
4970106505Smaxim
497198943Sluigi#define NOT_BLOCK						\
4972140271Sbrooks	if (ac && _substrcmp(*av, "not") == 0) {		\
497398943Sluigi		if (cmd->len & F_NOT)				\
497498943Sluigi			errx(EX_USAGE, "double \"not\" not allowed\n"); \
497598943Sluigi		cmd->len |= F_NOT;				\
497698943Sluigi		ac--; av++;					\
497798943Sluigi	}
497898943Sluigi
497998943Sluigi#define OR_BLOCK(target)					\
4980140271Sbrooks	if (ac && _substrcmp(*av, "or") == 0) {		\
498198943Sluigi		if (prev == NULL || open_par == 0)		\
498298943Sluigi			errx(EX_DATAERR, "invalid OR block");	\
498398943Sluigi		prev->len |= F_OR;				\
498498943Sluigi		ac--; av++;					\
498598943Sluigi		goto target;					\
498698943Sluigi	}							\
498798943Sluigi	CLOSE_PAR;
498898943Sluigi
4989102087Sluigi	first_cmd = cmd;
4990102098Sluigi
4991102098Sluigi#if 0
499298943Sluigi	/*
4993102087Sluigi	 * MAC addresses, optional.
4994102087Sluigi	 * If we have this, we skip the part "proto from src to dst"
4995102087Sluigi	 * and jump straight to the option parsing.
4996102087Sluigi	 */
4997102087Sluigi	NOT_BLOCK;
4998102087Sluigi	NEED1("missing protocol");
4999140271Sbrooks	if (_substrcmp(*av, "MAC") == 0 ||
5000140271Sbrooks	    _substrcmp(*av, "mac") == 0) {
5001102087Sluigi		ac--; av++;	/* the "MAC" keyword */
5002102087Sluigi		add_mac(cmd, ac, av); /* exits in case of errors */
5003102087Sluigi		cmd = next_cmd(cmd);
5004102087Sluigi		ac -= 2; av += 2;	/* dst-mac and src-mac */
5005102087Sluigi		NOT_BLOCK;
5006102087Sluigi		NEED1("missing mac type");
5007102087Sluigi		if (add_mactype(cmd, ac, av[0]))
5008102087Sluigi			cmd = next_cmd(cmd);
5009102087Sluigi		ac--; av++;	/* any or mac-type */
5010102087Sluigi		goto read_options;
5011102087Sluigi	}
5012102098Sluigi#endif
5013102087Sluigi
5014102087Sluigi	/*
501598943Sluigi	 * protocol, mandatory
501698943Sluigi	 */
501798943Sluigi    OR_START(get_proto);
501898943Sluigi	NOT_BLOCK;
501998943Sluigi	NEED1("missing protocol");
5020152923Sume	if (add_proto_compat(cmd, *av, &proto)) {
5021102087Sluigi		av++; ac--;
5022147105Smlaier		if (F_LEN(cmd) != 0) {
5023102087Sluigi			prev = cmd;
5024102087Sluigi			cmd = next_cmd(cmd);
5025102087Sluigi		}
5026102098Sluigi	} else if (first_cmd != cmd) {
5027116438Smaxim		errx(EX_DATAERR, "invalid protocol ``%s''", *av);
5028102098Sluigi	} else
5029102098Sluigi		goto read_options;
503098943Sluigi    OR_BLOCK(get_proto);
503198943Sluigi
503298943Sluigi	/*
5033102087Sluigi	 * "from", mandatory
503498943Sluigi	 */
5035140271Sbrooks	if (!ac || _substrcmp(*av, "from") != 0)
503698943Sluigi		errx(EX_USAGE, "missing ``from''");
503798943Sluigi	ac--; av++;
503898943Sluigi
503998943Sluigi	/*
504098943Sluigi	 * source IP, mandatory
504198943Sluigi	 */
504298943Sluigi    OR_START(source_ip);
504398943Sluigi	NOT_BLOCK;	/* optional "not" */
504498943Sluigi	NEED1("missing source address");
5045145246Sbrooks	if (add_src(cmd, *av, proto)) {
5046102087Sluigi		ac--; av++;
5047102087Sluigi		if (F_LEN(cmd) != 0) {	/* ! any */
5048102087Sluigi			prev = cmd;
5049102087Sluigi			cmd = next_cmd(cmd);
5050102087Sluigi		}
5051145246Sbrooks	} else
5052145246Sbrooks		errx(EX_USAGE, "bad source address %s", *av);
505398943Sluigi    OR_BLOCK(source_ip);
505498943Sluigi
505598943Sluigi	/*
505698943Sluigi	 * source ports, optional
505798943Sluigi	 */
505898943Sluigi	NOT_BLOCK;	/* optional "not" */
5059101641Sluigi	if (ac) {
5060140271Sbrooks		if (_substrcmp(*av, "any") == 0 ||
5061102087Sluigi		    add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
5062102087Sluigi			ac--; av++;
5063102087Sluigi			if (F_LEN(cmd) != 0)
5064102087Sluigi				cmd = next_cmd(cmd);
5065101641Sluigi		}
506698943Sluigi	}
506798943Sluigi
506898943Sluigi	/*
5069102087Sluigi	 * "to", mandatory
507098943Sluigi	 */
5071140271Sbrooks	if (!ac || _substrcmp(*av, "to") != 0)
507298943Sluigi		errx(EX_USAGE, "missing ``to''");
507398943Sluigi	av++; ac--;
507498943Sluigi
507598943Sluigi	/*
507698943Sluigi	 * destination, mandatory
507798943Sluigi	 */
507898943Sluigi    OR_START(dest_ip);
507998943Sluigi	NOT_BLOCK;	/* optional "not" */
508098943Sluigi	NEED1("missing dst address");
5081145246Sbrooks	if (add_dst(cmd, *av, proto)) {
5082102087Sluigi		ac--; av++;
5083102087Sluigi		if (F_LEN(cmd) != 0) {	/* ! any */
5084102087Sluigi			prev = cmd;
5085102087Sluigi			cmd = next_cmd(cmd);
5086102087Sluigi		}
5087145246Sbrooks	} else
5088145246Sbrooks		errx( EX_USAGE, "bad destination address %s", *av);
508998943Sluigi    OR_BLOCK(dest_ip);
509098943Sluigi
509198943Sluigi	/*
509298943Sluigi	 * dest. ports, optional
509398943Sluigi	 */
509498943Sluigi	NOT_BLOCK;	/* optional "not" */
5095101641Sluigi	if (ac) {
5096140271Sbrooks		if (_substrcmp(*av, "any") == 0 ||
5097102087Sluigi		    add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
5098102087Sluigi			ac--; av++;
5099102087Sluigi			if (F_LEN(cmd) != 0)
5100102087Sluigi				cmd = next_cmd(cmd);
5101101641Sluigi		}
510298943Sluigi	}
510398943Sluigi
510498943Sluigiread_options:
5105102087Sluigi	if (ac && first_cmd == cmd) {
5106102087Sluigi		/*
5107102087Sluigi		 * nothing specified so far, store in the rule to ease
5108102087Sluigi		 * printout later.
5109102087Sluigi		 */
5110102087Sluigi		 rule->_pad = 1;
5111102087Sluigi	}
511298943Sluigi	prev = NULL;
511398943Sluigi	while (ac) {
5114101641Sluigi		char *s;
5115101641Sluigi		ipfw_insn_u32 *cmd32;	/* alias for cmd */
511698943Sluigi
5117101641Sluigi		s = *av;
5118101641Sluigi		cmd32 = (ipfw_insn_u32 *)cmd;
5119101641Sluigi
512098943Sluigi		if (*s == '!') {	/* alternate syntax for NOT */
512198943Sluigi			if (cmd->len & F_NOT)
512298943Sluigi				errx(EX_USAGE, "double \"not\" not allowed\n");
512398943Sluigi			cmd->len = F_NOT;
512498943Sluigi			s++;
512598943Sluigi		}
512698943Sluigi		i = match_token(rule_options, s);
512798943Sluigi		ac--; av++;
512898943Sluigi		switch(i) {
512998943Sluigi		case TOK_NOT:
513098943Sluigi			if (cmd->len & F_NOT)
513198943Sluigi				errx(EX_USAGE, "double \"not\" not allowed\n");
513298943Sluigi			cmd->len = F_NOT;
513398943Sluigi			break;
513498943Sluigi
513598943Sluigi		case TOK_OR:
5136101641Sluigi			if (open_par == 0 || prev == NULL)
513798943Sluigi				errx(EX_USAGE, "invalid \"or\" block\n");
513898943Sluigi			prev->len |= F_OR;
513998943Sluigi			break;
5140101641Sluigi
5141101641Sluigi		case TOK_STARTBRACE:
5142101641Sluigi			if (open_par)
5143101641Sluigi				errx(EX_USAGE, "+nested \"(\" not allowed\n");
5144101641Sluigi			open_par = 1;
5145101641Sluigi			break;
5146101641Sluigi
5147101641Sluigi		case TOK_ENDBRACE:
5148101641Sluigi			if (!open_par)
5149101641Sluigi				errx(EX_USAGE, "+missing \")\"\n");
5150101641Sluigi			open_par = 0;
5151102087Sluigi			prev = NULL;
5152101641Sluigi        		break;
5153101641Sluigi
515498943Sluigi		case TOK_IN:
515598943Sluigi			fill_cmd(cmd, O_IN, 0, 0);
515698943Sluigi			break;
515798943Sluigi
515898943Sluigi		case TOK_OUT:
515998943Sluigi			cmd->len ^= F_NOT; /* toggle F_NOT */
516098943Sluigi			fill_cmd(cmd, O_IN, 0, 0);
516198943Sluigi			break;
516298943Sluigi
5163136073Sgreen		case TOK_DIVERTED:
5164136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 3);
5165136073Sgreen			break;
5166136073Sgreen
5167136073Sgreen		case TOK_DIVERTEDLOOPBACK:
5168136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 1);
5169136073Sgreen			break;
5170136073Sgreen
5171136073Sgreen		case TOK_DIVERTEDOUTPUT:
5172136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 2);
5173136073Sgreen			break;
5174136073Sgreen
517598943Sluigi		case TOK_FRAG:
517698943Sluigi			fill_cmd(cmd, O_FRAG, 0, 0);
517798943Sluigi			break;
517898943Sluigi
517998943Sluigi		case TOK_LAYER2:
518098943Sluigi			fill_cmd(cmd, O_LAYER2, 0, 0);
518198943Sluigi			break;
518298943Sluigi
518398943Sluigi		case TOK_XMIT:
518498943Sluigi		case TOK_RECV:
518598943Sluigi		case TOK_VIA:
518698943Sluigi			NEED1("recv, xmit, via require interface name"
518798943Sluigi				" or address");
518898943Sluigi			fill_iface((ipfw_insn_if *)cmd, av[0]);
518998943Sluigi			ac--; av++;
519098943Sluigi			if (F_LEN(cmd) == 0)	/* not a valid address */
519198943Sluigi				break;
519298943Sluigi			if (i == TOK_XMIT)
519398943Sluigi				cmd->opcode = O_XMIT;
519498943Sluigi			else if (i == TOK_RECV)
519598943Sluigi				cmd->opcode = O_RECV;
519698943Sluigi			else if (i == TOK_VIA)
519798943Sluigi				cmd->opcode = O_VIA;
519898943Sluigi			break;
519998943Sluigi
520099475Sluigi		case TOK_ICMPTYPES:
520199475Sluigi			NEED1("icmptypes requires list of types");
520299475Sluigi			fill_icmptypes((ipfw_insn_u32 *)cmd, *av);
520399475Sluigi			av++; ac--;
520499475Sluigi			break;
5205145246Sbrooks
5206145246Sbrooks		case TOK_ICMP6TYPES:
5207145246Sbrooks			NEED1("icmptypes requires list of types");
5208145246Sbrooks			fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av);
5209145246Sbrooks			av++; ac--;
5210145246Sbrooks			break;
521199475Sluigi
521298943Sluigi		case TOK_IPTTL:
521398943Sluigi			NEED1("ipttl requires TTL");
5214116690Sluigi			if (strpbrk(*av, "-,")) {
5215116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPTTL))
5216116690Sluigi				errx(EX_DATAERR, "invalid ipttl %s", *av);
5217116690Sluigi			} else
5218116690Sluigi			    fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
521998943Sluigi			ac--; av++;
522098943Sluigi			break;
522198943Sluigi
522298943Sluigi		case TOK_IPID:
5223116690Sluigi			NEED1("ipid requires id");
5224116690Sluigi			if (strpbrk(*av, "-,")) {
5225116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPID))
5226116690Sluigi				errx(EX_DATAERR, "invalid ipid %s", *av);
5227116690Sluigi			} else
5228116690Sluigi			    fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0));
522998943Sluigi			ac--; av++;
523098943Sluigi			break;
523198943Sluigi
523298943Sluigi		case TOK_IPLEN:
523398943Sluigi			NEED1("iplen requires length");
5234116690Sluigi			if (strpbrk(*av, "-,")) {
5235116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPLEN))
5236116690Sluigi				errx(EX_DATAERR, "invalid ip len %s", *av);
5237116690Sluigi			} else
5238116690Sluigi			    fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0));
523998943Sluigi			ac--; av++;
524098943Sluigi			break;
524198943Sluigi
524298943Sluigi		case TOK_IPVER:
524398943Sluigi			NEED1("ipver requires version");
524498943Sluigi			fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0));
524598943Sluigi			ac--; av++;
524698943Sluigi			break;
524798943Sluigi
524899475Sluigi		case TOK_IPPRECEDENCE:
524999475Sluigi			NEED1("ipprecedence requires value");
525099475Sluigi			fill_cmd(cmd, O_IPPRECEDENCE, 0,
525199475Sluigi			    (strtoul(*av, NULL, 0) & 7) << 5);
525299475Sluigi			ac--; av++;
525399475Sluigi			break;
525499475Sluigi
525598943Sluigi		case TOK_IPOPTS:
525698943Sluigi			NEED1("missing argument for ipoptions");
5257101116Sluigi			fill_flags(cmd, O_IPOPT, f_ipopts, *av);
525898943Sluigi			ac--; av++;
525998943Sluigi			break;
526098943Sluigi
526199475Sluigi		case TOK_IPTOS:
526299475Sluigi			NEED1("missing argument for iptos");
5263101116Sluigi			fill_flags(cmd, O_IPTOS, f_iptos, *av);
526499475Sluigi			ac--; av++;
526599475Sluigi			break;
526699475Sluigi
526798943Sluigi		case TOK_UID:
526898943Sluigi			NEED1("uid requires argument");
526998943Sluigi		    {
527098943Sluigi			char *end;
527198943Sluigi			uid_t uid;
527298943Sluigi			struct passwd *pwd;
527398943Sluigi
527498943Sluigi			cmd->opcode = O_UID;
527598943Sluigi			uid = strtoul(*av, &end, 0);
527698943Sluigi			pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av);
527798943Sluigi			if (pwd == NULL)
527898943Sluigi				errx(EX_DATAERR, "uid \"%s\" nonexistent", *av);
5279106504Smaxim			cmd32->d[0] = pwd->pw_uid;
5280135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
528198943Sluigi			ac--; av++;
528298943Sluigi		    }
528398943Sluigi			break;
528498943Sluigi
528598943Sluigi		case TOK_GID:
528698943Sluigi			NEED1("gid requires argument");
528798943Sluigi		    {
528898943Sluigi			char *end;
528998943Sluigi			gid_t gid;
529098943Sluigi			struct group *grp;
529198943Sluigi
529298943Sluigi			cmd->opcode = O_GID;
529398943Sluigi			gid = strtoul(*av, &end, 0);
529498943Sluigi			grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av);
529598943Sluigi			if (grp == NULL)
529698943Sluigi				errx(EX_DATAERR, "gid \"%s\" nonexistent", *av);
5297106504Smaxim			cmd32->d[0] = grp->gr_gid;
5298135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
529998943Sluigi			ac--; av++;
530098943Sluigi		    }
530198943Sluigi			break;
530298943Sluigi
5303133600Scsjp		case TOK_JAIL:
5304133600Scsjp			NEED1("jail requires argument");
5305133600Scsjp		    {
5306133600Scsjp			char *end;
5307133600Scsjp			int jid;
5308133600Scsjp
5309133600Scsjp			cmd->opcode = O_JAIL;
5310133600Scsjp			jid = (int)strtol(*av, &end, 0);
5311133600Scsjp			if (jid < 0 || *end != '\0')
5312133600Scsjp				errx(EX_DATAERR, "jail requires prison ID");
5313135554Scsjp			cmd32->d[0] = (uint32_t)jid;
5314135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
5315133600Scsjp			ac--; av++;
5316133600Scsjp		    }
5317133600Scsjp			break;
5318133600Scsjp
531998943Sluigi		case TOK_ESTAB:
532098943Sluigi			fill_cmd(cmd, O_ESTAB, 0, 0);
532198943Sluigi			break;
532298943Sluigi
532398943Sluigi		case TOK_SETUP:
532498943Sluigi			fill_cmd(cmd, O_TCPFLAGS, 0,
532598943Sluigi				(TH_SYN) | ( (TH_ACK) & 0xff) <<8 );
532698943Sluigi			break;
532798943Sluigi
5328136075Sgreen		case TOK_TCPDATALEN:
5329136075Sgreen			NEED1("tcpdatalen requires length");
5330136075Sgreen			if (strpbrk(*av, "-,")) {
5331136075Sgreen			    if (!add_ports(cmd, *av, 0, O_TCPDATALEN))
5332136075Sgreen				errx(EX_DATAERR, "invalid tcpdata len %s", *av);
5333136075Sgreen			} else
5334136075Sgreen			    fill_cmd(cmd, O_TCPDATALEN, 0,
5335136075Sgreen				    strtoul(*av, NULL, 0));
5336136075Sgreen			ac--; av++;
5337136075Sgreen			break;
5338136075Sgreen
533998943Sluigi		case TOK_TCPOPTS:
534098943Sluigi			NEED1("missing argument for tcpoptions");
534198943Sluigi			fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av);
534298943Sluigi			ac--; av++;
534398943Sluigi			break;
534498943Sluigi
534598943Sluigi		case TOK_TCPSEQ:
534698943Sluigi		case TOK_TCPACK:
534798943Sluigi			NEED1("tcpseq/tcpack requires argument");
534898943Sluigi			cmd->len = F_INSN_SIZE(ipfw_insn_u32);
534998943Sluigi			cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK;
535098943Sluigi			cmd32->d[0] = htonl(strtoul(*av, NULL, 0));
535198943Sluigi			ac--; av++;
535298943Sluigi			break;
535398943Sluigi
535498943Sluigi		case TOK_TCPWIN:
535598943Sluigi			NEED1("tcpwin requires length");
535698943Sluigi			fill_cmd(cmd, O_TCPWIN, 0,
535798943Sluigi			    htons(strtoul(*av, NULL, 0)));
535898943Sluigi			ac--; av++;
535998943Sluigi			break;
536098943Sluigi
536198943Sluigi		case TOK_TCPFLAGS:
536298943Sluigi			NEED1("missing argument for tcpflags");
536398943Sluigi			cmd->opcode = O_TCPFLAGS;
536498943Sluigi			fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av);
536598943Sluigi			ac--; av++;
536698943Sluigi			break;
536798943Sluigi
536898943Sluigi		case TOK_KEEPSTATE:
5369101641Sluigi			if (open_par)
5370101641Sluigi				errx(EX_USAGE, "keep-state cannot be part "
5371101641Sluigi				    "of an or block");
537299909Sluigi			if (have_state)
5373101116Sluigi				errx(EX_USAGE, "only one of keep-state "
537499909Sluigi					"and limit is allowed");
5375101116Sluigi			have_state = cmd;
537698943Sluigi			fill_cmd(cmd, O_KEEP_STATE, 0, 0);
537798943Sluigi			break;
537898943Sluigi
5379159636Soleg		case TOK_LIMIT: {
5380159636Soleg			ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
5381159636Soleg			int val;
5382159636Soleg
5383101641Sluigi			if (open_par)
5384159636Soleg				errx(EX_USAGE,
5385159636Soleg				    "limit cannot be part of an or block");
538699909Sluigi			if (have_state)
5387168819Smaxim				errx(EX_USAGE, "only one of keep-state and "
5388159636Soleg				    "limit is allowed");
5389101116Sluigi			have_state = cmd;
539098943Sluigi
539198943Sluigi			cmd->len = F_INSN_SIZE(ipfw_insn_limit);
539298943Sluigi			cmd->opcode = O_LIMIT;
5393159636Soleg			c->limit_mask = c->conn_limit = 0;
539498943Sluigi
5395159636Soleg			while (ac > 0) {
5396159636Soleg				if ((val = match_token(limit_masks, *av)) <= 0)
539798943Sluigi					break;
539898943Sluigi				c->limit_mask |= val;
539998943Sluigi				ac--; av++;
540098943Sluigi			}
5401159636Soleg
540298943Sluigi			if (c->limit_mask == 0)
5403159636Soleg				errx(EX_USAGE, "limit: missing limit mask");
5404159636Soleg
5405159636Soleg			GET_UINT_ARG(c->conn_limit, 1, 65534, TOK_LIMIT,
5406159636Soleg			    rule_options);
5407159636Soleg
540898943Sluigi			ac--; av++;
540998943Sluigi			break;
5410159636Soleg		}
541198943Sluigi
5412102087Sluigi		case TOK_PROTO:
5413102087Sluigi			NEED1("missing protocol");
5414145246Sbrooks			if (add_proto(cmd, *av, &proto)) {
5415102087Sluigi				ac--; av++;
5416102098Sluigi			} else
5417116438Smaxim				errx(EX_DATAERR, "invalid protocol ``%s''",
5418116438Smaxim				    *av);
5419102087Sluigi			break;
5420106505Smaxim
5421102087Sluigi		case TOK_SRCIP:
5422102087Sluigi			NEED1("missing source IP");
5423102087Sluigi			if (add_srcip(cmd, *av)) {
5424102087Sluigi				ac--; av++;
5425102087Sluigi			}
5426102087Sluigi			break;
5427102087Sluigi
5428102087Sluigi		case TOK_DSTIP:
5429102087Sluigi			NEED1("missing destination IP");
5430102087Sluigi			if (add_dstip(cmd, *av)) {
5431102087Sluigi				ac--; av++;
5432102087Sluigi			}
5433102087Sluigi			break;
5434102087Sluigi
5435145246Sbrooks		case TOK_SRCIP6:
5436145246Sbrooks			NEED1("missing source IP6");
5437145246Sbrooks			if (add_srcip6(cmd, *av)) {
5438145246Sbrooks				ac--; av++;
5439145246Sbrooks			}
5440145246Sbrooks			break;
5441145246Sbrooks
5442145246Sbrooks		case TOK_DSTIP6:
5443145246Sbrooks			NEED1("missing destination IP6");
5444145246Sbrooks			if (add_dstip6(cmd, *av)) {
5445145246Sbrooks				ac--; av++;
5446145246Sbrooks			}
5447145246Sbrooks			break;
5448145246Sbrooks
5449102087Sluigi		case TOK_SRCPORT:
5450102087Sluigi			NEED1("missing source port");
5451140271Sbrooks			if (_substrcmp(*av, "any") == 0 ||
5452102087Sluigi			    add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
5453102087Sluigi				ac--; av++;
5454102087Sluigi			} else
5455102087Sluigi				errx(EX_DATAERR, "invalid source port %s", *av);
5456102087Sluigi			break;
5457102087Sluigi
5458102087Sluigi		case TOK_DSTPORT:
5459102087Sluigi			NEED1("missing destination port");
5460140271Sbrooks			if (_substrcmp(*av, "any") == 0 ||
5461102087Sluigi			    add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
5462102087Sluigi				ac--; av++;
5463102087Sluigi			} else
5464102087Sluigi				errx(EX_DATAERR, "invalid destination port %s",
5465102087Sluigi				    *av);
5466102087Sluigi			break;
5467102087Sluigi
5468102087Sluigi		case TOK_MAC:
5469102087Sluigi			if (add_mac(cmd, ac, av)) {
5470102087Sluigi				ac -= 2; av += 2;
5471102087Sluigi			}
5472102087Sluigi			break;
5473102087Sluigi
5474102087Sluigi		case TOK_MACTYPE:
5475102087Sluigi			NEED1("missing mac type");
5476102087Sluigi			if (!add_mactype(cmd, ac, *av))
5477116438Smaxim				errx(EX_DATAERR, "invalid mac type %s", *av);
5478102087Sluigi			ac--; av++;
5479102087Sluigi			break;
5480102087Sluigi
5481112250Scjc		case TOK_VERREVPATH:
5482112250Scjc			fill_cmd(cmd, O_VERREVPATH, 0, 0);
5483112250Scjc			break;
5484116919Sluigi
5485128575Sandre		case TOK_VERSRCREACH:
5486128575Sandre			fill_cmd(cmd, O_VERSRCREACH, 0, 0);
5487128575Sandre			break;
5488128575Sandre
5489133387Sandre		case TOK_ANTISPOOF:
5490133387Sandre			fill_cmd(cmd, O_ANTISPOOF, 0, 0);
5491133387Sandre			break;
5492133387Sandre
5493117241Sluigi		case TOK_IPSEC:
5494117241Sluigi			fill_cmd(cmd, O_IPSEC, 0, 0);
5495117241Sluigi			break;
5496117241Sluigi
5497145246Sbrooks		case TOK_IPV6:
5498145246Sbrooks			fill_cmd(cmd, O_IP6, 0, 0);
5499145246Sbrooks			break;
5500145246Sbrooks
5501146894Smlaier		case TOK_IPV4:
5502146894Smlaier			fill_cmd(cmd, O_IP4, 0, 0);
5503146894Smlaier			break;
5504146894Smlaier
5505145246Sbrooks		case TOK_EXT6HDR:
5506145246Sbrooks			fill_ext6hdr( cmd, *av );
5507145246Sbrooks			ac--; av++;
5508145246Sbrooks			break;
5509145246Sbrooks
5510145246Sbrooks		case TOK_FLOWID:
5511145246Sbrooks			if (proto != IPPROTO_IPV6 )
5512145246Sbrooks				errx( EX_USAGE, "flow-id filter is active "
5513145246Sbrooks				    "only for ipv6 protocol\n");
5514145246Sbrooks			fill_flow6( (ipfw_insn_u32 *) cmd, *av );
5515145246Sbrooks			ac--; av++;
5516145246Sbrooks			break;
5517145246Sbrooks
5518117469Sluigi		case TOK_COMMENT:
5519117469Sluigi			fill_comment(cmd, ac, av);
5520117469Sluigi			av += ac;
5521117469Sluigi			ac = 0;
5522117469Sluigi			break;
5523117469Sluigi
5524158879Soleg		case TOK_TAGGED:
5525159636Soleg			if (ac > 0 && strpbrk(*av, "-,")) {
5526158879Soleg				if (!add_ports(cmd, *av, 0, O_TAGGED))
5527159636Soleg					errx(EX_DATAERR, "tagged: invalid tag"
5528159636Soleg					    " list: %s", *av);
5529158879Soleg			}
5530159636Soleg			else {
5531159636Soleg				uint16_t tag;
5532159636Soleg
5533159636Soleg				GET_UINT_ARG(tag, 1, 65534, TOK_TAGGED,
5534159636Soleg				    rule_options);
5535159636Soleg				fill_cmd(cmd, O_TAGGED, 0, tag);
5536159636Soleg			}
5537158879Soleg			ac--; av++;
5538158879Soleg			break;
5539158879Soleg
554098943Sluigi		default:
554198943Sluigi			errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
554298943Sluigi		}
554398943Sluigi		if (F_LEN(cmd) > 0) {	/* prepare to advance */
554498943Sluigi			prev = cmd;
554598943Sluigi			cmd = next_cmd(cmd);
554698943Sluigi		}
554798943Sluigi	}
554898943Sluigi
554998943Sluigidone:
555098943Sluigi	/*
555198943Sluigi	 * Now copy stuff into the rule.
555298943Sluigi	 * If we have a keep-state option, the first instruction
555398943Sluigi	 * must be a PROBE_STATE (which is generated here).
555498943Sluigi	 * If we have a LOG option, it was stored as the first command,
555598943Sluigi	 * and now must be moved to the top of the action part.
555698943Sluigi	 */
555798943Sluigi	dst = (ipfw_insn *)rule->cmd;
555898943Sluigi
555998943Sluigi	/*
5560107289Sluigi	 * First thing to write into the command stream is the match probability.
5561107289Sluigi	 */
5562107289Sluigi	if (match_prob != 1) { /* 1 means always match */
5563107289Sluigi		dst->opcode = O_PROB;
5564107289Sluigi		dst->len = 2;
5565107289Sluigi		*((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff);
5566107289Sluigi		dst += dst->len;
5567107289Sluigi	}
5568107289Sluigi
5569107289Sluigi	/*
557098943Sluigi	 * generate O_PROBE_STATE if necessary
557198943Sluigi	 */
5572101116Sluigi	if (have_state && have_state->opcode != O_CHECK_STATE) {
557398943Sluigi		fill_cmd(dst, O_PROBE_STATE, 0, 0);
557498943Sluigi		dst = next_cmd(dst);
557598943Sluigi	}
5576158879Soleg
5577158879Soleg	/* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */
557898943Sluigi	for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
557998943Sluigi		i = F_LEN(src);
558098943Sluigi
5581101116Sluigi		switch (src->opcode) {
5582101116Sluigi		case O_LOG:
5583101116Sluigi		case O_KEEP_STATE:
5584101116Sluigi		case O_LIMIT:
5585136071Sgreen		case O_ALTQ:
5586158879Soleg		case O_TAG:
5587101116Sluigi			break;
5588101116Sluigi		default:
5589117328Sluigi			bcopy(src, dst, i * sizeof(uint32_t));
559098943Sluigi			dst += i;
559198943Sluigi		}
559298943Sluigi	}
559398943Sluigi
559498943Sluigi	/*
5595101116Sluigi	 * put back the have_state command as last opcode
5596101116Sluigi	 */
5597101295Sluigi	if (have_state && have_state->opcode != O_CHECK_STATE) {
5598101116Sluigi		i = F_LEN(have_state);
5599117328Sluigi		bcopy(have_state, dst, i * sizeof(uint32_t));
5600101116Sluigi		dst += i;
5601101116Sluigi	}
5602101116Sluigi	/*
560398943Sluigi	 * start action section
560498943Sluigi	 */
560598943Sluigi	rule->act_ofs = dst - rule->cmd;
560698943Sluigi
5607158879Soleg	/* put back O_LOG, O_ALTQ, O_TAG if necessary */
5608136071Sgreen	if (have_log) {
5609136071Sgreen		i = F_LEN(have_log);
5610136071Sgreen		bcopy(have_log, dst, i * sizeof(uint32_t));
561198943Sluigi		dst += i;
561298943Sluigi	}
5613136071Sgreen	if (have_altq) {
5614136071Sgreen		i = F_LEN(have_altq);
5615136071Sgreen		bcopy(have_altq, dst, i * sizeof(uint32_t));
5616136071Sgreen		dst += i;
5617136071Sgreen	}
5618158879Soleg	if (have_tag) {
5619158879Soleg		i = F_LEN(have_tag);
5620158879Soleg		bcopy(have_tag, dst, i * sizeof(uint32_t));
5621158879Soleg		dst += i;
5622158879Soleg	}
562398943Sluigi	/*
562498943Sluigi	 * copy all other actions
562598943Sluigi	 */
562698943Sluigi	for (src = (ipfw_insn *)actbuf; src != action; src += i) {
562798943Sluigi		i = F_LEN(src);
5628117328Sluigi		bcopy(src, dst, i * sizeof(uint32_t));
562998943Sluigi		dst += i;
563098943Sluigi	}
563198943Sluigi
5632117328Sluigi	rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd);
5633117469Sluigi	i = (char *)dst - (char *)rule;
5634119740Stmm	if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1)
563598943Sluigi		err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
563698943Sluigi	if (!do_quiet)
5637117469Sluigi		show_ipfw(rule, 0, 0);
563898943Sluigi}
563998943Sluigi
564098943Sluigistatic void
5641117328Sluigizero(int ac, char *av[], int optname /* IP_FW_ZERO or IP_FW_RESETLOG */)
564298943Sluigi{
564398943Sluigi	int rulenum;
564498943Sluigi	int failed = EX_OK;
5645117469Sluigi	char const *name = optname == IP_FW_ZERO ?  "ZERO" : "RESETLOG";
564698943Sluigi
564798943Sluigi	av++; ac--;
564898943Sluigi
564998943Sluigi	if (!ac) {
565098943Sluigi		/* clear all entries */
5651117328Sluigi		if (do_cmd(optname, NULL, 0) < 0)
5652117328Sluigi			err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name);
565398943Sluigi		if (!do_quiet)
5654117328Sluigi			printf("%s.\n", optname == IP_FW_ZERO ?
5655117328Sluigi			    "Accounting cleared":"Logging counts reset");
565698943Sluigi
565798943Sluigi		return;
565898943Sluigi	}
565998943Sluigi
566098943Sluigi	while (ac) {
566198943Sluigi		/* Rule number */
566298943Sluigi		if (isdigit(**av)) {
566398943Sluigi			rulenum = atoi(*av);
566498943Sluigi			av++;
566598943Sluigi			ac--;
5666117328Sluigi			if (do_cmd(optname, &rulenum, sizeof rulenum)) {
5667117328Sluigi				warn("rule %u: setsockopt(IP_FW_%s)",
5668117328Sluigi				    rulenum, name);
566998943Sluigi				failed = EX_UNAVAILABLE;
567098943Sluigi			} else if (!do_quiet)
5671117328Sluigi				printf("Entry %d %s.\n", rulenum,
5672117328Sluigi				    optname == IP_FW_ZERO ?
5673117328Sluigi					"cleared" : "logging count reset");
567498943Sluigi		} else {
567598943Sluigi			errx(EX_USAGE, "invalid rule number ``%s''", *av);
567698943Sluigi		}
567798943Sluigi	}
567898943Sluigi	if (failed != EX_OK)
567998943Sluigi		exit(failed);
568098943Sluigi}
568198943Sluigi
568298943Sluigistatic void
5683117544Sluigiflush(int force)
568498943Sluigi{
568598943Sluigi	int cmd = do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH;
568698943Sluigi
5687117544Sluigi	if (!force && !do_quiet) { /* need to ask user */
568898943Sluigi		int c;
568998943Sluigi
569098943Sluigi		printf("Are you sure? [yn] ");
569198943Sluigi		fflush(stdout);
569298943Sluigi		do {
569398943Sluigi			c = toupper(getc(stdin));
569498943Sluigi			while (c != '\n' && getc(stdin) != '\n')
569598943Sluigi				if (feof(stdin))
569698943Sluigi					return; /* and do not flush */
569798943Sluigi		} while (c != 'Y' && c != 'N');
569898943Sluigi		printf("\n");
569998943Sluigi		if (c == 'N')	/* user said no */
570098943Sluigi			return;
570198943Sluigi	}
5702117328Sluigi	if (do_cmd(cmd, NULL, 0) < 0)
570398943Sluigi		err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
570498943Sluigi		    do_pipe ? "DUMMYNET" : "FW");
570598943Sluigi	if (!do_quiet)
570698943Sluigi		printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules");
570798943Sluigi}
570898943Sluigi
5709117469Sluigi/*
5710117544Sluigi * Free a the (locally allocated) copy of command line arguments.
5711117469Sluigi */
5712117544Sluigistatic void
5713117544Sluigifree_args(int ac, char **av)
5714117544Sluigi{
5715117544Sluigi	int i;
5716117544Sluigi
5717117544Sluigi	for (i=0; i < ac; i++)
5718117544Sluigi		free(av[i]);
5719117544Sluigi	free(av);
5720117544Sluigi}
5721117544Sluigi
5722117544Sluigi/*
5723130281Sru * This one handles all table-related commands
5724130281Sru * 	ipfw table N add addr[/masklen] [value]
5725130281Sru * 	ipfw table N delete addr[/masklen]
5726130281Sru * 	ipfw table N flush
5727130281Sru * 	ipfw table N list
5728130281Sru */
5729130281Srustatic void
5730130281Srutable_handler(int ac, char *av[])
5731130281Sru{
5732130281Sru	ipfw_table_entry ent;
5733130281Sru	ipfw_table *tbl;
5734130281Sru	int do_add;
5735130281Sru	char *p;
5736130281Sru	socklen_t l;
5737130281Sru	uint32_t a;
5738130281Sru
5739130281Sru	ac--; av++;
5740130281Sru	if (ac && isdigit(**av)) {
5741130281Sru		ent.tbl = atoi(*av);
5742130281Sru		ac--; av++;
5743130281Sru	} else
5744130281Sru		errx(EX_USAGE, "table number required");
5745130281Sru	NEED1("table needs command");
5746140271Sbrooks	if (_substrcmp(*av, "add") == 0 ||
5747140271Sbrooks	    _substrcmp(*av, "delete") == 0) {
5748130281Sru		do_add = **av == 'a';
5749130281Sru		ac--; av++;
5750130281Sru		if (!ac)
5751130281Sru			errx(EX_USAGE, "IP address required");
5752130281Sru		p = strchr(*av, '/');
5753130281Sru		if (p) {
5754130281Sru			*p++ = '\0';
5755130281Sru			ent.masklen = atoi(p);
5756130281Sru			if (ent.masklen > 32)
5757130281Sru				errx(EX_DATAERR, "bad width ``%s''", p);
5758130281Sru		} else
5759130281Sru			ent.masklen = 32;
5760130281Sru		if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0)
5761130298Sru			errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
5762130281Sru		ac--; av++;
5763161424Sjulian		if (do_add && ac) {
5764161424Sjulian			unsigned int tval;
5765161424Sjulian			/* isdigit is a bit of a hack here.. */
5766161424Sjulian			if (strchr(*av, (int)'.') == NULL && isdigit(**av))  {
5767161424Sjulian				ent.value = strtoul(*av, NULL, 0);
5768161424Sjulian			} else {
5769161424Sjulian		        	if (lookup_host(*av, (struct in_addr *)&tval) == 0) {
5770161424Sjulian					/* The value must be stored in host order	 *
5771161424Sjulian					 * so that the values < 65k can be distinguished */
5772161424Sjulian		       			ent.value = ntohl(tval);
5773161424Sjulian				} else {
5774161424Sjulian					errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
5775161424Sjulian				}
5776161424Sjulian			}
5777161424Sjulian		} else
5778130281Sru			ent.value = 0;
5779130281Sru		if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL,
5780157335Sjulian		    &ent, sizeof(ent)) < 0) {
5781155639Sjulian			/* If running silent, don't bomb out on these errors. */
5782155639Sjulian			if (!(do_quiet && (errno == (do_add ? EEXIST : ESRCH))))
5783155639Sjulian				err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
5784155639Sjulian				    do_add ? "ADD" : "DEL");
5785155639Sjulian			/* In silent mode, react to a failed add by deleting */
5786157332Sjulian			if (do_add) {
5787155639Sjulian				do_cmd(IP_FW_TABLE_DEL, &ent, sizeof(ent));
5788155639Sjulian				if (do_cmd(IP_FW_TABLE_ADD,
5789155639Sjulian				    &ent, sizeof(ent)) < 0)
5790155639Sjulian					err(EX_OSERR,
5791155639Sjulian				            "setsockopt(IP_FW_TABLE_ADD)");
5792157332Sjulian			}
5793157335Sjulian		}
5794140271Sbrooks	} else if (_substrcmp(*av, "flush") == 0) {
5795130281Sru		if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl, sizeof(ent.tbl)) < 0)
5796130281Sru			err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
5797140271Sbrooks	} else if (_substrcmp(*av, "list") == 0) {
5798130281Sru		a = ent.tbl;
5799130281Sru		l = sizeof(a);
5800130281Sru		if (do_cmd(IP_FW_TABLE_GETSIZE, &a, (uintptr_t)&l) < 0)
5801130281Sru			err(EX_OSERR, "getsockopt(IP_FW_TABLE_GETSIZE)");
5802130281Sru		l = sizeof(*tbl) + a * sizeof(ipfw_table_entry);
5803130281Sru		tbl = malloc(l);
5804130281Sru		if (tbl == NULL)
5805130281Sru			err(EX_OSERR, "malloc");
5806130281Sru		tbl->tbl = ent.tbl;
5807130281Sru		if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0)
5808130281Sru			err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)");
5809130281Sru		for (a = 0; a < tbl->cnt; a++) {
5810161424Sjulian			/* Heuristic to print it the right way */
5811161456Sjulian			/* values < 64k are printed as numbers */
5812161424Sjulian			unsigned int tval;
5813161424Sjulian			tval = tbl->ent[a].value;
5814161424Sjulian			if (tval > 0xffff) {
5815161424Sjulian			    char tbuf[128];
5816161456Sjulian			    strncpy(tbuf, inet_ntoa(*(struct in_addr *)
5817161456Sjulian				&tbl->ent[a].addr), 127);
5818161424Sjulian			    /* inet_ntoa expects host order */
5819161424Sjulian			    tval = htonl(tval);
5820161456Sjulian			    printf("%s/%u %s\n", tbuf, tbl->ent[a].masklen,
5821161456Sjulian			        inet_ntoa(*(struct in_addr *)&tval));
5822161424Sjulian			} else {
5823161424Sjulian			    printf("%s/%u %u\n",
5824161456Sjulian			        inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
5825161456Sjulian			        tbl->ent[a].masklen, tbl->ent[a].value);
5826161424Sjulian			}
5827130281Sru		}
5828130281Sru	} else
5829130281Sru		errx(EX_USAGE, "invalid table command %s", *av);
5830130281Sru}
5831130281Sru
5832165648Spisostatic void
5833165648Spisoshow_nat(int ac, char **av) {
5834165648Spiso	struct cfg_nat *n;
5835165648Spiso	struct cfg_redir *e;
5836165648Spiso	int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
5837165648Spiso	int nat_cnt, r;
5838165648Spiso	uint8_t *data, *p;
5839165648Spiso	char **lav, *endptr;
5840165648Spiso
5841165648Spiso	do_rule = 0;
5842165648Spiso	nalloc = 1024;
5843165648Spiso	size = 0;
5844165648Spiso	data = NULL;
5845165648Spiso	ac--; av++;
5846165648Spiso
5847165648Spiso	/* Parse parameters. */
5848165648Spiso	for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
5849165648Spiso		if (!strncmp(av[0], "config", strlen(av[0]))) {
5850165648Spiso			cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
5851165648Spiso			continue;
5852165648Spiso		}
5853165648Spiso		/* Convert command line rule #. */
5854165648Spiso		frule = lrule = strtoul(av[0], &endptr, 10);
5855165648Spiso		if (*endptr == '-')
5856165648Spiso			lrule = strtoul(endptr+1, &endptr, 10);
5857165648Spiso		if (lrule == 0)
5858165648Spiso			err(EX_USAGE, "invalid rule number: %s", av[0]);
5859165648Spiso		do_rule = 1;
5860165648Spiso	}
5861165648Spiso
5862165648Spiso	nbytes = nalloc;
5863165648Spiso	while (nbytes >= nalloc) {
5864165648Spiso		nalloc = nalloc * 2;
5865165648Spiso		nbytes = nalloc;
5866165648Spiso		if ((data = realloc(data, nbytes)) == NULL)
5867165648Spiso			err(EX_OSERR, "realloc");
5868165648Spiso		if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
5869165648Spiso			err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
5870165648Spiso			    (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
5871165648Spiso	}
5872165648Spiso	if (nbytes == 0)
5873165648Spiso		exit(0);
5874165648Spiso	if (do_cfg) {
5875165648Spiso		nat_cnt = *((int *)data);
5876165648Spiso		for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
5877165648Spiso			n = (struct cfg_nat *)&data[i];
5878165648Spiso			if (do_rule) {
5879165648Spiso				if (!(frule <= n->id && lrule >= n->id))
5880165648Spiso					continue;
5881165648Spiso			}
5882165648Spiso			print_nat_config(&data[i]);
5883165648Spiso			i += sizeof(struct cfg_nat);
5884165648Spiso			e = (struct cfg_redir *)&data[i];
5885165648Spiso			if (e->mode == REDIR_ADDR || e->mode == REDIR_PORT ||
5886165648Spiso			    e->mode == REDIR_PROTO)
5887165648Spiso				i += sizeof(struct cfg_redir) + e->spool_cnt *
5888165648Spiso				    sizeof(struct cfg_spool);
5889165648Spiso		}
5890165648Spiso	} else {
5891165648Spiso		for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
5892165648Spiso			p = &data[i];
5893165648Spiso			if (p == data + nbytes)
5894165648Spiso				break;
5895165648Spiso			bcopy(p, &r, sizeof(int));
5896165648Spiso			if (do_rule) {
5897165648Spiso				if (!(frule <= r && lrule >= r))
5898165648Spiso					continue;
5899165648Spiso			}
5900165648Spiso			printf("nat %u: %s\n", r, p+sizeof(int));
5901165648Spiso		}
5902165648Spiso	}
5903165648Spiso}
5904165648Spiso
5905130281Sru/*
5906117544Sluigi * Called with the arguments (excluding program name).
5907117544Sluigi * Returns 0 if successful, 1 if empty command, errx() in case of errors.
5908117544Sluigi */
590998943Sluigistatic int
5910117328Sluigiipfw_main(int oldac, char **oldav)
591198943Sluigi{
5912117469Sluigi	int ch, ac, save_ac;
5913117469Sluigi	char **av, **save_av;
5914117469Sluigi	int do_acct = 0;		/* Show packet/byte count */
5915117469Sluigi
5916117469Sluigi#define WHITESP		" \t\f\v\n\r"
5917117544Sluigi	if (oldac == 0)
5918117544Sluigi		return 1;
5919117544Sluigi	else if (oldac == 1) {
5920117328Sluigi		/*
5921117328Sluigi		 * If we are called with a single string, try to split it into
5922117328Sluigi		 * arguments for subsequent parsing.
5923117328Sluigi		 * But first, remove spaces after a ',', by copying the string
5924117328Sluigi		 * in-place.
5925117328Sluigi		 */
5926117469Sluigi		char *arg = oldav[0];	/* The string... */
5927117328Sluigi		int l = strlen(arg);
5928117328Sluigi		int copy = 0;		/* 1 if we need to copy, 0 otherwise */
5929117328Sluigi		int i, j;
5930117469Sluigi		for (i = j = 0; i < l; i++) {
5931117469Sluigi			if (arg[i] == '#')	/* comment marker */
5932117469Sluigi				break;
5933117328Sluigi			if (copy) {
5934117328Sluigi				arg[j++] = arg[i];
5935117469Sluigi				copy = !index("," WHITESP, arg[i]);
5936117328Sluigi			} else {
5937117469Sluigi				copy = !index(WHITESP, arg[i]);
5938117328Sluigi				if (copy)
5939117328Sluigi					arg[j++] = arg[i];
5940117328Sluigi			}
5941117469Sluigi		}
5942117328Sluigi		if (!copy && j > 0)	/* last char was a 'blank', remove it */
5943117328Sluigi			j--;
5944117328Sluigi		l = j;			/* the new argument length */
5945117328Sluigi		arg[j++] = '\0';
5946117469Sluigi		if (l == 0)		/* empty string! */
5947117544Sluigi			return 1;
5948117328Sluigi
5949117328Sluigi		/*
5950117328Sluigi		 * First, count number of arguments. Because of the previous
5951117469Sluigi		 * processing, this is just the number of blanks plus 1.
5952117328Sluigi		 */
5953117469Sluigi		for (i = 0, ac = 1; i < l; i++)
5954117469Sluigi			if (index(WHITESP, arg[i]) != NULL)
5955117328Sluigi				ac++;
5956117328Sluigi
5957117328Sluigi		av = calloc(ac, sizeof(char *));
5958117328Sluigi
5959117328Sluigi		/*
5960117328Sluigi		 * Second, copy arguments from cmd[] to av[]. For each one,
5961117328Sluigi		 * j is the initial character, i is the one past the end.
5962117328Sluigi		 */
5963117469Sluigi		for (ac = 0, i = j = 0; i < l; i++)
5964117469Sluigi			if (index(WHITESP, arg[i]) != NULL || i == l-1) {
5965117328Sluigi				if (i == l-1)
5966117328Sluigi					i++;
5967117328Sluigi				av[ac] = calloc(i-j+1, 1);
5968117328Sluigi				bcopy(arg+j, av[ac], i-j);
5969117328Sluigi				ac++;
5970117328Sluigi				j = i + 1;
5971117328Sluigi			}
5972117328Sluigi	} else {
5973117328Sluigi		/*
5974117328Sluigi		 * If an argument ends with ',' join with the next one.
5975117328Sluigi		 */
5976117328Sluigi		int first, i, l;
5977117328Sluigi
5978117328Sluigi		av = calloc(oldac, sizeof(char *));
5979117328Sluigi		for (first = i = ac = 0, l = 0; i < oldac; i++) {
5980117328Sluigi			char *arg = oldav[i];
5981117328Sluigi			int k = strlen(arg);
5982117328Sluigi
5983117328Sluigi			l += k;
5984117328Sluigi			if (arg[k-1] != ',' || i == oldac-1) {
5985117328Sluigi				/* Time to copy. */
5986117328Sluigi				av[ac] = calloc(l+1, 1);
5987117328Sluigi				for (l=0; first <= i; first++) {
5988117328Sluigi					strcat(av[ac]+l, oldav[first]);
5989117328Sluigi					l += strlen(oldav[first]);
5990117328Sluigi				}
5991117328Sluigi				ac++;
5992117328Sluigi				l = 0;
5993117328Sluigi				first = i+1;
5994117328Sluigi			}
5995117328Sluigi		}
5996117328Sluigi	}
5997117328Sluigi
599898943Sluigi	/* Set the force flag for non-interactive processes */
5999123804Smaxim	if (!do_force)
6000123804Smaxim		do_force = !isatty(STDIN_FILENO);
600198943Sluigi
6002117469Sluigi	/* Save arguments for final freeing of memory. */
6003117469Sluigi	save_ac = ac;
6004117469Sluigi	save_av = av;
6005117469Sluigi
6006117469Sluigi	optind = optreset = 0;
6007123495Sluigi	while ((ch = getopt(ac, av, "abcdefhnNqs:STtv")) != -1)
600898943Sluigi		switch (ch) {
600998943Sluigi		case 'a':
601098943Sluigi			do_acct = 1;
601198943Sluigi			break;
6012117328Sluigi
6013123495Sluigi		case 'b':
6014123495Sluigi			comment_only = 1;
6015123495Sluigi			do_compact = 1;
6016123495Sluigi			break;
6017123495Sluigi
6018102098Sluigi		case 'c':
6019102098Sluigi			do_compact = 1;
6020102098Sluigi			break;
6021117328Sluigi
602298943Sluigi		case 'd':
602398943Sluigi			do_dynamic = 1;
602498943Sluigi			break;
6025117328Sluigi
602698943Sluigi		case 'e':
602798943Sluigi			do_expired = 1;
602898943Sluigi			break;
6029117328Sluigi
603098943Sluigi		case 'f':
603198943Sluigi			do_force = 1;
603298943Sluigi			break;
6033117328Sluigi
6034117328Sluigi		case 'h': /* help */
6035117544Sluigi			free_args(save_ac, save_av);
6036117328Sluigi			help();
6037117328Sluigi			break;	/* NOTREACHED */
6038117328Sluigi
6039117328Sluigi		case 'n':
6040117328Sluigi			test_only = 1;
6041117328Sluigi			break;
6042117328Sluigi
604398943Sluigi		case 'N':
604498943Sluigi			do_resolv = 1;
604598943Sluigi			break;
6046117328Sluigi
604798943Sluigi		case 'q':
604898943Sluigi			do_quiet = 1;
604998943Sluigi			break;
6050117328Sluigi
6051117328Sluigi		case 's': /* sort */
6052117328Sluigi			do_sort = atoi(optarg);
6053117328Sluigi			break;
6054117328Sluigi
6055101628Sluigi		case 'S':
6056101628Sluigi			show_sets = 1;
6057101628Sluigi			break;
6058117328Sluigi
605998943Sluigi		case 't':
606098943Sluigi			do_time = 1;
606198943Sluigi			break;
6062117328Sluigi
6063117472Sluigi		case 'T':
6064117472Sluigi			do_time = 2;	/* numeric timestamp */
6065117472Sluigi			break;
6066117472Sluigi
606798943Sluigi		case 'v': /* verbose */
6068117328Sluigi			verbose = 1;
606998943Sluigi			break;
6070117328Sluigi
607198943Sluigi		default:
6072117544Sluigi			free_args(save_ac, save_av);
6073117544Sluigi			return 1;
607498943Sluigi		}
607598943Sluigi
607698943Sluigi	ac -= optind;
607798943Sluigi	av += optind;
607898943Sluigi	NEED1("bad arguments, for usage summary ``ipfw''");
607998943Sluigi
608098943Sluigi	/*
6081117544Sluigi	 * An undocumented behaviour of ipfw1 was to allow rule numbers first,
6082117544Sluigi	 * e.g. "100 add allow ..." instead of "add 100 allow ...".
6083117544Sluigi	 * In case, swap first and second argument to get the normal form.
6084117544Sluigi	 */
6085117544Sluigi	if (ac > 1 && isdigit(*av[0])) {
6086117544Sluigi		char *p = av[0];
6087117544Sluigi
6088117544Sluigi		av[0] = av[1];
6089117544Sluigi		av[1] = p;
6090117544Sluigi	}
6091117544Sluigi
6092117544Sluigi	/*
6093165648Spiso	 * Optional: pipe, queue or nat.
609498943Sluigi	 */
6095165648Spiso	do_nat = 0;
6096117821Smaxim	do_pipe = 0;
6097165648Spiso	if (!strncmp(*av, "nat", strlen(*av)))
6098165648Spiso 	        do_nat = 1;
6099165648Spiso 	else if (!strncmp(*av, "pipe", strlen(*av)))
610098943Sluigi		do_pipe = 1;
6101140271Sbrooks	else if (_substrcmp(*av, "queue") == 0)
610298943Sluigi		do_pipe = 2;
6103165648Spiso	if (do_pipe || do_nat) {
610498943Sluigi		ac--;
610598943Sluigi		av++;
610698943Sluigi	}
610798943Sluigi	NEED1("missing command");
610898943Sluigi
610998943Sluigi	/*
6110165648Spiso	 * For pipes, queues and nats we normally say 'nat|pipe NN config'
6111165648Spiso	 * but the code is easier to parse as 'nat|pipe config NN'
611298943Sluigi	 * so we swap the two arguments.
611398943Sluigi	 */
6114165648Spiso	if ((do_pipe || do_nat) && ac > 1 && isdigit(*av[0])) {
611598943Sluigi		char *p = av[0];
6116117544Sluigi
611798943Sluigi		av[0] = av[1];
611898943Sluigi		av[1] = p;
611998943Sluigi	}
6120117328Sluigi
6121140271Sbrooks	if (_substrcmp(*av, "add") == 0)
612298943Sluigi		add(ac, av);
6123165648Spiso	else if (do_nat && _substrcmp(*av, "show") == 0)
6124165648Spiso 		show_nat(ac, av);
6125140271Sbrooks	else if (do_pipe && _substrcmp(*av, "config") == 0)
612698943Sluigi		config_pipe(ac, av);
6127165648Spiso	else if (do_nat && _substrcmp(*av, "config") == 0)
6128165648Spiso 		config_nat(ac, av);
6129140271Sbrooks	else if (_substrcmp(*av, "delete") == 0)
613098943Sluigi		delete(ac, av);
6131140271Sbrooks	else if (_substrcmp(*av, "flush") == 0)
6132117544Sluigi		flush(do_force);
6133140271Sbrooks	else if (_substrcmp(*av, "zero") == 0)
6134117328Sluigi		zero(ac, av, IP_FW_ZERO);
6135140271Sbrooks	else if (_substrcmp(*av, "resetlog") == 0)
6136117328Sluigi		zero(ac, av, IP_FW_RESETLOG);
6137140271Sbrooks	else if (_substrcmp(*av, "print") == 0 ||
6138140271Sbrooks	         _substrcmp(*av, "list") == 0)
6139117469Sluigi		list(ac, av, do_acct);
6140140271Sbrooks	else if (_substrcmp(*av, "set") == 0)
6141101978Sluigi		sets_handler(ac, av);
6142140271Sbrooks	else if (_substrcmp(*av, "table") == 0)
6143130281Sru		table_handler(ac, av);
6144140271Sbrooks	else if (_substrcmp(*av, "enable") == 0)
6145109126Sdillon		sysctl_handler(ac, av, 1);
6146140271Sbrooks	else if (_substrcmp(*av, "disable") == 0)
6147109126Sdillon		sysctl_handler(ac, av, 0);
6148140271Sbrooks	else if (_substrcmp(*av, "show") == 0)
6149117469Sluigi		list(ac, av, 1 /* show counters */);
6150117469Sluigi	else
615198943Sluigi		errx(EX_USAGE, "bad command `%s'", *av);
6152117469Sluigi
6153117469Sluigi	/* Free memory allocated in the argument parsing. */
6154117544Sluigi	free_args(save_ac, save_av);
615598943Sluigi	return 0;
615698943Sluigi}
615798943Sluigi
615898943Sluigi
615998943Sluigistatic void
6160106505Smaximipfw_readfile(int ac, char *av[])
616198943Sluigi{
616298943Sluigi#define MAX_ARGS	32
616398943Sluigi	char	buf[BUFSIZ];
6164117469Sluigi	char	*cmd = NULL, *filename = av[ac-1];
6165117469Sluigi	int	c, lineno=0;
616698943Sluigi	FILE	*f = NULL;
616798943Sluigi	pid_t	preproc = 0;
616898943Sluigi
6169117469Sluigi	filename = av[ac-1];
6170117469Sluigi
6171123804Smaxim	while ((c = getopt(ac, av, "cfNnp:qS")) != -1) {
617298943Sluigi		switch(c) {
6173117469Sluigi		case 'c':
6174117469Sluigi			do_compact = 1;
6175117469Sluigi			break;
6176117469Sluigi
6177123804Smaxim		case 'f':
6178123804Smaxim			do_force = 1;
6179123804Smaxim			break;
6180123804Smaxim
6181117469Sluigi		case 'N':
6182117469Sluigi			do_resolv = 1;
6183117469Sluigi			break;
6184117469Sluigi
6185117328Sluigi		case 'n':
6186117328Sluigi			test_only = 1;
6187117328Sluigi			break;
6188117328Sluigi
618998943Sluigi		case 'p':
619098943Sluigi			cmd = optarg;
6191117469Sluigi			/*
6192117469Sluigi			 * Skip previous args and delete last one, so we
6193117469Sluigi			 * pass all but the last argument to the preprocessor
6194117469Sluigi			 * via av[optind-1]
6195117469Sluigi			 */
6196117469Sluigi			av += optind - 1;
6197117469Sluigi			ac -= optind - 1;
6198162773Smaxim			if (ac < 2)
6199162773Smaxim				errx(EX_USAGE, "no filename argument");
6200117469Sluigi			av[ac-1] = NULL;
6201117469Sluigi			fprintf(stderr, "command is %s\n", av[0]);
620298943Sluigi			break;
620398943Sluigi
620498943Sluigi		case 'q':
6205117469Sluigi			do_quiet = 1;
620698943Sluigi			break;
620798943Sluigi
6208117469Sluigi		case 'S':
6209117469Sluigi			show_sets = 1;
6210117469Sluigi			break;
6211117469Sluigi
621298943Sluigi		default:
621398943Sluigi			errx(EX_USAGE, "bad arguments, for usage"
621498943Sluigi			     " summary ``ipfw''");
621598943Sluigi		}
621698943Sluigi
6217117469Sluigi		if (cmd != NULL)
6218108231Skbyanc			break;
6219108231Skbyanc	}
6220108231Skbyanc
6221117469Sluigi	if (cmd == NULL && ac != optind + 1) {
6222117469Sluigi		fprintf(stderr, "ac %d, optind %d\n", ac, optind);
6223117469Sluigi		errx(EX_USAGE, "extraneous filename arguments");
6224108231Skbyanc	}
6225108231Skbyanc
6226117469Sluigi	if ((f = fopen(filename, "r")) == NULL)
6227117469Sluigi		err(EX_UNAVAILABLE, "fopen: %s", filename);
622898943Sluigi
6229117469Sluigi	if (cmd != NULL) {			/* pipe through preprocessor */
623098943Sluigi		int pipedes[2];
623198943Sluigi
623298943Sluigi		if (pipe(pipedes) == -1)
623398943Sluigi			err(EX_OSERR, "cannot create pipe");
623498943Sluigi
6235117469Sluigi		preproc = fork();
6236117469Sluigi		if (preproc == -1)
623798943Sluigi			err(EX_OSERR, "cannot fork");
623898943Sluigi
6239117577Sluigi		if (preproc == 0) {
6240117469Sluigi			/*
6241117469Sluigi			 * Child, will run the preprocessor with the
6242117469Sluigi			 * file on stdin and the pipe on stdout.
6243117469Sluigi			 */
624498943Sluigi			if (dup2(fileno(f), 0) == -1
624598943Sluigi			    || dup2(pipedes[1], 1) == -1)
624698943Sluigi				err(EX_OSERR, "dup2()");
624798943Sluigi			fclose(f);
624898943Sluigi			close(pipedes[1]);
624998943Sluigi			close(pipedes[0]);
6250117469Sluigi			execvp(cmd, av);
625198943Sluigi			err(EX_OSERR, "execvp(%s) failed", cmd);
6252117469Sluigi		} else { /* parent, will reopen f as the pipe */
625398943Sluigi			fclose(f);
625498943Sluigi			close(pipedes[1]);
625598943Sluigi			if ((f = fdopen(pipedes[0], "r")) == NULL) {
625698943Sluigi				int savederrno = errno;
625798943Sluigi
625898943Sluigi				(void)kill(preproc, SIGTERM);
625998943Sluigi				errno = savederrno;
626098943Sluigi				err(EX_OSERR, "fdopen()");
626198943Sluigi			}
626298943Sluigi		}
626398943Sluigi	}
626498943Sluigi
6265117469Sluigi	while (fgets(buf, BUFSIZ, f)) {		/* read commands */
6266117469Sluigi		char linename[10];
6267117469Sluigi		char *args[1];
6268117469Sluigi
626998943Sluigi		lineno++;
627098943Sluigi		sprintf(linename, "Line %d", lineno);
6271117328Sluigi		setprogname(linename); /* XXX */
6272117469Sluigi		args[0] = buf;
6273117469Sluigi		ipfw_main(1, args);
627498943Sluigi	}
627598943Sluigi	fclose(f);
6276117469Sluigi	if (cmd != NULL) {
6277117469Sluigi		int status;
6278117469Sluigi
627998943Sluigi		if (waitpid(preproc, &status, 0) == -1)
628098943Sluigi			errx(EX_OSERR, "waitpid()");
628198943Sluigi		if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
628298943Sluigi			errx(EX_UNAVAILABLE,
628398943Sluigi			    "preprocessor exited with status %d",
628498943Sluigi			    WEXITSTATUS(status));
628598943Sluigi		else if (WIFSIGNALED(status))
628698943Sluigi			errx(EX_UNAVAILABLE,
628798943Sluigi			    "preprocessor exited with signal %d",
628898943Sluigi			    WTERMSIG(status));
628998943Sluigi	}
629098943Sluigi}
629198943Sluigi
629298943Sluigiint
629398943Sluigimain(int ac, char *av[])
629498943Sluigi{
629598943Sluigi	/*
629698943Sluigi	 * If the last argument is an absolute pathname, interpret it
629798943Sluigi	 * as a file to be preprocessed.
629898943Sluigi	 */
629998943Sluigi
630098943Sluigi	if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0)
630198943Sluigi		ipfw_readfile(ac, av);
6302117544Sluigi	else {
6303117544Sluigi		if (ipfw_main(ac-1, av+1))
6304117544Sluigi			show_usage();
6305117544Sluigi	}
630698943Sluigi	return EX_OK;
630798943Sluigi}
6308