ipfw2.c revision 187716
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 187716 2009-01-26 14:26:35Z luigi $
2198943Sluigi */
2298943Sluigi
23187604Sluigi#include <sys/types.h>
2498943Sluigi#include <sys/socket.h>
2598943Sluigi#include <sys/sockio.h>
2698943Sluigi#include <sys/sysctl.h>
2798943Sluigi#include <sys/wait.h>
2898943Sluigi
2998943Sluigi#include <ctype.h>
3098943Sluigi#include <err.h>
3198943Sluigi#include <errno.h>
3298943Sluigi#include <grp.h>
3398943Sluigi#include <netdb.h>
3498943Sluigi#include <pwd.h>
3598943Sluigi#include <signal.h>
3698943Sluigi#include <stdio.h>
3798943Sluigi#include <stdlib.h>
3898943Sluigi#include <string.h>
3998943Sluigi#include <sysexits.h>
40187604Sluigi#include <timeconv.h>	/* _long_to_time */
41136071Sgreen#include <unistd.h>
42136071Sgreen#include <fcntl.h>
4398943Sluigi
44175659Srwatson#define IPFW_INTERNAL	/* Access to protected structures in ip_fw.h. */
45175659Srwatson
46169424Smaxim#include <net/ethernet.h>
4798943Sluigi#include <net/if.h>
48165648Spiso#include <net/if_dl.h>
49136071Sgreen#include <net/pfvar.h>
50145246Sbrooks#include <net/route.h> /* def. of struct route */
5198943Sluigi#include <netinet/in.h>
5298943Sluigi#include <netinet/in_systm.h>
5398943Sluigi#include <netinet/ip.h>
5498943Sluigi#include <netinet/ip_icmp.h>
55145246Sbrooks#include <netinet/icmp6.h>
5698943Sluigi#include <netinet/ip_fw.h>
5798943Sluigi#include <netinet/ip_dummynet.h>
5898943Sluigi#include <netinet/tcp.h>
5998943Sluigi#include <arpa/inet.h>
60165648Spiso#include <alias.h>
6198943Sluigi
62117328Sluigiint
63176391Sjulian		do_value_as_ip,		/* show table value as IP */
6498943Sluigi		do_resolv,		/* Would try to resolve all */
6598943Sluigi		do_time,		/* Show time stamps */
6698943Sluigi		do_quiet,		/* Be quiet in add and flush */
6798943Sluigi		do_pipe,		/* this cmd refers to a pipe */
68165648Spiso	        do_nat, 		/* Nat configuration. */
6998943Sluigi		do_sort,		/* field to sort results (0 = no) */
7098943Sluigi		do_dynamic,		/* display dynamic rules */
7198943Sluigi		do_expired,		/* display expired dynamic rules */
72102098Sluigi		do_compact,		/* show rules in compact mode */
73123804Smaxim		do_force,		/* do not ask for confirmation */
74170923Smaxim		use_set,		/* work with specified set number */
75101628Sluigi		show_sets,		/* display rule sets */
76117328Sluigi		test_only,		/* only check syntax */
77123495Sluigi		comment_only,		/* only print action and comment */
7898943Sluigi		verbose;
7998943Sluigi
8098943Sluigi#define	IP_MASK_ALL	0xffffffff
81130013Scsjp/*
82130013Scsjp * the following macro returns an error message if we run out of
83130013Scsjp * arguments.
84130013Scsjp */
85130013Scsjp#define NEED1(msg)      {if (!ac) errx(EX_USAGE, msg);}
8698943Sluigi
87159636Soleg#define GET_UINT_ARG(arg, min, max, tok, s_x) do {			\
88159636Soleg	if (!ac)							\
89159636Soleg		errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \
90159636Soleg	if (_substrcmp(*av, "tablearg") == 0) {				\
91159636Soleg		arg = IP_FW_TABLEARG;					\
92159636Soleg		break;							\
93159636Soleg	}								\
94159636Soleg									\
95159636Soleg	{								\
96159636Soleg	long val;							\
97159636Soleg	char *end;							\
98159636Soleg									\
99159636Soleg	val = strtol(*av, &end, 10);					\
100159636Soleg									\
101159636Soleg	if (!isdigit(**av) || *end != '\0' || (val == 0 && errno == EINVAL)) \
102159636Soleg		errx(EX_DATAERR, "%s: invalid argument: %s",		\
103159636Soleg		    match_value(s_x, tok), *av);			\
104159636Soleg									\
105159636Soleg	if (errno == ERANGE || val < min || val > max)			\
106159636Soleg		errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \
107159636Soleg		    match_value(s_x, tok), min, max, *av);		\
108159636Soleg									\
109159636Soleg	if (val == IP_FW_TABLEARG)					\
110159636Soleg		errx(EX_DATAERR, "%s: illegal argument value: %s",	\
111159636Soleg		    match_value(s_x, tok), *av);			\
112159636Soleg	arg = val;							\
113159636Soleg	}								\
114158879Soleg} while (0)
115158879Soleg
116159636Soleg#define PRINT_UINT_ARG(str, arg) do {					\
117159636Soleg	if (str != NULL)						\
118159636Soleg		printf("%s",str);					\
119159636Soleg	if (arg == IP_FW_TABLEARG)					\
120159636Soleg		printf("tablearg");					\
121159636Soleg	else								\
122159636Soleg		printf("%u", (uint32_t)arg);				\
123159636Soleg} while (0)
124159636Soleg
12598943Sluigi/*
126117328Sluigi * _s_x is a structure that stores a string <-> token pairs, used in
127117328Sluigi * various places in the parser. Entries are stored in arrays,
128117328Sluigi * with an entry with s=NULL as terminator.
129117328Sluigi * The search routines are match_token() and match_value().
130117328Sluigi * Often, an element with x=0 contains an error string.
13198943Sluigi *
13298943Sluigi */
13398943Sluigistruct _s_x {
134117469Sluigi	char const *s;
13598943Sluigi	int x;
13698943Sluigi};
13798943Sluigi
13898943Sluigistatic struct _s_x f_tcpflags[] = {
13998943Sluigi	{ "syn", TH_SYN },
14098943Sluigi	{ "fin", TH_FIN },
14198943Sluigi	{ "ack", TH_ACK },
14298943Sluigi	{ "psh", TH_PUSH },
14398943Sluigi	{ "rst", TH_RST },
14498943Sluigi	{ "urg", TH_URG },
14598943Sluigi	{ "tcp flag", 0 },
14698943Sluigi	{ NULL,	0 }
14798943Sluigi};
14898943Sluigi
14998943Sluigistatic struct _s_x f_tcpopts[] = {
15098943Sluigi	{ "mss",	IP_FW_TCPOPT_MSS },
15198943Sluigi	{ "maxseg",	IP_FW_TCPOPT_MSS },
15298943Sluigi	{ "window",	IP_FW_TCPOPT_WINDOW },
15398943Sluigi	{ "sack",	IP_FW_TCPOPT_SACK },
15498943Sluigi	{ "ts",		IP_FW_TCPOPT_TS },
15598943Sluigi	{ "timestamp",	IP_FW_TCPOPT_TS },
15698943Sluigi	{ "cc",		IP_FW_TCPOPT_CC },
15798943Sluigi	{ "tcp option",	0 },
15898943Sluigi	{ NULL,	0 }
15998943Sluigi};
16098943Sluigi
16198943Sluigi/*
16298943Sluigi * IP options span the range 0 to 255 so we need to remap them
16398943Sluigi * (though in fact only the low 5 bits are significant).
16498943Sluigi */
16598943Sluigistatic struct _s_x f_ipopts[] = {
16698943Sluigi	{ "ssrr",	IP_FW_IPOPT_SSRR},
16798943Sluigi	{ "lsrr",	IP_FW_IPOPT_LSRR},
16898943Sluigi	{ "rr",		IP_FW_IPOPT_RR},
16998943Sluigi	{ "ts",		IP_FW_IPOPT_TS},
17098943Sluigi	{ "ip option",	0 },
17198943Sluigi	{ NULL,	0 }
17298943Sluigi};
17398943Sluigi
17498943Sluigistatic struct _s_x f_iptos[] = {
17598943Sluigi	{ "lowdelay",	IPTOS_LOWDELAY},
17698943Sluigi	{ "throughput",	IPTOS_THROUGHPUT},
17798943Sluigi	{ "reliability", IPTOS_RELIABILITY},
17898943Sluigi	{ "mincost",	IPTOS_MINCOST},
179172801Srpaulo	{ "congestion",	IPTOS_ECN_CE},
180172801Srpaulo	{ "ecntransport", IPTOS_ECN_ECT0},
18198943Sluigi	{ "ip tos option", 0},
18298943Sluigi	{ NULL,	0 }
18398943Sluigi};
18498943Sluigi
18598943Sluigistatic struct _s_x limit_masks[] = {
18698943Sluigi	{"all",		DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
18798943Sluigi	{"src-addr",	DYN_SRC_ADDR},
18898943Sluigi	{"src-port",	DYN_SRC_PORT},
18998943Sluigi	{"dst-addr",	DYN_DST_ADDR},
19098943Sluigi	{"dst-port",	DYN_DST_PORT},
19198943Sluigi	{NULL,		0}
19298943Sluigi};
19398943Sluigi
19498943Sluigi/*
19598943Sluigi * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines
19698943Sluigi * This is only used in this code.
19798943Sluigi */
19898943Sluigi#define IPPROTO_ETHERTYPE	0x1000
19998943Sluigistatic struct _s_x ether_types[] = {
20098943Sluigi    /*
20198943Sluigi     * Note, we cannot use "-:&/" in the names because they are field
20298943Sluigi     * separators in the type specifications. Also, we use s = NULL as
20398943Sluigi     * end-delimiter, because a type of 0 can be legal.
20498943Sluigi     */
20598943Sluigi	{ "ip",		0x0800 },
20698943Sluigi	{ "ipv4",	0x0800 },
20798943Sluigi	{ "ipv6",	0x86dd },
20898943Sluigi	{ "arp",	0x0806 },
20998943Sluigi	{ "rarp",	0x8035 },
21098943Sluigi	{ "vlan",	0x8100 },
21198943Sluigi	{ "loop",	0x9000 },
21298943Sluigi	{ "trail",	0x1000 },
21398943Sluigi	{ "at",		0x809b },
21498943Sluigi	{ "atalk",	0x809b },
21598943Sluigi	{ "aarp",	0x80f3 },
21698943Sluigi	{ "pppoe_disc",	0x8863 },
21798943Sluigi	{ "pppoe_sess",	0x8864 },
21898943Sluigi	{ "ipx_8022",	0x00E0 },
21998943Sluigi	{ "ipx_8023",	0x0000 },
22098943Sluigi	{ "ipx_ii",	0x8137 },
22198943Sluigi	{ "ipx_snap",	0x8137 },
22298943Sluigi	{ "ipx",	0x8137 },
22398943Sluigi	{ "ns",		0x0600 },
22498943Sluigi	{ NULL,		0 }
22598943Sluigi};
22698943Sluigi
22798943Sluigistatic void show_usage(void);
22898943Sluigi
22998943Sluigienum tokens {
23098943Sluigi	TOK_NULL=0,
23198943Sluigi
23298943Sluigi	TOK_OR,
23398943Sluigi	TOK_NOT,
234101641Sluigi	TOK_STARTBRACE,
235101641Sluigi	TOK_ENDBRACE,
23698943Sluigi
23798943Sluigi	TOK_ACCEPT,
23898943Sluigi	TOK_COUNT,
23998943Sluigi	TOK_PIPE,
24098943Sluigi	TOK_QUEUE,
24198943Sluigi	TOK_DIVERT,
24298943Sluigi	TOK_TEE,
243141351Sglebius	TOK_NETGRAPH,
244141351Sglebius	TOK_NGTEE,
24598943Sluigi	TOK_FORWARD,
24698943Sluigi	TOK_SKIPTO,
24798943Sluigi	TOK_DENY,
24898943Sluigi	TOK_REJECT,
24998943Sluigi	TOK_RESET,
25098943Sluigi	TOK_UNREACH,
25198943Sluigi	TOK_CHECKSTATE,
252165648Spiso	TOK_NAT,
25398943Sluigi
254136071Sgreen	TOK_ALTQ,
255136071Sgreen	TOK_LOG,
256158879Soleg	TOK_TAG,
257158879Soleg	TOK_UNTAG,
258136071Sgreen
259158879Soleg	TOK_TAGGED,
26098943Sluigi	TOK_UID,
26198943Sluigi	TOK_GID,
262133600Scsjp	TOK_JAIL,
26398943Sluigi	TOK_IN,
26498943Sluigi	TOK_LIMIT,
26598943Sluigi	TOK_KEEPSTATE,
26698943Sluigi	TOK_LAYER2,
26798943Sluigi	TOK_OUT,
268136073Sgreen	TOK_DIVERTED,
269136073Sgreen	TOK_DIVERTEDLOOPBACK,
270136073Sgreen	TOK_DIVERTEDOUTPUT,
27198943Sluigi	TOK_XMIT,
27298943Sluigi	TOK_RECV,
27398943Sluigi	TOK_VIA,
27498943Sluigi	TOK_FRAG,
27598943Sluigi	TOK_IPOPTS,
27698943Sluigi	TOK_IPLEN,
27798943Sluigi	TOK_IPID,
27898943Sluigi	TOK_IPPRECEDENCE,
27998943Sluigi	TOK_IPTOS,
28098943Sluigi	TOK_IPTTL,
28198943Sluigi	TOK_IPVER,
28298943Sluigi	TOK_ESTAB,
28398943Sluigi	TOK_SETUP,
284136075Sgreen	TOK_TCPDATALEN,
28598943Sluigi	TOK_TCPFLAGS,
28698943Sluigi	TOK_TCPOPTS,
28798943Sluigi	TOK_TCPSEQ,
28898943Sluigi	TOK_TCPACK,
28998943Sluigi	TOK_TCPWIN,
29098943Sluigi	TOK_ICMPTYPES,
291102087Sluigi	TOK_MAC,
292102087Sluigi	TOK_MACTYPE,
293112250Scjc	TOK_VERREVPATH,
294128575Sandre	TOK_VERSRCREACH,
295133387Sandre	TOK_ANTISPOOF,
296117241Sluigi	TOK_IPSEC,
297117469Sluigi	TOK_COMMENT,
29898943Sluigi
29998943Sluigi	TOK_PLR,
300101978Sluigi	TOK_NOERROR,
30198943Sluigi	TOK_BUCKETS,
30298943Sluigi	TOK_DSTIP,
30398943Sluigi	TOK_SRCIP,
30498943Sluigi	TOK_DSTPORT,
30598943Sluigi	TOK_SRCPORT,
30698943Sluigi	TOK_ALL,
30798943Sluigi	TOK_MASK,
30898943Sluigi	TOK_BW,
30998943Sluigi	TOK_DELAY,
31098943Sluigi	TOK_RED,
31198943Sluigi	TOK_GRED,
31298943Sluigi	TOK_DROPTAIL,
31398943Sluigi	TOK_PROTO,
31498943Sluigi	TOK_WEIGHT,
315165648Spiso	TOK_IP,
316165648Spiso	TOK_IF,
317165648Spiso 	TOK_ALOG,
318165648Spiso 	TOK_DENY_INC,
319165648Spiso 	TOK_SAME_PORTS,
320165648Spiso 	TOK_UNREG_ONLY,
321165648Spiso 	TOK_RESET_ADDR,
322165648Spiso 	TOK_ALIAS_REV,
323165648Spiso 	TOK_PROXY_ONLY,
324165648Spiso	TOK_REDIR_ADDR,
325165648Spiso	TOK_REDIR_PORT,
326165648Spiso	TOK_REDIR_PROTO,
327145246Sbrooks
328145246Sbrooks	TOK_IPV6,
329145246Sbrooks	TOK_FLOWID,
330145246Sbrooks	TOK_ICMP6TYPES,
331145246Sbrooks	TOK_EXT6HDR,
332145246Sbrooks	TOK_DSTIP6,
333145246Sbrooks	TOK_SRCIP6,
334146894Smlaier
335146894Smlaier	TOK_IPV4,
336149020Sbz	TOK_UNREACH6,
337149020Sbz	TOK_RESET6,
338178888Sjulian
339178888Sjulian	TOK_FIB,
340178888Sjulian	TOK_SETFIB,
34198943Sluigi};
34298943Sluigi
34398943Sluigistruct _s_x dummynet_params[] = {
34498943Sluigi	{ "plr",		TOK_PLR },
345101978Sluigi	{ "noerror",		TOK_NOERROR },
34698943Sluigi	{ "buckets",		TOK_BUCKETS },
34798943Sluigi	{ "dst-ip",		TOK_DSTIP },
34898943Sluigi	{ "src-ip",		TOK_SRCIP },
34998943Sluigi	{ "dst-port",		TOK_DSTPORT },
35098943Sluigi	{ "src-port",		TOK_SRCPORT },
35198943Sluigi	{ "proto",		TOK_PROTO },
35298943Sluigi	{ "weight",		TOK_WEIGHT },
35398943Sluigi	{ "all",		TOK_ALL },
35498943Sluigi	{ "mask",		TOK_MASK },
35598943Sluigi	{ "droptail",		TOK_DROPTAIL },
35698943Sluigi	{ "red",		TOK_RED },
35798943Sluigi	{ "gred",		TOK_GRED },
35898943Sluigi	{ "bw",			TOK_BW },
35998943Sluigi	{ "bandwidth",		TOK_BW },
36098943Sluigi	{ "delay",		TOK_DELAY },
36199475Sluigi	{ "pipe",		TOK_PIPE },
36298943Sluigi	{ "queue",		TOK_QUEUE },
363145246Sbrooks	{ "flow-id",		TOK_FLOWID},
364145246Sbrooks	{ "dst-ipv6",		TOK_DSTIP6},
365145246Sbrooks	{ "dst-ip6",		TOK_DSTIP6},
366145246Sbrooks	{ "src-ipv6",		TOK_SRCIP6},
367145246Sbrooks	{ "src-ip6",		TOK_SRCIP6},
36898943Sluigi	{ "dummynet-params",	TOK_NULL },
369117328Sluigi	{ NULL, 0 }	/* terminator */
37098943Sluigi};
37198943Sluigi
372165648Spisostruct _s_x nat_params[] = {
373165648Spiso	{ "ip",	                TOK_IP },
374165648Spiso	{ "if",	                TOK_IF },
375165648Spiso 	{ "log",                TOK_ALOG },
376165648Spiso 	{ "deny_in",	        TOK_DENY_INC },
377165648Spiso 	{ "same_ports",	        TOK_SAME_PORTS },
378165648Spiso 	{ "unreg_only",	        TOK_UNREG_ONLY },
379165648Spiso 	{ "reset",	        TOK_RESET_ADDR },
380165648Spiso 	{ "reverse",	        TOK_ALIAS_REV },
381165648Spiso 	{ "proxy_only",	        TOK_PROXY_ONLY },
382165648Spiso	{ "redirect_addr",	TOK_REDIR_ADDR },
383165648Spiso	{ "redirect_port",	TOK_REDIR_PORT },
384165648Spiso	{ "redirect_proto",	TOK_REDIR_PROTO },
385165648Spiso 	{ NULL, 0 }	/* terminator */
386165648Spiso};
387165648Spiso
38898943Sluigistruct _s_x rule_actions[] = {
38998943Sluigi	{ "accept",		TOK_ACCEPT },
39098943Sluigi	{ "pass",		TOK_ACCEPT },
39198943Sluigi	{ "allow",		TOK_ACCEPT },
39298943Sluigi	{ "permit",		TOK_ACCEPT },
39398943Sluigi	{ "count",		TOK_COUNT },
39498943Sluigi	{ "pipe",		TOK_PIPE },
39598943Sluigi	{ "queue",		TOK_QUEUE },
39698943Sluigi	{ "divert",		TOK_DIVERT },
39798943Sluigi	{ "tee",		TOK_TEE },
398141351Sglebius	{ "netgraph",		TOK_NETGRAPH },
399141351Sglebius	{ "ngtee",		TOK_NGTEE },
40098943Sluigi	{ "fwd",		TOK_FORWARD },
40198943Sluigi	{ "forward",		TOK_FORWARD },
40298943Sluigi	{ "skipto",		TOK_SKIPTO },
40398943Sluigi	{ "deny",		TOK_DENY },
40498943Sluigi	{ "drop",		TOK_DENY },
40598943Sluigi	{ "reject",		TOK_REJECT },
406149020Sbz	{ "reset6",		TOK_RESET6 },
40798943Sluigi	{ "reset",		TOK_RESET },
408149020Sbz	{ "unreach6",		TOK_UNREACH6 },
40999475Sluigi	{ "unreach",		TOK_UNREACH },
41098943Sluigi	{ "check-state",	TOK_CHECKSTATE },
411117469Sluigi	{ "//",			TOK_COMMENT },
412165648Spiso	{ "nat",                TOK_NAT },
413178888Sjulian	{ "setfib",		TOK_SETFIB },
414117328Sluigi	{ NULL, 0 }	/* terminator */
41598943Sluigi};
41698943Sluigi
417136071Sgreenstruct _s_x rule_action_params[] = {
418136071Sgreen	{ "altq",		TOK_ALTQ },
419136071Sgreen	{ "log",		TOK_LOG },
420158879Soleg	{ "tag",		TOK_TAG },
421158879Soleg	{ "untag",		TOK_UNTAG },
422136071Sgreen	{ NULL, 0 }	/* terminator */
423136071Sgreen};
424136071Sgreen
42598943Sluigistruct _s_x rule_options[] = {
426158879Soleg	{ "tagged",		TOK_TAGGED },
42798943Sluigi	{ "uid",		TOK_UID },
42898943Sluigi	{ "gid",		TOK_GID },
429133600Scsjp	{ "jail",		TOK_JAIL },
43098943Sluigi	{ "in",			TOK_IN },
43198943Sluigi	{ "limit",		TOK_LIMIT },
43298943Sluigi	{ "keep-state",		TOK_KEEPSTATE },
43398943Sluigi	{ "bridged",		TOK_LAYER2 },
43498943Sluigi	{ "layer2",		TOK_LAYER2 },
43598943Sluigi	{ "out",		TOK_OUT },
436136073Sgreen	{ "diverted",		TOK_DIVERTED },
437136073Sgreen	{ "diverted-loopback",	TOK_DIVERTEDLOOPBACK },
438136073Sgreen	{ "diverted-output",	TOK_DIVERTEDOUTPUT },
43998943Sluigi	{ "xmit",		TOK_XMIT },
44098943Sluigi	{ "recv",		TOK_RECV },
44198943Sluigi	{ "via",		TOK_VIA },
44298943Sluigi	{ "fragment",		TOK_FRAG },
44398943Sluigi	{ "frag",		TOK_FRAG },
444178888Sjulian	{ "fib",		TOK_FIB },
44598943Sluigi	{ "ipoptions",		TOK_IPOPTS },
44698943Sluigi	{ "ipopts",		TOK_IPOPTS },
44798943Sluigi	{ "iplen",		TOK_IPLEN },
44898943Sluigi	{ "ipid",		TOK_IPID },
44998943Sluigi	{ "ipprecedence",	TOK_IPPRECEDENCE },
45098943Sluigi	{ "iptos",		TOK_IPTOS },
45198943Sluigi	{ "ipttl",		TOK_IPTTL },
45298943Sluigi	{ "ipversion",		TOK_IPVER },
45398943Sluigi	{ "ipver",		TOK_IPVER },
45498943Sluigi	{ "estab",		TOK_ESTAB },
45598943Sluigi	{ "established",	TOK_ESTAB },
45698943Sluigi	{ "setup",		TOK_SETUP },
457136075Sgreen	{ "tcpdatalen",		TOK_TCPDATALEN },
45898943Sluigi	{ "tcpflags",		TOK_TCPFLAGS },
45998943Sluigi	{ "tcpflgs",		TOK_TCPFLAGS },
46098943Sluigi	{ "tcpoptions",		TOK_TCPOPTS },
46198943Sluigi	{ "tcpopts",		TOK_TCPOPTS },
46298943Sluigi	{ "tcpseq",		TOK_TCPSEQ },
46398943Sluigi	{ "tcpack",		TOK_TCPACK },
46498943Sluigi	{ "tcpwin",		TOK_TCPWIN },
46599909Sluigi	{ "icmptype",		TOK_ICMPTYPES },
46698943Sluigi	{ "icmptypes",		TOK_ICMPTYPES },
467102087Sluigi	{ "dst-ip",		TOK_DSTIP },
468102087Sluigi	{ "src-ip",		TOK_SRCIP },
469102087Sluigi	{ "dst-port",		TOK_DSTPORT },
470102087Sluigi	{ "src-port",		TOK_SRCPORT },
471102087Sluigi	{ "proto",		TOK_PROTO },
472102087Sluigi	{ "MAC",		TOK_MAC },
473102087Sluigi	{ "mac",		TOK_MAC },
474102087Sluigi	{ "mac-type",		TOK_MACTYPE },
475112250Scjc	{ "verrevpath",		TOK_VERREVPATH },
476128575Sandre	{ "versrcreach",	TOK_VERSRCREACH },
477133387Sandre	{ "antispoof",		TOK_ANTISPOOF },
478117241Sluigi	{ "ipsec",		TOK_IPSEC },
479145246Sbrooks	{ "icmp6type",		TOK_ICMP6TYPES },
480145246Sbrooks	{ "icmp6types",		TOK_ICMP6TYPES },
481145246Sbrooks	{ "ext6hdr",		TOK_EXT6HDR},
482145246Sbrooks	{ "flow-id",		TOK_FLOWID},
483145246Sbrooks	{ "ipv6",		TOK_IPV6},
484145246Sbrooks	{ "ip6",		TOK_IPV6},
485146894Smlaier	{ "ipv4",		TOK_IPV4},
486146894Smlaier	{ "ip4",		TOK_IPV4},
487145246Sbrooks	{ "dst-ipv6",		TOK_DSTIP6},
488145246Sbrooks	{ "dst-ip6",		TOK_DSTIP6},
489145246Sbrooks	{ "src-ipv6",		TOK_SRCIP6},
490145246Sbrooks	{ "src-ip6",		TOK_SRCIP6},
491117469Sluigi	{ "//",			TOK_COMMENT },
49298943Sluigi
49398943Sluigi	{ "not",		TOK_NOT },		/* pseudo option */
49498943Sluigi	{ "!", /* escape ? */	TOK_NOT },		/* pseudo option */
49598943Sluigi	{ "or",			TOK_OR },		/* pseudo option */
49698943Sluigi	{ "|", /* escape */	TOK_OR },		/* pseudo option */
497101641Sluigi	{ "{",			TOK_STARTBRACE },	/* pseudo option */
498101641Sluigi	{ "(",			TOK_STARTBRACE },	/* pseudo option */
499101641Sluigi	{ "}",			TOK_ENDBRACE },		/* pseudo option */
500101641Sluigi	{ ")",			TOK_ENDBRACE },		/* pseudo option */
501117328Sluigi	{ NULL, 0 }	/* terminator */
50298943Sluigi};
50398943Sluigi
504153374Sglebius#define	TABLEARG	"tablearg"
505153374Sglebius
506117328Sluigistatic __inline uint64_t
507117328Sluigialign_uint64(uint64_t *pll) {
508117328Sluigi	uint64_t ret;
509115793Sticso
510115793Sticso	bcopy (pll, &ret, sizeof(ret));
511115793Sticso	return ret;
512129389Sstefanf}
513115793Sticso
514187716Sluigistatic void *
515187716Sluigisafe_calloc(size_t number, size_t size)
516187716Sluigi{
517187716Sluigi	void *ret = calloc(number, size);
518187716Sluigi
519187716Sluigi	if (ret == NULL)
520187716Sluigi		err(EX_OSERR, "calloc");
521187716Sluigi	return ret;
522187716Sluigi}
523187716Sluigi
524187716Sluigistatic void *
525187716Sluigisafe_realloc(void *ptr, size_t size)
526187716Sluigi{
527187716Sluigi	void *ret = realloc(ptr, size);
528187716Sluigi
529187716Sluigi	if (ret == NULL)
530187716Sluigi		err(EX_OSERR, "realloc");
531187716Sluigi	return ret;
532187716Sluigi}
533187716Sluigi
534117328Sluigi/*
535117328Sluigi * conditionally runs the command.
536117328Sluigi */
537117469Sluigistatic int
538119740Stmmdo_cmd(int optname, void *optval, uintptr_t optlen)
539117328Sluigi{
540117328Sluigi	static int s = -1;	/* the socket */
541117328Sluigi	int i;
542117577Sluigi
543117328Sluigi	if (test_only)
544117328Sluigi		return 0;
545117328Sluigi
546117328Sluigi	if (s == -1)
547117328Sluigi		s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
548117328Sluigi	if (s < 0)
549117328Sluigi		err(EX_UNAVAILABLE, "socket");
550117328Sluigi
551117328Sluigi	if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
552130281Sru	    optname == IP_FW_ADD || optname == IP_FW_TABLE_LIST ||
553165648Spiso	    optname == IP_FW_TABLE_GETSIZE ||
554165648Spiso	    optname == IP_FW_NAT_GET_CONFIG ||
555165648Spiso	    optname == IP_FW_NAT_GET_LOG)
556117328Sluigi		i = getsockopt(s, IPPROTO_IP, optname, optval,
557117328Sluigi			(socklen_t *)optlen);
558117328Sluigi	else
559117328Sluigi		i = setsockopt(s, IPPROTO_IP, optname, optval, optlen);
560117328Sluigi	return i;
561117328Sluigi}
562117328Sluigi
56398943Sluigi/**
56498943Sluigi * match_token takes a table and a string, returns the value associated
565117328Sluigi * with the string (-1 in case of failure).
56698943Sluigi */
56798943Sluigistatic int
56898943Sluigimatch_token(struct _s_x *table, char *string)
56998943Sluigi{
57098943Sluigi	struct _s_x *pt;
571117469Sluigi	uint i = strlen(string);
57298943Sluigi
57398943Sluigi	for (pt = table ; i && pt->s != NULL ; pt++)
57498943Sluigi		if (strlen(pt->s) == i && !bcmp(string, pt->s, i))
57598943Sluigi			return pt->x;
57698943Sluigi	return -1;
577129389Sstefanf}
57898943Sluigi
579117328Sluigi/**
580117328Sluigi * match_value takes a table and a value, returns the string associated
581117328Sluigi * with the value (NULL in case of failure).
582117328Sluigi */
583117469Sluigistatic char const *
584117469Sluigimatch_value(struct _s_x *p, int value)
58598943Sluigi{
58698943Sluigi	for (; p->s != NULL; p++)
58798943Sluigi		if (p->x == value)
58898943Sluigi			return p->s;
58998943Sluigi	return NULL;
59098943Sluigi}
59198943Sluigi
59298943Sluigi/*
593140271Sbrooks * _substrcmp takes two strings and returns 1 if they do not match,
594140271Sbrooks * and 0 if they match exactly or the first string is a sub-string
595140271Sbrooks * of the second.  A warning is printed to stderr in the case that the
596140271Sbrooks * first string is a sub-string of the second.
597140271Sbrooks *
598140271Sbrooks * This function will be removed in the future through the usual
599140271Sbrooks * deprecation process.
600140271Sbrooks */
601140271Sbrooksstatic int
602140271Sbrooks_substrcmp(const char *str1, const char* str2)
603140271Sbrooks{
604140271Sbrooks
605140271Sbrooks	if (strncmp(str1, str2, strlen(str1)) != 0)
606140271Sbrooks		return 1;
607140271Sbrooks
608140271Sbrooks	if (strlen(str1) != strlen(str2))
609140271Sbrooks		warnx("DEPRECATED: '%s' matched '%s' as a sub-string",
610140271Sbrooks		    str1, str2);
611140271Sbrooks	return 0;
612140271Sbrooks}
613140271Sbrooks
614140271Sbrooks/*
615140271Sbrooks * _substrcmp2 takes three strings and returns 1 if the first two do not match,
616140271Sbrooks * and 0 if they match exactly or the second string is a sub-string
617140271Sbrooks * of the first.  A warning is printed to stderr in the case that the
618140271Sbrooks * first string does not match the third.
619140271Sbrooks *
620140271Sbrooks * This function exists to warn about the bizzare construction
621140271Sbrooks * strncmp(str, "by", 2) which is used to allow people to use a shotcut
622140271Sbrooks * for "bytes".  The problem is that in addition to accepting "by",
623140271Sbrooks * "byt", "byte", and "bytes", it also excepts "by_rabid_dogs" and any
624140271Sbrooks * other string beginning with "by".
625140271Sbrooks *
626140271Sbrooks * This function will be removed in the future through the usual
627140271Sbrooks * deprecation process.
628140271Sbrooks */
629140271Sbrooksstatic int
630140271Sbrooks_substrcmp2(const char *str1, const char* str2, const char* str3)
631140271Sbrooks{
632140271Sbrooks
633140271Sbrooks	if (strncmp(str1, str2, strlen(str2)) != 0)
634140271Sbrooks		return 1;
635140271Sbrooks
636140271Sbrooks	if (strcmp(str1, str3) != 0)
637140271Sbrooks		warnx("DEPRECATED: '%s' matched '%s'",
638140271Sbrooks		    str1, str3);
639140271Sbrooks	return 0;
640140271Sbrooks}
641140271Sbrooks
642140271Sbrooks/*
64398943Sluigi * prints one port, symbolic or numeric
64498943Sluigi */
64598943Sluigistatic void
646117328Sluigiprint_port(int proto, uint16_t port)
64798943Sluigi{
64898943Sluigi
64998943Sluigi	if (proto == IPPROTO_ETHERTYPE) {
650117469Sluigi		char const *s;
65198943Sluigi
65298943Sluigi		if (do_resolv && (s = match_value(ether_types, port)) )
65398943Sluigi			printf("%s", s);
65498943Sluigi		else
65598943Sluigi			printf("0x%04x", port);
65698943Sluigi	} else {
65798943Sluigi		struct servent *se = NULL;
65898943Sluigi		if (do_resolv) {
65998943Sluigi			struct protoent *pe = getprotobynumber(proto);
66098943Sluigi
66198943Sluigi			se = getservbyport(htons(port), pe ? pe->p_name : NULL);
66298943Sluigi		}
66398943Sluigi		if (se)
66498943Sluigi			printf("%s", se->s_name);
66598943Sluigi		else
66698943Sluigi			printf("%d", port);
66798943Sluigi	}
66898943Sluigi}
66998943Sluigi
670117328Sluigistruct _s_x _port_name[] = {
671117328Sluigi	{"dst-port",	O_IP_DSTPORT},
672117328Sluigi	{"src-port",	O_IP_SRCPORT},
673117328Sluigi	{"ipid",	O_IPID},
674117328Sluigi	{"iplen",	O_IPLEN},
675117328Sluigi	{"ipttl",	O_IPTTL},
676117328Sluigi	{"mac-type",	O_MAC_TYPE},
677136075Sgreen	{"tcpdatalen",	O_TCPDATALEN},
678158879Soleg	{"tagged",	O_TAGGED},
679117328Sluigi	{NULL,		0}
680117328Sluigi};
681117328Sluigi
68298943Sluigi/*
683117328Sluigi * Print the values in a list 16-bit items of the types above.
68498943Sluigi * XXX todo: add support for mask.
68598943Sluigi */
68698943Sluigistatic void
687102087Sluigiprint_newports(ipfw_insn_u16 *cmd, int proto, int opcode)
68898943Sluigi{
689117328Sluigi	uint16_t *p = cmd->ports;
69098943Sluigi	int i;
691117469Sluigi	char const *sep;
69298943Sluigi
693116690Sluigi	if (opcode != 0) {
694117328Sluigi		sep = match_value(_port_name, opcode);
695117328Sluigi		if (sep == NULL)
696116690Sluigi			sep = "???";
697116690Sluigi		printf (" %s", sep);
698116690Sluigi	}
699116690Sluigi	sep = " ";
70098943Sluigi	for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
70198943Sluigi		printf(sep);
70298943Sluigi		print_port(proto, p[0]);
70398943Sluigi		if (p[0] != p[1]) {
70498943Sluigi			printf("-");
70598943Sluigi			print_port(proto, p[1]);
70698943Sluigi		}
70798943Sluigi		sep = ",";
70898943Sluigi	}
70998943Sluigi}
71098943Sluigi
71198943Sluigi/*
71298943Sluigi * Like strtol, but also translates service names into port numbers
71398943Sluigi * for some protocols.
71498943Sluigi * In particular:
71598943Sluigi *	proto == -1 disables the protocol check;
71698943Sluigi *	proto == IPPROTO_ETHERTYPE looks up an internal table
71798943Sluigi *	proto == <some value in /etc/protocols> matches the values there.
718101628Sluigi * Returns *end == s in case the parameter is not found.
71998943Sluigi */
72098943Sluigistatic int
72198943Sluigistrtoport(char *s, char **end, int base, int proto)
72298943Sluigi{
723101628Sluigi	char *p, *buf;
724101628Sluigi	char *s1;
72598943Sluigi	int i;
72698943Sluigi
727101628Sluigi	*end = s;		/* default - not found */
728117577Sluigi	if (*s == '\0')
729101628Sluigi		return 0;	/* not found */
730106505Smaxim
73198943Sluigi	if (isdigit(*s))
73298943Sluigi		return strtol(s, end, base);
73398943Sluigi
73498943Sluigi	/*
735101628Sluigi	 * find separator. '\\' escapes the next char.
73698943Sluigi	 */
737101628Sluigi	for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++)
738101628Sluigi		if (*s1 == '\\' && s1[1] != '\0')
739101628Sluigi			s1++;
74098943Sluigi
741187716Sluigi	buf = safe_calloc(s1 - s + 1, 1);
742101628Sluigi
743101628Sluigi	/*
744101628Sluigi	 * copy into a buffer skipping backslashes
745101628Sluigi	 */
746101628Sluigi	for (p = s, i = 0; p != s1 ; p++)
747117577Sluigi		if (*p != '\\')
748101628Sluigi			buf[i++] = *p;
749101628Sluigi	buf[i++] = '\0';
750101628Sluigi
75198943Sluigi	if (proto == IPPROTO_ETHERTYPE) {
752101628Sluigi		i = match_token(ether_types, buf);
753101628Sluigi		free(buf);
754101628Sluigi		if (i != -1) {	/* found */
75598943Sluigi			*end = s1;
75698943Sluigi			return i;
75798943Sluigi		}
75898943Sluigi	} else {
75998943Sluigi		struct protoent *pe = NULL;
76098943Sluigi		struct servent *se;
76198943Sluigi
76298943Sluigi		if (proto != 0)
76398943Sluigi			pe = getprotobynumber(proto);
76498943Sluigi		setservent(1);
765101628Sluigi		se = getservbyname(buf, pe ? pe->p_name : NULL);
766101628Sluigi		free(buf);
76798943Sluigi		if (se != NULL) {
76898943Sluigi			*end = s1;
76998943Sluigi			return ntohs(se->s_port);
77098943Sluigi		}
77198943Sluigi	}
772101628Sluigi	return 0;	/* not found */
77398943Sluigi}
77498943Sluigi
77598943Sluigi/*
776136071Sgreen * Map between current altq queue id numbers and names.
777136071Sgreen */
778136071Sgreenstatic int altq_fetched = 0;
779136071Sgreenstatic TAILQ_HEAD(, pf_altq) altq_entries =
780136071Sgreen	TAILQ_HEAD_INITIALIZER(altq_entries);
781136071Sgreen
782136071Sgreenstatic void
783136071Sgreenaltq_set_enabled(int enabled)
784136071Sgreen{
785136071Sgreen	int pffd;
786136071Sgreen
787136071Sgreen	pffd = open("/dev/pf", O_RDWR);
788136071Sgreen	if (pffd == -1)
789136071Sgreen		err(EX_UNAVAILABLE,
790136071Sgreen		    "altq support opening pf(4) control device");
791136071Sgreen	if (enabled) {
792136071Sgreen		if (ioctl(pffd, DIOCSTARTALTQ) != 0 && errno != EEXIST)
793136071Sgreen			err(EX_UNAVAILABLE, "enabling altq");
794136071Sgreen	} else {
795136071Sgreen		if (ioctl(pffd, DIOCSTOPALTQ) != 0 && errno != ENOENT)
796136071Sgreen			err(EX_UNAVAILABLE, "disabling altq");
797136071Sgreen	}
798136071Sgreen	close(pffd);
799136071Sgreen}
800136071Sgreen
801136071Sgreenstatic void
802187477Sluigialtq_fetch(void)
803136071Sgreen{
804136071Sgreen	struct pfioc_altq pfioc;
805136071Sgreen	struct pf_altq *altq;
806187477Sluigi	int pffd;
807187477Sluigi	unsigned int mnr;
808136071Sgreen
809136071Sgreen	if (altq_fetched)
810136071Sgreen		return;
811136071Sgreen	altq_fetched = 1;
812136071Sgreen	pffd = open("/dev/pf", O_RDONLY);
813136071Sgreen	if (pffd == -1) {
814136071Sgreen		warn("altq support opening pf(4) control device");
815136071Sgreen		return;
816136071Sgreen	}
817136071Sgreen	bzero(&pfioc, sizeof(pfioc));
818136071Sgreen	if (ioctl(pffd, DIOCGETALTQS, &pfioc) != 0) {
819136071Sgreen		warn("altq support getting queue list");
820136071Sgreen		close(pffd);
821136071Sgreen		return;
822136071Sgreen	}
823136071Sgreen	mnr = pfioc.nr;
824136071Sgreen	for (pfioc.nr = 0; pfioc.nr < mnr; pfioc.nr++) {
825136071Sgreen		if (ioctl(pffd, DIOCGETALTQ, &pfioc) != 0) {
826136071Sgreen			if (errno == EBUSY)
827136071Sgreen				break;
828136071Sgreen			warn("altq support getting queue list");
829136071Sgreen			close(pffd);
830136071Sgreen			return;
831136071Sgreen		}
832136071Sgreen		if (pfioc.altq.qid == 0)
833136071Sgreen			continue;
834187716Sluigi		altq = safe_calloc(1, sizeof(*altq));
835136071Sgreen		*altq = pfioc.altq;
836136071Sgreen		TAILQ_INSERT_TAIL(&altq_entries, altq, entries);
837136071Sgreen	}
838136071Sgreen	close(pffd);
839136071Sgreen}
840136071Sgreen
841136071Sgreenstatic u_int32_t
842136071Sgreenaltq_name_to_qid(const char *name)
843136071Sgreen{
844136071Sgreen	struct pf_altq *altq;
845136071Sgreen
846136071Sgreen	altq_fetch();
847136071Sgreen	TAILQ_FOREACH(altq, &altq_entries, entries)
848136071Sgreen		if (strcmp(name, altq->qname) == 0)
849136071Sgreen			break;
850136071Sgreen	if (altq == NULL)
851136071Sgreen		errx(EX_DATAERR, "altq has no queue named `%s'", name);
852136071Sgreen	return altq->qid;
853136071Sgreen}
854136071Sgreen
855136071Sgreenstatic const char *
856136071Sgreenaltq_qid_to_name(u_int32_t qid)
857136071Sgreen{
858136071Sgreen	struct pf_altq *altq;
859136071Sgreen
860136071Sgreen	altq_fetch();
861136071Sgreen	TAILQ_FOREACH(altq, &altq_entries, entries)
862136071Sgreen		if (qid == altq->qid)
863136071Sgreen			break;
864136071Sgreen	if (altq == NULL)
865136071Sgreen		return NULL;
866136071Sgreen	return altq->qname;
867136071Sgreen}
868136071Sgreen
869136071Sgreenstatic void
870136071Sgreenfill_altq_qid(u_int32_t *qid, const char *av)
871136071Sgreen{
872136071Sgreen	*qid = altq_name_to_qid(av);
873136071Sgreen}
874136071Sgreen
875136071Sgreen/*
876117328Sluigi * Fill the body of the command with the list of port ranges.
87798943Sluigi */
87898943Sluigistatic int
87998943Sluigifill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
88098943Sluigi{
881117328Sluigi	uint16_t a, b, *p = cmd->ports;
88298943Sluigi	int i = 0;
883102087Sluigi	char *s = av;
88498943Sluigi
885102087Sluigi	while (*s) {
88698943Sluigi		a = strtoport(av, &s, 0, proto);
887159636Soleg		if (s == av) 			/* empty or invalid argument */
888159636Soleg			return (0);
889159636Soleg
890159636Soleg		switch (*s) {
891159636Soleg		case '-':			/* a range */
892159636Soleg			av = s + 1;
89398943Sluigi			b = strtoport(av, &s, 0, proto);
894159636Soleg			/* Reject expressions like '1-abc' or '1-2-3'. */
895159636Soleg			if (s == av || (*s != ',' && *s != '\0'))
896159636Soleg				return (0);
89798943Sluigi			p[0] = a;
89898943Sluigi			p[1] = b;
899159636Soleg			break;
900159636Soleg		case ',':			/* comma separated list */
901159636Soleg		case '\0':
90298943Sluigi			p[0] = p[1] = a;
903159636Soleg			break;
904159636Soleg		default:
905159636Soleg			warnx("port list: invalid separator <%c> in <%s>",
906101978Sluigi				*s, av);
907159636Soleg			return (0);
908159636Soleg		}
909159636Soleg
910102087Sluigi		i++;
911102087Sluigi		p += 2;
912159636Soleg		av = s + 1;
91398943Sluigi	}
91498943Sluigi	if (i > 0) {
915159636Soleg		if (i + 1 > F_LEN_MASK)
916102087Sluigi			errx(EX_DATAERR, "too many ports/ranges\n");
917159636Soleg		cmd->o.len |= i + 1;	/* leave F_NOT and F_OR untouched */
91898943Sluigi	}
919159636Soleg	return (i);
92098943Sluigi}
92198943Sluigi
92298943Sluigistatic struct _s_x icmpcodes[] = {
92398943Sluigi      { "net",			ICMP_UNREACH_NET },
92498943Sluigi      { "host",			ICMP_UNREACH_HOST },
92598943Sluigi      { "protocol",		ICMP_UNREACH_PROTOCOL },
92698943Sluigi      { "port",			ICMP_UNREACH_PORT },
92798943Sluigi      { "needfrag",		ICMP_UNREACH_NEEDFRAG },
92898943Sluigi      { "srcfail",		ICMP_UNREACH_SRCFAIL },
92998943Sluigi      { "net-unknown",		ICMP_UNREACH_NET_UNKNOWN },
93098943Sluigi      { "host-unknown",		ICMP_UNREACH_HOST_UNKNOWN },
93198943Sluigi      { "isolated",		ICMP_UNREACH_ISOLATED },
93298943Sluigi      { "net-prohib",		ICMP_UNREACH_NET_PROHIB },
93398943Sluigi      { "host-prohib",		ICMP_UNREACH_HOST_PROHIB },
93498943Sluigi      { "tosnet",		ICMP_UNREACH_TOSNET },
93598943Sluigi      { "toshost",		ICMP_UNREACH_TOSHOST },
93698943Sluigi      { "filter-prohib",	ICMP_UNREACH_FILTER_PROHIB },
93798943Sluigi      { "host-precedence",	ICMP_UNREACH_HOST_PRECEDENCE },
93898943Sluigi      { "precedence-cutoff",	ICMP_UNREACH_PRECEDENCE_CUTOFF },
93998943Sluigi      { NULL, 0 }
94098943Sluigi};
94198943Sluigi
94298943Sluigistatic void
94398943Sluigifill_reject_code(u_short *codep, char *str)
94498943Sluigi{
94598943Sluigi	int val;
94698943Sluigi	char *s;
94798943Sluigi
94898943Sluigi	val = strtoul(str, &s, 0);
94998943Sluigi	if (s == str || *s != '\0' || val >= 0x100)
95098943Sluigi		val = match_token(icmpcodes, str);
951102087Sluigi	if (val < 0)
95298943Sluigi		errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
95398943Sluigi	*codep = val;
95498943Sluigi	return;
95598943Sluigi}
95698943Sluigi
95798943Sluigistatic void
958117328Sluigiprint_reject_code(uint16_t code)
95998943Sluigi{
960117469Sluigi	char const *s = match_value(icmpcodes, code);
96198943Sluigi
96298943Sluigi	if (s != NULL)
96399475Sluigi		printf("unreach %s", s);
96498943Sluigi	else
96599475Sluigi		printf("unreach %u", code);
96698943Sluigi}
96798943Sluigi
968149020Sbzstatic struct _s_x icmp6codes[] = {
969149020Sbz      { "no-route",		ICMP6_DST_UNREACH_NOROUTE },
970149020Sbz      { "admin-prohib",		ICMP6_DST_UNREACH_ADMIN },
971149020Sbz      { "address",		ICMP6_DST_UNREACH_ADDR },
972149020Sbz      { "port",			ICMP6_DST_UNREACH_NOPORT },
973149020Sbz      { NULL, 0 }
974149020Sbz};
975149020Sbz
976149020Sbzstatic void
977149020Sbzfill_unreach6_code(u_short *codep, char *str)
978149020Sbz{
979149020Sbz	int val;
980149020Sbz	char *s;
981149020Sbz
982149020Sbz	val = strtoul(str, &s, 0);
983149020Sbz	if (s == str || *s != '\0' || val >= 0x100)
984149020Sbz		val = match_token(icmp6codes, str);
985149020Sbz	if (val < 0)
986149020Sbz		errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str);
987149020Sbz	*codep = val;
988149020Sbz	return;
989149020Sbz}
990149020Sbz
991149020Sbzstatic void
992149020Sbzprint_unreach6_code(uint16_t code)
993149020Sbz{
994149020Sbz	char const *s = match_value(icmp6codes, code);
995149020Sbz
996149020Sbz	if (s != NULL)
997149020Sbz		printf("unreach6 %s", s);
998149020Sbz	else
999149020Sbz		printf("unreach6 %u", code);
1000149020Sbz}
1001149020Sbz
100298943Sluigi/*
100398943Sluigi * Returns the number of bits set (from left) in a contiguous bitmask,
100498943Sluigi * or -1 if the mask is not contiguous.
100598943Sluigi * XXX this needs a proper fix.
100698943Sluigi * This effectively works on masks in big-endian (network) format.
100798943Sluigi * when compiled on little endian architectures.
100898943Sluigi *
100998943Sluigi * First bit is bit 7 of the first byte -- note, for MAC addresses,
101098943Sluigi * the first bit on the wire is bit 0 of the first byte.
101198943Sluigi * len is the max length in bits.
101298943Sluigi */
101398943Sluigistatic int
1014117577Sluigicontigmask(uint8_t *p, int len)
101598943Sluigi{
101698943Sluigi	int i, n;
1017117577Sluigi
101898943Sluigi	for (i=0; i<len ; i++)
101998943Sluigi		if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
102098943Sluigi			break;
102198943Sluigi	for (n=i+1; n < len; n++)
102298943Sluigi		if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
102398943Sluigi			return -1; /* mask not contiguous */
102498943Sluigi	return i;
102598943Sluigi}
102698943Sluigi
102798943Sluigi/*
102898943Sluigi * print flags set/clear in the two bitmasks passed as parameters.
102998943Sluigi * There is a specialized check for f_tcpflags.
103098943Sluigi */
103198943Sluigistatic void
1032117469Sluigiprint_flags(char const *name, ipfw_insn *cmd, struct _s_x *list)
103398943Sluigi{
1034117469Sluigi	char const *comma = "";
103598943Sluigi	int i;
1036117577Sluigi	uint8_t set = cmd->arg1 & 0xff;
1037117577Sluigi	uint8_t clear = (cmd->arg1 >> 8) & 0xff;
103898943Sluigi
103998943Sluigi	if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) {
104098943Sluigi		printf(" setup");
104198943Sluigi		return;
104298943Sluigi	}
104398943Sluigi
104498943Sluigi	printf(" %s ", name);
104598943Sluigi	for (i=0; list[i].x != 0; i++) {
104698943Sluigi		if (set & list[i].x) {
104798943Sluigi			set &= ~list[i].x;
104898943Sluigi			printf("%s%s", comma, list[i].s);
104998943Sluigi			comma = ",";
105098943Sluigi		}
105198943Sluigi		if (clear & list[i].x) {
105298943Sluigi			clear &= ~list[i].x;
105398943Sluigi			printf("%s!%s", comma, list[i].s);
105498943Sluigi			comma = ",";
105598943Sluigi		}
105698943Sluigi	}
105798943Sluigi}
105898943Sluigi
105998943Sluigi/*
106098943Sluigi * Print the ip address contained in a command.
106198943Sluigi */
106298943Sluigistatic void
1063117469Sluigiprint_ip(ipfw_insn_ip *cmd, char const *s)
106498943Sluigi{
106598943Sluigi	struct hostent *he = NULL;
1066117328Sluigi	int len = F_LEN((ipfw_insn *)cmd);
1067117328Sluigi	uint32_t *a = ((ipfw_insn_u32 *)cmd)->d;
106898943Sluigi
1069102087Sluigi	printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
107098943Sluigi
107198943Sluigi	if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) {
107298943Sluigi		printf("me");
107398943Sluigi		return;
107498943Sluigi	}
1075130281Sru	if (cmd->o.opcode == O_IP_SRC_LOOKUP ||
1076130281Sru	    cmd->o.opcode == O_IP_DST_LOOKUP) {
1077130281Sru		printf("table(%u", ((ipfw_insn *)cmd)->arg1);
1078130281Sru		if (len == F_INSN_SIZE(ipfw_insn_u32))
1079130281Sru			printf(",%u", *a);
1080130281Sru		printf(")");
1081130281Sru		return;
1082130281Sru	}
108398943Sluigi	if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) {
1084117328Sluigi		uint32_t x, *map = (uint32_t *)&(cmd->mask);
1085116716Sluigi		int i, j;
108698943Sluigi		char comma = '{';
108798943Sluigi
108898943Sluigi		x = cmd->o.arg1 - 1;
108998943Sluigi		x = htonl( ~x );
109098943Sluigi		cmd->addr.s_addr = htonl(cmd->addr.s_addr);
109198943Sluigi		printf("%s/%d", inet_ntoa(cmd->addr),
1092117577Sluigi			contigmask((uint8_t *)&x, 32));
109398943Sluigi		x = cmd->addr.s_addr = htonl(cmd->addr.s_addr);
109498943Sluigi		x &= 0xff; /* base */
1095116716Sluigi		/*
1096116716Sluigi		 * Print bits and ranges.
1097116716Sluigi		 * Locate first bit set (i), then locate first bit unset (j).
1098116716Sluigi		 * If we have 3+ consecutive bits set, then print them as a
1099116716Sluigi		 * range, otherwise only print the initial bit and rescan.
1100116716Sluigi		 */
110198943Sluigi		for (i=0; i < cmd->o.arg1; i++)
1102117328Sluigi			if (map[i/32] & (1<<(i & 31))) {
1103116716Sluigi				for (j=i+1; j < cmd->o.arg1; j++)
1104117328Sluigi					if (!(map[ j/32] & (1<<(j & 31))))
1105116716Sluigi						break;
110698943Sluigi				printf("%c%d", comma, i+x);
1107116716Sluigi				if (j>i+2) { /* range has at least 3 elements */
1108116716Sluigi					printf("-%d", j-1+x);
1109116716Sluigi					i = j-1;
1110116716Sluigi				}
111198943Sluigi				comma = ',';
111298943Sluigi			}
111398943Sluigi		printf("}");
111498943Sluigi		return;
111598943Sluigi	}
1116117328Sluigi	/*
1117117328Sluigi	 * len == 2 indicates a single IP, whereas lists of 1 or more
1118117328Sluigi	 * addr/mask pairs have len = (2n+1). We convert len to n so we
1119117328Sluigi	 * use that to count the number of entries.
1120117328Sluigi	 */
1121117328Sluigi    for (len = len / 2; len > 0; len--, a += 2) {
1122117328Sluigi	int mb =	/* mask length */
1123117328Sluigi	    (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ?
1124117577Sluigi		32 : contigmask((uint8_t *)&(a[1]), 32);
112598943Sluigi	if (mb == 32 && do_resolv)
1126117328Sluigi		he = gethostbyaddr((char *)&(a[0]), sizeof(u_long), AF_INET);
112798943Sluigi	if (he != NULL)		/* resolved to name */
112898943Sluigi		printf("%s", he->h_name);
112998943Sluigi	else if (mb == 0)	/* any */
113098943Sluigi		printf("any");
113198943Sluigi	else {		/* numeric IP followed by some kind of mask */
1132117328Sluigi		printf("%s", inet_ntoa( *((struct in_addr *)&a[0]) ) );
113398943Sluigi		if (mb < 0)
1134117328Sluigi			printf(":%s", inet_ntoa( *((struct in_addr *)&a[1]) ) );
113598943Sluigi		else if (mb < 32)
113698943Sluigi			printf("/%d", mb);
113798943Sluigi	}
1138117328Sluigi	if (len > 1)
1139117328Sluigi		printf(",");
1140117328Sluigi    }
114198943Sluigi}
114298943Sluigi
114398943Sluigi/*
114498943Sluigi * prints a MAC address/mask pair
114598943Sluigi */
114698943Sluigistatic void
1147117577Sluigiprint_mac(uint8_t *addr, uint8_t *mask)
114898943Sluigi{
114998943Sluigi	int l = contigmask(mask, 48);
115098943Sluigi
115198943Sluigi	if (l == 0)
115298943Sluigi		printf(" any");
115398943Sluigi	else {
115498943Sluigi		printf(" %02x:%02x:%02x:%02x:%02x:%02x",
115598943Sluigi		    addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
115698943Sluigi		if (l == -1)
115798943Sluigi			printf("&%02x:%02x:%02x:%02x:%02x:%02x",
115898943Sluigi			    mask[0], mask[1], mask[2],
115998943Sluigi			    mask[3], mask[4], mask[5]);
116098943Sluigi		else if (l < 48)
116198943Sluigi			printf("/%d", l);
116298943Sluigi	}
116398943Sluigi}
116498943Sluigi
116599475Sluigistatic void
116699475Sluigifill_icmptypes(ipfw_insn_u32 *cmd, char *av)
116799475Sluigi{
1168117328Sluigi	uint8_t type;
116998943Sluigi
117099475Sluigi	cmd->d[0] = 0;
117199475Sluigi	while (*av) {
117299475Sluigi		if (*av == ',')
117399475Sluigi			av++;
117499475Sluigi
117599475Sluigi		type = strtoul(av, &av, 0);
117699475Sluigi
117799475Sluigi		if (*av != ',' && *av != '\0')
117899475Sluigi			errx(EX_DATAERR, "invalid ICMP type");
117999475Sluigi
118099475Sluigi		if (type > 31)
118199475Sluigi			errx(EX_DATAERR, "ICMP type out of range");
118299475Sluigi
118399475Sluigi		cmd->d[0] |= 1 << type;
118499475Sluigi	}
118599475Sluigi	cmd->o.opcode = O_ICMPTYPE;
118699475Sluigi	cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
118799475Sluigi}
118899475Sluigi
118999475Sluigistatic void
119099475Sluigiprint_icmptypes(ipfw_insn_u32 *cmd)
119199475Sluigi{
119299475Sluigi	int i;
119399475Sluigi	char sep= ' ';
119499475Sluigi
119599475Sluigi	printf(" icmptypes");
119699475Sluigi	for (i = 0; i < 32; i++) {
119799475Sluigi		if ( (cmd->d[0] & (1 << (i))) == 0)
119899475Sluigi			continue;
119999475Sluigi		printf("%c%d", sep, i);
120099475Sluigi		sep = ',';
120199475Sluigi	}
120299475Sluigi}
120399475Sluigi
1204145246Sbrooks/*
1205145246Sbrooks * Print the ip address contained in a command.
1206145246Sbrooks */
1207145246Sbrooksstatic void
1208145246Sbrooksprint_ip6(ipfw_insn_ip6 *cmd, char const *s)
1209145246Sbrooks{
1210145246Sbrooks       struct hostent *he = NULL;
1211145246Sbrooks       int len = F_LEN((ipfw_insn *) cmd) - 1;
1212145246Sbrooks       struct in6_addr *a = &(cmd->addr6);
1213145246Sbrooks       char trad[255];
1214145246Sbrooks
1215145246Sbrooks       printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
1216145246Sbrooks
1217145246Sbrooks       if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) {
1218145246Sbrooks               printf("me6");
1219145246Sbrooks               return;
1220145246Sbrooks       }
1221145246Sbrooks       if (cmd->o.opcode == O_IP6) {
1222152923Sume               printf(" ip6");
1223145246Sbrooks               return;
1224145246Sbrooks       }
1225145246Sbrooks
1226145246Sbrooks       /*
1227145246Sbrooks        * len == 4 indicates a single IP, whereas lists of 1 or more
1228145246Sbrooks        * addr/mask pairs have len = (2n+1). We convert len to n so we
1229145246Sbrooks        * use that to count the number of entries.
1230145246Sbrooks        */
1231145246Sbrooks
1232145246Sbrooks       for (len = len / 4; len > 0; len -= 2, a += 2) {
1233145246Sbrooks           int mb =        /* mask length */
1234145246Sbrooks               (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ?
1235145246Sbrooks               128 : contigmask((uint8_t *)&(a[1]), 128);
1236145246Sbrooks
1237145246Sbrooks           if (mb == 128 && do_resolv)
1238145246Sbrooks               he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6);
1239145246Sbrooks           if (he != NULL)             /* resolved to name */
1240145246Sbrooks               printf("%s", he->h_name);
1241145246Sbrooks           else if (mb == 0)           /* any */
1242145246Sbrooks               printf("any");
1243145246Sbrooks           else {          /* numeric IP followed by some kind of mask */
1244145246Sbrooks               if (inet_ntop(AF_INET6,  a, trad, sizeof( trad ) ) == NULL)
1245145246Sbrooks                   printf("Error ntop in print_ip6\n");
1246145246Sbrooks               printf("%s",  trad );
1247145246Sbrooks               if (mb < 0)     /* XXX not really legal... */
1248145246Sbrooks                   printf(":%s",
1249145246Sbrooks                       inet_ntop(AF_INET6, &a[1], trad, sizeof(trad)));
1250145246Sbrooks               else if (mb < 128)
1251145246Sbrooks                   printf("/%d", mb);
1252145246Sbrooks           }
1253145246Sbrooks           if (len > 2)
1254145246Sbrooks               printf(",");
1255145246Sbrooks       }
1256145246Sbrooks}
1257145246Sbrooks
1258145246Sbrooksstatic void
1259145246Sbrooksfill_icmp6types(ipfw_insn_icmp6 *cmd, char *av)
1260145246Sbrooks{
1261145246Sbrooks       uint8_t type;
1262145246Sbrooks
1263162344Sjhay       bzero(cmd, sizeof(*cmd));
1264145246Sbrooks       while (*av) {
1265145246Sbrooks           if (*av == ',')
1266145246Sbrooks               av++;
1267145246Sbrooks           type = strtoul(av, &av, 0);
1268145246Sbrooks           if (*av != ',' && *av != '\0')
1269145246Sbrooks               errx(EX_DATAERR, "invalid ICMP6 type");
1270145246Sbrooks	   /*
1271145246Sbrooks	    * XXX: shouldn't this be 0xFF?  I can't see any reason why
1272145246Sbrooks	    * we shouldn't be able to filter all possiable values
1273145246Sbrooks	    * regardless of the ability of the rest of the kernel to do
1274145246Sbrooks	    * anything useful with them.
1275145246Sbrooks	    */
1276145246Sbrooks           if (type > ICMP6_MAXTYPE)
1277145246Sbrooks               errx(EX_DATAERR, "ICMP6 type out of range");
1278145246Sbrooks           cmd->d[type / 32] |= ( 1 << (type % 32));
1279145246Sbrooks       }
1280145246Sbrooks       cmd->o.opcode = O_ICMP6TYPE;
1281145246Sbrooks       cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6);
1282145246Sbrooks}
1283145246Sbrooks
1284145246Sbrooks
1285145246Sbrooksstatic void
1286145246Sbrooksprint_icmp6types(ipfw_insn_u32 *cmd)
1287145246Sbrooks{
1288145246Sbrooks       int i, j;
1289145246Sbrooks       char sep= ' ';
1290145246Sbrooks
1291152923Sume       printf(" ip6 icmp6types");
1292145246Sbrooks       for (i = 0; i < 7; i++)
1293145246Sbrooks               for (j=0; j < 32; ++j) {
1294145246Sbrooks                       if ( (cmd->d[i] & (1 << (j))) == 0)
1295145246Sbrooks                               continue;
1296145246Sbrooks                       printf("%c%d", sep, (i*32 + j));
1297145246Sbrooks                       sep = ',';
1298145246Sbrooks               }
1299145246Sbrooks}
1300145246Sbrooks
1301145246Sbrooksstatic void
1302145246Sbrooksprint_flow6id( ipfw_insn_u32 *cmd)
1303145246Sbrooks{
1304145246Sbrooks       uint16_t i, limit = cmd->o.arg1;
1305145246Sbrooks       char sep = ',';
1306145246Sbrooks
1307145246Sbrooks       printf(" flow-id ");
1308145246Sbrooks       for( i=0; i < limit; ++i) {
1309145246Sbrooks               if (i == limit - 1)
1310145246Sbrooks                       sep = ' ';
1311145246Sbrooks               printf("%d%c", cmd->d[i], sep);
1312145246Sbrooks       }
1313145246Sbrooks}
1314145246Sbrooks
1315145246Sbrooks/* structure and define for the extension header in ipv6 */
1316145246Sbrooksstatic struct _s_x ext6hdrcodes[] = {
1317145246Sbrooks       { "frag",       EXT_FRAGMENT },
1318145246Sbrooks       { "hopopt",     EXT_HOPOPTS },
1319145246Sbrooks       { "route",      EXT_ROUTING },
1320149020Sbz       { "dstopt",     EXT_DSTOPTS },
1321145246Sbrooks       { "ah",         EXT_AH },
1322145246Sbrooks       { "esp",        EXT_ESP },
1323169245Sbz       { "rthdr0",     EXT_RTHDR0 },
1324169245Sbz       { "rthdr2",     EXT_RTHDR2 },
1325145246Sbrooks       { NULL,         0 }
1326145246Sbrooks};
1327145246Sbrooks
1328145246Sbrooks/* fills command for the extension header filtering */
1329187477Sluigistatic int
1330145246Sbrooksfill_ext6hdr( ipfw_insn *cmd, char *av)
1331145246Sbrooks{
1332145246Sbrooks       int tok;
1333145246Sbrooks       char *s = av;
1334145246Sbrooks
1335145246Sbrooks       cmd->arg1 = 0;
1336145246Sbrooks
1337145246Sbrooks       while(s) {
1338145246Sbrooks           av = strsep( &s, ",") ;
1339145246Sbrooks           tok = match_token(ext6hdrcodes, av);
1340145246Sbrooks           switch (tok) {
1341145246Sbrooks           case EXT_FRAGMENT:
1342145246Sbrooks               cmd->arg1 |= EXT_FRAGMENT;
1343145246Sbrooks               break;
1344145246Sbrooks
1345145246Sbrooks           case EXT_HOPOPTS:
1346145246Sbrooks               cmd->arg1 |= EXT_HOPOPTS;
1347145246Sbrooks               break;
1348145246Sbrooks
1349145246Sbrooks           case EXT_ROUTING:
1350145246Sbrooks               cmd->arg1 |= EXT_ROUTING;
1351145246Sbrooks               break;
1352145246Sbrooks
1353149020Sbz           case EXT_DSTOPTS:
1354149020Sbz               cmd->arg1 |= EXT_DSTOPTS;
1355149020Sbz               break;
1356149020Sbz
1357145246Sbrooks           case EXT_AH:
1358145246Sbrooks               cmd->arg1 |= EXT_AH;
1359145246Sbrooks               break;
1360145246Sbrooks
1361145246Sbrooks           case EXT_ESP:
1362145246Sbrooks               cmd->arg1 |= EXT_ESP;
1363145246Sbrooks               break;
1364145246Sbrooks
1365169245Sbz           case EXT_RTHDR0:
1366169245Sbz               cmd->arg1 |= EXT_RTHDR0;
1367169245Sbz               break;
1368169245Sbz
1369169245Sbz           case EXT_RTHDR2:
1370169245Sbz               cmd->arg1 |= EXT_RTHDR2;
1371169245Sbz               break;
1372169245Sbz
1373145246Sbrooks           default:
1374145246Sbrooks               errx( EX_DATAERR, "invalid option for ipv6 exten header" );
1375145246Sbrooks               break;
1376145246Sbrooks           }
1377145246Sbrooks       }
1378145246Sbrooks       if (cmd->arg1 == 0 )
1379145246Sbrooks           return 0;
1380145246Sbrooks       cmd->opcode = O_EXT_HDR;
1381145246Sbrooks       cmd->len |= F_INSN_SIZE( ipfw_insn );
1382145246Sbrooks       return 1;
1383145246Sbrooks}
1384145246Sbrooks
1385187477Sluigistatic void
1386145246Sbrooksprint_ext6hdr( ipfw_insn *cmd )
1387145246Sbrooks{
1388145246Sbrooks       char sep = ' ';
1389145246Sbrooks
1390145246Sbrooks       printf(" extension header:");
1391145246Sbrooks       if (cmd->arg1 & EXT_FRAGMENT ) {
1392145246Sbrooks           printf("%cfragmentation", sep);
1393145246Sbrooks           sep = ',';
1394145246Sbrooks       }
1395145246Sbrooks       if (cmd->arg1 & EXT_HOPOPTS ) {
1396145246Sbrooks           printf("%chop options", sep);
1397145246Sbrooks           sep = ',';
1398145246Sbrooks       }
1399145246Sbrooks       if (cmd->arg1 & EXT_ROUTING ) {
1400145246Sbrooks           printf("%crouting options", sep);
1401145246Sbrooks           sep = ',';
1402145246Sbrooks       }
1403169245Sbz       if (cmd->arg1 & EXT_RTHDR0 ) {
1404169245Sbz           printf("%crthdr0", sep);
1405169245Sbz           sep = ',';
1406169245Sbz       }
1407169245Sbz       if (cmd->arg1 & EXT_RTHDR2 ) {
1408169245Sbz           printf("%crthdr2", sep);
1409169245Sbz           sep = ',';
1410169245Sbz       }
1411149020Sbz       if (cmd->arg1 & EXT_DSTOPTS ) {
1412149020Sbz           printf("%cdestination options", sep);
1413149020Sbz           sep = ',';
1414149020Sbz       }
1415145246Sbrooks       if (cmd->arg1 & EXT_AH ) {
1416145246Sbrooks           printf("%cauthentication header", sep);
1417145246Sbrooks           sep = ',';
1418145246Sbrooks       }
1419145246Sbrooks       if (cmd->arg1 & EXT_ESP ) {
1420145246Sbrooks           printf("%cencapsulated security payload", sep);
1421145246Sbrooks       }
1422145246Sbrooks}
1423145246Sbrooks
142498943Sluigi/*
142598943Sluigi * show_ipfw() prints the body of an ipfw rule.
142698943Sluigi * Because the standard rule has at least proto src_ip dst_ip, we use
142798943Sluigi * a helper function to produce these entries if not provided explicitly.
1428102087Sluigi * The first argument is the list of fields we have, the second is
1429102087Sluigi * the list of fields we want to be printed.
1430101978Sluigi *
1431102087Sluigi * Special cases if we have provided a MAC header:
1432102087Sluigi *   + if the rule does not contain IP addresses/ports, do not print them;
1433102087Sluigi *   + if the rule does not contain an IP proto, print "all" instead of "ip";
1434102087Sluigi *
1435102087Sluigi * Once we have 'have_options', IP header fields are printed as options.
143698943Sluigi */
1437101978Sluigi#define	HAVE_PROTO	0x0001
1438101978Sluigi#define	HAVE_SRCIP	0x0002
1439101978Sluigi#define	HAVE_DSTIP	0x0004
1440169139Smaxim#define	HAVE_PROTO4	0x0008
1441169139Smaxim#define	HAVE_PROTO6	0x0010
1442102087Sluigi#define	HAVE_OPTIONS	0x8000
144398943Sluigi
1444101978Sluigi#define	HAVE_IP		(HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP)
144598943Sluigistatic void
1446187477Sluigishow_prerequisites(int *flags, int want, int cmd __unused)
144798943Sluigi{
1448123495Sluigi	if (comment_only)
1449123495Sluigi		return;
1450102087Sluigi	if ( (*flags & HAVE_IP) == HAVE_IP)
1451102087Sluigi		*flags |= HAVE_OPTIONS;
1452102087Sluigi
1453102087Sluigi	if ( !(*flags & HAVE_OPTIONS)) {
1454187477Sluigi		if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO)) {
1455146894Smlaier			if ( (*flags & HAVE_PROTO4))
1456146894Smlaier				printf(" ip4");
1457146894Smlaier			else if ( (*flags & HAVE_PROTO6))
1458146894Smlaier				printf(" ip6");
1459146894Smlaier			else
1460146894Smlaier				printf(" ip");
1461187477Sluigi		}
1462102087Sluigi		if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
1463102087Sluigi			printf(" from any");
1464102087Sluigi		if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP))
1465102087Sluigi			printf(" to any");
1466102087Sluigi	}
146798943Sluigi	*flags |= want;
146898943Sluigi}
146998943Sluigi
147098943Sluigistatic void
1471112189Smaximshow_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
147298943Sluigi{
1473107291Skeramida	static int twidth = 0;
147498943Sluigi	int l;
1475158879Soleg	ipfw_insn *cmd, *tagptr = NULL;
1476187477Sluigi	const char *comment = NULL;	/* ptr to comment if we have one */
147798943Sluigi	int proto = 0;		/* default */
147898943Sluigi	int flags = 0;	/* prerequisites */
147998943Sluigi	ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */
1480136071Sgreen	ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */
148198943Sluigi	int or_block = 0;	/* we are in an or block */
1482117328Sluigi	uint32_t set_disable;
148398943Sluigi
1484115793Sticso	bcopy(&rule->next_rule, &set_disable, sizeof(set_disable));
1485101628Sluigi
1486101628Sluigi	if (set_disable & (1 << rule->set)) { /* disabled */
1487101628Sluigi		if (!show_sets)
1488101628Sluigi			return;
1489101628Sluigi		else
1490101628Sluigi			printf("# DISABLED ");
1491101628Sluigi	}
149298943Sluigi	printf("%05u ", rule->rulenum);
149398943Sluigi
1494117469Sluigi	if (pcwidth>0 || bcwidth>0)
1495115793Sticso		printf("%*llu %*llu ", pcwidth, align_uint64(&rule->pcnt),
1496115793Sticso		    bcwidth, align_uint64(&rule->bcnt));
149798943Sluigi
1498117472Sluigi	if (do_time == 2)
1499117472Sluigi		printf("%10u ", rule->timestamp);
1500117472Sluigi	else if (do_time == 1) {
1501107291Skeramida		char timestr[30];
1502107291Skeramida		time_t t = (time_t)0;
1503107291Skeramida
1504107291Skeramida		if (twidth == 0) {
1505107291Skeramida			strcpy(timestr, ctime(&t));
1506107291Skeramida			*strchr(timestr, '\n') = '\0';
1507107291Skeramida			twidth = strlen(timestr);
1508107291Skeramida		}
150998943Sluigi		if (rule->timestamp) {
1510107291Skeramida			t = _long_to_time(rule->timestamp);
151198943Sluigi
151298943Sluigi			strcpy(timestr, ctime(&t));
151398943Sluigi			*strchr(timestr, '\n') = '\0';
151498943Sluigi			printf("%s ", timestr);
151598943Sluigi		} else {
1516107291Skeramida			printf("%*s", twidth, " ");
151798943Sluigi		}
151898943Sluigi	}
151998943Sluigi
1520101628Sluigi	if (show_sets)
1521101628Sluigi		printf("set %d ", rule->set);
1522101628Sluigi
152398943Sluigi	/*
1524107289Sluigi	 * print the optional "match probability"
1525107289Sluigi	 */
1526107289Sluigi	if (rule->cmd_len > 0) {
1527107289Sluigi		cmd = rule->cmd ;
1528107289Sluigi		if (cmd->opcode == O_PROB) {
1529107289Sluigi			ipfw_insn_u32 *p = (ipfw_insn_u32 *)cmd;
1530107289Sluigi			double d = 1.0 * p->d[0];
1531107289Sluigi
1532107289Sluigi			d = (d / 0x7fffffff);
1533107289Sluigi			printf("prob %f ", d);
1534107289Sluigi		}
1535107289Sluigi	}
1536107289Sluigi
1537107289Sluigi	/*
153898943Sluigi	 * first print actions
153998943Sluigi	 */
154098943Sluigi        for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule);
154198943Sluigi			l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
154298943Sluigi		switch(cmd->opcode) {
154398943Sluigi		case O_CHECK_STATE:
154498943Sluigi			printf("check-state");
1545102087Sluigi			flags = HAVE_IP; /* avoid printing anything else */
154698943Sluigi			break;
154798943Sluigi
154898943Sluigi		case O_ACCEPT:
154998943Sluigi			printf("allow");
155098943Sluigi			break;
155198943Sluigi
155298943Sluigi		case O_COUNT:
155398943Sluigi			printf("count");
155498943Sluigi			break;
155598943Sluigi
155698943Sluigi		case O_DENY:
155798943Sluigi			printf("deny");
155898943Sluigi			break;
155998943Sluigi
156099475Sluigi		case O_REJECT:
156199475Sluigi			if (cmd->arg1 == ICMP_REJECT_RST)
156299475Sluigi				printf("reset");
156399475Sluigi			else if (cmd->arg1 == ICMP_UNREACH_HOST)
156499475Sluigi				printf("reject");
156599475Sluigi			else
156699475Sluigi				print_reject_code(cmd->arg1);
156799475Sluigi			break;
156899475Sluigi
1569149020Sbz		case O_UNREACH6:
1570149020Sbz			if (cmd->arg1 == ICMP6_UNREACH_RST)
1571149020Sbz				printf("reset6");
1572149020Sbz			else
1573149020Sbz				print_unreach6_code(cmd->arg1);
1574149020Sbz			break;
1575149020Sbz
1576159636Soleg		case O_SKIPTO:
1577159636Soleg			PRINT_UINT_ARG("skipto ", cmd->arg1);
157898943Sluigi			break;
157998943Sluigi
158098943Sluigi		case O_PIPE:
1581159636Soleg			PRINT_UINT_ARG("pipe ", cmd->arg1);
1582159636Soleg			break;
1583159636Soleg
158498943Sluigi		case O_QUEUE:
1585159636Soleg			PRINT_UINT_ARG("queue ", cmd->arg1);
1586159636Soleg			break;
1587159636Soleg
158898943Sluigi		case O_DIVERT:
1589159636Soleg			PRINT_UINT_ARG("divert ", cmd->arg1);
1590159636Soleg			break;
1591159636Soleg
159298943Sluigi		case O_TEE:
1593159636Soleg			PRINT_UINT_ARG("tee ", cmd->arg1);
1594159636Soleg			break;
1595159636Soleg
1596141351Sglebius		case O_NETGRAPH:
1597159636Soleg			PRINT_UINT_ARG("netgraph ", cmd->arg1);
1598159636Soleg			break;
1599159636Soleg
1600141351Sglebius		case O_NGTEE:
1601159636Soleg			PRINT_UINT_ARG("ngtee ", cmd->arg1);
1602159636Soleg			break;
1603141351Sglebius
160498943Sluigi		case O_FORWARD_IP:
160598943Sluigi		    {
160698943Sluigi			ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
160798943Sluigi
1608161424Sjulian			if (s->sa.sin_addr.s_addr == INADDR_ANY) {
1609161424Sjulian				printf("fwd tablearg");
1610161424Sjulian			} else {
1611161424Sjulian				printf("fwd %s", inet_ntoa(s->sa.sin_addr));
1612161424Sjulian			}
161398943Sluigi			if (s->sa.sin_port)
1614103241Sluigi				printf(",%d", s->sa.sin_port);
161598943Sluigi		    }
161698943Sluigi			break;
161798943Sluigi
161898943Sluigi		case O_LOG: /* O_LOG is printed last */
161998943Sluigi			logptr = (ipfw_insn_log *)cmd;
162098943Sluigi			break;
162198943Sluigi
1622136071Sgreen		case O_ALTQ: /* O_ALTQ is printed after O_LOG */
1623136071Sgreen			altqptr = (ipfw_insn_altq *)cmd;
1624136071Sgreen			break;
1625136071Sgreen
1626158879Soleg		case O_TAG:
1627158879Soleg			tagptr = cmd;
1628158879Soleg			break;
1629158879Soleg
1630165648Spiso		case O_NAT:
1631176517Spiso			PRINT_UINT_ARG("nat ", cmd->arg1);
1632165648Spiso 			break;
1633165648Spiso
1634178888Sjulian		case O_SETFIB:
1635178888Sjulian			PRINT_UINT_ARG("setfib ", cmd->arg1);
1636178888Sjulian 			break;
1637178888Sjulian
163898943Sluigi		default:
1639136071Sgreen			printf("** unrecognized action %d len %d ",
164098943Sluigi				cmd->opcode, cmd->len);
164198943Sluigi		}
164298943Sluigi	}
164398943Sluigi	if (logptr) {
164498943Sluigi		if (logptr->max_log > 0)
164599909Sluigi			printf(" log logamount %d", logptr->max_log);
164698943Sluigi		else
164799909Sluigi			printf(" log");
164898943Sluigi	}
1649136071Sgreen	if (altqptr) {
1650136071Sgreen		const char *qname;
1651102087Sluigi
1652136071Sgreen		qname = altq_qid_to_name(altqptr->qid);
1653136071Sgreen		if (qname == NULL)
1654136071Sgreen			printf(" altq ?<%u>", altqptr->qid);
1655136071Sgreen		else
1656136071Sgreen			printf(" altq %s", qname);
1657136071Sgreen	}
1658158879Soleg	if (tagptr) {
1659158879Soleg		if (tagptr->len & F_NOT)
1660159636Soleg			PRINT_UINT_ARG(" untag ", tagptr->arg1);
1661158879Soleg		else
1662159636Soleg			PRINT_UINT_ARG(" tag ", tagptr->arg1);
1663158879Soleg	}
1664136071Sgreen
166598943Sluigi	/*
1666102087Sluigi	 * then print the body.
166798943Sluigi	 */
1668146894Smlaier        for (l = rule->act_ofs, cmd = rule->cmd ;
1669146894Smlaier			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
1670146894Smlaier		if ((cmd->len & F_OR) || (cmd->len & F_NOT))
1671146894Smlaier			continue;
1672146894Smlaier		if (cmd->opcode == O_IP4) {
1673146894Smlaier			flags |= HAVE_PROTO4;
1674146894Smlaier			break;
1675146894Smlaier		} else if (cmd->opcode == O_IP6) {
1676146894Smlaier			flags |= HAVE_PROTO6;
1677146894Smlaier			break;
1678146894Smlaier		}
1679146894Smlaier	}
1680102087Sluigi	if (rule->_pad & 1) {	/* empty rules before options */
1681146894Smlaier		if (!do_compact) {
1682146894Smlaier			show_prerequisites(&flags, HAVE_PROTO, 0);
1683146894Smlaier			printf(" from any to any");
1684146894Smlaier		}
1685102087Sluigi		flags |= HAVE_IP | HAVE_OPTIONS;
1686102087Sluigi	}
1687102087Sluigi
1688123495Sluigi	if (comment_only)
1689123495Sluigi		comment = "...";
1690123495Sluigi
169198943Sluigi        for (l = rule->act_ofs, cmd = rule->cmd ;
169298943Sluigi			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
169399475Sluigi		/* useful alias */
169499475Sluigi		ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
169598943Sluigi
1696123495Sluigi		if (comment_only) {
1697123495Sluigi			if (cmd->opcode != O_NOP)
1698123495Sluigi				continue;
1699123495Sluigi			printf(" // %s\n", (char *)(cmd + 1));
1700123495Sluigi			return;
1701123495Sluigi		}
1702123495Sluigi
1703102087Sluigi		show_prerequisites(&flags, 0, cmd->opcode);
1704102087Sluigi
170598943Sluigi		switch(cmd->opcode) {
1706117577Sluigi		case O_PROB:
1707107289Sluigi			break;	/* done already */
1708107289Sluigi
170998943Sluigi		case O_PROBE_STATE:
171098943Sluigi			break; /* no need to print anything here */
171198943Sluigi
171298943Sluigi		case O_IP_SRC:
1713130281Sru		case O_IP_SRC_LOOKUP:
171498943Sluigi		case O_IP_SRC_MASK:
171598943Sluigi		case O_IP_SRC_ME:
171698943Sluigi		case O_IP_SRC_SET:
1717102087Sluigi			show_prerequisites(&flags, HAVE_PROTO, 0);
171898943Sluigi			if (!(flags & HAVE_SRCIP))
171998943Sluigi				printf(" from");
172098943Sluigi			if ((cmd->len & F_OR) && !or_block)
172198943Sluigi				printf(" {");
1722102087Sluigi			print_ip((ipfw_insn_ip *)cmd,
1723102087Sluigi				(flags & HAVE_OPTIONS) ? " src-ip" : "");
172498943Sluigi			flags |= HAVE_SRCIP;
172598943Sluigi			break;
172698943Sluigi
172798943Sluigi		case O_IP_DST:
1728130281Sru		case O_IP_DST_LOOKUP:
172998943Sluigi		case O_IP_DST_MASK:
173098943Sluigi		case O_IP_DST_ME:
173198943Sluigi		case O_IP_DST_SET:
1732102087Sluigi			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
173398943Sluigi			if (!(flags & HAVE_DSTIP))
173498943Sluigi				printf(" to");
173598943Sluigi			if ((cmd->len & F_OR) && !or_block)
173698943Sluigi				printf(" {");
1737102087Sluigi			print_ip((ipfw_insn_ip *)cmd,
1738102087Sluigi				(flags & HAVE_OPTIONS) ? " dst-ip" : "");
173998943Sluigi			flags |= HAVE_DSTIP;
174098943Sluigi			break;
174198943Sluigi
1742145246Sbrooks		case O_IP6_SRC:
1743145246Sbrooks		case O_IP6_SRC_MASK:
1744145246Sbrooks		case O_IP6_SRC_ME:
1745147105Smlaier			show_prerequisites(&flags, HAVE_PROTO, 0);
1746145246Sbrooks			if (!(flags & HAVE_SRCIP))
1747145246Sbrooks				printf(" from");
1748145246Sbrooks			if ((cmd->len & F_OR) && !or_block)
1749145246Sbrooks				printf(" {");
1750145246Sbrooks			print_ip6((ipfw_insn_ip6 *)cmd,
1751145246Sbrooks			    (flags & HAVE_OPTIONS) ? " src-ip6" : "");
1752145246Sbrooks			flags |= HAVE_SRCIP | HAVE_PROTO;
1753145246Sbrooks			break;
1754145246Sbrooks
1755145246Sbrooks		case O_IP6_DST:
1756145246Sbrooks		case O_IP6_DST_MASK:
1757145246Sbrooks		case O_IP6_DST_ME:
1758145246Sbrooks			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1759145246Sbrooks			if (!(flags & HAVE_DSTIP))
1760145246Sbrooks				printf(" to");
1761145246Sbrooks			if ((cmd->len & F_OR) && !or_block)
1762145246Sbrooks				printf(" {");
1763145246Sbrooks			print_ip6((ipfw_insn_ip6 *)cmd,
1764145246Sbrooks			    (flags & HAVE_OPTIONS) ? " dst-ip6" : "");
1765145246Sbrooks			flags |= HAVE_DSTIP;
1766145246Sbrooks			break;
1767145246Sbrooks
1768145246Sbrooks		case O_FLOW6ID:
1769145246Sbrooks		print_flow6id( (ipfw_insn_u32 *) cmd );
1770145246Sbrooks		flags |= HAVE_OPTIONS;
1771145246Sbrooks		break;
1772145246Sbrooks
177398943Sluigi		case O_IP_DSTPORT:
1774102087Sluigi			show_prerequisites(&flags, HAVE_IP, 0);
177598943Sluigi		case O_IP_SRCPORT:
1776102087Sluigi			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1777101641Sluigi			if ((cmd->len & F_OR) && !or_block)
1778101641Sluigi				printf(" {");
1779172306Smaxim			if (cmd->len & F_NOT)
1780172306Smaxim				printf(" not");
1781102087Sluigi			print_newports((ipfw_insn_u16 *)cmd, proto,
1782102087Sluigi				(flags & HAVE_OPTIONS) ? cmd->opcode : 0);
178398943Sluigi			break;
178498943Sluigi
178598943Sluigi		case O_PROTO: {
1786145246Sbrooks			struct protoent *pe = NULL;
178798943Sluigi
178898943Sluigi			if ((cmd->len & F_OR) && !or_block)
178998943Sluigi				printf(" {");
179098943Sluigi			if (cmd->len & F_NOT)
179198943Sluigi				printf(" not");
179298943Sluigi			proto = cmd->arg1;
1793145567Sbrooks			pe = getprotobynumber(cmd->arg1);
1794146894Smlaier			if ((flags & (HAVE_PROTO4 | HAVE_PROTO6)) &&
1795146894Smlaier			    !(flags & HAVE_PROTO))
1796146894Smlaier				show_prerequisites(&flags,
1797146894Smlaier				    HAVE_IP | HAVE_OPTIONS, 0);
1798102087Sluigi			if (flags & HAVE_OPTIONS)
1799102087Sluigi				printf(" proto");
180098943Sluigi			if (pe)
180198943Sluigi				printf(" %s", pe->p_name);
180298943Sluigi			else
180398943Sluigi				printf(" %u", cmd->arg1);
180498943Sluigi			}
180598943Sluigi			flags |= HAVE_PROTO;
180698943Sluigi			break;
1807106505Smaxim
180898943Sluigi		default: /*options ... */
1809146894Smlaier			if (!(cmd->len & (F_OR|F_NOT)))
1810146894Smlaier				if (((cmd->opcode == O_IP6) &&
1811146894Smlaier				    (flags & HAVE_PROTO6)) ||
1812146894Smlaier				    ((cmd->opcode == O_IP4) &&
1813146894Smlaier				    (flags & HAVE_PROTO4)))
1814146894Smlaier					break;
1815102087Sluigi			show_prerequisites(&flags, HAVE_IP | HAVE_OPTIONS, 0);
181698943Sluigi			if ((cmd->len & F_OR) && !or_block)
181798943Sluigi				printf(" {");
181898943Sluigi			if (cmd->len & F_NOT && cmd->opcode != O_IN)
181998943Sluigi				printf(" not");
182098943Sluigi			switch(cmd->opcode) {
1821169139Smaxim			case O_MACADDR2: {
1822169139Smaxim				ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
1823169139Smaxim
1824169139Smaxim				printf(" MAC");
1825169139Smaxim				print_mac(m->addr, m->mask);
1826169139Smaxim				print_mac(m->addr + 6, m->mask + 6);
1827169139Smaxim				}
1828169139Smaxim				break;
1829169139Smaxim
1830169139Smaxim			case O_MAC_TYPE:
1831169139Smaxim				print_newports((ipfw_insn_u16 *)cmd,
1832169139Smaxim						IPPROTO_ETHERTYPE, cmd->opcode);
1833169139Smaxim				break;
1834169139Smaxim
1835169139Smaxim
183698943Sluigi			case O_FRAG:
183798943Sluigi				printf(" frag");
183898943Sluigi				break;
183998943Sluigi
1840178888Sjulian			case O_FIB:
1841178888Sjulian				printf(" fib %u", cmd->arg1 );
1842178888Sjulian				break;
1843178888Sjulian
184498943Sluigi			case O_IN:
184598943Sluigi				printf(cmd->len & F_NOT ? " out" : " in");
184698943Sluigi				break;
184798943Sluigi
1848136073Sgreen			case O_DIVERTED:
1849136073Sgreen				switch (cmd->arg1) {
1850136073Sgreen				case 3:
1851136073Sgreen					printf(" diverted");
1852136073Sgreen					break;
1853136073Sgreen				case 1:
1854136073Sgreen					printf(" diverted-loopback");
1855136073Sgreen					break;
1856136073Sgreen				case 2:
1857136073Sgreen					printf(" diverted-output");
1858136073Sgreen					break;
1859136073Sgreen				default:
1860136073Sgreen					printf(" diverted-?<%u>", cmd->arg1);
1861136073Sgreen					break;
1862136073Sgreen				}
1863136073Sgreen				break;
1864136073Sgreen
186598943Sluigi			case O_LAYER2:
186698943Sluigi				printf(" layer2");
186798943Sluigi				break;
186898943Sluigi			case O_XMIT:
186998943Sluigi			case O_RECV:
1870140423Sglebius			case O_VIA:
1871140423Sglebius			    {
1872117469Sluigi				char const *s;
187398943Sluigi				ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd;
187498943Sluigi
187598943Sluigi				if (cmd->opcode == O_XMIT)
187698943Sluigi					s = "xmit";
187798943Sluigi				else if (cmd->opcode == O_RECV)
187898943Sluigi					s = "recv";
1879117469Sluigi				else /* if (cmd->opcode == O_VIA) */
188098943Sluigi					s = "via";
188198943Sluigi				if (cmdif->name[0] == '\0')
188299475Sluigi					printf(" %s %s", s,
188399475Sluigi					    inet_ntoa(cmdif->p.ip));
1884140423Sglebius				else
1885140423Sglebius					printf(" %s %s", s, cmdif->name);
1886140423Sglebius
188798943Sluigi				break;
1888140423Sglebius			    }
188998943Sluigi			case O_IPID:
1890116690Sluigi				if (F_LEN(cmd) == 1)
1891116690Sluigi				    printf(" ipid %u", cmd->arg1 );
1892116690Sluigi				else
1893116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1894116690Sluigi					O_IPID);
189598943Sluigi				break;
189698943Sluigi
189798943Sluigi			case O_IPTTL:
1898116690Sluigi				if (F_LEN(cmd) == 1)
1899116690Sluigi				    printf(" ipttl %u", cmd->arg1 );
1900116690Sluigi				else
1901116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1902116690Sluigi					O_IPTTL);
190398943Sluigi				break;
190498943Sluigi
190598943Sluigi			case O_IPVER:
190698943Sluigi				printf(" ipver %u", cmd->arg1 );
190798943Sluigi				break;
190898943Sluigi
190999475Sluigi			case O_IPPRECEDENCE:
191099475Sluigi				printf(" ipprecedence %u", (cmd->arg1) >> 5 );
191199475Sluigi				break;
191299475Sluigi
191398943Sluigi			case O_IPLEN:
1914116690Sluigi				if (F_LEN(cmd) == 1)
1915116690Sluigi				    printf(" iplen %u", cmd->arg1 );
1916116690Sluigi				else
1917116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1918116690Sluigi					O_IPLEN);
191998943Sluigi				break;
192098943Sluigi
1921101116Sluigi			case O_IPOPT:
192298943Sluigi				print_flags("ipoptions", cmd, f_ipopts);
192398943Sluigi				break;
192498943Sluigi
192599475Sluigi			case O_IPTOS:
192699475Sluigi				print_flags("iptos", cmd, f_iptos);
192799475Sluigi				break;
192899475Sluigi
192999475Sluigi			case O_ICMPTYPE:
193099475Sluigi				print_icmptypes((ipfw_insn_u32 *)cmd);
193199475Sluigi				break;
193299475Sluigi
193398943Sluigi			case O_ESTAB:
193498943Sluigi				printf(" established");
193598943Sluigi				break;
193698943Sluigi
1937136075Sgreen			case O_TCPDATALEN:
1938136075Sgreen				if (F_LEN(cmd) == 1)
1939136075Sgreen				    printf(" tcpdatalen %u", cmd->arg1 );
1940136075Sgreen				else
1941136075Sgreen				    print_newports((ipfw_insn_u16 *)cmd, 0,
1942136075Sgreen					O_TCPDATALEN);
1943136075Sgreen				break;
1944136075Sgreen
194598943Sluigi			case O_TCPFLAGS:
194698943Sluigi				print_flags("tcpflags", cmd, f_tcpflags);
194798943Sluigi				break;
194898943Sluigi
194998943Sluigi			case O_TCPOPTS:
195098943Sluigi				print_flags("tcpoptions", cmd, f_tcpopts);
195198943Sluigi				break;
195298943Sluigi
195398943Sluigi			case O_TCPWIN:
195498943Sluigi				printf(" tcpwin %d", ntohs(cmd->arg1));
195598943Sluigi				break;
195698943Sluigi
195798943Sluigi			case O_TCPACK:
195898943Sluigi				printf(" tcpack %d", ntohl(cmd32->d[0]));
195998943Sluigi				break;
196098943Sluigi
196198943Sluigi			case O_TCPSEQ:
196298943Sluigi				printf(" tcpseq %d", ntohl(cmd32->d[0]));
196398943Sluigi				break;
196498943Sluigi
196598943Sluigi			case O_UID:
196698943Sluigi			    {
196798943Sluigi				struct passwd *pwd = getpwuid(cmd32->d[0]);
196898943Sluigi
196998943Sluigi				if (pwd)
197098943Sluigi					printf(" uid %s", pwd->pw_name);
197198943Sluigi				else
197298943Sluigi					printf(" uid %u", cmd32->d[0]);
197398943Sluigi			    }
197498943Sluigi				break;
197598943Sluigi
197698943Sluigi			case O_GID:
197798943Sluigi			    {
197898943Sluigi				struct group *grp = getgrgid(cmd32->d[0]);
197998943Sluigi
198098943Sluigi				if (grp)
198198943Sluigi					printf(" gid %s", grp->gr_name);
198298943Sluigi				else
198398943Sluigi					printf(" gid %u", cmd32->d[0]);
198498943Sluigi			    }
198598943Sluigi				break;
198698943Sluigi
1987133600Scsjp			case O_JAIL:
1988133600Scsjp				printf(" jail %d", cmd32->d[0]);
1989133600Scsjp				break;
1990133600Scsjp
1991112250Scjc			case O_VERREVPATH:
1992112250Scjc				printf(" verrevpath");
1993112250Scjc				break;
1994116919Sluigi
1995128575Sandre			case O_VERSRCREACH:
1996128575Sandre				printf(" versrcreach");
1997128575Sandre				break;
1998128575Sandre
1999133387Sandre			case O_ANTISPOOF:
2000133387Sandre				printf(" antispoof");
2001133387Sandre				break;
2002133387Sandre
2003117241Sluigi			case O_IPSEC:
2004117241Sluigi				printf(" ipsec");
2005117241Sluigi				break;
2006117241Sluigi
2007117469Sluigi			case O_NOP:
2008117626Sluigi				comment = (char *)(cmd + 1);
2009117469Sluigi				break;
2010117469Sluigi
201198943Sluigi			case O_KEEP_STATE:
201298943Sluigi				printf(" keep-state");
201398943Sluigi				break;
201498943Sluigi
2015159636Soleg			case O_LIMIT: {
201698943Sluigi				struct _s_x *p = limit_masks;
201798943Sluigi				ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
2018117328Sluigi				uint8_t x = c->limit_mask;
2019117469Sluigi				char const *comma = " ";
202098943Sluigi
202198943Sluigi				printf(" limit");
2022117577Sluigi				for (; p->x != 0 ; p++)
202399909Sluigi					if ((x & p->x) == p->x) {
202498943Sluigi						x &= ~p->x;
202598943Sluigi						printf("%s%s", comma, p->s);
202698943Sluigi						comma = ",";
202798943Sluigi					}
2028159636Soleg				PRINT_UINT_ARG(" ", c->conn_limit);
202998943Sluigi				break;
2030159636Soleg			}
203198943Sluigi
2032146894Smlaier			case O_IP6:
2033152923Sume				printf(" ip6");
2034145246Sbrooks				break;
2035145246Sbrooks
2036146894Smlaier			case O_IP4:
2037152923Sume				printf(" ip4");
2038146894Smlaier				break;
2039146894Smlaier
2040145246Sbrooks			case O_ICMP6TYPE:
2041145246Sbrooks				print_icmp6types((ipfw_insn_u32 *)cmd);
2042145246Sbrooks				break;
2043145246Sbrooks
2044145246Sbrooks			case O_EXT_HDR:
2045145246Sbrooks				print_ext6hdr( (ipfw_insn *) cmd );
2046145246Sbrooks				break;
2047145246Sbrooks
2048158879Soleg			case O_TAGGED:
2049158879Soleg				if (F_LEN(cmd) == 1)
2050159636Soleg					PRINT_UINT_ARG(" tagged ", cmd->arg1);
2051158879Soleg				else
2052159636Soleg					print_newports((ipfw_insn_u16 *)cmd, 0,
2053159636Soleg					    O_TAGGED);
2054158879Soleg				break;
2055158879Soleg
205698943Sluigi			default:
205798943Sluigi				printf(" [opcode %d len %d]",
205898943Sluigi				    cmd->opcode, cmd->len);
205998943Sluigi			}
206098943Sluigi		}
206198943Sluigi		if (cmd->len & F_OR) {
206298943Sluigi			printf(" or");
206398943Sluigi			or_block = 1;
206498943Sluigi		} else if (or_block) {
206598943Sluigi			printf(" }");
206698943Sluigi			or_block = 0;
206798943Sluigi		}
206898943Sluigi	}
2069102087Sluigi	show_prerequisites(&flags, HAVE_IP, 0);
2070117626Sluigi	if (comment)
2071117626Sluigi		printf(" // %s", comment);
207298943Sluigi	printf("\n");
207398943Sluigi}
207498943Sluigi
207598943Sluigistatic void
2076112189Smaximshow_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth)
207798943Sluigi{
207898943Sluigi	struct protoent *pe;
207998943Sluigi	struct in_addr a;
2080115793Sticso	uint16_t rulenum;
2081159160Smlaier	char buf[INET6_ADDRSTRLEN];
208298943Sluigi
208398943Sluigi	if (!do_expired) {
208498943Sluigi		if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT))
208598943Sluigi			return;
208698943Sluigi	}
2087115793Sticso	bcopy(&d->rule, &rulenum, sizeof(rulenum));
2088117328Sluigi	printf("%05d", rulenum);
2089117469Sluigi	if (pcwidth>0 || bcwidth>0)
2090117328Sluigi	    printf(" %*llu %*llu (%ds)", pcwidth,
2091117328Sluigi		align_uint64(&d->pcnt), bcwidth,
2092117328Sluigi		align_uint64(&d->bcnt), d->expire);
209398943Sluigi	switch (d->dyn_type) {
209498943Sluigi	case O_LIMIT_PARENT:
209598943Sluigi		printf(" PARENT %d", d->count);
209698943Sluigi		break;
209798943Sluigi	case O_LIMIT:
209898943Sluigi		printf(" LIMIT");
209998943Sluigi		break;
210098943Sluigi	case O_KEEP_STATE: /* bidir, no mask */
2101106505Smaxim		printf(" STATE");
210298943Sluigi		break;
210398943Sluigi	}
210498943Sluigi
210598943Sluigi	if ((pe = getprotobynumber(d->id.proto)) != NULL)
210698943Sluigi		printf(" %s", pe->p_name);
210798943Sluigi	else
210898943Sluigi		printf(" proto %u", d->id.proto);
210998943Sluigi
2110159160Smlaier	if (d->id.addr_type == 4) {
2111159160Smlaier		a.s_addr = htonl(d->id.src_ip);
2112159160Smlaier		printf(" %s %d", inet_ntoa(a), d->id.src_port);
211398943Sluigi
2114159160Smlaier		a.s_addr = htonl(d->id.dst_ip);
2115159160Smlaier		printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port);
2116159160Smlaier	} else if (d->id.addr_type == 6) {
2117159160Smlaier		printf(" %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf,
2118159160Smlaier		    sizeof(buf)), d->id.src_port);
2119159160Smlaier		printf(" <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf,
2120159160Smlaier		    sizeof(buf)), d->id.dst_port);
2121159160Smlaier	} else
2122159160Smlaier		printf(" UNKNOWN <-> UNKNOWN\n");
2123159160Smlaier
212498943Sluigi	printf("\n");
212598943Sluigi}
212698943Sluigi
2127117469Sluigistatic int
212898943Sluigisort_q(const void *pa, const void *pb)
212998943Sluigi{
213098943Sluigi	int rev = (do_sort < 0);
213198943Sluigi	int field = rev ? -do_sort : do_sort;
213298943Sluigi	long long res = 0;
213398943Sluigi	const struct dn_flow_queue *a = pa;
213498943Sluigi	const struct dn_flow_queue *b = pb;
213598943Sluigi
213698943Sluigi	switch (field) {
213798943Sluigi	case 1: /* pkts */
213898943Sluigi		res = a->len - b->len;
213998943Sluigi		break;
214098943Sluigi	case 2: /* bytes */
214198943Sluigi		res = a->len_bytes - b->len_bytes;
214298943Sluigi		break;
214398943Sluigi
214498943Sluigi	case 3: /* tot pkts */
214598943Sluigi		res = a->tot_pkts - b->tot_pkts;
214698943Sluigi		break;
214798943Sluigi
214898943Sluigi	case 4: /* tot bytes */
214998943Sluigi		res = a->tot_bytes - b->tot_bytes;
215098943Sluigi		break;
215198943Sluigi	}
215298943Sluigi	if (res < 0)
215398943Sluigi		res = -1;
215498943Sluigi	if (res > 0)
215598943Sluigi		res = 1;
215698943Sluigi	return (int)(rev ? res : -res);
215798943Sluigi}
215898943Sluigi
215998943Sluigistatic void
216098943Sluigilist_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
216198943Sluigi{
216298943Sluigi	int l;
2163145246Sbrooks	int index_printed, indexes = 0;
2164145246Sbrooks	char buff[255];
2165145246Sbrooks	struct protoent *pe;
216698943Sluigi
216798943Sluigi	if (fs->rq_elements == 0)
216898943Sluigi		return;
216998943Sluigi
217098943Sluigi	if (do_sort != 0)
217198943Sluigi		heapsort(q, fs->rq_elements, sizeof *q, sort_q);
2172145246Sbrooks
2173145246Sbrooks	/* Print IPv4 flows */
2174145246Sbrooks	index_printed = 0;
217598943Sluigi	for (l = 0; l < fs->rq_elements; l++) {
217698943Sluigi		struct in_addr ina;
217798943Sluigi
2178145246Sbrooks		/* XXX: Should check for IPv4 flows */
2179145246Sbrooks		if (IS_IP6_FLOW_ID(&(q[l].id)))
2180145246Sbrooks			continue;
2181145246Sbrooks
2182145246Sbrooks		if (!index_printed) {
2183145246Sbrooks			index_printed = 1;
2184145246Sbrooks			if (indexes > 0)	/* currently a no-op */
2185145246Sbrooks				printf("\n");
2186145246Sbrooks			indexes++;
2187145246Sbrooks			printf("    "
2188145246Sbrooks			    "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
2189145246Sbrooks			    fs->flow_mask.proto,
2190145246Sbrooks			    fs->flow_mask.src_ip, fs->flow_mask.src_port,
2191145246Sbrooks			    fs->flow_mask.dst_ip, fs->flow_mask.dst_port);
2192145246Sbrooks
2193145246Sbrooks			printf("BKT Prot ___Source IP/port____ "
2194145246Sbrooks			    "____Dest. IP/port____ "
2195145246Sbrooks			    "Tot_pkt/bytes Pkt/Byte Drp\n");
2196145246Sbrooks		}
2197145246Sbrooks
219898943Sluigi		printf("%3d ", q[l].hash_slot);
219998943Sluigi		pe = getprotobynumber(q[l].id.proto);
220098943Sluigi		if (pe)
220198943Sluigi			printf("%-4s ", pe->p_name);
220298943Sluigi		else
220398943Sluigi			printf("%4u ", q[l].id.proto);
2204145246Sbrooks		ina.s_addr = htonl(q[l].id.src_ip);
220598943Sluigi		printf("%15s/%-5d ",
220698943Sluigi		    inet_ntoa(ina), q[l].id.src_port);
220798943Sluigi		ina.s_addr = htonl(q[l].id.dst_ip);
220898943Sluigi		printf("%15s/%-5d ",
220998943Sluigi		    inet_ntoa(ina), q[l].id.dst_port);
221098943Sluigi		printf("%4qu %8qu %2u %4u %3u\n",
221198943Sluigi		    q[l].tot_pkts, q[l].tot_bytes,
221298943Sluigi		    q[l].len, q[l].len_bytes, q[l].drops);
221398943Sluigi		if (verbose)
221498943Sluigi			printf("   S %20qd  F %20qd\n",
221598943Sluigi			    q[l].S, q[l].F);
221698943Sluigi	}
2217145246Sbrooks
2218145246Sbrooks	/* Print IPv6 flows */
2219145246Sbrooks	index_printed = 0;
2220145246Sbrooks	for (l = 0; l < fs->rq_elements; l++) {
2221145246Sbrooks		if (!IS_IP6_FLOW_ID(&(q[l].id)))
2222145246Sbrooks			continue;
2223145246Sbrooks
2224145246Sbrooks		if (!index_printed) {
2225145246Sbrooks			index_printed = 1;
2226145246Sbrooks			if (indexes > 0)
2227145246Sbrooks				printf("\n");
2228145246Sbrooks			indexes++;
2229145246Sbrooks			printf("\n        mask: proto: 0x%02x, flow_id: 0x%08x,  ",
2230145246Sbrooks			    fs->flow_mask.proto, fs->flow_mask.flow_id6);
2231145246Sbrooks			inet_ntop(AF_INET6, &(fs->flow_mask.src_ip6),
2232145246Sbrooks			    buff, sizeof(buff));
2233145246Sbrooks			printf("%s/0x%04x -> ", buff, fs->flow_mask.src_port);
2234145246Sbrooks			inet_ntop( AF_INET6, &(fs->flow_mask.dst_ip6),
2235145246Sbrooks			    buff, sizeof(buff) );
2236145246Sbrooks			printf("%s/0x%04x\n", buff, fs->flow_mask.dst_port);
2237145246Sbrooks
2238145246Sbrooks			printf("BKT ___Prot___ _flow-id_ "
2239145246Sbrooks			    "______________Source IPv6/port_______________ "
2240145246Sbrooks			    "_______________Dest. IPv6/port_______________ "
2241145246Sbrooks			    "Tot_pkt/bytes Pkt/Byte Drp\n");
2242145246Sbrooks		}
2243145246Sbrooks		printf("%3d ", q[l].hash_slot);
2244145246Sbrooks		pe = getprotobynumber(q[l].id.proto);
2245145246Sbrooks		if (pe != NULL)
2246145246Sbrooks			printf("%9s ", pe->p_name);
2247145246Sbrooks		else
2248145246Sbrooks			printf("%9u ", q[l].id.proto);
2249145246Sbrooks		printf("%7d  %39s/%-5d ", q[l].id.flow_id6,
2250145246Sbrooks		    inet_ntop(AF_INET6, &(q[l].id.src_ip6), buff, sizeof(buff)),
2251145246Sbrooks		    q[l].id.src_port);
2252145246Sbrooks		printf(" %39s/%-5d ",
2253145246Sbrooks		    inet_ntop(AF_INET6, &(q[l].id.dst_ip6), buff, sizeof(buff)),
2254145246Sbrooks		    q[l].id.dst_port);
2255145246Sbrooks		printf(" %4qu %8qu %2u %4u %3u\n",
2256145246Sbrooks		    q[l].tot_pkts, q[l].tot_bytes,
2257145246Sbrooks		    q[l].len, q[l].len_bytes, q[l].drops);
2258145246Sbrooks		if (verbose)
2259145246Sbrooks			printf("   S %20qd  F %20qd\n", q[l].S, q[l].F);
2260145246Sbrooks	}
226198943Sluigi}
226298943Sluigi
226398943Sluigistatic void
226498943Sluigiprint_flowset_parms(struct dn_flow_set *fs, char *prefix)
226598943Sluigi{
226698943Sluigi	int l;
226798943Sluigi	char qs[30];
226898943Sluigi	char plr[30];
226998943Sluigi	char red[90];	/* Display RED parameters */
227098943Sluigi
227198943Sluigi	l = fs->qsize;
227298943Sluigi	if (fs->flags_fs & DN_QSIZE_IS_BYTES) {
227398943Sluigi		if (l >= 8192)
227498943Sluigi			sprintf(qs, "%d KB", l / 1024);
227598943Sluigi		else
227698943Sluigi			sprintf(qs, "%d B", l);
227798943Sluigi	} else
227898943Sluigi		sprintf(qs, "%3d sl.", l);
227998943Sluigi	if (fs->plr)
228098943Sluigi		sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
228198943Sluigi	else
228298943Sluigi		plr[0] = '\0';
228398943Sluigi	if (fs->flags_fs & DN_IS_RED)	/* RED parameters */
228498943Sluigi		sprintf(red,
228598943Sluigi		    "\n\t  %cRED w_q %f min_th %d max_th %d max_p %f",
228698943Sluigi		    (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ',
228798943Sluigi		    1.0 * fs->w_q / (double)(1 << SCALE_RED),
228898943Sluigi		    SCALE_VAL(fs->min_th),
228998943Sluigi		    SCALE_VAL(fs->max_th),
229098943Sluigi		    1.0 * fs->max_p / (double)(1 << SCALE_RED));
229198943Sluigi	else
229298943Sluigi		sprintf(red, "droptail");
229398943Sluigi
229498943Sluigi	printf("%s %s%s %d queues (%d buckets) %s\n",
229598943Sluigi	    prefix, qs, plr, fs->rq_elements, fs->rq_size, red);
229698943Sluigi}
229798943Sluigi
229898943Sluigistatic void
2299117469Sluigilist_pipes(void *data, uint nbytes, int ac, char *av[])
230098943Sluigi{
2301117469Sluigi	int rulenum;
230298943Sluigi	void *next = data;
230398943Sluigi	struct dn_pipe *p = (struct dn_pipe *) data;
230498943Sluigi	struct dn_flow_set *fs;
230598943Sluigi	struct dn_flow_queue *q;
230698943Sluigi	int l;
230798943Sluigi
230898943Sluigi	if (ac > 0)
230998943Sluigi		rulenum = strtoul(*av++, NULL, 10);
231098943Sluigi	else
231198943Sluigi		rulenum = 0;
231298943Sluigi	for (; nbytes >= sizeof *p; p = (struct dn_pipe *)next) {
231398943Sluigi		double b = p->bandwidth;
231498943Sluigi		char buf[30];
231598943Sluigi		char prefix[80];
231698943Sluigi
2317161001Sstefanf		if (SLIST_NEXT(p, next) != (struct dn_pipe *)DN_IS_PIPE)
231898943Sluigi			break;	/* done with pipes, now queues */
231998943Sluigi
232098943Sluigi		/*
232198943Sluigi		 * compute length, as pipe have variable size
232298943Sluigi		 */
232398943Sluigi		l = sizeof(*p) + p->fs.rq_elements * sizeof(*q);
2324117469Sluigi		next = (char *)p + l;
232598943Sluigi		nbytes -= l;
232698943Sluigi
2327134225Spjd		if ((rulenum != 0 && rulenum != p->pipe_nr) || do_pipe == 2)
232898943Sluigi			continue;
232998943Sluigi
233098943Sluigi		/*
233198943Sluigi		 * Print rate (or clocking interface)
233298943Sluigi		 */
233398943Sluigi		if (p->if_name[0] != '\0')
233498943Sluigi			sprintf(buf, "%s", p->if_name);
233598943Sluigi		else if (b == 0)
233698943Sluigi			sprintf(buf, "unlimited");
233798943Sluigi		else if (b >= 1000000)
233898943Sluigi			sprintf(buf, "%7.3f Mbit/s", b/1000000);
233998943Sluigi		else if (b >= 1000)
234098943Sluigi			sprintf(buf, "%7.3f Kbit/s", b/1000);
234198943Sluigi		else
234298943Sluigi			sprintf(buf, "%7.3f bit/s ", b);
234398943Sluigi
234498943Sluigi		sprintf(prefix, "%05d: %s %4d ms ",
234598943Sluigi		    p->pipe_nr, buf, p->delay);
234698943Sluigi		print_flowset_parms(&(p->fs), prefix);
234798943Sluigi		if (verbose)
234898943Sluigi			printf("   V %20qd\n", p->V >> MY_M);
2349106505Smaxim
235098943Sluigi		q = (struct dn_flow_queue *)(p+1);
235198943Sluigi		list_queues(&(p->fs), q);
235298943Sluigi	}
235398943Sluigi	for (fs = next; nbytes >= sizeof *fs; fs = next) {
235498943Sluigi		char prefix[80];
235598943Sluigi
2356161001Sstefanf		if (SLIST_NEXT(fs, next) != (struct dn_flow_set *)DN_IS_QUEUE)
235798943Sluigi			break;
235898943Sluigi		l = sizeof(*fs) + fs->rq_elements * sizeof(*q);
2359117469Sluigi		next = (char *)fs + l;
236098943Sluigi		nbytes -= l;
2361134225Spjd
2362134225Spjd		if (rulenum != 0 && ((rulenum != fs->fs_nr && do_pipe == 2) ||
2363134225Spjd		    (rulenum != fs->parent_nr && do_pipe == 1))) {
2364134225Spjd			continue;
2365134225Spjd		}
2366134225Spjd
236798943Sluigi		q = (struct dn_flow_queue *)(fs+1);
236898943Sluigi		sprintf(prefix, "q%05d: weight %d pipe %d ",
236998943Sluigi		    fs->fs_nr, fs->weight, fs->parent_nr);
237098943Sluigi		print_flowset_parms(fs, prefix);
237198943Sluigi		list_queues(fs, q);
237298943Sluigi	}
237398943Sluigi}
237498943Sluigi
2375101978Sluigi/*
2376101978Sluigi * This one handles all set-related commands
2377101978Sluigi * 	ipfw set { show | enable | disable }
2378101978Sluigi * 	ipfw set swap X Y
2379101978Sluigi * 	ipfw set move X to Y
2380101978Sluigi * 	ipfw set move rule X to Y
2381101978Sluigi */
238298943Sluigistatic void
2383101978Sluigisets_handler(int ac, char *av[])
2384101978Sluigi{
2385117328Sluigi	uint32_t set_disable, masks[2];
2386101978Sluigi	int i, nbytes;
2387117328Sluigi	uint16_t rulenum;
2388117328Sluigi	uint8_t cmd, new_set;
2389101978Sluigi
2390101978Sluigi	ac--;
2391101978Sluigi	av++;
2392101978Sluigi
2393101978Sluigi	if (!ac)
2394101978Sluigi		errx(EX_USAGE, "set needs command");
2395140271Sbrooks	if (_substrcmp(*av, "show") == 0) {
2396101978Sluigi		void *data;
2397117469Sluigi		char const *msg;
2398101978Sluigi
2399101978Sluigi		nbytes = sizeof(struct ip_fw);
2400187716Sluigi		data = safe_calloc(1, nbytes);
2401119740Stmm		if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0)
2402101978Sluigi			err(EX_OSERR, "getsockopt(IP_FW_GET)");
2403115793Sticso		bcopy(&((struct ip_fw *)data)->next_rule,
2404115793Sticso			&set_disable, sizeof(set_disable));
2405101978Sluigi
2406117655Sluigi		for (i = 0, msg = "disable" ; i < RESVD_SET; i++)
2407117577Sluigi			if ((set_disable & (1<<i))) {
2408101978Sluigi				printf("%s %d", msg, i);
2409101978Sluigi				msg = "";
2410101978Sluigi			}
2411101978Sluigi		msg = (set_disable) ? " enable" : "enable";
2412117655Sluigi		for (i = 0; i < RESVD_SET; i++)
2413117577Sluigi			if (!(set_disable & (1<<i))) {
2414101978Sluigi				printf("%s %d", msg, i);
2415101978Sluigi				msg = "";
2416101978Sluigi			}
2417101978Sluigi		printf("\n");
2418140271Sbrooks	} else if (_substrcmp(*av, "swap") == 0) {
2419101978Sluigi		ac--; av++;
2420101978Sluigi		if (ac != 2)
2421101978Sluigi			errx(EX_USAGE, "set swap needs 2 set numbers\n");
2422101978Sluigi		rulenum = atoi(av[0]);
2423101978Sluigi		new_set = atoi(av[1]);
2424117655Sluigi		if (!isdigit(*(av[0])) || rulenum > RESVD_SET)
2425101978Sluigi			errx(EX_DATAERR, "invalid set number %s\n", av[0]);
2426117655Sluigi		if (!isdigit(*(av[1])) || new_set > RESVD_SET)
2427101978Sluigi			errx(EX_DATAERR, "invalid set number %s\n", av[1]);
2428101978Sluigi		masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
2429117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
2430140271Sbrooks	} else if (_substrcmp(*av, "move") == 0) {
2431101978Sluigi		ac--; av++;
2432140271Sbrooks		if (ac && _substrcmp(*av, "rule") == 0) {
2433101978Sluigi			cmd = 2;
2434101978Sluigi			ac--; av++;
2435101978Sluigi		} else
2436101978Sluigi			cmd = 3;
2437140271Sbrooks		if (ac != 3 || _substrcmp(av[1], "to") != 0)
2438101978Sluigi			errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
2439101978Sluigi		rulenum = atoi(av[0]);
2440101978Sluigi		new_set = atoi(av[2]);
2441117655Sluigi		if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) ||
2442182823Srik			(cmd == 2 && rulenum == IPFW_DEFAULT_RULE) )
2443101978Sluigi			errx(EX_DATAERR, "invalid source number %s\n", av[0]);
2444117655Sluigi		if (!isdigit(*(av[2])) || new_set > RESVD_SET)
2445101978Sluigi			errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
2446101978Sluigi		masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
2447117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
2448140271Sbrooks	} else if (_substrcmp(*av, "disable") == 0 ||
2449140271Sbrooks		   _substrcmp(*av, "enable") == 0 ) {
2450140271Sbrooks		int which = _substrcmp(*av, "enable") == 0 ? 1 : 0;
2451101978Sluigi
2452101978Sluigi		ac--; av++;
2453101978Sluigi		masks[0] = masks[1] = 0;
2454101978Sluigi
2455101978Sluigi		while (ac) {
2456101978Sluigi			if (isdigit(**av)) {
2457101978Sluigi				i = atoi(*av);
2458117655Sluigi				if (i < 0 || i > RESVD_SET)
2459101978Sluigi					errx(EX_DATAERR,
2460101978Sluigi					    "invalid set number %d\n", i);
2461101978Sluigi				masks[which] |= (1<<i);
2462140271Sbrooks			} else if (_substrcmp(*av, "disable") == 0)
2463101978Sluigi				which = 0;
2464140271Sbrooks			else if (_substrcmp(*av, "enable") == 0)
2465101978Sluigi				which = 1;
2466101978Sluigi			else
2467101978Sluigi				errx(EX_DATAERR,
2468101978Sluigi					"invalid set command %s\n", *av);
2469101978Sluigi			av++; ac--;
2470101978Sluigi		}
2471101978Sluigi		if ( (masks[0] & masks[1]) != 0 )
2472101978Sluigi			errx(EX_DATAERR,
2473101978Sluigi			    "cannot enable and disable the same set\n");
2474101978Sluigi
2475117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(masks));
2476101978Sluigi		if (i)
2477101978Sluigi			warn("set enable/disable: setsockopt(IP_FW_DEL)");
2478101978Sluigi	} else
2479101978Sluigi		errx(EX_USAGE, "invalid set command %s\n", *av);
2480101978Sluigi}
2481101978Sluigi
2482101978Sluigistatic void
2483109126Sdillonsysctl_handler(int ac, char *av[], int which)
2484109126Sdillon{
2485109126Sdillon	ac--;
2486109126Sdillon	av++;
2487109126Sdillon
2488119668Smaxim	if (ac == 0) {
2489109126Sdillon		warnx("missing keyword to enable/disable\n");
2490140271Sbrooks	} else if (_substrcmp(*av, "firewall") == 0) {
2491116770Sluigi		sysctlbyname("net.inet.ip.fw.enable", NULL, 0,
2492116770Sluigi		    &which, sizeof(which));
2493140271Sbrooks	} else if (_substrcmp(*av, "one_pass") == 0) {
2494116770Sluigi		sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0,
2495116770Sluigi		    &which, sizeof(which));
2496140271Sbrooks	} else if (_substrcmp(*av, "debug") == 0) {
2497116770Sluigi		sysctlbyname("net.inet.ip.fw.debug", NULL, 0,
2498116770Sluigi		    &which, sizeof(which));
2499140271Sbrooks	} else if (_substrcmp(*av, "verbose") == 0) {
2500116770Sluigi		sysctlbyname("net.inet.ip.fw.verbose", NULL, 0,
2501116770Sluigi		    &which, sizeof(which));
2502140271Sbrooks	} else if (_substrcmp(*av, "dyn_keepalive") == 0) {
2503116770Sluigi		sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0,
2504116770Sluigi		    &which, sizeof(which));
2505140271Sbrooks	} else if (_substrcmp(*av, "altq") == 0) {
2506136071Sgreen		altq_set_enabled(which);
2507109126Sdillon	} else {
2508109126Sdillon		warnx("unrecognize enable/disable keyword: %s\n", *av);
2509109126Sdillon	}
2510109126Sdillon}
2511109126Sdillon
2512109126Sdillonstatic void
2513117469Sluigilist(int ac, char *av[], int show_counters)
251498943Sluigi{
251598943Sluigi	struct ip_fw *r;
251698943Sluigi	ipfw_dyn_rule *dynrules, *d;
251798943Sluigi
2518117469Sluigi#define NEXT(r)	((struct ip_fw *)((char *)r + RULESIZE(r)))
2519117469Sluigi	char *lim;
2520117469Sluigi	void *data = NULL;
2521112189Smaxim	int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width;
252298943Sluigi	int exitval = EX_OK;
252398943Sluigi	int lac;
252498943Sluigi	char **lav;
2525117469Sluigi	u_long rnum, last;
252698943Sluigi	char *endptr;
252798943Sluigi	int seen = 0;
2528170923Smaxim	uint8_t set;
252998943Sluigi
253098943Sluigi	const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
253198943Sluigi	int nalloc = 1024;	/* start somewhere... */
253298943Sluigi
2533135036Smaxim	last = 0;
2534135036Smaxim
2535117328Sluigi	if (test_only) {
2536117328Sluigi		fprintf(stderr, "Testing only, list disabled\n");
2537117328Sluigi		return;
2538117328Sluigi	}
2539117328Sluigi
254098943Sluigi	ac--;
254198943Sluigi	av++;
254298943Sluigi
254398943Sluigi	/* get rules or pipes from kernel, resizing array as necessary */
254498943Sluigi	nbytes = nalloc;
254598943Sluigi
254698943Sluigi	while (nbytes >= nalloc) {
254798943Sluigi		nalloc = nalloc * 2 + 200;
254898943Sluigi		nbytes = nalloc;
2549187716Sluigi		data = safe_realloc(data, nbytes);
2550119740Stmm		if (do_cmd(ocmd, data, (uintptr_t)&nbytes) < 0)
255198943Sluigi			err(EX_OSERR, "getsockopt(IP_%s_GET)",
255298943Sluigi				do_pipe ? "DUMMYNET" : "FW");
255398943Sluigi	}
255498943Sluigi
255598943Sluigi	if (do_pipe) {
255698943Sluigi		list_pipes(data, nbytes, ac, av);
255798943Sluigi		goto done;
255898943Sluigi	}
255998943Sluigi
256098943Sluigi	/*
256198943Sluigi	 * Count static rules. They have variable size so we
256298943Sluigi	 * need to scan the list to count them.
256398943Sluigi	 */
2564117469Sluigi	for (nstat = 1, r = data, lim = (char *)data + nbytes;
2565182823Srik		    r->rulenum < IPFW_DEFAULT_RULE && (char *)r < lim;
2566117469Sluigi		    ++nstat, r = NEXT(r) )
256798943Sluigi		; /* nothing */
256898943Sluigi
256998943Sluigi	/*
257098943Sluigi	 * Count dynamic rules. This is easier as they have
257198943Sluigi	 * fixed size.
257298943Sluigi	 */
2573117469Sluigi	r = NEXT(r);
257498943Sluigi	dynrules = (ipfw_dyn_rule *)r ;
2575117469Sluigi	n = (char *)r - (char *)data;
257698943Sluigi	ndyn = (nbytes - n) / sizeof *dynrules;
257798943Sluigi
2578112189Smaxim	/* if showing stats, figure out column widths ahead of time */
2579112189Smaxim	bcwidth = pcwidth = 0;
2580117469Sluigi	if (show_counters) {
2581117469Sluigi		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
2582170923Smaxim			/* skip rules from another set */
2583170923Smaxim			if (use_set && r->set != use_set - 1)
2584170923Smaxim				continue;
2585170923Smaxim
2586112189Smaxim			/* packet counter */
2587115793Sticso			width = snprintf(NULL, 0, "%llu",
2588115793Sticso			    align_uint64(&r->pcnt));
2589112189Smaxim			if (width > pcwidth)
2590112189Smaxim				pcwidth = width;
2591112189Smaxim
2592112189Smaxim			/* byte counter */
2593115793Sticso			width = snprintf(NULL, 0, "%llu",
2594115793Sticso			    align_uint64(&r->bcnt));
2595112189Smaxim			if (width > bcwidth)
2596112189Smaxim				bcwidth = width;
2597112189Smaxim		}
2598112189Smaxim	}
2599112189Smaxim	if (do_dynamic && ndyn) {
2600112189Smaxim		for (n = 0, d = dynrules; n < ndyn; n++, d++) {
2601170923Smaxim			if (use_set) {
2602170923Smaxim				/* skip rules from another set */
2603171989Smaxim				bcopy((char *)&d->rule + sizeof(uint16_t),
2604170923Smaxim				      &set, sizeof(uint8_t));
2605170923Smaxim				if (set != use_set - 1)
2606170923Smaxim					continue;
2607170923Smaxim			}
2608115793Sticso			width = snprintf(NULL, 0, "%llu",
2609115793Sticso			    align_uint64(&d->pcnt));
2610112189Smaxim			if (width > pcwidth)
2611112189Smaxim				pcwidth = width;
2612112189Smaxim
2613115793Sticso			width = snprintf(NULL, 0, "%llu",
2614115793Sticso			    align_uint64(&d->bcnt));
2615112189Smaxim			if (width > bcwidth)
2616112189Smaxim				bcwidth = width;
2617112189Smaxim		}
2618112189Smaxim	}
261998943Sluigi	/* if no rule numbers were specified, list all rules */
262098943Sluigi	if (ac == 0) {
2621170923Smaxim		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
2622170923Smaxim			if (use_set && r->set != use_set - 1)
2623170923Smaxim				continue;
2624112189Smaxim			show_ipfw(r, pcwidth, bcwidth);
2625170923Smaxim		}
262698943Sluigi
262798943Sluigi		if (do_dynamic && ndyn) {
262898943Sluigi			printf("## Dynamic rules (%d):\n", ndyn);
2629170923Smaxim			for (n = 0, d = dynrules; n < ndyn; n++, d++) {
2630170923Smaxim				if (use_set) {
2631171989Smaxim					bcopy((char *)&d->rule + sizeof(uint16_t),
2632170923Smaxim					      &set, sizeof(uint8_t));
2633170923Smaxim					if (set != use_set - 1)
2634170923Smaxim						continue;
2635170923Smaxim				}
2636112189Smaxim				show_dyn_ipfw(d, pcwidth, bcwidth);
263798943Sluigi		}
2638170923Smaxim		}
263998943Sluigi		goto done;
264098943Sluigi	}
264198943Sluigi
264298943Sluigi	/* display specific rules requested on command line */
264398943Sluigi
264498943Sluigi	for (lac = ac, lav = av; lac != 0; lac--) {
264598943Sluigi		/* convert command line rule # */
2646117469Sluigi		last = rnum = strtoul(*lav++, &endptr, 10);
2647117469Sluigi		if (*endptr == '-')
2648117469Sluigi			last = strtoul(endptr+1, &endptr, 10);
264998943Sluigi		if (*endptr) {
265098943Sluigi			exitval = EX_USAGE;
265198943Sluigi			warnx("invalid rule number: %s", *(lav - 1));
265298943Sluigi			continue;
265398943Sluigi		}
2654117469Sluigi		for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) {
2655117469Sluigi			if (r->rulenum > last)
265698943Sluigi				break;
2657170923Smaxim			if (use_set && r->set != use_set - 1)
2658170923Smaxim				continue;
2659117469Sluigi			if (r->rulenum >= rnum && r->rulenum <= last) {
2660112189Smaxim				show_ipfw(r, pcwidth, bcwidth);
266198943Sluigi				seen = 1;
266298943Sluigi			}
266398943Sluigi		}
266498943Sluigi		if (!seen) {
266598943Sluigi			/* give precedence to other error(s) */
266698943Sluigi			if (exitval == EX_OK)
266798943Sluigi				exitval = EX_UNAVAILABLE;
266898943Sluigi			warnx("rule %lu does not exist", rnum);
266998943Sluigi		}
267098943Sluigi	}
267198943Sluigi
267298943Sluigi	if (do_dynamic && ndyn) {
267398943Sluigi		printf("## Dynamic rules:\n");
267498943Sluigi		for (lac = ac, lav = av; lac != 0; lac--) {
2675145246Sbrooks			last = rnum = strtoul(*lav++, &endptr, 10);
2676117469Sluigi			if (*endptr == '-')
2677117469Sluigi				last = strtoul(endptr+1, &endptr, 10);
267898943Sluigi			if (*endptr)
267998943Sluigi				/* already warned */
268098943Sluigi				continue;
268198943Sluigi			for (n = 0, d = dynrules; n < ndyn; n++, d++) {
2682115793Sticso				uint16_t rulenum;
2683115793Sticso
2684115793Sticso				bcopy(&d->rule, &rulenum, sizeof(rulenum));
2685115793Sticso				if (rulenum > rnum)
268698943Sluigi					break;
2687170923Smaxim				if (use_set) {
2688171989Smaxim					bcopy((char *)&d->rule + sizeof(uint16_t),
2689170923Smaxim					      &set, sizeof(uint8_t));
2690170923Smaxim					if (set != use_set - 1)
2691170923Smaxim						continue;
2692170923Smaxim				}
2693117469Sluigi				if (r->rulenum >= rnum && r->rulenum <= last)
2694112189Smaxim					show_dyn_ipfw(d, pcwidth, bcwidth);
269598943Sluigi			}
269698943Sluigi		}
269798943Sluigi	}
269898943Sluigi
269998943Sluigi	ac = 0;
270098943Sluigi
270198943Sluigidone:
270298943Sluigi	free(data);
270398943Sluigi
270498943Sluigi	if (exitval != EX_OK)
270598943Sluigi		exit(exitval);
2706117469Sluigi#undef NEXT
270798943Sluigi}
270898943Sluigi
270998943Sluigistatic void
271098943Sluigishow_usage(void)
271198943Sluigi{
271298943Sluigi	fprintf(stderr, "usage: ipfw [options]\n"
271398943Sluigi"do \"ipfw -h\" or see ipfw manpage for details\n"
271498943Sluigi);
271598943Sluigi	exit(EX_USAGE);
271698943Sluigi}
271798943Sluigi
271898943Sluigistatic void
271998943Sluigihelp(void)
272098943Sluigi{
2721117328Sluigi	fprintf(stderr,
2722117328Sluigi"ipfw syntax summary (but please do read the ipfw(8) manpage):\n"
2723123495Sluigi"ipfw [-abcdefhnNqStTv] <command> where <command> is one of:\n"
2724117328Sluigi"add [num] [set N] [prob x] RULE-BODY\n"
2725117328Sluigi"{pipe|queue} N config PIPE-BODY\n"
2726117328Sluigi"[pipe|queue] {zero|delete|show} [N{,N}]\n"
2727165648Spiso"nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|reset|\n"
2728165648Spiso"		reverse|proxy_only|redirect_addr linkspec|\n"
2729165648Spiso"		redirect_port linkspec|redirect_proto linkspec}\n"
2730117328Sluigi"set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n"
2731170923Smaxim"set N {show|list|zero|resetlog|delete} [N{,N}] | flush\n"
2732130281Sru"table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n"
2733183407Srik"table all {flush | list}\n"
273498943Sluigi"\n"
2735136071Sgreen"RULE-BODY:	check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n"
2736149020Sbz"ACTION:	check-state | allow | count | deny | unreach{,6} CODE |\n"
2737149020Sbz"               skipto N | {divert|tee} PORT | forward ADDR |\n"
2738178888Sjulian"               pipe N | queue N | nat N | setfib FIB\n"
2739136071Sgreen"PARAMS: 	[log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n"
274098943Sluigi"ADDR:		[ MAC dst src ether_type ] \n"
2741145246Sbrooks"		[ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
2742145246Sbrooks"		[ ipv6|ip6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n"
2743130281Sru"IPADDR:	[not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n"
2744145246Sbrooks"IP6ADDR:	[not] { any | me | me6 | ip6/bits | IP6LIST }\n"
2745145246Sbrooks"IP6LIST:	{ ip6 | ip6/bits }[,IP6LIST]\n"
2746117544Sluigi"IPLIST:	{ ip | ip/bits | ip:mask }[,IPLIST]\n"
2747117544Sluigi"OPTION_LIST:	OPTION [OPTION_LIST]\n"
2748136247Sgreen"OPTION:	bridged | diverted | diverted-loopback | diverted-output |\n"
2749145246Sbrooks"	{dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n"
2750145246Sbrooks"	{dst-port|src-port} LIST |\n"
2751117328Sluigi"	estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n"
2752117328Sluigi"	iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n"
2753117328Sluigi"	ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n"
2754178888Sjulian"	icmp6types LIST | ext6hdr LIST | flow-id N[,N] | fib FIB |\n"
2755117328Sluigi"	mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n"
2756117328Sluigi"	setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n"
2757136075Sgreen"	tcpdatalen LIST | verrevpath | versrcreach | antispoof\n"
275898943Sluigi);
275998943Sluigiexit(0);
276098943Sluigi}
276198943Sluigi
276298943Sluigi
276398943Sluigistatic int
276498943Sluigilookup_host (char *host, struct in_addr *ipaddr)
276598943Sluigi{
276698943Sluigi	struct hostent *he;
276798943Sluigi
276898943Sluigi	if (!inet_aton(host, ipaddr)) {
276998943Sluigi		if ((he = gethostbyname(host)) == NULL)
277098943Sluigi			return(-1);
277198943Sluigi		*ipaddr = *(struct in_addr *)he->h_addr_list[0];
277298943Sluigi	}
277398943Sluigi	return(0);
277498943Sluigi}
277598943Sluigi
277698943Sluigi/*
277798943Sluigi * fills the addr and mask fields in the instruction as appropriate from av.
277898943Sluigi * Update length as appropriate.
277998943Sluigi * The following formats are allowed:
278098943Sluigi *	me	returns O_IP_*_ME
278198943Sluigi *	1.2.3.4		single IP address
278298943Sluigi *	1.2.3.4:5.6.7.8	address:mask
278398943Sluigi *	1.2.3.4/24	address/mask
278498943Sluigi *	1.2.3.4/26{1,6,5,4,23}	set of addresses in a subnet
2785117328Sluigi * We can have multiple comma-separated address/mask entries.
278698943Sluigi */
278798943Sluigistatic void
278898943Sluigifill_ip(ipfw_insn_ip *cmd, char *av)
278998943Sluigi{
2790117328Sluigi	int len = 0;
2791117328Sluigi	uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
279298943Sluigi
279398943Sluigi	cmd->o.len &= ~F_LEN_MASK;	/* zero len */
279498943Sluigi
2795140271Sbrooks	if (_substrcmp(av, "any") == 0)
279698943Sluigi		return;
279798943Sluigi
2798140271Sbrooks	if (_substrcmp(av, "me") == 0) {
279998943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn);
280098943Sluigi		return;
280198943Sluigi	}
280298943Sluigi
2803140271Sbrooks	if (strncmp(av, "table(", 6) == 0) {
2804130281Sru		char *p = strchr(av + 6, ',');
2805130281Sru
2806130281Sru		if (p)
2807130281Sru			*p++ = '\0';
2808130281Sru		cmd->o.opcode = O_IP_DST_LOOKUP;
2809130281Sru		cmd->o.arg1 = strtoul(av + 6, NULL, 0);
2810130281Sru		if (p) {
2811130281Sru			cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2812130281Sru			d[0] = strtoul(p, NULL, 0);
2813130281Sru		} else
2814130281Sru			cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2815130281Sru		return;
2816130281Sru	}
2817130281Sru
2818117328Sluigi    while (av) {
2819117328Sluigi	/*
2820117328Sluigi	 * After the address we can have '/' or ':' indicating a mask,
2821117328Sluigi	 * ',' indicating another address follows, '{' indicating a
2822117328Sluigi	 * set of addresses of unspecified size.
2823117328Sluigi	 */
2824165851Smlaier	char *t = NULL, *p = strpbrk(av, "/:,{");
2825117328Sluigi	int masklen;
2826187477Sluigi	char md, nd = '\0';
2827117328Sluigi
282898943Sluigi	if (p) {
282998943Sluigi		md = *p;
283098943Sluigi		*p++ = '\0';
2831165851Smlaier		if ((t = strpbrk(p, ",{")) != NULL) {
2832165851Smlaier			nd = *t;
2833165851Smlaier			*t = '\0';
2834165851Smlaier		}
2835117328Sluigi	} else
2836117328Sluigi		md = '\0';
283798943Sluigi
2838117328Sluigi	if (lookup_host(av, (struct in_addr *)&d[0]) != 0)
283998943Sluigi		errx(EX_NOHOST, "hostname ``%s'' unknown", av);
284098943Sluigi	switch (md) {
284198943Sluigi	case ':':
2842117328Sluigi		if (!inet_aton(p, (struct in_addr *)&d[1]))
284398943Sluigi			errx(EX_DATAERR, "bad netmask ``%s''", p);
284498943Sluigi		break;
284598943Sluigi	case '/':
2846117328Sluigi		masklen = atoi(p);
2847117328Sluigi		if (masklen == 0)
2848117328Sluigi			d[1] = htonl(0);	/* mask */
2849117328Sluigi		else if (masklen > 32)
285098943Sluigi			errx(EX_DATAERR, "bad width ``%s''", p);
285198943Sluigi		else
2852117328Sluigi			d[1] = htonl(~0 << (32 - masklen));
285398943Sluigi		break;
2854117328Sluigi	case '{':	/* no mask, assume /24 and put back the '{' */
2855117328Sluigi		d[1] = htonl(~0 << (32 - 24));
2856117328Sluigi		*(--p) = md;
2857117328Sluigi		break;
2858117328Sluigi
2859117328Sluigi	case ',':	/* single address plus continuation */
2860117328Sluigi		*(--p) = md;
2861117328Sluigi		/* FALLTHROUGH */
2862117328Sluigi	case 0:		/* initialization value */
286398943Sluigi	default:
2864117328Sluigi		d[1] = htonl(~0);	/* force /32 */
286598943Sluigi		break;
286698943Sluigi	}
2867117328Sluigi	d[0] &= d[1];		/* mask base address with mask */
2868165851Smlaier	if (t)
2869165851Smlaier		*t = nd;
2870117328Sluigi	/* find next separator */
287198943Sluigi	if (p)
2872117328Sluigi		p = strpbrk(p, ",{");
2873117328Sluigi	if (p && *p == '{') {
2874117328Sluigi		/*
2875117328Sluigi		 * We have a set of addresses. They are stored as follows:
2876117328Sluigi		 *   arg1	is the set size (powers of 2, 2..256)
2877117328Sluigi		 *   addr	is the base address IN HOST FORMAT
2878117328Sluigi		 *   mask..	is an array of arg1 bits (rounded up to
2879117328Sluigi		 *		the next multiple of 32) with bits set
2880117328Sluigi		 *		for each host in the map.
2881117328Sluigi		 */
2882117328Sluigi		uint32_t *map = (uint32_t *)&cmd->mask;
288398943Sluigi		int low, high;
2884117577Sluigi		int i = contigmask((uint8_t *)&(d[1]), 32);
288598943Sluigi
2886117328Sluigi		if (len > 0)
2887117328Sluigi			errx(EX_DATAERR, "address set cannot be in a list");
2888117328Sluigi		if (i < 24 || i > 31)
2889117328Sluigi			errx(EX_DATAERR, "invalid set with mask %d\n", i);
2890117328Sluigi		cmd->o.arg1 = 1<<(32-i);	/* map length		*/
2891117328Sluigi		d[0] = ntohl(d[0]);		/* base addr in host format */
289298943Sluigi		cmd->o.opcode = O_IP_DST_SET;	/* default */
289398943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32;
2894101117Sluigi		for (i = 0; i < (cmd->o.arg1+31)/32 ; i++)
2895117328Sluigi			map[i] = 0;	/* clear map */
289698943Sluigi
2897117328Sluigi		av = p + 1;
2898117328Sluigi		low = d[0] & 0xff;
289998943Sluigi		high = low + cmd->o.arg1 - 1;
2900117328Sluigi		/*
2901117328Sluigi		 * Here, i stores the previous value when we specify a range
2902117328Sluigi		 * of addresses within a mask, e.g. 45-63. i = -1 means we
2903117328Sluigi		 * have no previous value.
2904117328Sluigi		 */
2905116716Sluigi		i = -1;	/* previous value in a range */
290698943Sluigi		while (isdigit(*av)) {
290798943Sluigi			char *s;
2908117328Sluigi			int a = strtol(av, &s, 0);
290998943Sluigi
2910117328Sluigi			if (s == av) { /* no parameter */
2911117328Sluigi			    if (*av != '}')
2912117328Sluigi				errx(EX_DATAERR, "set not closed\n");
2913117328Sluigi			    if (i != -1)
2914117328Sluigi				errx(EX_DATAERR, "incomplete range %d-", i);
2915117328Sluigi			    break;
2916117328Sluigi			}
2917117328Sluigi			if (a < low || a > high)
2918117328Sluigi			    errx(EX_DATAERR, "addr %d out of range [%d-%d]\n",
291998943Sluigi				a, low, high);
292098943Sluigi			a -= low;
2921116716Sluigi			if (i == -1)	/* no previous in range */
2922116716Sluigi			    i = a;
2923116716Sluigi			else {		/* check that range is valid */
2924116716Sluigi			    if (i > a)
2925116716Sluigi				errx(EX_DATAERR, "invalid range %d-%d",
2926116716Sluigi					i+low, a+low);
2927116716Sluigi			    if (*s == '-')
2928116716Sluigi				errx(EX_DATAERR, "double '-' in range");
2929116716Sluigi			}
2930116716Sluigi			for (; i <= a; i++)
2931117328Sluigi			    map[i/32] |= 1<<(i & 31);
2932116716Sluigi			i = -1;
2933116716Sluigi			if (*s == '-')
2934116716Sluigi			    i = a;
2935117328Sluigi			else if (*s == '}')
2936116716Sluigi			    break;
293798943Sluigi			av = s+1;
293898943Sluigi		}
293998943Sluigi		return;
294098943Sluigi	}
2941117328Sluigi	av = p;
2942117328Sluigi	if (av)			/* then *av must be a ',' */
2943117328Sluigi		av++;
294498943Sluigi
2945117328Sluigi	/* Check this entry */
2946117328Sluigi	if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */
2947117328Sluigi		/*
2948117328Sluigi		 * 'any' turns the entire list into a NOP.
2949117328Sluigi		 * 'not any' never matches, so it is removed from the
2950117328Sluigi		 * list unless it is the only item, in which case we
2951117328Sluigi		 * report an error.
2952117328Sluigi		 */
2953117328Sluigi		if (cmd->o.len & F_NOT) {	/* "not any" never matches */
2954117328Sluigi			if (av == NULL && len == 0) /* only this entry */
2955117328Sluigi				errx(EX_DATAERR, "not any never matches");
2956117328Sluigi		}
2957117328Sluigi		/* else do nothing and skip this entry */
2958128067Smaxim		return;
2959117328Sluigi	}
2960117328Sluigi	/* A single IP can be stored in an optimized format */
2961117328Sluigi	if (d[1] == IP_MASK_ALL && av == NULL && len == 0) {
296298943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2963117328Sluigi		return;
2964117328Sluigi	}
2965117328Sluigi	len += 2;	/* two words... */
2966117328Sluigi	d += 2;
2967117328Sluigi    } /* end while */
2968162363Sjhay    if (len + 1 > F_LEN_MASK)
2969162363Sjhay	errx(EX_DATAERR, "address list too long");
2970117328Sluigi    cmd->o.len |= len+1;
297198943Sluigi}
297298943Sluigi
297398943Sluigi
2974145246Sbrooks/* Try to find ipv6 address by hostname */
2975145246Sbrooksstatic int
2976145246Sbrookslookup_host6 (char *host, struct in6_addr *ip6addr)
2977145246Sbrooks{
2978145246Sbrooks	struct hostent *he;
2979145246Sbrooks
2980145246Sbrooks	if (!inet_pton(AF_INET6, host, ip6addr)) {
2981145246Sbrooks		if ((he = gethostbyname2(host, AF_INET6)) == NULL)
2982145246Sbrooks			return(-1);
2983145246Sbrooks		memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr));
2984145246Sbrooks	}
2985145246Sbrooks	return(0);
2986145246Sbrooks}
2987145246Sbrooks
2988145246Sbrooks
2989145246Sbrooks/* n2mask sets n bits of the mask */
2990145246Sbrooksstatic void
2991145246Sbrooksn2mask(struct in6_addr *mask, int n)
2992145246Sbrooks{
2993145246Sbrooks	static int	minimask[9] =
2994145246Sbrooks	    { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
2995145246Sbrooks	u_char		*p;
2996145246Sbrooks
2997145246Sbrooks	memset(mask, 0, sizeof(struct in6_addr));
2998145246Sbrooks	p = (u_char *) mask;
2999145246Sbrooks	for (; n > 0; p++, n -= 8) {
3000145246Sbrooks		if (n >= 8)
3001145246Sbrooks			*p = 0xff;
3002145246Sbrooks		else
3003145246Sbrooks			*p = minimask[n];
3004145246Sbrooks	}
3005145246Sbrooks	return;
3006145246Sbrooks}
3007145246Sbrooks
3008145246Sbrooks
300998943Sluigi/*
3010145246Sbrooks * fill the addr and mask fields in the instruction as appropriate from av.
3011145246Sbrooks * Update length as appropriate.
3012145246Sbrooks * The following formats are allowed:
3013145246Sbrooks *     any     matches any IP6. Actually returns an empty instruction.
3014145246Sbrooks *     me      returns O_IP6_*_ME
3015145246Sbrooks *
3016145246Sbrooks *     03f1::234:123:0342                single IP6 addres
3017145246Sbrooks *     03f1::234:123:0342/24            address/mask
3018145246Sbrooks *     03f1::234:123:0342/24,03f1::234:123:0343/               List of address
3019145246Sbrooks *
3020145246Sbrooks * Set of address (as in ipv6) not supported because ipv6 address
3021145246Sbrooks * are typically random past the initial prefix.
3022145246Sbrooks * Return 1 on success, 0 on failure.
3023145246Sbrooks */
3024145246Sbrooksstatic int
3025145246Sbrooksfill_ip6(ipfw_insn_ip6 *cmd, char *av)
3026145246Sbrooks{
3027145246Sbrooks	int len = 0;
3028145246Sbrooks	struct in6_addr *d = &(cmd->addr6);
3029145246Sbrooks	/*
3030145246Sbrooks	 * Needed for multiple address.
3031145246Sbrooks	 * Note d[1] points to struct in6_add r mask6 of cmd
3032145246Sbrooks	 */
3033145246Sbrooks
3034145246Sbrooks       cmd->o.len &= ~F_LEN_MASK;	/* zero len */
3035145246Sbrooks
3036145246Sbrooks       if (strcmp(av, "any") == 0)
3037145246Sbrooks	       return (1);
3038145246Sbrooks
3039145246Sbrooks
3040145246Sbrooks       if (strcmp(av, "me") == 0) {	/* Set the data for "me" opt*/
3041145246Sbrooks	       cmd->o.len |= F_INSN_SIZE(ipfw_insn);
3042145246Sbrooks	       return (1);
3043145246Sbrooks       }
3044145246Sbrooks
3045145246Sbrooks       if (strcmp(av, "me6") == 0) {	/* Set the data for "me" opt*/
3046145246Sbrooks	       cmd->o.len |= F_INSN_SIZE(ipfw_insn);
3047145246Sbrooks	       return (1);
3048145246Sbrooks       }
3049145246Sbrooks
3050145246Sbrooks       av = strdup(av);
3051145246Sbrooks       while (av) {
3052145246Sbrooks		/*
3053145246Sbrooks		 * After the address we can have '/' indicating a mask,
3054145246Sbrooks		 * or ',' indicating another address follows.
3055145246Sbrooks		 */
3056145246Sbrooks
3057145246Sbrooks		char *p;
3058145246Sbrooks		int masklen;
3059145246Sbrooks		char md = '\0';
3060145246Sbrooks
3061145246Sbrooks		if ((p = strpbrk(av, "/,")) ) {
3062145246Sbrooks			md = *p;	/* save the separator */
3063145246Sbrooks			*p = '\0';	/* terminate address string */
3064145246Sbrooks			p++;		/* and skip past it */
3065145246Sbrooks		}
3066145246Sbrooks		/* now p points to NULL, mask or next entry */
3067145246Sbrooks
3068145246Sbrooks		/* lookup stores address in *d as a side effect */
3069145246Sbrooks		if (lookup_host6(av, d) != 0) {
3070145246Sbrooks			/* XXX: failed. Free memory and go */
3071145246Sbrooks			errx(EX_DATAERR, "bad address \"%s\"", av);
3072145246Sbrooks		}
3073145246Sbrooks		/* next, look at the mask, if any */
3074145246Sbrooks		masklen = (md == '/') ? atoi(p) : 128;
3075145246Sbrooks		if (masklen > 128 || masklen < 0)
3076145246Sbrooks			errx(EX_DATAERR, "bad width \"%s\''", p);
3077145246Sbrooks		else
3078145246Sbrooks			n2mask(&d[1], masklen);
3079145246Sbrooks
3080145246Sbrooks		APPLY_MASK(d, &d[1])   /* mask base address with mask */
3081145246Sbrooks
3082145246Sbrooks		/* find next separator */
3083145246Sbrooks
3084145246Sbrooks		if (md == '/') {	/* find separator past the mask */
3085145246Sbrooks			p = strpbrk(p, ",");
3086145246Sbrooks			if (p != NULL)
3087145246Sbrooks				p++;
3088145246Sbrooks		}
3089145246Sbrooks		av = p;
3090145246Sbrooks
3091145246Sbrooks		/* Check this entry */
3092145246Sbrooks		if (masklen == 0) {
3093145246Sbrooks			/*
3094145246Sbrooks			 * 'any' turns the entire list into a NOP.
3095145246Sbrooks			 * 'not any' never matches, so it is removed from the
3096145246Sbrooks			 * list unless it is the only item, in which case we
3097145246Sbrooks			 * report an error.
3098145246Sbrooks			 */
3099145246Sbrooks			if (cmd->o.len & F_NOT && av == NULL && len == 0)
3100145246Sbrooks				errx(EX_DATAERR, "not any never matches");
3101145246Sbrooks			continue;
3102145246Sbrooks		}
3103145246Sbrooks
3104145246Sbrooks		/*
3105145246Sbrooks		 * A single IP can be stored alone
3106145246Sbrooks		 */
3107145246Sbrooks		if (masklen == 128 && av == NULL && len == 0) {
3108145246Sbrooks			len = F_INSN_SIZE(struct in6_addr);
3109145246Sbrooks			break;
3110145246Sbrooks		}
3111145246Sbrooks
3112145246Sbrooks		/* Update length and pointer to arguments */
3113145246Sbrooks		len += F_INSN_SIZE(struct in6_addr)*2;
3114145246Sbrooks		d += 2;
3115145246Sbrooks	} /* end while */
3116145246Sbrooks
3117145246Sbrooks	/*
3118145246Sbrooks	 * Total length of the command, remember that 1 is the size of
3119145246Sbrooks	 * the base command.
3120145246Sbrooks	 */
3121162363Sjhay	if (len + 1 > F_LEN_MASK)
3122162363Sjhay		errx(EX_DATAERR, "address list too long");
3123145246Sbrooks	cmd->o.len |= len+1;
3124145246Sbrooks	free(av);
3125145246Sbrooks	return (1);
3126145246Sbrooks}
3127145246Sbrooks
3128145246Sbrooks/*
3129145246Sbrooks * fills command for ipv6 flow-id filtering
3130145246Sbrooks * note that the 20 bit flow number is stored in a array of u_int32_t
3131145246Sbrooks * it's supported lists of flow-id, so in the o.arg1 we store how many
3132145246Sbrooks * additional flow-id we want to filter, the basic is 1
3133145246Sbrooks */
3134187477Sluigistatic void
3135145246Sbrooksfill_flow6( ipfw_insn_u32 *cmd, char *av )
3136145246Sbrooks{
3137145246Sbrooks	u_int32_t type;	 /* Current flow number */
3138145246Sbrooks	u_int16_t nflow = 0;    /* Current flow index */
3139145246Sbrooks	char *s = av;
3140145246Sbrooks	cmd->d[0] = 0;	  /* Initializing the base number*/
3141145246Sbrooks
3142145246Sbrooks	while (s) {
3143145246Sbrooks		av = strsep( &s, ",") ;
3144145246Sbrooks		type = strtoul(av, &av, 0);
3145145246Sbrooks		if (*av != ',' && *av != '\0')
3146145246Sbrooks			errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
3147145246Sbrooks		if (type > 0xfffff)
3148145246Sbrooks			errx(EX_DATAERR, "flow number out of range %s", av);
3149145246Sbrooks		cmd->d[nflow] |= type;
3150145246Sbrooks		nflow++;
3151145246Sbrooks	}
3152145246Sbrooks	if( nflow > 0 ) {
3153145246Sbrooks		cmd->o.opcode = O_FLOW6ID;
3154145246Sbrooks		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow;
3155145246Sbrooks		cmd->o.arg1 = nflow;
3156145246Sbrooks	}
3157145246Sbrooks	else {
3158145246Sbrooks		errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
3159145246Sbrooks	}
3160145246Sbrooks}
3161145246Sbrooks
3162145246Sbrooksstatic ipfw_insn *
3163145246Sbrooksadd_srcip6(ipfw_insn *cmd, char *av)
3164145246Sbrooks{
3165145246Sbrooks
3166145246Sbrooks	fill_ip6((ipfw_insn_ip6 *)cmd, av);
3167187477Sluigi	if (F_LEN(cmd) == 0) {				/* any */
3168187477Sluigi	} else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) {	/* "me" */
3169145246Sbrooks		cmd->opcode = O_IP6_SRC_ME;
3170145246Sbrooks	} else if (F_LEN(cmd) ==
3171145246Sbrooks	    (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
3172145246Sbrooks		/* single IP, no mask*/
3173145246Sbrooks		cmd->opcode = O_IP6_SRC;
3174145246Sbrooks	} else {					/* addr/mask opt */
3175145246Sbrooks		cmd->opcode = O_IP6_SRC_MASK;
3176145246Sbrooks	}
3177145246Sbrooks	return cmd;
3178145246Sbrooks}
3179145246Sbrooks
3180145246Sbrooksstatic ipfw_insn *
3181145246Sbrooksadd_dstip6(ipfw_insn *cmd, char *av)
3182145246Sbrooks{
3183145246Sbrooks
3184145246Sbrooks	fill_ip6((ipfw_insn_ip6 *)cmd, av);
3185187477Sluigi	if (F_LEN(cmd) == 0) {				/* any */
3186187477Sluigi	} else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) {	/* "me" */
3187145246Sbrooks		cmd->opcode = O_IP6_DST_ME;
3188145246Sbrooks	} else if (F_LEN(cmd) ==
3189145246Sbrooks	    (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
3190145246Sbrooks		/* single IP, no mask*/
3191145246Sbrooks		cmd->opcode = O_IP6_DST;
3192145246Sbrooks	} else {					/* addr/mask opt */
3193145246Sbrooks		cmd->opcode = O_IP6_DST_MASK;
3194145246Sbrooks	}
3195145246Sbrooks	return cmd;
3196145246Sbrooks}
3197145246Sbrooks
3198145246Sbrooks
3199145246Sbrooks/*
320098943Sluigi * helper function to process a set of flags and set bits in the
320198943Sluigi * appropriate masks.
320298943Sluigi */
320398943Sluigistatic void
320498943Sluigifill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
320598943Sluigi	struct _s_x *flags, char *p)
320698943Sluigi{
3207117328Sluigi	uint8_t set=0, clear=0;
320898943Sluigi
320998943Sluigi	while (p && *p) {
321098943Sluigi		char *q;	/* points to the separator */
321198943Sluigi		int val;
3212117328Sluigi		uint8_t *which;	/* mask we are working on */
321398943Sluigi
321498943Sluigi		if (*p == '!') {
321598943Sluigi			p++;
321698943Sluigi			which = &clear;
321798943Sluigi		} else
321898943Sluigi			which = &set;
321998943Sluigi		q = strchr(p, ',');
322098943Sluigi		if (q)
322198943Sluigi			*q++ = '\0';
322298943Sluigi		val = match_token(flags, p);
322398943Sluigi		if (val <= 0)
322498943Sluigi			errx(EX_DATAERR, "invalid flag %s", p);
3225117328Sluigi		*which |= (uint8_t)val;
322698943Sluigi		p = q;
322798943Sluigi	}
322898943Sluigi        cmd->opcode = opcode;
322998943Sluigi        cmd->len =  (cmd->len & (F_NOT | F_OR)) | 1;
323098943Sluigi        cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
323198943Sluigi}
323298943Sluigi
323398943Sluigi
323498943Sluigistatic void
323598943Sluigidelete(int ac, char *av[])
323698943Sluigi{
3237117328Sluigi	uint32_t rulenum;
3238117469Sluigi	struct dn_pipe p;
323998943Sluigi	int i;
324098943Sluigi	int exitval = EX_OK;
3241101628Sluigi	int do_set = 0;
324298943Sluigi
3243117469Sluigi	memset(&p, 0, sizeof p);
324498943Sluigi
324598943Sluigi	av++; ac--;
3246130013Scsjp	NEED1("missing rule specification");
3247140271Sbrooks	if (ac > 0 && _substrcmp(*av, "set") == 0) {
3248170923Smaxim		/* Do not allow using the following syntax:
3249170923Smaxim		 *	ipfw set N delete set M
3250170923Smaxim		 */
3251170923Smaxim		if (use_set)
3252170923Smaxim			errx(EX_DATAERR, "invalid syntax");
3253101978Sluigi		do_set = 1;	/* delete set */
3254101628Sluigi		ac--; av++;
3255101978Sluigi	}
325698943Sluigi
325798943Sluigi	/* Rule number */
325898943Sluigi	while (ac && isdigit(**av)) {
325998943Sluigi		i = atoi(*av); av++; ac--;
3260165648Spiso		if (do_nat) {
3261165648Spiso			exitval = do_cmd(IP_FW_NAT_DEL, &i, sizeof i);
3262165648Spiso			if (exitval) {
3263165648Spiso				exitval = EX_UNAVAILABLE;
3264165648Spiso				warn("rule %u not available", i);
3265165648Spiso			}
3266165648Spiso 		} else if (do_pipe) {
326798943Sluigi			if (do_pipe == 1)
3268117469Sluigi				p.pipe_nr = i;
326998943Sluigi			else
3270117469Sluigi				p.fs.fs_nr = i;
3271117469Sluigi			i = do_cmd(IP_DUMMYNET_DEL, &p, sizeof p);
327298943Sluigi			if (i) {
327398943Sluigi				exitval = 1;
327498943Sluigi				warn("rule %u: setsockopt(IP_DUMMYNET_DEL)",
3275117469Sluigi				    do_pipe == 1 ? p.pipe_nr : p.fs.fs_nr);
327698943Sluigi			}
327798943Sluigi		} else {
3278170923Smaxim			if (use_set)
3279170923Smaxim				rulenum = (i & 0xffff) | (5 << 24) |
3280170923Smaxim				    ((use_set - 1) << 16);
3281170923Smaxim			else
3282101978Sluigi			rulenum =  (i & 0xffff) | (do_set << 24);
3283117328Sluigi			i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum);
328498943Sluigi			if (i) {
328598943Sluigi				exitval = EX_UNAVAILABLE;
328698943Sluigi				warn("rule %u: setsockopt(IP_FW_DEL)",
328798943Sluigi				    rulenum);
328898943Sluigi			}
328998943Sluigi		}
329098943Sluigi	}
329198943Sluigi	if (exitval != EX_OK)
329298943Sluigi		exit(exitval);
329398943Sluigi}
329498943Sluigi
329598943Sluigi
329698943Sluigi/*
329798943Sluigi * fill the interface structure. We do not check the name as we can
329898943Sluigi * create interfaces dynamically, so checking them at insert time
329998943Sluigi * makes relatively little sense.
3300121816Sbrooks * Interface names containing '*', '?', or '[' are assumed to be shell
3301121816Sbrooks * patterns which match interfaces.
330298943Sluigi */
330398943Sluigistatic void
330498943Sluigifill_iface(ipfw_insn_if *cmd, char *arg)
330598943Sluigi{
330698943Sluigi	cmd->name[0] = '\0';
330798943Sluigi	cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
330898943Sluigi
330998943Sluigi	/* Parse the interface or address */
3310140271Sbrooks	if (strcmp(arg, "any") == 0)
331198943Sluigi		cmd->o.len = 0;		/* effectively ignore this command */
331298943Sluigi	else if (!isdigit(*arg)) {
3313121816Sbrooks		strlcpy(cmd->name, arg, sizeof(cmd->name));
3314121816Sbrooks		cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
331598943Sluigi	} else if (!inet_aton(arg, &cmd->p.ip))
331698943Sluigi		errx(EX_DATAERR, "bad ip address ``%s''", arg);
331798943Sluigi}
331898943Sluigi
3319165648Spiso/*
3320165648Spiso * Search for interface with name "ifn", and fill n accordingly:
3321165648Spiso *
3322165648Spiso * n->ip        ip address of interface "ifn"
3323165648Spiso * n->if_name   copy of interface name "ifn"
3324165648Spiso */
332598943Sluigistatic void
3326165648Spisoset_addr_dynamic(const char *ifn, struct cfg_nat *n)
3327165648Spiso{
3328165648Spiso	size_t needed;
3329165648Spiso	int mib[6];
3330165648Spiso	char *buf, *lim, *next;
3331165648Spiso	struct if_msghdr *ifm;
3332165648Spiso	struct ifa_msghdr *ifam;
3333165648Spiso	struct sockaddr_dl *sdl;
3334165648Spiso	struct sockaddr_in *sin;
3335165648Spiso	int ifIndex, ifMTU;
3336165648Spiso
3337165648Spiso	mib[0] = CTL_NET;
3338165648Spiso	mib[1] = PF_ROUTE;
3339165648Spiso	mib[2] = 0;
3340165648Spiso	mib[3] = AF_INET;
3341165648Spiso	mib[4] = NET_RT_IFLIST;
3342165648Spiso	mib[5] = 0;
3343165648Spiso/*
3344165648Spiso * Get interface data.
3345165648Spiso */
3346165648Spiso	if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
3347165648Spiso		err(1, "iflist-sysctl-estimate");
3348187716Sluigi	buf = safe_calloc(1, needed);
3349165648Spiso	if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
3350165648Spiso		err(1, "iflist-sysctl-get");
3351165648Spiso	lim = buf + needed;
3352165648Spiso/*
3353165648Spiso * Loop through interfaces until one with
3354165648Spiso * given name is found. This is done to
3355165648Spiso * find correct interface index for routing
3356165648Spiso * message processing.
3357165648Spiso */
3358165648Spiso	ifIndex	= 0;
3359165648Spiso	next = buf;
3360165648Spiso	while (next < lim) {
3361165648Spiso		ifm = (struct if_msghdr *)next;
3362165648Spiso		next += ifm->ifm_msglen;
3363165648Spiso		if (ifm->ifm_version != RTM_VERSION) {
3364165648Spiso			if (verbose)
3365165648Spiso				warnx("routing message version %d "
3366165648Spiso				    "not understood", ifm->ifm_version);
3367165648Spiso			continue;
3368165648Spiso		}
3369165648Spiso		if (ifm->ifm_type == RTM_IFINFO) {
3370165648Spiso			sdl = (struct sockaddr_dl *)(ifm + 1);
3371165648Spiso			if (strlen(ifn) == sdl->sdl_nlen &&
3372165648Spiso			    strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
3373165648Spiso				ifIndex = ifm->ifm_index;
3374165648Spiso				ifMTU = ifm->ifm_data.ifi_mtu;
3375165648Spiso				break;
3376165648Spiso			}
3377165648Spiso		}
3378165648Spiso	}
3379165648Spiso	if (!ifIndex)
3380165648Spiso		errx(1, "unknown interface name %s", ifn);
3381165648Spiso/*
3382165648Spiso * Get interface address.
3383165648Spiso */
3384165648Spiso	sin = NULL;
3385165648Spiso	while (next < lim) {
3386165648Spiso		ifam = (struct ifa_msghdr *)next;
3387165648Spiso		next += ifam->ifam_msglen;
3388165648Spiso		if (ifam->ifam_version != RTM_VERSION) {
3389165648Spiso			if (verbose)
3390165648Spiso				warnx("routing message version %d "
3391165648Spiso				    "not understood", ifam->ifam_version);
3392165648Spiso			continue;
3393165648Spiso		}
3394165648Spiso		if (ifam->ifam_type != RTM_NEWADDR)
3395165648Spiso			break;
3396165648Spiso		if (ifam->ifam_addrs & RTA_IFA) {
3397165648Spiso			int i;
3398165648Spiso			char *cp = (char *)(ifam + 1);
3399165648Spiso
3400165648Spiso			for (i = 1; i < RTA_IFA; i <<= 1) {
3401165648Spiso				if (ifam->ifam_addrs & i)
3402165648Spiso					cp += SA_SIZE((struct sockaddr *)cp);
3403165648Spiso			}
3404165648Spiso			if (((struct sockaddr *)cp)->sa_family == AF_INET) {
3405165648Spiso				sin = (struct sockaddr_in *)cp;
3406165648Spiso				break;
3407165648Spiso			}
3408165648Spiso		}
3409165648Spiso	}
3410165648Spiso	if (sin == NULL)
3411165648Spiso		errx(1, "%s: cannot get interface address", ifn);
3412165648Spiso
3413165648Spiso	n->ip = sin->sin_addr;
3414165648Spiso	strncpy(n->if_name, ifn, IF_NAMESIZE);
3415165648Spiso
3416165648Spiso	free(buf);
3417165648Spiso}
3418165648Spiso
3419165648Spiso/*
3420165648Spiso * XXX - The following functions, macros and definitions come from natd.c:
3421165648Spiso * it would be better to move them outside natd.c, in a file
3422165648Spiso * (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live
3423165648Spiso * with it.
3424165648Spiso */
3425165648Spiso
3426165648Spiso/*
3427165648Spiso * Definition of a port range, and macros to deal with values.
3428165648Spiso * FORMAT:  HI 16-bits == first port in range, 0 == all ports.
3429165648Spiso *          LO 16-bits == number of ports in range
3430165648Spiso * NOTES:   - Port values are not stored in network byte order.
3431165648Spiso */
3432165648Spiso
3433165648Spiso#define port_range u_long
3434165648Spiso
3435165648Spiso#define GETLOPORT(x)     ((x) >> 0x10)
3436165648Spiso#define GETNUMPORTS(x)   ((x) & 0x0000ffff)
3437165648Spiso#define GETHIPORT(x)     (GETLOPORT((x)) + GETNUMPORTS((x)))
3438165648Spiso
3439165648Spiso/* Set y to be the low-port value in port_range variable x. */
3440165648Spiso#define SETLOPORT(x,y)   ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
3441165648Spiso
3442165648Spiso/* Set y to be the number of ports in port_range variable x. */
3443165648Spiso#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
3444165648Spiso
3445165648Spisostatic void
3446165648SpisoStrToAddr (const char* str, struct in_addr* addr)
3447165648Spiso{
3448165648Spiso	struct hostent* hp;
3449165648Spiso
3450165648Spiso	if (inet_aton (str, addr))
3451165648Spiso		return;
3452165648Spiso
3453165648Spiso	hp = gethostbyname (str);
3454165648Spiso	if (!hp)
3455165648Spiso		errx (1, "unknown host %s", str);
3456165648Spiso
3457165648Spiso	memcpy (addr, hp->h_addr, sizeof (struct in_addr));
3458165648Spiso}
3459165648Spiso
3460165648Spisostatic int
3461165648SpisoStrToPortRange (const char* str, const char* proto, port_range *portRange)
3462165648Spiso{
3463165648Spiso	char*           sep;
3464165648Spiso	struct servent*	sp;
3465165648Spiso	char*		end;
3466165648Spiso	u_short         loPort;
3467165648Spiso	u_short         hiPort;
3468165648Spiso
3469165648Spiso	/* First see if this is a service, return corresponding port if so. */
3470165648Spiso	sp = getservbyname (str,proto);
3471165648Spiso	if (sp) {
3472165648Spiso	        SETLOPORT(*portRange, ntohs(sp->s_port));
3473165648Spiso		SETNUMPORTS(*portRange, 1);
3474165648Spiso		return 0;
3475165648Spiso	}
3476165648Spiso
3477165648Spiso	/* Not a service, see if it's a single port or port range. */
3478165648Spiso	sep = strchr (str, '-');
3479165648Spiso	if (sep == NULL) {
3480165648Spiso	        SETLOPORT(*portRange, strtol(str, &end, 10));
3481165648Spiso		if (end != str) {
3482165648Spiso		        /* Single port. */
3483165648Spiso		        SETNUMPORTS(*portRange, 1);
3484165648Spiso			return 0;
3485165648Spiso		}
3486165648Spiso
3487165648Spiso		/* Error in port range field. */
3488165648Spiso		errx (EX_DATAERR, "%s/%s: unknown service", str, proto);
3489165648Spiso	}
3490165648Spiso
3491165648Spiso	/* Port range, get the values and sanity check. */
3492165648Spiso	sscanf (str, "%hu-%hu", &loPort, &hiPort);
3493165648Spiso	SETLOPORT(*portRange, loPort);
3494165648Spiso	SETNUMPORTS(*portRange, 0);	/* Error by default */
3495165648Spiso	if (loPort <= hiPort)
3496165648Spiso	        SETNUMPORTS(*portRange, hiPort - loPort + 1);
3497165648Spiso
3498165648Spiso	if (GETNUMPORTS(*portRange) == 0)
3499165648Spiso	        errx (EX_DATAERR, "invalid port range %s", str);
3500165648Spiso
3501165648Spiso	return 0;
3502165648Spiso}
3503165648Spiso
3504165648Spisostatic int
3505165648SpisoStrToProto (const char* str)
3506165648Spiso{
3507165648Spiso	if (!strcmp (str, "tcp"))
3508165648Spiso		return IPPROTO_TCP;
3509165648Spiso
3510165648Spiso	if (!strcmp (str, "udp"))
3511165648Spiso		return IPPROTO_UDP;
3512165648Spiso
3513165648Spiso	errx (EX_DATAERR, "unknown protocol %s. Expected tcp or udp", str);
3514165648Spiso}
3515165648Spiso
3516165648Spisostatic int
3517165648SpisoStrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
3518165648Spiso		       port_range *portRange)
3519165648Spiso{
3520165648Spiso	char*	ptr;
3521165648Spiso
3522165648Spiso	ptr = strchr (str, ':');
3523165648Spiso	if (!ptr)
3524165648Spiso		errx (EX_DATAERR, "%s is missing port number", str);
3525165648Spiso
3526165648Spiso	*ptr = '\0';
3527165648Spiso	++ptr;
3528165648Spiso
3529165648Spiso	StrToAddr (str, addr);
3530165648Spiso	return StrToPortRange (ptr, proto, portRange);
3531165648Spiso}
3532165648Spiso
3533165648Spiso/* End of stuff taken from natd.c. */
3534165648Spiso
3535165648Spiso#define INC_ARGCV() do {        \
3536165648Spiso	(*_av)++;               \
3537165648Spiso	(*_ac)--;               \
3538165648Spiso	av = *_av;              \
3539165648Spiso	ac = *_ac;              \
3540165648Spiso} while(0)
3541165648Spiso
3542165648Spiso/*
3543165648Spiso * The next 3 functions add support for the addr, port and proto redirect and
3544165648Spiso * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect()
3545165648Spiso * and SetupProtoRedirect() from natd.c.
3546165648Spiso *
3547165648Spiso * Every setup_* function fills at least one redirect entry
3548165648Spiso * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool)
3549165648Spiso * in buf.
3550165648Spiso *
3551165648Spiso * The format of data in buf is:
3552165648Spiso *
3553165648Spiso *
3554165648Spiso *     cfg_nat    cfg_redir    cfg_spool    ......  cfg_spool
3555165648Spiso *
3556165648Spiso *    -------------------------------------        ------------
3557165648Spiso *   |          | .....X ... |          |         |           |  .....
3558165648Spiso *    ------------------------------------- ...... ------------
3559165648Spiso *                     ^
3560165648Spiso *                spool_cnt       n=0       ......   n=(X-1)
3561165648Spiso *
3562165648Spiso * len points to the amount of available space in buf
3563165648Spiso * space counts the memory consumed by every function
3564165648Spiso *
3565165648Spiso * XXX - Every function get all the argv params so it
3566165648Spiso * has to check, in optional parameters, that the next
3567165648Spiso * args is a valid option for the redir entry and not
3568165648Spiso * another token. Only redir_port and redir_proto are
3569165648Spiso * affected by this.
3570165648Spiso */
3571165648Spiso
3572165648Spisostatic int
3573165648Spisosetup_redir_addr(char *spool_buf, int len,
3574165648Spiso		 int *_ac, char ***_av)
3575165648Spiso{
3576165648Spiso	char **av, *sep; /* Token separator. */
3577165648Spiso	/* Temporary buffer used to hold server pool ip's. */
3578165648Spiso	char tmp_spool_buf[NAT_BUF_LEN];
3579183208Srik	int ac, space, lsnat;
3580165648Spiso	struct cfg_redir *r;
3581165648Spiso	struct cfg_spool *tmp;
3582165648Spiso
3583165648Spiso	av = *_av;
3584165648Spiso	ac = *_ac;
3585165648Spiso	space = 0;
3586165648Spiso	lsnat = 0;
3587165648Spiso	if (len >= SOF_REDIR) {
3588165648Spiso		r = (struct cfg_redir *)spool_buf;
3589165648Spiso		/* Skip cfg_redir at beginning of buf. */
3590165648Spiso		spool_buf = &spool_buf[SOF_REDIR];
3591165648Spiso		space = SOF_REDIR;
3592165648Spiso		len -= SOF_REDIR;
3593165648Spiso	} else
3594165648Spiso		goto nospace;
3595165648Spiso	r->mode = REDIR_ADDR;
3596165648Spiso	/* Extract local address. */
3597165648Spiso	if (ac == 0)
3598165648Spiso		errx(EX_DATAERR, "redirect_addr: missing local address");
3599165648Spiso	sep = strchr(*av, ',');
3600165648Spiso	if (sep) {		/* LSNAT redirection syntax. */
3601165648Spiso		r->laddr.s_addr = INADDR_NONE;
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		StrToAddr(*av, &r->laddr);
3607165648Spiso	INC_ARGCV();
3608165648Spiso
3609165648Spiso	/* Extract public address. */
3610165648Spiso	if (ac == 0)
3611165648Spiso		errx(EX_DATAERR, "redirect_addr: missing public address");
3612165648Spiso	StrToAddr(*av, &r->paddr);
3613165648Spiso	INC_ARGCV();
3614165648Spiso
3615165648Spiso	/* Setup LSNAT server pool. */
3616165648Spiso	if (sep) {
3617165648Spiso		sep = strtok(tmp_spool_buf, ",");
3618165648Spiso		while (sep != NULL) {
3619165648Spiso			tmp = (struct cfg_spool *)spool_buf;
3620165648Spiso			if (len < SOF_SPOOL)
3621165648Spiso				goto nospace;
3622165648Spiso			len -= SOF_SPOOL;
3623165648Spiso			space += SOF_SPOOL;
3624165648Spiso			StrToAddr(sep, &tmp->addr);
3625165648Spiso			tmp->port = ~0;
3626165648Spiso			r->spool_cnt++;
3627165648Spiso			/* Point to the next possible cfg_spool. */
3628165648Spiso			spool_buf = &spool_buf[SOF_SPOOL];
3629165648Spiso			sep = strtok(NULL, ",");
3630165648Spiso		}
3631165648Spiso	}
3632165648Spiso	return(space);
3633165648Spisonospace:
3634165648Spiso	errx(EX_DATAERR, "redirect_addr: buf is too small\n");
3635165648Spiso}
3636165648Spiso
3637165648Spisostatic int
3638165648Spisosetup_redir_port(char *spool_buf, int len,
3639165648Spiso		 int *_ac, char ***_av)
3640165648Spiso{
3641165648Spiso	char **av, *sep, *protoName;
3642165648Spiso	char tmp_spool_buf[NAT_BUF_LEN];
3643165648Spiso	int ac, space, lsnat;
3644165648Spiso	struct cfg_redir *r;
3645165648Spiso	struct cfg_spool *tmp;
3646165648Spiso	u_short numLocalPorts;
3647165648Spiso	port_range portRange;
3648165648Spiso
3649165648Spiso	av = *_av;
3650165648Spiso	ac = *_ac;
3651165648Spiso	space = 0;
3652165648Spiso	lsnat = 0;
3653165648Spiso	numLocalPorts = 0;
3654165648Spiso
3655165648Spiso	if (len >= SOF_REDIR) {
3656165648Spiso		r = (struct cfg_redir *)spool_buf;
3657165648Spiso		/* Skip cfg_redir at beginning of buf. */
3658165648Spiso		spool_buf = &spool_buf[SOF_REDIR];
3659165648Spiso		space = SOF_REDIR;
3660165648Spiso		len -= SOF_REDIR;
3661165648Spiso	} else
3662165648Spiso		goto nospace;
3663165648Spiso	r->mode = REDIR_PORT;
3664165648Spiso	/*
3665165648Spiso	 * Extract protocol.
3666165648Spiso	 */
3667165648Spiso	if (ac == 0)
3668165648Spiso		errx (EX_DATAERR, "redirect_port: missing protocol");
3669165648Spiso	r->proto = StrToProto(*av);
3670165648Spiso	protoName = *av;
3671165648Spiso	INC_ARGCV();
3672165648Spiso
3673165648Spiso	/*
3674165648Spiso	 * Extract local address.
3675165648Spiso	 */
3676165648Spiso	if (ac == 0)
3677165648Spiso		errx (EX_DATAERR, "redirect_port: missing local address");
3678165648Spiso
3679165648Spiso	sep = strchr(*av, ',');
3680165648Spiso	/* LSNAT redirection syntax. */
3681165648Spiso	if (sep) {
3682165648Spiso		r->laddr.s_addr = INADDR_NONE;
3683165648Spiso		r->lport = ~0;
3684165648Spiso		numLocalPorts = 1;
3685165648Spiso		/* Preserve av, copy spool servers to tmp_spool_buf. */
3686165648Spiso		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
3687165648Spiso		lsnat = 1;
3688165648Spiso	} else {
3689165648Spiso		if (StrToAddrAndPortRange (*av, &r->laddr, protoName,
3690165648Spiso		    &portRange) != 0)
3691165648Spiso			errx(EX_DATAERR, "redirect_port:"
3692165648Spiso			    "invalid local port range");
3693165648Spiso
3694165648Spiso		r->lport = GETLOPORT(portRange);
3695165648Spiso		numLocalPorts = GETNUMPORTS(portRange);
3696165648Spiso	}
3697165648Spiso	INC_ARGCV();
3698165648Spiso
3699165648Spiso	/*
3700165648Spiso	 * Extract public port and optionally address.
3701165648Spiso	 */
3702165648Spiso	if (ac == 0)
3703165648Spiso		errx (EX_DATAERR, "redirect_port: missing public port");
3704165648Spiso
3705165648Spiso	sep = strchr (*av, ':');
3706165648Spiso	if (sep) {
3707165648Spiso	        if (StrToAddrAndPortRange (*av, &r->paddr, protoName,
3708165648Spiso		    &portRange) != 0)
3709165648Spiso		        errx(EX_DATAERR, "redirect_port:"
3710165648Spiso			    "invalid public port range");
3711165648Spiso	} else {
3712165648Spiso		r->paddr.s_addr = INADDR_ANY;
3713165648Spiso		if (StrToPortRange (*av, protoName, &portRange) != 0)
3714165648Spiso		        errx(EX_DATAERR, "redirect_port:"
3715165648Spiso			    "invalid public port range");
3716165648Spiso	}
3717165648Spiso
3718165648Spiso	r->pport = GETLOPORT(portRange);
3719165648Spiso	r->pport_cnt = GETNUMPORTS(portRange);
3720165648Spiso	INC_ARGCV();
3721165648Spiso
3722165648Spiso	/*
3723165648Spiso	 * Extract remote address and optionally port.
3724165648Spiso	 */
3725165648Spiso	/*
3726165648Spiso	 * NB: isalpha(**av) => we've to check that next parameter is really an
3727165648Spiso	 * option for this redirect entry, else stop here processing arg[cv].
3728165648Spiso	 */
3729165648Spiso	if (ac != 0 && !isalpha(**av)) {
3730165648Spiso		sep = strchr (*av, ':');
3731165648Spiso		if (sep) {
3732165648Spiso		        if (StrToAddrAndPortRange (*av, &r->raddr, protoName,
3733165648Spiso			    &portRange) != 0)
3734165648Spiso				errx(EX_DATAERR, "redirect_port:"
3735165648Spiso				    "invalid remote port range");
3736165648Spiso		} else {
3737165648Spiso		        SETLOPORT(portRange, 0);
3738165648Spiso			SETNUMPORTS(portRange, 1);
3739165648Spiso			StrToAddr (*av, &r->raddr);
3740165648Spiso		}
3741165648Spiso		INC_ARGCV();
3742165648Spiso	} else {
3743165648Spiso		SETLOPORT(portRange, 0);
3744165648Spiso		SETNUMPORTS(portRange, 1);
3745165648Spiso		r->raddr.s_addr = INADDR_ANY;
3746165648Spiso	}
3747165648Spiso	r->rport = GETLOPORT(portRange);
3748165648Spiso	r->rport_cnt = GETNUMPORTS(portRange);
3749165648Spiso
3750165648Spiso	/*
3751165648Spiso	 * Make sure port ranges match up, then add the redirect ports.
3752165648Spiso	 */
3753165648Spiso	if (numLocalPorts != r->pport_cnt)
3754165648Spiso	        errx(EX_DATAERR, "redirect_port:"
3755165648Spiso		    "port ranges must be equal in size");
3756165648Spiso
3757165648Spiso	/* Remote port range is allowed to be '0' which means all ports. */
3758165648Spiso	if (r->rport_cnt != numLocalPorts &&
3759165648Spiso	    (r->rport_cnt != 1 || r->rport != 0))
3760165648Spiso	        errx(EX_DATAERR, "redirect_port: remote port must"
3761165648Spiso		    "be 0 or equal to local port range in size");
3762165648Spiso
3763165648Spiso	/*
3764165648Spiso	 * Setup LSNAT server pool.
3765165648Spiso	 */
3766165648Spiso	if (lsnat) {
3767165648Spiso		sep = strtok(tmp_spool_buf, ",");
3768165648Spiso		while (sep != NULL) {
3769165648Spiso			tmp = (struct cfg_spool *)spool_buf;
3770165648Spiso			if (len < SOF_SPOOL)
3771165648Spiso				goto nospace;
3772165648Spiso			len -= SOF_SPOOL;
3773165648Spiso			space += SOF_SPOOL;
3774165648Spiso			if (StrToAddrAndPortRange(sep, &tmp->addr, protoName,
3775165648Spiso			    &portRange) != 0)
3776165648Spiso				errx(EX_DATAERR, "redirect_port:"
3777165648Spiso				    "invalid local port range");
3778165648Spiso			if (GETNUMPORTS(portRange) != 1)
3779165648Spiso				errx(EX_DATAERR, "redirect_port: local port"
3780165648Spiso				    "must be single in this context");
3781165648Spiso			tmp->port = GETLOPORT(portRange);
3782165648Spiso			r->spool_cnt++;
3783165648Spiso			/* Point to the next possible cfg_spool. */
3784165648Spiso			spool_buf = &spool_buf[SOF_SPOOL];
3785165648Spiso			sep = strtok(NULL, ",");
3786165648Spiso		}
3787165648Spiso	}
3788165648Spiso	return (space);
3789165648Spisonospace:
3790165648Spiso	errx(EX_DATAERR, "redirect_port: buf is too small\n");
3791165648Spiso}
3792165648Spiso
3793165648Spisostatic int
3794165648Spisosetup_redir_proto(char *spool_buf, int len,
3795165648Spiso		 int *_ac, char ***_av)
3796165648Spiso{
3797165648Spiso	char **av;
3798183208Srik	int ac, space;
3799165648Spiso	struct protoent *protoent;
3800165648Spiso	struct cfg_redir *r;
3801165648Spiso
3802165648Spiso	av = *_av;
3803165648Spiso	ac = *_ac;
3804165648Spiso	if (len >= SOF_REDIR) {
3805165648Spiso		r = (struct cfg_redir *)spool_buf;
3806165648Spiso		/* Skip cfg_redir at beginning of buf. */
3807165648Spiso		spool_buf = &spool_buf[SOF_REDIR];
3808165648Spiso		space = SOF_REDIR;
3809165648Spiso		len -= SOF_REDIR;
3810165648Spiso	} else
3811165648Spiso		goto nospace;
3812165648Spiso	r->mode = REDIR_PROTO;
3813165648Spiso	/*
3814165648Spiso	 * Extract protocol.
3815165648Spiso	 */
3816165648Spiso	if (ac == 0)
3817165648Spiso		errx(EX_DATAERR, "redirect_proto: missing protocol");
3818165648Spiso
3819165648Spiso	protoent = getprotobyname(*av);
3820165648Spiso	if (protoent == NULL)
3821165648Spiso		errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av);
3822165648Spiso	else
3823165648Spiso		r->proto = protoent->p_proto;
3824165648Spiso
3825165648Spiso	INC_ARGCV();
3826165648Spiso
3827165648Spiso	/*
3828165648Spiso	 * Extract local address.
3829165648Spiso	 */
3830165648Spiso	if (ac == 0)
3831165648Spiso		errx(EX_DATAERR, "redirect_proto: missing local address");
3832165648Spiso	else
3833165648Spiso		StrToAddr(*av, &r->laddr);
3834165648Spiso
3835165648Spiso	INC_ARGCV();
3836165648Spiso
3837165648Spiso	/*
3838165648Spiso	 * Extract optional public address.
3839165648Spiso	 */
3840165648Spiso	if (ac == 0) {
3841165648Spiso		r->paddr.s_addr = INADDR_ANY;
3842165648Spiso		r->raddr.s_addr = INADDR_ANY;
3843165648Spiso	} else {
3844165648Spiso		/* see above in setup_redir_port() */
3845165648Spiso		if (!isalpha(**av)) {
3846165648Spiso			StrToAddr(*av, &r->paddr);
3847165648Spiso			INC_ARGCV();
3848165648Spiso
3849165648Spiso			/*
3850165648Spiso			 * Extract optional remote address.
3851165648Spiso			 */
3852165648Spiso			/* see above in setup_redir_port() */
3853165648Spiso			if (ac!=0 && !isalpha(**av)) {
3854165648Spiso				StrToAddr(*av, &r->raddr);
3855165648Spiso				INC_ARGCV();
3856165648Spiso			}
3857165648Spiso		}
3858165648Spiso	}
3859165648Spiso	return (space);
3860165648Spisonospace:
3861165648Spiso	errx(EX_DATAERR, "redirect_proto: buf is too small\n");
3862165648Spiso}
3863165648Spiso
3864165648Spisostatic void
3865183890Smaximshow_nat(int ac, char **av);
3866183890Smaxim
3867183890Smaximstatic void
3868187477Sluigiprint_nat_config(unsigned char *buf)
3869187477Sluigi{
3870165648Spiso	struct cfg_nat *n;
3871165648Spiso	int i, cnt, flag, off;
3872165648Spiso	struct cfg_redir *t;
3873165648Spiso	struct cfg_spool *s;
3874165648Spiso	struct protoent *p;
3875165648Spiso
3876165648Spiso	n = (struct cfg_nat *)buf;
3877165648Spiso	flag = 1;
3878165648Spiso	off  = sizeof(*n);
3879165648Spiso	printf("ipfw nat %u config", n->id);
3880165648Spiso	if (strlen(n->if_name) != 0)
3881165648Spiso		printf(" if %s", n->if_name);
3882165648Spiso	else if (n->ip.s_addr != 0)
3883165648Spiso		printf(" ip %s", inet_ntoa(n->ip));
3884165648Spiso	while (n->mode != 0) {
3885165648Spiso		if (n->mode & PKT_ALIAS_LOG) {
3886165648Spiso			printf(" log");
3887165648Spiso			n->mode &= ~PKT_ALIAS_LOG;
3888165648Spiso		} else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
3889165648Spiso			printf(" deny_in");
3890165648Spiso			n->mode &= ~PKT_ALIAS_DENY_INCOMING;
3891165648Spiso		} else if (n->mode & PKT_ALIAS_SAME_PORTS) {
3892165648Spiso			printf(" same_ports");
3893165648Spiso			n->mode &= ~PKT_ALIAS_SAME_PORTS;
3894165648Spiso		} else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
3895165648Spiso			printf(" unreg_only");
3896165648Spiso			n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
3897165648Spiso		} else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
3898165648Spiso			printf(" reset");
3899165648Spiso			n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
3900165648Spiso		} else if (n->mode & PKT_ALIAS_REVERSE) {
3901165648Spiso			printf(" reverse");
3902165648Spiso			n->mode &= ~PKT_ALIAS_REVERSE;
3903165648Spiso		} else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
3904165648Spiso			printf(" proxy_only");
3905165648Spiso			n->mode &= ~PKT_ALIAS_PROXY_ONLY;
3906165648Spiso		}
3907165648Spiso	}
3908165648Spiso	/* Print all the redirect's data configuration. */
3909165648Spiso	for (cnt = 0; cnt < n->redir_cnt; cnt++) {
3910165648Spiso		t = (struct cfg_redir *)&buf[off];
3911165648Spiso		off += SOF_REDIR;
3912165648Spiso		switch (t->mode) {
3913165648Spiso		case REDIR_ADDR:
3914165648Spiso			printf(" redirect_addr");
3915165648Spiso			if (t->spool_cnt == 0)
3916165648Spiso				printf(" %s", inet_ntoa(t->laddr));
3917165648Spiso			else
3918165648Spiso				for (i = 0; i < t->spool_cnt; i++) {
3919165648Spiso					s = (struct cfg_spool *)&buf[off];
3920165648Spiso					if (i)
3921165648Spiso						printf(",");
3922165648Spiso					else
3923165648Spiso						printf(" ");
3924165648Spiso					printf("%s", inet_ntoa(s->addr));
3925165648Spiso					off += SOF_SPOOL;
3926165648Spiso				}
3927165648Spiso			printf(" %s", inet_ntoa(t->paddr));
3928165648Spiso			break;
3929165648Spiso		case REDIR_PORT:
3930165648Spiso			p = getprotobynumber(t->proto);
3931165648Spiso			printf(" redirect_port %s ", p->p_name);
3932165648Spiso			if (!t->spool_cnt) {
3933165648Spiso				printf("%s:%u", inet_ntoa(t->laddr), t->lport);
3934165648Spiso				if (t->pport_cnt > 1)
3935165648Spiso					printf("-%u", t->lport +
3936165648Spiso					    t->pport_cnt - 1);
3937165648Spiso			} else
3938165648Spiso				for (i=0; i < t->spool_cnt; i++) {
3939165648Spiso					s = (struct cfg_spool *)&buf[off];
3940165648Spiso					if (i)
3941165648Spiso						printf(",");
3942165648Spiso					printf("%s:%u", inet_ntoa(s->addr),
3943165648Spiso					    s->port);
3944165648Spiso					off += SOF_SPOOL;
3945165648Spiso				}
3946165648Spiso
3947165648Spiso			printf(" ");
3948165648Spiso			if (t->paddr.s_addr)
3949165648Spiso				printf("%s:", inet_ntoa(t->paddr));
3950165648Spiso			printf("%u", t->pport);
3951165648Spiso			if (!t->spool_cnt && t->pport_cnt > 1)
3952165648Spiso				printf("-%u", t->pport + t->pport_cnt - 1);
3953165648Spiso
3954165648Spiso			if (t->raddr.s_addr) {
3955165648Spiso				printf(" %s", inet_ntoa(t->raddr));
3956165648Spiso				if (t->rport) {
3957165648Spiso					printf(":%u", t->rport);
3958165648Spiso					if (!t->spool_cnt && t->rport_cnt > 1)
3959165648Spiso						printf("-%u", t->rport +
3960165648Spiso						    t->rport_cnt - 1);
3961165648Spiso				}
3962165648Spiso			}
3963165648Spiso			break;
3964165648Spiso		case REDIR_PROTO:
3965165648Spiso			p = getprotobynumber(t->proto);
3966165648Spiso			printf(" redirect_proto %s %s", p->p_name,
3967165648Spiso			    inet_ntoa(t->laddr));
3968165648Spiso			if (t->paddr.s_addr != 0) {
3969165648Spiso				printf(" %s", inet_ntoa(t->paddr));
3970165648Spiso				if (t->raddr.s_addr)
3971165648Spiso					printf(" %s", inet_ntoa(t->raddr));
3972165648Spiso			}
3973165648Spiso			break;
3974165648Spiso		default:
3975165648Spiso			errx(EX_DATAERR, "unknown redir mode");
3976165648Spiso			break;
3977165648Spiso		}
3978165648Spiso	}
3979165648Spiso	printf("\n");
3980165648Spiso}
3981165648Spiso
3982165648Spisostatic void
3983165648Spisoconfig_nat(int ac, char **av)
3984165648Spiso{
3985165648Spiso	struct cfg_nat *n;              /* Nat instance configuration. */
3986165648Spiso	int i, len, off, tok;
3987165648Spiso	char *id, buf[NAT_BUF_LEN]; 	/* Buffer for serialized data. */
3988165648Spiso
3989165648Spiso	len = NAT_BUF_LEN;
3990165648Spiso	/* Offset in buf: save space for n at the beginning. */
3991165648Spiso	off = sizeof(*n);
3992165648Spiso	memset(buf, 0, sizeof(buf));
3993165648Spiso	n = (struct cfg_nat *)buf;
3994165648Spiso
3995165648Spiso	av++; ac--;
3996165648Spiso	/* Nat id. */
3997165648Spiso	if (ac && isdigit(**av)) {
3998165648Spiso		id = *av;
3999165648Spiso		i = atoi(*av);
4000165648Spiso		ac--; av++;
4001165648Spiso		n->id = i;
4002165648Spiso	} else
4003165648Spiso		errx(EX_DATAERR, "missing nat id");
4004165648Spiso	if (ac == 0)
4005165648Spiso		errx(EX_DATAERR, "missing option");
4006165648Spiso
4007165648Spiso	while (ac > 0) {
4008165648Spiso		tok = match_token(nat_params, *av);
4009165648Spiso		ac--; av++;
4010165648Spiso		switch (tok) {
4011165648Spiso		case TOK_IP:
4012165648Spiso			if (ac == 0)
4013165648Spiso				errx(EX_DATAERR, "missing option");
4014165648Spiso			if (!inet_aton(av[0], &(n->ip)))
4015165648Spiso				errx(EX_DATAERR, "bad ip address ``%s''",
4016165648Spiso				    av[0]);
4017165648Spiso			ac--; av++;
4018165648Spiso			break;
4019165648Spiso		case TOK_IF:
4020175511Smaxim			if (ac == 0)
4021175511Smaxim				errx(EX_DATAERR, "missing option");
4022165648Spiso			set_addr_dynamic(av[0], n);
4023165648Spiso			ac--; av++;
4024165648Spiso			break;
4025165648Spiso		case TOK_ALOG:
4026165648Spiso			n->mode |= PKT_ALIAS_LOG;
4027165648Spiso			break;
4028165648Spiso		case TOK_DENY_INC:
4029165648Spiso			n->mode |= PKT_ALIAS_DENY_INCOMING;
4030165648Spiso			break;
4031165648Spiso		case TOK_SAME_PORTS:
4032165648Spiso			n->mode |= PKT_ALIAS_SAME_PORTS;
4033165648Spiso			break;
4034165648Spiso		case TOK_UNREG_ONLY:
4035165648Spiso			n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
4036165648Spiso			break;
4037165648Spiso		case TOK_RESET_ADDR:
4038165648Spiso			n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
4039165648Spiso			break;
4040165648Spiso		case TOK_ALIAS_REV:
4041165648Spiso			n->mode |= PKT_ALIAS_REVERSE;
4042165648Spiso			break;
4043165648Spiso		case TOK_PROXY_ONLY:
4044165648Spiso			n->mode |= PKT_ALIAS_PROXY_ONLY;
4045165648Spiso			break;
4046165648Spiso			/*
4047165648Spiso			 * All the setup_redir_* functions work directly in the final
4048165648Spiso			 * buffer, see above for details.
4049165648Spiso			 */
4050165648Spiso		case TOK_REDIR_ADDR:
4051165648Spiso		case TOK_REDIR_PORT:
4052165648Spiso		case TOK_REDIR_PROTO:
4053165648Spiso			switch (tok) {
4054165648Spiso			case TOK_REDIR_ADDR:
4055165648Spiso				i = setup_redir_addr(&buf[off], len, &ac, &av);
4056165648Spiso				break;
4057165648Spiso			case TOK_REDIR_PORT:
4058165648Spiso				i = setup_redir_port(&buf[off], len, &ac, &av);
4059165648Spiso				break;
4060165648Spiso			case TOK_REDIR_PROTO:
4061165648Spiso				i = setup_redir_proto(&buf[off], len, &ac, &av);
4062165648Spiso				break;
4063165648Spiso			}
4064165648Spiso			n->redir_cnt++;
4065165648Spiso			off += i;
4066165648Spiso			len -= i;
4067165648Spiso			break;
4068165648Spiso		default:
4069165648Spiso			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
4070165648Spiso		}
4071165648Spiso	}
4072165648Spiso
4073165648Spiso	i = do_cmd(IP_FW_NAT_CFG, buf, off);
4074165648Spiso	if (i)
4075165648Spiso		err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
4076183890Smaxim
4077186297Spiso	if (!do_quiet) {
4078186297Spiso		/* After every modification, we show the resultant rule. */
4079186297Spiso		int _ac = 3;
4080186297Spiso		char *_av[] = {"show", "config", id};
4081186297Spiso		show_nat(_ac, _av);
4082186297Spiso	}
4083165648Spiso}
4084165648Spiso
4085165648Spisostatic void
408698943Sluigiconfig_pipe(int ac, char **av)
408798943Sluigi{
4088117469Sluigi	struct dn_pipe p;
408998943Sluigi	int i;
409098943Sluigi	char *end;
409198943Sluigi	void *par = NULL;
409298943Sluigi
4093117469Sluigi	memset(&p, 0, sizeof p);
409498943Sluigi
409598943Sluigi	av++; ac--;
409698943Sluigi	/* Pipe number */
409798943Sluigi	if (ac && isdigit(**av)) {
409898943Sluigi		i = atoi(*av); av++; ac--;
409998943Sluigi		if (do_pipe == 1)
4100117469Sluigi			p.pipe_nr = i;
410198943Sluigi		else
4102117469Sluigi			p.fs.fs_nr = i;
410398943Sluigi	}
410499475Sluigi	while (ac > 0) {
410598943Sluigi		double d;
410698943Sluigi		int tok = match_token(dummynet_params, *av);
410798943Sluigi		ac--; av++;
410898943Sluigi
410998943Sluigi		switch(tok) {
4110101978Sluigi		case TOK_NOERROR:
4111117469Sluigi			p.fs.flags_fs |= DN_NOERROR;
4112101978Sluigi			break;
4113101978Sluigi
411498943Sluigi		case TOK_PLR:
411598943Sluigi			NEED1("plr needs argument 0..1\n");
411698943Sluigi			d = strtod(av[0], NULL);
411798943Sluigi			if (d > 1)
411898943Sluigi				d = 1;
411998943Sluigi			else if (d < 0)
412098943Sluigi				d = 0;
4121117469Sluigi			p.fs.plr = (int)(d*0x7fffffff);
412298943Sluigi			ac--; av++;
412398943Sluigi			break;
412498943Sluigi
412598943Sluigi		case TOK_QUEUE:
412698943Sluigi			NEED1("queue needs queue size\n");
412798943Sluigi			end = NULL;
4128117469Sluigi			p.fs.qsize = strtoul(av[0], &end, 0);
412998943Sluigi			if (*end == 'K' || *end == 'k') {
4130117469Sluigi				p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
4131117469Sluigi				p.fs.qsize *= 1024;
4132140271Sbrooks			} else if (*end == 'B' ||
4133140271Sbrooks			    _substrcmp2(end, "by", "bytes") == 0) {
4134117469Sluigi				p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
413598943Sluigi			}
413698943Sluigi			ac--; av++;
413798943Sluigi			break;
413898943Sluigi
413998943Sluigi		case TOK_BUCKETS:
414098943Sluigi			NEED1("buckets needs argument\n");
4141117469Sluigi			p.fs.rq_size = strtoul(av[0], NULL, 0);
414298943Sluigi			ac--; av++;
414398943Sluigi			break;
414498943Sluigi
414598943Sluigi		case TOK_MASK:
414698943Sluigi			NEED1("mask needs mask specifier\n");
414798943Sluigi			/*
414898943Sluigi			 * per-flow queue, mask is dst_ip, dst_port,
414998943Sluigi			 * src_ip, src_port, proto measured in bits
415098943Sluigi			 */
415198943Sluigi			par = NULL;
415298943Sluigi
4153145246Sbrooks			bzero(&p.fs.flow_mask, sizeof(p.fs.flow_mask));
415498943Sluigi			end = NULL;
415598943Sluigi
415698943Sluigi			while (ac >= 1) {
4157117328Sluigi			    uint32_t *p32 = NULL;
4158117328Sluigi			    uint16_t *p16 = NULL;
4159145246Sbrooks			    uint32_t *p20 = NULL;
4160145246Sbrooks			    struct in6_addr *pa6 = NULL;
4161145246Sbrooks			    uint32_t a;
416298943Sluigi
416398943Sluigi			    tok = match_token(dummynet_params, *av);
416498943Sluigi			    ac--; av++;
416598943Sluigi			    switch(tok) {
416698943Sluigi			    case TOK_ALL:
416798943Sluigi				    /*
416898943Sluigi				     * special case, all bits significant
416998943Sluigi				     */
4170117469Sluigi				    p.fs.flow_mask.dst_ip = ~0;
4171117469Sluigi				    p.fs.flow_mask.src_ip = ~0;
4172117469Sluigi				    p.fs.flow_mask.dst_port = ~0;
4173117469Sluigi				    p.fs.flow_mask.src_port = ~0;
4174117469Sluigi				    p.fs.flow_mask.proto = ~0;
4175145246Sbrooks				    n2mask(&(p.fs.flow_mask.dst_ip6), 128);
4176145246Sbrooks				    n2mask(&(p.fs.flow_mask.src_ip6), 128);
4177145246Sbrooks				    p.fs.flow_mask.flow_id6 = ~0;
4178117469Sluigi				    p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
417998943Sluigi				    goto end_mask;
418098943Sluigi
418198943Sluigi			    case TOK_DSTIP:
4182117469Sluigi				    p32 = &p.fs.flow_mask.dst_ip;
418398943Sluigi				    break;
418498943Sluigi
418598943Sluigi			    case TOK_SRCIP:
4186117469Sluigi				    p32 = &p.fs.flow_mask.src_ip;
418798943Sluigi				    break;
418898943Sluigi
4189145246Sbrooks			    case TOK_DSTIP6:
4190145246Sbrooks				    pa6 = &(p.fs.flow_mask.dst_ip6);
4191145246Sbrooks				    break;
4192145246Sbrooks
4193145246Sbrooks			    case TOK_SRCIP6:
4194145246Sbrooks				    pa6 = &(p.fs.flow_mask.src_ip6);
4195145246Sbrooks				    break;
4196145246Sbrooks
4197145246Sbrooks			    case TOK_FLOWID:
4198145246Sbrooks				    p20 = &p.fs.flow_mask.flow_id6;
4199145246Sbrooks				    break;
4200145246Sbrooks
420198943Sluigi			    case TOK_DSTPORT:
4202117469Sluigi				    p16 = &p.fs.flow_mask.dst_port;
420398943Sluigi				    break;
420498943Sluigi
420598943Sluigi			    case TOK_SRCPORT:
4206117469Sluigi				    p16 = &p.fs.flow_mask.src_port;
420798943Sluigi				    break;
420898943Sluigi
420998943Sluigi			    case TOK_PROTO:
421098943Sluigi				    break;
421198943Sluigi
421298943Sluigi			    default:
421398943Sluigi				    ac++; av--; /* backtrack */
421498943Sluigi				    goto end_mask;
421598943Sluigi			    }
421698943Sluigi			    if (ac < 1)
421798943Sluigi				    errx(EX_USAGE, "mask: value missing");
421898943Sluigi			    if (*av[0] == '/') {
421998943Sluigi				    a = strtoul(av[0]+1, &end, 0);
4220145246Sbrooks				    if (pa6 == NULL)
4221145246Sbrooks					    a = (a == 32) ? ~0 : (1 << a) - 1;
4222106505Smaxim			    } else
422399909Sluigi				    a = strtoul(av[0], &end, 0);
422498943Sluigi			    if (p32 != NULL)
422598943Sluigi				    *p32 = a;
422698943Sluigi			    else if (p16 != NULL) {
4227139821Sbrooks				    if (a > 0xFFFF)
422898943Sluigi					    errx(EX_DATAERR,
4229144687Sbrooks						"port mask must be 16 bit");
4230117328Sluigi				    *p16 = (uint16_t)a;
4231145246Sbrooks			    } else if (p20 != NULL) {
4232145246Sbrooks				    if (a > 0xfffff)
4233145246Sbrooks					errx(EX_DATAERR,
4234145246Sbrooks					    "flow_id mask must be 20 bit");
4235145246Sbrooks				    *p20 = (uint32_t)a;
4236145246Sbrooks			    } else if (pa6 != NULL) {
4237187477Sluigi				    if (a > 128)
4238145246Sbrooks					errx(EX_DATAERR,
4239145246Sbrooks					    "in6addr invalid mask len");
4240145246Sbrooks				    else
4241145246Sbrooks					n2mask(pa6, a);
424298943Sluigi			    } else {
4243139821Sbrooks				    if (a > 0xFF)
424498943Sluigi					    errx(EX_DATAERR,
4245144687Sbrooks						"proto mask must be 8 bit");
4246117469Sluigi				    p.fs.flow_mask.proto = (uint8_t)a;
424798943Sluigi			    }
424898943Sluigi			    if (a != 0)
4249117469Sluigi				    p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
425098943Sluigi			    ac--; av++;
425198943Sluigi			} /* end while, config masks */
425298943Sluigiend_mask:
425398943Sluigi			break;
425498943Sluigi
425598943Sluigi		case TOK_RED:
425698943Sluigi		case TOK_GRED:
425798943Sluigi			NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
4258117469Sluigi			p.fs.flags_fs |= DN_IS_RED;
425998943Sluigi			if (tok == TOK_GRED)
4260117469Sluigi				p.fs.flags_fs |= DN_IS_GENTLE_RED;
426198943Sluigi			/*
426298943Sluigi			 * the format for parameters is w_q/min_th/max_th/max_p
426398943Sluigi			 */
426498943Sluigi			if ((end = strsep(&av[0], "/"))) {
426598943Sluigi			    double w_q = strtod(end, NULL);
426698943Sluigi			    if (w_q > 1 || w_q <= 0)
426798943Sluigi				errx(EX_DATAERR, "0 < w_q <= 1");
4268117469Sluigi			    p.fs.w_q = (int) (w_q * (1 << SCALE_RED));
426998943Sluigi			}
427098943Sluigi			if ((end = strsep(&av[0], "/"))) {
4271117469Sluigi			    p.fs.min_th = strtoul(end, &end, 0);
427298943Sluigi			    if (*end == 'K' || *end == 'k')
4273117469Sluigi				p.fs.min_th *= 1024;
427498943Sluigi			}
427598943Sluigi			if ((end = strsep(&av[0], "/"))) {
4276117469Sluigi			    p.fs.max_th = strtoul(end, &end, 0);
427798943Sluigi			    if (*end == 'K' || *end == 'k')
4278117469Sluigi				p.fs.max_th *= 1024;
427998943Sluigi			}
428098943Sluigi			if ((end = strsep(&av[0], "/"))) {
428198943Sluigi			    double max_p = strtod(end, NULL);
428298943Sluigi			    if (max_p > 1 || max_p <= 0)
428398943Sluigi				errx(EX_DATAERR, "0 < max_p <= 1");
4284117469Sluigi			    p.fs.max_p = (int)(max_p * (1 << SCALE_RED));
428598943Sluigi			}
428698943Sluigi			ac--; av++;
428798943Sluigi			break;
428898943Sluigi
428998943Sluigi		case TOK_DROPTAIL:
4290117469Sluigi			p.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
429198943Sluigi			break;
4292106505Smaxim
429398943Sluigi		case TOK_BW:
429498943Sluigi			NEED1("bw needs bandwidth or interface\n");
429598943Sluigi			if (do_pipe != 1)
429698943Sluigi			    errx(EX_DATAERR, "bandwidth only valid for pipes");
429798943Sluigi			/*
429898943Sluigi			 * set clocking interface or bandwidth value
429998943Sluigi			 */
430098943Sluigi			if (av[0][0] >= 'a' && av[0][0] <= 'z') {
4301117469Sluigi			    int l = sizeof(p.if_name)-1;
430298943Sluigi			    /* interface name */
4303117469Sluigi			    strncpy(p.if_name, av[0], l);
4304117469Sluigi			    p.if_name[l] = '\0';
4305117469Sluigi			    p.bandwidth = 0;
430698943Sluigi			} else {
4307117469Sluigi			    p.if_name[0] = '\0';
4308117469Sluigi			    p.bandwidth = strtoul(av[0], &end, 0);
430998943Sluigi			    if (*end == 'K' || *end == 'k') {
431098943Sluigi				end++;
4311117469Sluigi				p.bandwidth *= 1000;
431298943Sluigi			    } else if (*end == 'M') {
431398943Sluigi				end++;
4314117469Sluigi				p.bandwidth *= 1000000;
431598943Sluigi			    }
4316161550Sdwmalone			    if ((*end == 'B' &&
4317161550Sdwmalone				  _substrcmp2(end, "Bi", "Bit/s") != 0) ||
4318140271Sbrooks			        _substrcmp2(end, "by", "bytes") == 0)
4319117469Sluigi				p.bandwidth *= 8;
4320117469Sluigi			    if (p.bandwidth < 0)
432198943Sluigi				errx(EX_DATAERR, "bandwidth too large");
432298943Sluigi			}
432398943Sluigi			ac--; av++;
432498943Sluigi			break;
432598943Sluigi
432698943Sluigi		case TOK_DELAY:
432798943Sluigi			if (do_pipe != 1)
432898943Sluigi				errx(EX_DATAERR, "delay only valid for pipes");
432998943Sluigi			NEED1("delay needs argument 0..10000ms\n");
4330117469Sluigi			p.delay = strtoul(av[0], NULL, 0);
433198943Sluigi			ac--; av++;
433298943Sluigi			break;
433398943Sluigi
433498943Sluigi		case TOK_WEIGHT:
433598943Sluigi			if (do_pipe == 1)
433698943Sluigi				errx(EX_DATAERR,"weight only valid for queues");
433798943Sluigi			NEED1("weight needs argument 0..100\n");
4338117469Sluigi			p.fs.weight = strtoul(av[0], &end, 0);
433998943Sluigi			ac--; av++;
434098943Sluigi			break;
434198943Sluigi
434298943Sluigi		case TOK_PIPE:
434398943Sluigi			if (do_pipe == 1)
434498943Sluigi				errx(EX_DATAERR,"pipe only valid for queues");
434598943Sluigi			NEED1("pipe needs pipe_number\n");
4346117469Sluigi			p.fs.parent_nr = strtoul(av[0], &end, 0);
434798943Sluigi			ac--; av++;
434898943Sluigi			break;
434998943Sluigi
435098943Sluigi		default:
4351124924Smaxim			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
435298943Sluigi		}
435398943Sluigi	}
435498943Sluigi	if (do_pipe == 1) {
4355117469Sluigi		if (p.pipe_nr == 0)
435698943Sluigi			errx(EX_DATAERR, "pipe_nr must be > 0");
4357117469Sluigi		if (p.delay > 10000)
435898943Sluigi			errx(EX_DATAERR, "delay must be < 10000");
435998943Sluigi	} else { /* do_pipe == 2, queue */
4360117469Sluigi		if (p.fs.parent_nr == 0)
436198943Sluigi			errx(EX_DATAERR, "pipe must be > 0");
4362117469Sluigi		if (p.fs.weight >100)
436398943Sluigi			errx(EX_DATAERR, "weight must be <= 100");
436498943Sluigi	}
4365117469Sluigi	if (p.fs.flags_fs & DN_QSIZE_IS_BYTES) {
4366176626Sdwmalone		size_t len;
4367176626Sdwmalone		long limit;
4368176626Sdwmalone
4369176626Sdwmalone		len = sizeof(limit);
4370176626Sdwmalone		if (sysctlbyname("net.inet.ip.dummynet.pipe_byte_limit",
4371176626Sdwmalone			&limit, &len, NULL, 0) == -1)
4372176626Sdwmalone			limit = 1024*1024;
4373176626Sdwmalone		if (p.fs.qsize > limit)
4374176626Sdwmalone			errx(EX_DATAERR, "queue size must be < %ldB", limit);
437598943Sluigi	} else {
4376176626Sdwmalone		size_t len;
4377176626Sdwmalone		long limit;
4378176626Sdwmalone
4379176626Sdwmalone		len = sizeof(limit);
4380176626Sdwmalone		if (sysctlbyname("net.inet.ip.dummynet.pipe_slot_limit",
4381176626Sdwmalone			&limit, &len, NULL, 0) == -1)
4382176626Sdwmalone			limit = 100;
4383176626Sdwmalone		if (p.fs.qsize > limit)
4384176626Sdwmalone			errx(EX_DATAERR, "2 <= queue size <= %ld", limit);
438598943Sluigi	}
4386117469Sluigi	if (p.fs.flags_fs & DN_IS_RED) {
438798943Sluigi		size_t len;
438898943Sluigi		int lookup_depth, avg_pkt_size;
438998943Sluigi		double s, idle, weight, w_q;
4390117469Sluigi		struct clockinfo ck;
439198943Sluigi		int t;
439298943Sluigi
4393117469Sluigi		if (p.fs.min_th >= p.fs.max_th)
439498943Sluigi		    errx(EX_DATAERR, "min_th %d must be < than max_th %d",
4395117469Sluigi			p.fs.min_th, p.fs.max_th);
4396117469Sluigi		if (p.fs.max_th == 0)
439798943Sluigi		    errx(EX_DATAERR, "max_th must be > 0");
439898943Sluigi
439998943Sluigi		len = sizeof(int);
440098943Sluigi		if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
440198943Sluigi			&lookup_depth, &len, NULL, 0) == -1)
440298943Sluigi		    errx(1, "sysctlbyname(\"%s\")",
440398943Sluigi			"net.inet.ip.dummynet.red_lookup_depth");
440498943Sluigi		if (lookup_depth == 0)
440598943Sluigi		    errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth"
440698943Sluigi			" must be greater than zero");
440798943Sluigi
440898943Sluigi		len = sizeof(int);
440998943Sluigi		if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
441098943Sluigi			&avg_pkt_size, &len, NULL, 0) == -1)
441198943Sluigi
441298943Sluigi		    errx(1, "sysctlbyname(\"%s\")",
441398943Sluigi			"net.inet.ip.dummynet.red_avg_pkt_size");
441498943Sluigi		if (avg_pkt_size == 0)
441598943Sluigi			errx(EX_DATAERR,
441698943Sluigi			    "net.inet.ip.dummynet.red_avg_pkt_size must"
441798943Sluigi			    " be greater than zero");
441898943Sluigi
441998943Sluigi		len = sizeof(struct clockinfo);
4420117469Sluigi		if (sysctlbyname("kern.clockrate", &ck, &len, NULL, 0) == -1)
442198943Sluigi			errx(1, "sysctlbyname(\"%s\")", "kern.clockrate");
442298943Sluigi
442398943Sluigi		/*
442498943Sluigi		 * Ticks needed for sending a medium-sized packet.
442598943Sluigi		 * Unfortunately, when we are configuring a WF2Q+ queue, we
442698943Sluigi		 * do not have bandwidth information, because that is stored
442798943Sluigi		 * in the parent pipe, and also we have multiple queues
442898943Sluigi		 * competing for it. So we set s=0, which is not very
442998943Sluigi		 * correct. But on the other hand, why do we want RED with
443098943Sluigi		 * WF2Q+ ?
443198943Sluigi		 */
4432117469Sluigi		if (p.bandwidth==0) /* this is a WF2Q+ queue */
443398943Sluigi			s = 0;
443498943Sluigi		else
4435174713Soleg			s = (double)ck.hz * avg_pkt_size * 8 / p.bandwidth;
443698943Sluigi
443798943Sluigi		/*
443898943Sluigi		 * max idle time (in ticks) before avg queue size becomes 0.
443998943Sluigi		 * NOTA:  (3/w_q) is approx the value x so that
444098943Sluigi		 * (1-w_q)^x < 10^-3.
444198943Sluigi		 */
4442117469Sluigi		w_q = ((double)p.fs.w_q) / (1 << SCALE_RED);
444398943Sluigi		idle = s * 3. / w_q;
4444117469Sluigi		p.fs.lookup_step = (int)idle / lookup_depth;
4445117469Sluigi		if (!p.fs.lookup_step)
4446117469Sluigi			p.fs.lookup_step = 1;
444798943Sluigi		weight = 1 - w_q;
4448174713Soleg		for (t = p.fs.lookup_step; t > 1; --t)
4449174713Soleg			weight *= 1 - w_q;
4450117469Sluigi		p.fs.lookup_weight = (int)(weight * (1 << SCALE_RED));
445198943Sluigi	}
4452117469Sluigi	i = do_cmd(IP_DUMMYNET_CONFIGURE, &p, sizeof p);
445398943Sluigi	if (i)
445498943Sluigi		err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
445598943Sluigi}
445698943Sluigi
445798943Sluigistatic void
4458169424Smaximget_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask)
445998943Sluigi{
446098943Sluigi	int i, l;
4461169424Smaxim	char *ap, *ptr, *optr;
4462169424Smaxim	struct ether_addr *mac;
4463169424Smaxim	const char *macset = "0123456789abcdefABCDEF:";
446498943Sluigi
4465169424Smaxim	if (strcmp(p, "any") == 0) {
4466169424Smaxim		for (i = 0; i < ETHER_ADDR_LEN; i++)
4467169424Smaxim			addr[i] = mask[i] = 0;
446898943Sluigi		return;
4469169424Smaxim	}
447098943Sluigi
4471169424Smaxim	optr = ptr = strdup(p);
4472169424Smaxim	if ((ap = strsep(&ptr, "&/")) != NULL && *ap != 0) {
4473169424Smaxim		l = strlen(ap);
4474169424Smaxim		if (strspn(ap, macset) != l || (mac = ether_aton(ap)) == NULL)
4475169424Smaxim			errx(EX_DATAERR, "Incorrect MAC address");
4476169424Smaxim		bcopy(mac, addr, ETHER_ADDR_LEN);
4477169424Smaxim	} else
4478169424Smaxim		errx(EX_DATAERR, "Incorrect MAC address");
4479169424Smaxim
4480169424Smaxim	if (ptr != NULL) { /* we have mask? */
4481169424Smaxim		if (p[ptr - optr - 1] == '/') { /* mask len */
4482169424Smaxim			l = strtol(ptr, &ap, 10);
4483169424Smaxim			if (*ap != 0 || l > ETHER_ADDR_LEN * 8 || l < 0)
4484169424Smaxim				errx(EX_DATAERR, "Incorrect mask length");
4485169424Smaxim			for (i = 0; l > 0 && i < ETHER_ADDR_LEN; l -= 8, i++)
4486169424Smaxim				mask[i] = (l >= 8) ? 0xff: (~0) << (8 - l);
4487169424Smaxim		} else { /* mask */
4488169424Smaxim			l = strlen(ptr);
4489169424Smaxim			if (strspn(ptr, macset) != l ||
4490169424Smaxim			    (mac = ether_aton(ptr)) == NULL)
4491169424Smaxim				errx(EX_DATAERR, "Incorrect mask");
4492169424Smaxim			bcopy(mac, mask, ETHER_ADDR_LEN);
449398943Sluigi		}
4494169424Smaxim	} else { /* default mask: ff:ff:ff:ff:ff:ff */
4495169424Smaxim		for (i = 0; i < ETHER_ADDR_LEN; i++)
449698943Sluigi			mask[i] = 0xff;
449798943Sluigi	}
4498169424Smaxim	for (i = 0; i < ETHER_ADDR_LEN; i++)
449998943Sluigi		addr[i] &= mask[i];
4500169424Smaxim
4501169424Smaxim	free(optr);
450298943Sluigi}
450398943Sluigi
450498943Sluigi/*
450598943Sluigi * helper function, updates the pointer to cmd with the length
450698943Sluigi * of the current command, and also cleans up the first word of
450798943Sluigi * the new command in case it has been clobbered before.
450898943Sluigi */
450998943Sluigistatic ipfw_insn *
451098943Sluiginext_cmd(ipfw_insn *cmd)
451198943Sluigi{
451298943Sluigi	cmd += F_LEN(cmd);
451398943Sluigi	bzero(cmd, sizeof(*cmd));
451498943Sluigi	return cmd;
451598943Sluigi}
451698943Sluigi
451798943Sluigi/*
4518117469Sluigi * Takes arguments and copies them into a comment
4519117469Sluigi */
4520117469Sluigistatic void
4521117469Sluigifill_comment(ipfw_insn *cmd, int ac, char **av)
4522117469Sluigi{
4523117469Sluigi	int i, l;
4524117469Sluigi	char *p = (char *)(cmd + 1);
4525117577Sluigi
4526117469Sluigi	cmd->opcode = O_NOP;
4527117469Sluigi	cmd->len =  (cmd->len & (F_NOT | F_OR));
4528117469Sluigi
4529117469Sluigi	/* Compute length of comment string. */
4530117469Sluigi	for (i = 0, l = 0; i < ac; i++)
4531117469Sluigi		l += strlen(av[i]) + 1;
4532117469Sluigi	if (l == 0)
4533117469Sluigi		return;
4534117469Sluigi	if (l > 84)
4535117469Sluigi		errx(EX_DATAERR,
4536117469Sluigi		    "comment too long (max 80 chars)");
4537117469Sluigi	l = 1 + (l+3)/4;
4538117469Sluigi	cmd->len =  (cmd->len & (F_NOT | F_OR)) | l;
4539117469Sluigi	for (i = 0; i < ac; i++) {
4540117469Sluigi		strcpy(p, av[i]);
4541117469Sluigi		p += strlen(av[i]);
4542117469Sluigi		*p++ = ' ';
4543117469Sluigi	}
4544117469Sluigi	*(--p) = '\0';
4545117469Sluigi}
4546117577Sluigi
4547117469Sluigi/*
454898943Sluigi * A function to fill simple commands of size 1.
454998943Sluigi * Existing flags are preserved.
455098943Sluigi */
455198943Sluigistatic void
4552117328Sluigifill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg)
455398943Sluigi{
455498943Sluigi	cmd->opcode = opcode;
455598943Sluigi	cmd->len =  ((cmd->len | flags) & (F_NOT | F_OR)) | 1;
455698943Sluigi	cmd->arg1 = arg;
455798943Sluigi}
455898943Sluigi
455998943Sluigi/*
456098943Sluigi * Fetch and add the MAC address and type, with masks. This generates one or
456198943Sluigi * two microinstructions, and returns the pointer to the last one.
456298943Sluigi */
456398943Sluigistatic ipfw_insn *
456498943Sluigiadd_mac(ipfw_insn *cmd, int ac, char *av[])
456598943Sluigi{
4566102087Sluigi	ipfw_insn_mac *mac;
456798943Sluigi
4568102087Sluigi	if (ac < 2)
4569102098Sluigi		errx(EX_DATAERR, "MAC dst src");
457098943Sluigi
457198943Sluigi	cmd->opcode = O_MACADDR2;
457298943Sluigi	cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
457398943Sluigi
457498943Sluigi	mac = (ipfw_insn_mac *)cmd;
4575101978Sluigi	get_mac_addr_mask(av[0], mac->addr, mac->mask);	/* dst */
4576169424Smaxim	get_mac_addr_mask(av[1], &(mac->addr[ETHER_ADDR_LEN]),
4577169424Smaxim	    &(mac->mask[ETHER_ADDR_LEN])); /* src */
4578102087Sluigi	return cmd;
4579102087Sluigi}
458098943Sluigi
4581102087Sluigistatic ipfw_insn *
4582102087Sluigiadd_mactype(ipfw_insn *cmd, int ac, char *av)
4583102087Sluigi{
4584102087Sluigi	if (ac < 1)
4585102087Sluigi		errx(EX_DATAERR, "missing MAC type");
4586102087Sluigi	if (strcmp(av, "any") != 0) { /* we have a non-null type */
4587102087Sluigi		fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE);
458898943Sluigi		cmd->opcode = O_MAC_TYPE;
4589102087Sluigi		return cmd;
4590102087Sluigi	} else
4591102087Sluigi		return NULL;
4592102087Sluigi}
459398943Sluigi
4594102087Sluigistatic ipfw_insn *
4595152923Sumeadd_proto0(ipfw_insn *cmd, char *av, u_char *protop)
4596102087Sluigi{
4597102087Sluigi	struct protoent *pe;
4598152923Sume	char *ep;
4599152923Sume	int proto;
4600102087Sluigi
4601156315Sume	proto = strtol(av, &ep, 10);
4602156315Sume	if (*ep != '\0' || proto <= 0) {
4603152923Sume		if ((pe = getprotobyname(av)) == NULL)
4604152923Sume			return NULL;
4605152923Sume		proto = pe->p_proto;
4606152923Sume	}
4607145246Sbrooks
4608152923Sume	fill_cmd(cmd, O_PROTO, 0, proto);
4609152923Sume	*protop = proto;
4610152923Sume	return cmd;
4611152923Sume}
4612152923Sume
4613152923Sumestatic ipfw_insn *
4614152923Sumeadd_proto(ipfw_insn *cmd, char *av, u_char *protop)
4615152923Sume{
4616152923Sume	u_char proto = IPPROTO_IP;
4617152923Sume
4618156315Sume	if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
4619146894Smlaier		; /* do not set O_IP4 nor O_IP6 */
4620152923Sume	else if (strcmp(av, "ip4") == 0)
4621152923Sume		/* explicit "just IPv4" rule */
4622152923Sume		fill_cmd(cmd, O_IP4, 0, 0);
4623152923Sume	else if (strcmp(av, "ip6") == 0) {
4624152923Sume		/* explicit "just IPv6" rule */
4625152923Sume		proto = IPPROTO_IPV6;
4626152923Sume		fill_cmd(cmd, O_IP6, 0, 0);
4627152923Sume	} else
4628152923Sume		return add_proto0(cmd, av, protop);
4629152923Sume
4630152923Sume	*protop = proto;
4631152923Sume	return cmd;
4632152923Sume}
4633152923Sume
4634152923Sumestatic ipfw_insn *
4635152923Sumeadd_proto_compat(ipfw_insn *cmd, char *av, u_char *protop)
4636152923Sume{
4637152923Sume	u_char proto = IPPROTO_IP;
4638152923Sume
4639152923Sume	if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
4640152923Sume		; /* do not set O_IP4 nor O_IP6 */
4641146894Smlaier	else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0)
4642146894Smlaier		/* explicit "just IPv4" rule */
4643146894Smlaier		fill_cmd(cmd, O_IP4, 0, 0);
4644146894Smlaier	else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) {
4645146894Smlaier		/* explicit "just IPv6" rule */
4646152923Sume		proto = IPPROTO_IPV6;
4647146894Smlaier		fill_cmd(cmd, O_IP6, 0, 0);
4648152923Sume	} else
4649152923Sume		return add_proto0(cmd, av, protop);
4650145246Sbrooks
4651152923Sume	*protop = proto;
465298943Sluigi	return cmd;
465398943Sluigi}
465498943Sluigi
4655102087Sluigistatic ipfw_insn *
4656102087Sluigiadd_srcip(ipfw_insn *cmd, char *av)
4657102087Sluigi{
4658102087Sluigi	fill_ip((ipfw_insn_ip *)cmd, av);
4659102087Sluigi	if (cmd->opcode == O_IP_DST_SET)			/* set */
4660102087Sluigi		cmd->opcode = O_IP_SRC_SET;
4661130281Sru	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
4662130281Sru		cmd->opcode = O_IP_SRC_LOOKUP;
4663102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
4664102087Sluigi		cmd->opcode = O_IP_SRC_ME;
4665102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
4666102087Sluigi		cmd->opcode = O_IP_SRC;
4667117328Sluigi	else							/* addr/mask */
4668102087Sluigi		cmd->opcode = O_IP_SRC_MASK;
4669102087Sluigi	return cmd;
4670102087Sluigi}
4671102087Sluigi
4672102087Sluigistatic ipfw_insn *
4673102087Sluigiadd_dstip(ipfw_insn *cmd, char *av)
4674102087Sluigi{
4675102087Sluigi	fill_ip((ipfw_insn_ip *)cmd, av);
4676102087Sluigi	if (cmd->opcode == O_IP_DST_SET)			/* set */
4677102087Sluigi		;
4678130281Sru	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
4679130281Sru		;
4680102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
4681102087Sluigi		cmd->opcode = O_IP_DST_ME;
4682102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
4683102087Sluigi		cmd->opcode = O_IP_DST;
4684117328Sluigi	else							/* addr/mask */
4685102087Sluigi		cmd->opcode = O_IP_DST_MASK;
4686102087Sluigi	return cmd;
4687102087Sluigi}
4688102087Sluigi
4689102087Sluigistatic ipfw_insn *
4690102087Sluigiadd_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
4691102087Sluigi{
4692140271Sbrooks	if (_substrcmp(av, "any") == 0) {
4693102087Sluigi		return NULL;
4694102087Sluigi	} else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) {
4695102087Sluigi		/* XXX todo: check that we have a protocol with ports */
4696102087Sluigi		cmd->opcode = opcode;
4697102087Sluigi		return cmd;
4698102087Sluigi	}
4699102087Sluigi	return NULL;
4700102087Sluigi}
4701102087Sluigi
4702145246Sbrooksstatic ipfw_insn *
4703145246Sbrooksadd_src(ipfw_insn *cmd, char *av, u_char proto)
4704145246Sbrooks{
4705145246Sbrooks	struct in6_addr a;
4706158553Smlaier	char *host, *ch;
4707158553Smlaier	ipfw_insn *ret = NULL;
4708145246Sbrooks
4709158553Smlaier	if ((host = strdup(av)) == NULL)
4710158553Smlaier		return NULL;
4711158553Smlaier	if ((ch = strrchr(host, '/')) != NULL)
4712158553Smlaier		*ch = '\0';
4713158553Smlaier
4714145246Sbrooks	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
4715158553Smlaier	    inet_pton(AF_INET6, host, &a))
4716158553Smlaier		ret = add_srcip6(cmd, av);
4717145246Sbrooks	/* XXX: should check for IPv4, not !IPv6 */
4718161483Sdwmalone	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
4719161483Sdwmalone	    !inet_pton(AF_INET6, host, &a)))
4720158553Smlaier		ret = add_srcip(cmd, av);
4721161483Sdwmalone	if (ret == NULL && strcmp(av, "any") != 0)
4722158553Smlaier		ret = cmd;
4723145246Sbrooks
4724158553Smlaier	free(host);
4725158553Smlaier	return ret;
4726145246Sbrooks}
4727145246Sbrooks
4728145246Sbrooksstatic ipfw_insn *
4729145246Sbrooksadd_dst(ipfw_insn *cmd, char *av, u_char proto)
4730145246Sbrooks{
4731145246Sbrooks	struct in6_addr a;
4732158553Smlaier	char *host, *ch;
4733158553Smlaier	ipfw_insn *ret = NULL;
4734145246Sbrooks
4735158553Smlaier	if ((host = strdup(av)) == NULL)
4736158553Smlaier		return NULL;
4737158553Smlaier	if ((ch = strrchr(host, '/')) != NULL)
4738158553Smlaier		*ch = '\0';
4739158553Smlaier
4740145246Sbrooks	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
4741158553Smlaier	    inet_pton(AF_INET6, host, &a))
4742158553Smlaier		ret = add_dstip6(cmd, av);
4743145246Sbrooks	/* XXX: should check for IPv4, not !IPv6 */
4744161483Sdwmalone	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
4745161483Sdwmalone	    !inet_pton(AF_INET6, host, &a)))
4746158553Smlaier		ret = add_dstip(cmd, av);
4747161483Sdwmalone	if (ret == NULL && strcmp(av, "any") != 0)
4748158553Smlaier		ret = cmd;
4749145246Sbrooks
4750158553Smlaier	free(host);
4751158553Smlaier	return ret;
4752145246Sbrooks}
4753145246Sbrooks
475498943Sluigi/*
475598943Sluigi * Parse arguments and assemble the microinstructions which make up a rule.
475698943Sluigi * Rules are added into the 'rulebuf' and then copied in the correct order
475798943Sluigi * into the actual rule.
475898943Sluigi *
4759136071Sgreen * The syntax for a rule starts with the action, followed by
4760136071Sgreen * optional action parameters, and the various match patterns.
4761108533Sschweikh * In the assembled microcode, the first opcode must be an O_PROBE_STATE
476298943Sluigi * (generated if the rule includes a keep-state option), then the
4763136071Sgreen * various match patterns, log/altq actions, and the actual action.
4764106505Smaxim *
476598943Sluigi */
476698943Sluigistatic void
476798943Sluigiadd(int ac, char *av[])
476898943Sluigi{
476998943Sluigi	/*
477098943Sluigi	 * rules are added into the 'rulebuf' and then copied in
477198943Sluigi	 * the correct order into the actual rule.
477298943Sluigi	 * Some things that need to go out of order (prob, action etc.)
477398943Sluigi	 * go into actbuf[].
477498943Sluigi	 */
4775117328Sluigi	static uint32_t rulebuf[255], actbuf[255], cmdbuf[255];
477698943Sluigi
4777117469Sluigi	ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
4778102087Sluigi	ipfw_insn *first_cmd;	/* first match pattern */
477998943Sluigi
478098943Sluigi	struct ip_fw *rule;
478198943Sluigi
478298943Sluigi	/*
478398943Sluigi	 * various flags used to record that we entered some fields.
478498943Sluigi	 */
4785101116Sluigi	ipfw_insn *have_state = NULL;	/* check-state or keep-state */
4786158879Soleg	ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL;
4787134475Smaxim	size_t len;
478898943Sluigi
478998943Sluigi	int i;
479098943Sluigi
479198943Sluigi	int open_par = 0;	/* open parenthesis ( */
479298943Sluigi
479398943Sluigi	/* proto is here because it is used to fetch ports */
479498943Sluigi	u_char proto = IPPROTO_IP;	/* default protocol */
479598943Sluigi
4796107289Sluigi	double match_prob = 1; /* match probability, default is always match */
4797107289Sluigi
479898943Sluigi	bzero(actbuf, sizeof(actbuf));		/* actions go here */
479998943Sluigi	bzero(cmdbuf, sizeof(cmdbuf));
480098943Sluigi	bzero(rulebuf, sizeof(rulebuf));
480198943Sluigi
480298943Sluigi	rule = (struct ip_fw *)rulebuf;
480398943Sluigi	cmd = (ipfw_insn *)cmdbuf;
480498943Sluigi	action = (ipfw_insn *)actbuf;
480598943Sluigi
480698943Sluigi	av++; ac--;
480798943Sluigi
480898943Sluigi	/* [rule N]	-- Rule number optional */
480998943Sluigi	if (ac && isdigit(**av)) {
481098943Sluigi		rule->rulenum = atoi(*av);
481198943Sluigi		av++;
481298943Sluigi		ac--;
481398943Sluigi	}
481498943Sluigi
4815117655Sluigi	/* [set N]	-- set number (0..RESVD_SET), optional */
4816140271Sbrooks	if (ac > 1 && _substrcmp(*av, "set") == 0) {
4817101628Sluigi		int set = strtoul(av[1], NULL, 10);
4818117655Sluigi		if (set < 0 || set > RESVD_SET)
4819101628Sluigi			errx(EX_DATAERR, "illegal set %s", av[1]);
4820101628Sluigi		rule->set = set;
4821101628Sluigi		av += 2; ac -= 2;
4822101628Sluigi	}
4823101628Sluigi
482498943Sluigi	/* [prob D]	-- match probability, optional */
4825140271Sbrooks	if (ac > 1 && _substrcmp(*av, "prob") == 0) {
4826107289Sluigi		match_prob = strtod(av[1], NULL);
482798943Sluigi
4828107289Sluigi		if (match_prob <= 0 || match_prob > 1)
482998943Sluigi			errx(EX_DATAERR, "illegal match prob. %s", av[1]);
483098943Sluigi		av += 2; ac -= 2;
483198943Sluigi	}
483298943Sluigi
483398943Sluigi	/* action	-- mandatory */
483498943Sluigi	NEED1("missing action");
483598943Sluigi	i = match_token(rule_actions, *av);
483698943Sluigi	ac--; av++;
483798943Sluigi	action->len = 1;	/* default */
483898943Sluigi	switch(i) {
483998943Sluigi	case TOK_CHECKSTATE:
4840101116Sluigi		have_state = action;
484198943Sluigi		action->opcode = O_CHECK_STATE;
484298943Sluigi		break;
484398943Sluigi
484498943Sluigi	case TOK_ACCEPT:
484598943Sluigi		action->opcode = O_ACCEPT;
484698943Sluigi		break;
484798943Sluigi
484898943Sluigi	case TOK_DENY:
484998943Sluigi		action->opcode = O_DENY;
485099475Sluigi		action->arg1 = 0;
485198943Sluigi		break;
485298943Sluigi
485399475Sluigi	case TOK_REJECT:
485499475Sluigi		action->opcode = O_REJECT;
485599475Sluigi		action->arg1 = ICMP_UNREACH_HOST;
485699475Sluigi		break;
485799475Sluigi
485899475Sluigi	case TOK_RESET:
485999475Sluigi		action->opcode = O_REJECT;
486099475Sluigi		action->arg1 = ICMP_REJECT_RST;
486199475Sluigi		break;
486299475Sluigi
4863149020Sbz	case TOK_RESET6:
4864149020Sbz		action->opcode = O_UNREACH6;
4865149020Sbz		action->arg1 = ICMP6_UNREACH_RST;
4866149020Sbz		break;
4867149020Sbz
486899475Sluigi	case TOK_UNREACH:
486999475Sluigi		action->opcode = O_REJECT;
487099475Sluigi		NEED1("missing reject code");
487199475Sluigi		fill_reject_code(&action->arg1, *av);
487299475Sluigi		ac--; av++;
487399475Sluigi		break;
487499475Sluigi
4875149020Sbz	case TOK_UNREACH6:
4876149020Sbz		action->opcode = O_UNREACH6;
4877149020Sbz		NEED1("missing unreach code");
4878149020Sbz		fill_unreach6_code(&action->arg1, *av);
4879149020Sbz		ac--; av++;
4880149020Sbz		break;
4881149020Sbz
488298943Sluigi	case TOK_COUNT:
488398943Sluigi		action->opcode = O_COUNT;
488498943Sluigi		break;
488598943Sluigi
4886176517Spiso	case TOK_NAT:
4887176517Spiso 		action->opcode = O_NAT;
4888176517Spiso 		action->len = F_INSN_SIZE(ipfw_insn_nat);
4889176517Spiso		goto chkarg;
4890178888Sjulian
489198943Sluigi	case TOK_QUEUE:
4892153374Sglebius		action->opcode = O_QUEUE;
4893153374Sglebius		goto chkarg;
489498943Sluigi	case TOK_PIPE:
4895153374Sglebius		action->opcode = O_PIPE;
4896153374Sglebius		goto chkarg;
489798943Sluigi	case TOK_SKIPTO:
4898153374Sglebius		action->opcode = O_SKIPTO;
4899153374Sglebius		goto chkarg;
4900153374Sglebius	case TOK_NETGRAPH:
4901153374Sglebius		action->opcode = O_NETGRAPH;
4902153374Sglebius		goto chkarg;
4903153374Sglebius	case TOK_NGTEE:
4904153374Sglebius		action->opcode = O_NGTEE;
4905153374Sglebius		goto chkarg;
490698943Sluigi	case TOK_DIVERT:
4907153374Sglebius		action->opcode = O_DIVERT;
4908153374Sglebius		goto chkarg;
490998943Sluigi	case TOK_TEE:
4910153374Sglebius		action->opcode = O_TEE;
4911153374Sglebiuschkarg:
4912153374Sglebius		if (!ac)
4913153374Sglebius			errx(EX_USAGE, "missing argument for %s", *(av - 1));
4914153374Sglebius		if (isdigit(**av)) {
4915153374Sglebius			action->arg1 = strtoul(*av, NULL, 10);
4916153374Sglebius			if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG)
4917153374Sglebius				errx(EX_DATAERR, "illegal argument for %s",
4918153374Sglebius				    *(av - 1));
4919153374Sglebius		} else if (_substrcmp(*av, TABLEARG) == 0) {
4920153374Sglebius			action->arg1 = IP_FW_TABLEARG;
4921153374Sglebius		} else if (i == TOK_DIVERT || i == TOK_TEE) {
492298943Sluigi			struct servent *s;
492398943Sluigi			setservent(1);
492498943Sluigi			s = getservbyname(av[0], "divert");
492598943Sluigi			if (s != NULL)
492698943Sluigi				action->arg1 = ntohs(s->s_port);
492798943Sluigi			else
492898943Sluigi				errx(EX_DATAERR, "illegal divert/tee port");
4929153374Sglebius		} else
4930153374Sglebius			errx(EX_DATAERR, "illegal argument for %s", *(av - 1));
493198943Sluigi		ac--; av++;
493298943Sluigi		break;
493398943Sluigi
493498943Sluigi	case TOK_FORWARD: {
493598943Sluigi		ipfw_insn_sa *p = (ipfw_insn_sa *)action;
493698943Sluigi		char *s, *end;
493798943Sluigi
493898943Sluigi		NEED1("missing forward address[:port]");
493998943Sluigi
494098943Sluigi		action->opcode = O_FORWARD_IP;
494198943Sluigi		action->len = F_INSN_SIZE(ipfw_insn_sa);
494298943Sluigi
494398943Sluigi		p->sa.sin_len = sizeof(struct sockaddr_in);
494498943Sluigi		p->sa.sin_family = AF_INET;
494598943Sluigi		p->sa.sin_port = 0;
494698943Sluigi		/*
494798943Sluigi		 * locate the address-port separator (':' or ',')
494898943Sluigi		 */
494998943Sluigi		s = strchr(*av, ':');
495098943Sluigi		if (s == NULL)
495198943Sluigi			s = strchr(*av, ',');
495298943Sluigi		if (s != NULL) {
495398943Sluigi			*(s++) = '\0';
495498943Sluigi			i = strtoport(s, &end, 0 /* base */, 0 /* proto */);
495598943Sluigi			if (s == end)
495698943Sluigi				errx(EX_DATAERR,
495798943Sluigi				    "illegal forwarding port ``%s''", s);
4958103241Sluigi			p->sa.sin_port = (u_short)i;
495998943Sluigi		}
4960161456Sjulian		if (_substrcmp(*av, "tablearg") == 0)
4961161456Sjulian			p->sa.sin_addr.s_addr = INADDR_ANY;
4962161456Sjulian		else
4963161424Sjulian			lookup_host(*av, &(p->sa.sin_addr));
496498943Sluigi		ac--; av++;
496598943Sluigi		break;
4966161424Sjulian	    }
4967117469Sluigi	case TOK_COMMENT:
4968117469Sluigi		/* pretend it is a 'count' rule followed by the comment */
4969117469Sluigi		action->opcode = O_COUNT;
4970117469Sluigi		ac++; av--;	/* go back... */
4971117469Sluigi		break;
4972178888Sjulian
4973178888Sjulian	case TOK_SETFIB:
4974178888Sjulian	    {
4975178888Sjulian		int numfibs;
4976178916Sjulian		size_t intsize = sizeof(int);
4977178888Sjulian
4978178888Sjulian		action->opcode = O_SETFIB;
4979178888Sjulian 		NEED1("missing fib number");
4980178888Sjulian 	        action->arg1 = strtoul(*av, NULL, 10);
4981178916Sjulian		if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1)
4982178888Sjulian			errx(EX_DATAERR, "fibs not suported.\n");
4983178888Sjulian		if (action->arg1 >= numfibs)  /* Temporary */
4984178888Sjulian			errx(EX_DATAERR, "fib too large.\n");
4985178888Sjulian 		ac--; av++;
4986178888Sjulian 		break;
4987178888Sjulian	    }
4988165648Spiso
498998943Sluigi	default:
4990102087Sluigi		errx(EX_DATAERR, "invalid action %s\n", av[-1]);
499198943Sluigi	}
499298943Sluigi	action = next_cmd(action);
499398943Sluigi
499498943Sluigi	/*
4995136071Sgreen	 * [altq queuename] -- altq tag, optional
499698943Sluigi	 * [log [logamount N]]	-- log, optional
499798943Sluigi	 *
4998136071Sgreen	 * If they exist, it go first in the cmdbuf, but then it is
499998943Sluigi	 * skipped in the copy section to the end of the buffer.
500098943Sluigi	 */
5001136071Sgreen	while (ac != 0 && (i = match_token(rule_action_params, *av)) != -1) {
5002136071Sgreen		ac--; av++;
5003136071Sgreen		switch (i) {
5004136071Sgreen		case TOK_LOG:
5005136071Sgreen		    {
5006136071Sgreen			ipfw_insn_log *c = (ipfw_insn_log *)cmd;
5007136071Sgreen			int l;
500898943Sluigi
5009136071Sgreen			if (have_log)
5010136071Sgreen				errx(EX_DATAERR,
5011136071Sgreen				    "log cannot be specified more than once");
5012136071Sgreen			have_log = (ipfw_insn *)c;
5013136071Sgreen			cmd->len = F_INSN_SIZE(ipfw_insn_log);
5014136071Sgreen			cmd->opcode = O_LOG;
5015140271Sbrooks			if (ac && _substrcmp(*av, "logamount") == 0) {
5016136071Sgreen				ac--; av++;
5017136071Sgreen				NEED1("logamount requires argument");
5018136071Sgreen				l = atoi(*av);
5019136071Sgreen				if (l < 0)
5020136071Sgreen					errx(EX_DATAERR,
5021136071Sgreen					    "logamount must be positive");
5022136071Sgreen				c->max_log = l;
5023136071Sgreen				ac--; av++;
5024136071Sgreen			} else {
5025136071Sgreen				len = sizeof(c->max_log);
5026136071Sgreen				if (sysctlbyname("net.inet.ip.fw.verbose_limit",
5027136071Sgreen				    &c->max_log, &len, NULL, 0) == -1)
5028136071Sgreen					errx(1, "sysctlbyname(\"%s\")",
5029136071Sgreen					    "net.inet.ip.fw.verbose_limit");
5030136071Sgreen			}
5031136071Sgreen		    }
5032136071Sgreen			break;
5033136071Sgreen
5034136071Sgreen		case TOK_ALTQ:
5035136071Sgreen		    {
5036136071Sgreen			ipfw_insn_altq *a = (ipfw_insn_altq *)cmd;
5037136071Sgreen
5038136071Sgreen			NEED1("missing altq queue name");
5039136071Sgreen			if (have_altq)
5040136071Sgreen				errx(EX_DATAERR,
5041136071Sgreen				    "altq cannot be specified more than once");
5042136071Sgreen			have_altq = (ipfw_insn *)a;
5043136071Sgreen			cmd->len = F_INSN_SIZE(ipfw_insn_altq);
5044136071Sgreen			cmd->opcode = O_ALTQ;
5045136071Sgreen			fill_altq_qid(&a->qid, *av);
504698943Sluigi			ac--; av++;
5047136071Sgreen		    }
5048136071Sgreen			break;
5049136071Sgreen
5050158879Soleg		case TOK_TAG:
5051159636Soleg		case TOK_UNTAG: {
5052159636Soleg			uint16_t tag;
5053159636Soleg
5054158879Soleg			if (have_tag)
5055159636Soleg				errx(EX_USAGE, "tag and untag cannot be "
5056159636Soleg				    "specified more than once");
5057182823Srik			GET_UINT_ARG(tag, 1, IPFW_DEFAULT_RULE - 1, i,
5058182823Srik			   rule_action_params);
5059158879Soleg			have_tag = cmd;
5060159636Soleg			fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag);
5061158879Soleg			ac--; av++;
5062158879Soleg			break;
5063159636Soleg		}
5064158879Soleg
5065136071Sgreen		default:
5066136071Sgreen			abort();
506798943Sluigi		}
506898943Sluigi		cmd = next_cmd(cmd);
506998943Sluigi	}
507098943Sluigi
5071101116Sluigi	if (have_state)	/* must be a check-state, we are done */
507298943Sluigi		goto done;
507398943Sluigi
507498943Sluigi#define OR_START(target)					\
507598943Sluigi	if (ac && (*av[0] == '(' || *av[0] == '{')) {		\
507698943Sluigi		if (open_par)					\
507798943Sluigi			errx(EX_USAGE, "nested \"(\" not allowed\n"); \
5078101641Sluigi		prev = NULL;					\
507998943Sluigi		open_par = 1;					\
508098943Sluigi		if ( (av[0])[1] == '\0') {			\
508198943Sluigi			ac--; av++;				\
508298943Sluigi		} else						\
508398943Sluigi			(*av)++;				\
508498943Sluigi	}							\
508598943Sluigi	target:							\
508698943Sluigi
508798943Sluigi
508898943Sluigi#define	CLOSE_PAR						\
508998943Sluigi	if (open_par) {						\
509098943Sluigi		if (ac && (					\
5091140271Sbrooks		    strcmp(*av, ")") == 0 ||			\
5092140271Sbrooks		    strcmp(*av, "}") == 0)) {			\
5093101641Sluigi			prev = NULL;				\
509498943Sluigi			open_par = 0;				\
509598943Sluigi			ac--; av++;				\
509698943Sluigi		} else						\
509798943Sluigi			errx(EX_USAGE, "missing \")\"\n");	\
509898943Sluigi	}
5099106505Smaxim
510098943Sluigi#define NOT_BLOCK						\
5101140271Sbrooks	if (ac && _substrcmp(*av, "not") == 0) {		\
510298943Sluigi		if (cmd->len & F_NOT)				\
510398943Sluigi			errx(EX_USAGE, "double \"not\" not allowed\n"); \
510498943Sluigi		cmd->len |= F_NOT;				\
510598943Sluigi		ac--; av++;					\
510698943Sluigi	}
510798943Sluigi
510898943Sluigi#define OR_BLOCK(target)					\
5109140271Sbrooks	if (ac && _substrcmp(*av, "or") == 0) {		\
511098943Sluigi		if (prev == NULL || open_par == 0)		\
511198943Sluigi			errx(EX_DATAERR, "invalid OR block");	\
511298943Sluigi		prev->len |= F_OR;				\
511398943Sluigi		ac--; av++;					\
511498943Sluigi		goto target;					\
511598943Sluigi	}							\
511698943Sluigi	CLOSE_PAR;
511798943Sluigi
5118102087Sluigi	first_cmd = cmd;
5119102098Sluigi
5120102098Sluigi#if 0
512198943Sluigi	/*
5122102087Sluigi	 * MAC addresses, optional.
5123102087Sluigi	 * If we have this, we skip the part "proto from src to dst"
5124102087Sluigi	 * and jump straight to the option parsing.
5125102087Sluigi	 */
5126102087Sluigi	NOT_BLOCK;
5127102087Sluigi	NEED1("missing protocol");
5128140271Sbrooks	if (_substrcmp(*av, "MAC") == 0 ||
5129140271Sbrooks	    _substrcmp(*av, "mac") == 0) {
5130102087Sluigi		ac--; av++;	/* the "MAC" keyword */
5131102087Sluigi		add_mac(cmd, ac, av); /* exits in case of errors */
5132102087Sluigi		cmd = next_cmd(cmd);
5133102087Sluigi		ac -= 2; av += 2;	/* dst-mac and src-mac */
5134102087Sluigi		NOT_BLOCK;
5135102087Sluigi		NEED1("missing mac type");
5136102087Sluigi		if (add_mactype(cmd, ac, av[0]))
5137102087Sluigi			cmd = next_cmd(cmd);
5138102087Sluigi		ac--; av++;	/* any or mac-type */
5139102087Sluigi		goto read_options;
5140102087Sluigi	}
5141102098Sluigi#endif
5142102087Sluigi
5143102087Sluigi	/*
514498943Sluigi	 * protocol, mandatory
514598943Sluigi	 */
514698943Sluigi    OR_START(get_proto);
514798943Sluigi	NOT_BLOCK;
514898943Sluigi	NEED1("missing protocol");
5149152923Sume	if (add_proto_compat(cmd, *av, &proto)) {
5150102087Sluigi		av++; ac--;
5151147105Smlaier		if (F_LEN(cmd) != 0) {
5152102087Sluigi			prev = cmd;
5153102087Sluigi			cmd = next_cmd(cmd);
5154102087Sluigi		}
5155102098Sluigi	} else if (first_cmd != cmd) {
5156116438Smaxim		errx(EX_DATAERR, "invalid protocol ``%s''", *av);
5157102098Sluigi	} else
5158102098Sluigi		goto read_options;
515998943Sluigi    OR_BLOCK(get_proto);
516098943Sluigi
516198943Sluigi	/*
5162102087Sluigi	 * "from", mandatory
516398943Sluigi	 */
5164140271Sbrooks	if (!ac || _substrcmp(*av, "from") != 0)
516598943Sluigi		errx(EX_USAGE, "missing ``from''");
516698943Sluigi	ac--; av++;
516798943Sluigi
516898943Sluigi	/*
516998943Sluigi	 * source IP, mandatory
517098943Sluigi	 */
517198943Sluigi    OR_START(source_ip);
517298943Sluigi	NOT_BLOCK;	/* optional "not" */
517398943Sluigi	NEED1("missing source address");
5174145246Sbrooks	if (add_src(cmd, *av, proto)) {
5175102087Sluigi		ac--; av++;
5176102087Sluigi		if (F_LEN(cmd) != 0) {	/* ! any */
5177102087Sluigi			prev = cmd;
5178102087Sluigi			cmd = next_cmd(cmd);
5179102087Sluigi		}
5180145246Sbrooks	} else
5181145246Sbrooks		errx(EX_USAGE, "bad source address %s", *av);
518298943Sluigi    OR_BLOCK(source_ip);
518398943Sluigi
518498943Sluigi	/*
518598943Sluigi	 * source ports, optional
518698943Sluigi	 */
518798943Sluigi	NOT_BLOCK;	/* optional "not" */
5188101641Sluigi	if (ac) {
5189140271Sbrooks		if (_substrcmp(*av, "any") == 0 ||
5190102087Sluigi		    add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
5191102087Sluigi			ac--; av++;
5192102087Sluigi			if (F_LEN(cmd) != 0)
5193102087Sluigi				cmd = next_cmd(cmd);
5194101641Sluigi		}
519598943Sluigi	}
519698943Sluigi
519798943Sluigi	/*
5198102087Sluigi	 * "to", mandatory
519998943Sluigi	 */
5200140271Sbrooks	if (!ac || _substrcmp(*av, "to") != 0)
520198943Sluigi		errx(EX_USAGE, "missing ``to''");
520298943Sluigi	av++; ac--;
520398943Sluigi
520498943Sluigi	/*
520598943Sluigi	 * destination, mandatory
520698943Sluigi	 */
520798943Sluigi    OR_START(dest_ip);
520898943Sluigi	NOT_BLOCK;	/* optional "not" */
520998943Sluigi	NEED1("missing dst address");
5210145246Sbrooks	if (add_dst(cmd, *av, proto)) {
5211102087Sluigi		ac--; av++;
5212102087Sluigi		if (F_LEN(cmd) != 0) {	/* ! any */
5213102087Sluigi			prev = cmd;
5214102087Sluigi			cmd = next_cmd(cmd);
5215102087Sluigi		}
5216145246Sbrooks	} else
5217145246Sbrooks		errx( EX_USAGE, "bad destination address %s", *av);
521898943Sluigi    OR_BLOCK(dest_ip);
521998943Sluigi
522098943Sluigi	/*
522198943Sluigi	 * dest. ports, optional
522298943Sluigi	 */
522398943Sluigi	NOT_BLOCK;	/* optional "not" */
5224101641Sluigi	if (ac) {
5225140271Sbrooks		if (_substrcmp(*av, "any") == 0 ||
5226102087Sluigi		    add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
5227102087Sluigi			ac--; av++;
5228102087Sluigi			if (F_LEN(cmd) != 0)
5229102087Sluigi				cmd = next_cmd(cmd);
5230101641Sluigi		}
523198943Sluigi	}
523298943Sluigi
523398943Sluigiread_options:
5234102087Sluigi	if (ac && first_cmd == cmd) {
5235102087Sluigi		/*
5236102087Sluigi		 * nothing specified so far, store in the rule to ease
5237102087Sluigi		 * printout later.
5238102087Sluigi		 */
5239102087Sluigi		 rule->_pad = 1;
5240102087Sluigi	}
524198943Sluigi	prev = NULL;
524298943Sluigi	while (ac) {
5243101641Sluigi		char *s;
5244101641Sluigi		ipfw_insn_u32 *cmd32;	/* alias for cmd */
524598943Sluigi
5246101641Sluigi		s = *av;
5247101641Sluigi		cmd32 = (ipfw_insn_u32 *)cmd;
5248101641Sluigi
524998943Sluigi		if (*s == '!') {	/* alternate syntax for NOT */
525098943Sluigi			if (cmd->len & F_NOT)
525198943Sluigi				errx(EX_USAGE, "double \"not\" not allowed\n");
525298943Sluigi			cmd->len = F_NOT;
525398943Sluigi			s++;
525498943Sluigi		}
525598943Sluigi		i = match_token(rule_options, s);
525698943Sluigi		ac--; av++;
525798943Sluigi		switch(i) {
525898943Sluigi		case TOK_NOT:
525998943Sluigi			if (cmd->len & F_NOT)
526098943Sluigi				errx(EX_USAGE, "double \"not\" not allowed\n");
526198943Sluigi			cmd->len = F_NOT;
526298943Sluigi			break;
526398943Sluigi
526498943Sluigi		case TOK_OR:
5265101641Sluigi			if (open_par == 0 || prev == NULL)
526698943Sluigi				errx(EX_USAGE, "invalid \"or\" block\n");
526798943Sluigi			prev->len |= F_OR;
526898943Sluigi			break;
5269101641Sluigi
5270101641Sluigi		case TOK_STARTBRACE:
5271101641Sluigi			if (open_par)
5272101641Sluigi				errx(EX_USAGE, "+nested \"(\" not allowed\n");
5273101641Sluigi			open_par = 1;
5274101641Sluigi			break;
5275101641Sluigi
5276101641Sluigi		case TOK_ENDBRACE:
5277101641Sluigi			if (!open_par)
5278101641Sluigi				errx(EX_USAGE, "+missing \")\"\n");
5279101641Sluigi			open_par = 0;
5280102087Sluigi			prev = NULL;
5281101641Sluigi        		break;
5282101641Sluigi
528398943Sluigi		case TOK_IN:
528498943Sluigi			fill_cmd(cmd, O_IN, 0, 0);
528598943Sluigi			break;
528698943Sluigi
528798943Sluigi		case TOK_OUT:
528898943Sluigi			cmd->len ^= F_NOT; /* toggle F_NOT */
528998943Sluigi			fill_cmd(cmd, O_IN, 0, 0);
529098943Sluigi			break;
529198943Sluigi
5292136073Sgreen		case TOK_DIVERTED:
5293136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 3);
5294136073Sgreen			break;
5295136073Sgreen
5296136073Sgreen		case TOK_DIVERTEDLOOPBACK:
5297136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 1);
5298136073Sgreen			break;
5299136073Sgreen
5300136073Sgreen		case TOK_DIVERTEDOUTPUT:
5301136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 2);
5302136073Sgreen			break;
5303136073Sgreen
530498943Sluigi		case TOK_FRAG:
530598943Sluigi			fill_cmd(cmd, O_FRAG, 0, 0);
530698943Sluigi			break;
530798943Sluigi
530898943Sluigi		case TOK_LAYER2:
530998943Sluigi			fill_cmd(cmd, O_LAYER2, 0, 0);
531098943Sluigi			break;
531198943Sluigi
531298943Sluigi		case TOK_XMIT:
531398943Sluigi		case TOK_RECV:
531498943Sluigi		case TOK_VIA:
531598943Sluigi			NEED1("recv, xmit, via require interface name"
531698943Sluigi				" or address");
531798943Sluigi			fill_iface((ipfw_insn_if *)cmd, av[0]);
531898943Sluigi			ac--; av++;
531998943Sluigi			if (F_LEN(cmd) == 0)	/* not a valid address */
532098943Sluigi				break;
532198943Sluigi			if (i == TOK_XMIT)
532298943Sluigi				cmd->opcode = O_XMIT;
532398943Sluigi			else if (i == TOK_RECV)
532498943Sluigi				cmd->opcode = O_RECV;
532598943Sluigi			else if (i == TOK_VIA)
532698943Sluigi				cmd->opcode = O_VIA;
532798943Sluigi			break;
532898943Sluigi
532999475Sluigi		case TOK_ICMPTYPES:
533099475Sluigi			NEED1("icmptypes requires list of types");
533199475Sluigi			fill_icmptypes((ipfw_insn_u32 *)cmd, *av);
533299475Sluigi			av++; ac--;
533399475Sluigi			break;
5334145246Sbrooks
5335145246Sbrooks		case TOK_ICMP6TYPES:
5336145246Sbrooks			NEED1("icmptypes requires list of types");
5337145246Sbrooks			fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av);
5338145246Sbrooks			av++; ac--;
5339145246Sbrooks			break;
534099475Sluigi
534198943Sluigi		case TOK_IPTTL:
534298943Sluigi			NEED1("ipttl requires TTL");
5343116690Sluigi			if (strpbrk(*av, "-,")) {
5344116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPTTL))
5345116690Sluigi				errx(EX_DATAERR, "invalid ipttl %s", *av);
5346116690Sluigi			} else
5347116690Sluigi			    fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
534898943Sluigi			ac--; av++;
534998943Sluigi			break;
535098943Sluigi
535198943Sluigi		case TOK_IPID:
5352116690Sluigi			NEED1("ipid requires id");
5353116690Sluigi			if (strpbrk(*av, "-,")) {
5354116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPID))
5355116690Sluigi				errx(EX_DATAERR, "invalid ipid %s", *av);
5356116690Sluigi			} else
5357116690Sluigi			    fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0));
535898943Sluigi			ac--; av++;
535998943Sluigi			break;
536098943Sluigi
536198943Sluigi		case TOK_IPLEN:
536298943Sluigi			NEED1("iplen requires length");
5363116690Sluigi			if (strpbrk(*av, "-,")) {
5364116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPLEN))
5365116690Sluigi				errx(EX_DATAERR, "invalid ip len %s", *av);
5366116690Sluigi			} else
5367116690Sluigi			    fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0));
536898943Sluigi			ac--; av++;
536998943Sluigi			break;
537098943Sluigi
537198943Sluigi		case TOK_IPVER:
537298943Sluigi			NEED1("ipver requires version");
537398943Sluigi			fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0));
537498943Sluigi			ac--; av++;
537598943Sluigi			break;
537698943Sluigi
537799475Sluigi		case TOK_IPPRECEDENCE:
537899475Sluigi			NEED1("ipprecedence requires value");
537999475Sluigi			fill_cmd(cmd, O_IPPRECEDENCE, 0,
538099475Sluigi			    (strtoul(*av, NULL, 0) & 7) << 5);
538199475Sluigi			ac--; av++;
538299475Sluigi			break;
538399475Sluigi
538498943Sluigi		case TOK_IPOPTS:
538598943Sluigi			NEED1("missing argument for ipoptions");
5386101116Sluigi			fill_flags(cmd, O_IPOPT, f_ipopts, *av);
538798943Sluigi			ac--; av++;
538898943Sluigi			break;
538998943Sluigi
539099475Sluigi		case TOK_IPTOS:
539199475Sluigi			NEED1("missing argument for iptos");
5392101116Sluigi			fill_flags(cmd, O_IPTOS, f_iptos, *av);
539399475Sluigi			ac--; av++;
539499475Sluigi			break;
539599475Sluigi
539698943Sluigi		case TOK_UID:
539798943Sluigi			NEED1("uid requires argument");
539898943Sluigi		    {
539998943Sluigi			char *end;
540098943Sluigi			uid_t uid;
540198943Sluigi			struct passwd *pwd;
540298943Sluigi
540398943Sluigi			cmd->opcode = O_UID;
540498943Sluigi			uid = strtoul(*av, &end, 0);
540598943Sluigi			pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av);
540698943Sluigi			if (pwd == NULL)
540798943Sluigi				errx(EX_DATAERR, "uid \"%s\" nonexistent", *av);
5408106504Smaxim			cmd32->d[0] = pwd->pw_uid;
5409135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
541098943Sluigi			ac--; av++;
541198943Sluigi		    }
541298943Sluigi			break;
541398943Sluigi
541498943Sluigi		case TOK_GID:
541598943Sluigi			NEED1("gid requires argument");
541698943Sluigi		    {
541798943Sluigi			char *end;
541898943Sluigi			gid_t gid;
541998943Sluigi			struct group *grp;
542098943Sluigi
542198943Sluigi			cmd->opcode = O_GID;
542298943Sluigi			gid = strtoul(*av, &end, 0);
542398943Sluigi			grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av);
542498943Sluigi			if (grp == NULL)
542598943Sluigi				errx(EX_DATAERR, "gid \"%s\" nonexistent", *av);
5426106504Smaxim			cmd32->d[0] = grp->gr_gid;
5427135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
542898943Sluigi			ac--; av++;
542998943Sluigi		    }
543098943Sluigi			break;
543198943Sluigi
5432133600Scsjp		case TOK_JAIL:
5433133600Scsjp			NEED1("jail requires argument");
5434133600Scsjp		    {
5435133600Scsjp			char *end;
5436133600Scsjp			int jid;
5437133600Scsjp
5438133600Scsjp			cmd->opcode = O_JAIL;
5439133600Scsjp			jid = (int)strtol(*av, &end, 0);
5440133600Scsjp			if (jid < 0 || *end != '\0')
5441133600Scsjp				errx(EX_DATAERR, "jail requires prison ID");
5442135554Scsjp			cmd32->d[0] = (uint32_t)jid;
5443135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
5444133600Scsjp			ac--; av++;
5445133600Scsjp		    }
5446133600Scsjp			break;
5447133600Scsjp
544898943Sluigi		case TOK_ESTAB:
544998943Sluigi			fill_cmd(cmd, O_ESTAB, 0, 0);
545098943Sluigi			break;
545198943Sluigi
545298943Sluigi		case TOK_SETUP:
545398943Sluigi			fill_cmd(cmd, O_TCPFLAGS, 0,
545498943Sluigi				(TH_SYN) | ( (TH_ACK) & 0xff) <<8 );
545598943Sluigi			break;
545698943Sluigi
5457136075Sgreen		case TOK_TCPDATALEN:
5458136075Sgreen			NEED1("tcpdatalen requires length");
5459136075Sgreen			if (strpbrk(*av, "-,")) {
5460136075Sgreen			    if (!add_ports(cmd, *av, 0, O_TCPDATALEN))
5461136075Sgreen				errx(EX_DATAERR, "invalid tcpdata len %s", *av);
5462136075Sgreen			} else
5463136075Sgreen			    fill_cmd(cmd, O_TCPDATALEN, 0,
5464136075Sgreen				    strtoul(*av, NULL, 0));
5465136075Sgreen			ac--; av++;
5466136075Sgreen			break;
5467136075Sgreen
546898943Sluigi		case TOK_TCPOPTS:
546998943Sluigi			NEED1("missing argument for tcpoptions");
547098943Sluigi			fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av);
547198943Sluigi			ac--; av++;
547298943Sluigi			break;
547398943Sluigi
547498943Sluigi		case TOK_TCPSEQ:
547598943Sluigi		case TOK_TCPACK:
547698943Sluigi			NEED1("tcpseq/tcpack requires argument");
547798943Sluigi			cmd->len = F_INSN_SIZE(ipfw_insn_u32);
547898943Sluigi			cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK;
547998943Sluigi			cmd32->d[0] = htonl(strtoul(*av, NULL, 0));
548098943Sluigi			ac--; av++;
548198943Sluigi			break;
548298943Sluigi
548398943Sluigi		case TOK_TCPWIN:
548498943Sluigi			NEED1("tcpwin requires length");
548598943Sluigi			fill_cmd(cmd, O_TCPWIN, 0,
548698943Sluigi			    htons(strtoul(*av, NULL, 0)));
548798943Sluigi			ac--; av++;
548898943Sluigi			break;
548998943Sluigi
549098943Sluigi		case TOK_TCPFLAGS:
549198943Sluigi			NEED1("missing argument for tcpflags");
549298943Sluigi			cmd->opcode = O_TCPFLAGS;
549398943Sluigi			fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av);
549498943Sluigi			ac--; av++;
549598943Sluigi			break;
549698943Sluigi
549798943Sluigi		case TOK_KEEPSTATE:
5498101641Sluigi			if (open_par)
5499101641Sluigi				errx(EX_USAGE, "keep-state cannot be part "
5500101641Sluigi				    "of an or block");
550199909Sluigi			if (have_state)
5502101116Sluigi				errx(EX_USAGE, "only one of keep-state "
550399909Sluigi					"and limit is allowed");
5504101116Sluigi			have_state = cmd;
550598943Sluigi			fill_cmd(cmd, O_KEEP_STATE, 0, 0);
550698943Sluigi			break;
550798943Sluigi
5508159636Soleg		case TOK_LIMIT: {
5509159636Soleg			ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
5510159636Soleg			int val;
5511159636Soleg
5512101641Sluigi			if (open_par)
5513159636Soleg				errx(EX_USAGE,
5514159636Soleg				    "limit cannot be part of an or block");
551599909Sluigi			if (have_state)
5516168819Smaxim				errx(EX_USAGE, "only one of keep-state and "
5517159636Soleg				    "limit is allowed");
5518101116Sluigi			have_state = cmd;
551998943Sluigi
552098943Sluigi			cmd->len = F_INSN_SIZE(ipfw_insn_limit);
552198943Sluigi			cmd->opcode = O_LIMIT;
5522159636Soleg			c->limit_mask = c->conn_limit = 0;
552398943Sluigi
5524159636Soleg			while (ac > 0) {
5525159636Soleg				if ((val = match_token(limit_masks, *av)) <= 0)
552698943Sluigi					break;
552798943Sluigi				c->limit_mask |= val;
552898943Sluigi				ac--; av++;
552998943Sluigi			}
5530159636Soleg
553198943Sluigi			if (c->limit_mask == 0)
5532159636Soleg				errx(EX_USAGE, "limit: missing limit mask");
5533159636Soleg
5534182823Srik			GET_UINT_ARG(c->conn_limit, 1, IPFW_DEFAULT_RULE - 1,
5535182823Srik			    TOK_LIMIT, rule_options);
5536159636Soleg
553798943Sluigi			ac--; av++;
553898943Sluigi			break;
5539159636Soleg		}
554098943Sluigi
5541102087Sluigi		case TOK_PROTO:
5542102087Sluigi			NEED1("missing protocol");
5543145246Sbrooks			if (add_proto(cmd, *av, &proto)) {
5544102087Sluigi				ac--; av++;
5545102098Sluigi			} else
5546116438Smaxim				errx(EX_DATAERR, "invalid protocol ``%s''",
5547116438Smaxim				    *av);
5548102087Sluigi			break;
5549106505Smaxim
5550102087Sluigi		case TOK_SRCIP:
5551102087Sluigi			NEED1("missing source IP");
5552102087Sluigi			if (add_srcip(cmd, *av)) {
5553102087Sluigi				ac--; av++;
5554102087Sluigi			}
5555102087Sluigi			break;
5556102087Sluigi
5557102087Sluigi		case TOK_DSTIP:
5558102087Sluigi			NEED1("missing destination IP");
5559102087Sluigi			if (add_dstip(cmd, *av)) {
5560102087Sluigi				ac--; av++;
5561102087Sluigi			}
5562102087Sluigi			break;
5563102087Sluigi
5564145246Sbrooks		case TOK_SRCIP6:
5565145246Sbrooks			NEED1("missing source IP6");
5566145246Sbrooks			if (add_srcip6(cmd, *av)) {
5567145246Sbrooks				ac--; av++;
5568145246Sbrooks			}
5569145246Sbrooks			break;
5570145246Sbrooks
5571145246Sbrooks		case TOK_DSTIP6:
5572145246Sbrooks			NEED1("missing destination IP6");
5573145246Sbrooks			if (add_dstip6(cmd, *av)) {
5574145246Sbrooks				ac--; av++;
5575145246Sbrooks			}
5576145246Sbrooks			break;
5577145246Sbrooks
5578102087Sluigi		case TOK_SRCPORT:
5579102087Sluigi			NEED1("missing source port");
5580140271Sbrooks			if (_substrcmp(*av, "any") == 0 ||
5581102087Sluigi			    add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
5582102087Sluigi				ac--; av++;
5583102087Sluigi			} else
5584102087Sluigi				errx(EX_DATAERR, "invalid source port %s", *av);
5585102087Sluigi			break;
5586102087Sluigi
5587102087Sluigi		case TOK_DSTPORT:
5588102087Sluigi			NEED1("missing destination port");
5589140271Sbrooks			if (_substrcmp(*av, "any") == 0 ||
5590102087Sluigi			    add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
5591102087Sluigi				ac--; av++;
5592102087Sluigi			} else
5593102087Sluigi				errx(EX_DATAERR, "invalid destination port %s",
5594102087Sluigi				    *av);
5595102087Sluigi			break;
5596102087Sluigi
5597102087Sluigi		case TOK_MAC:
5598102087Sluigi			if (add_mac(cmd, ac, av)) {
5599102087Sluigi				ac -= 2; av += 2;
5600102087Sluigi			}
5601102087Sluigi			break;
5602102087Sluigi
5603102087Sluigi		case TOK_MACTYPE:
5604102087Sluigi			NEED1("missing mac type");
5605102087Sluigi			if (!add_mactype(cmd, ac, *av))
5606116438Smaxim				errx(EX_DATAERR, "invalid mac type %s", *av);
5607102087Sluigi			ac--; av++;
5608102087Sluigi			break;
5609102087Sluigi
5610112250Scjc		case TOK_VERREVPATH:
5611112250Scjc			fill_cmd(cmd, O_VERREVPATH, 0, 0);
5612112250Scjc			break;
5613116919Sluigi
5614128575Sandre		case TOK_VERSRCREACH:
5615128575Sandre			fill_cmd(cmd, O_VERSRCREACH, 0, 0);
5616128575Sandre			break;
5617128575Sandre
5618133387Sandre		case TOK_ANTISPOOF:
5619133387Sandre			fill_cmd(cmd, O_ANTISPOOF, 0, 0);
5620133387Sandre			break;
5621133387Sandre
5622117241Sluigi		case TOK_IPSEC:
5623117241Sluigi			fill_cmd(cmd, O_IPSEC, 0, 0);
5624117241Sluigi			break;
5625117241Sluigi
5626145246Sbrooks		case TOK_IPV6:
5627145246Sbrooks			fill_cmd(cmd, O_IP6, 0, 0);
5628145246Sbrooks			break;
5629145246Sbrooks
5630146894Smlaier		case TOK_IPV4:
5631146894Smlaier			fill_cmd(cmd, O_IP4, 0, 0);
5632146894Smlaier			break;
5633146894Smlaier
5634145246Sbrooks		case TOK_EXT6HDR:
5635145246Sbrooks			fill_ext6hdr( cmd, *av );
5636145246Sbrooks			ac--; av++;
5637145246Sbrooks			break;
5638145246Sbrooks
5639145246Sbrooks		case TOK_FLOWID:
5640145246Sbrooks			if (proto != IPPROTO_IPV6 )
5641145246Sbrooks				errx( EX_USAGE, "flow-id filter is active "
5642145246Sbrooks				    "only for ipv6 protocol\n");
5643145246Sbrooks			fill_flow6( (ipfw_insn_u32 *) cmd, *av );
5644145246Sbrooks			ac--; av++;
5645145246Sbrooks			break;
5646145246Sbrooks
5647117469Sluigi		case TOK_COMMENT:
5648117469Sluigi			fill_comment(cmd, ac, av);
5649117469Sluigi			av += ac;
5650117469Sluigi			ac = 0;
5651117469Sluigi			break;
5652117469Sluigi
5653158879Soleg		case TOK_TAGGED:
5654159636Soleg			if (ac > 0 && strpbrk(*av, "-,")) {
5655158879Soleg				if (!add_ports(cmd, *av, 0, O_TAGGED))
5656159636Soleg					errx(EX_DATAERR, "tagged: invalid tag"
5657159636Soleg					    " list: %s", *av);
5658158879Soleg			}
5659159636Soleg			else {
5660159636Soleg				uint16_t tag;
5661159636Soleg
5662182823Srik				GET_UINT_ARG(tag, 1, IPFW_DEFAULT_RULE - 1,
5663182823Srik				    TOK_TAGGED, rule_options);
5664159636Soleg				fill_cmd(cmd, O_TAGGED, 0, tag);
5665159636Soleg			}
5666158879Soleg			ac--; av++;
5667158879Soleg			break;
5668158879Soleg
5669178888Sjulian		case TOK_FIB:
5670178888Sjulian			NEED1("fib requires fib number");
5671178888Sjulian			fill_cmd(cmd, O_FIB, 0, strtoul(*av, NULL, 0));
5672178888Sjulian			ac--; av++;
5673178888Sjulian			break;
5674178888Sjulian
567598943Sluigi		default:
567698943Sluigi			errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
567798943Sluigi		}
567898943Sluigi		if (F_LEN(cmd) > 0) {	/* prepare to advance */
567998943Sluigi			prev = cmd;
568098943Sluigi			cmd = next_cmd(cmd);
568198943Sluigi		}
568298943Sluigi	}
568398943Sluigi
568498943Sluigidone:
568598943Sluigi	/*
568698943Sluigi	 * Now copy stuff into the rule.
568798943Sluigi	 * If we have a keep-state option, the first instruction
568898943Sluigi	 * must be a PROBE_STATE (which is generated here).
568998943Sluigi	 * If we have a LOG option, it was stored as the first command,
569098943Sluigi	 * and now must be moved to the top of the action part.
569198943Sluigi	 */
569298943Sluigi	dst = (ipfw_insn *)rule->cmd;
569398943Sluigi
569498943Sluigi	/*
5695107289Sluigi	 * First thing to write into the command stream is the match probability.
5696107289Sluigi	 */
5697107289Sluigi	if (match_prob != 1) { /* 1 means always match */
5698107289Sluigi		dst->opcode = O_PROB;
5699107289Sluigi		dst->len = 2;
5700107289Sluigi		*((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff);
5701107289Sluigi		dst += dst->len;
5702107289Sluigi	}
5703107289Sluigi
5704107289Sluigi	/*
570598943Sluigi	 * generate O_PROBE_STATE if necessary
570698943Sluigi	 */
5707101116Sluigi	if (have_state && have_state->opcode != O_CHECK_STATE) {
570898943Sluigi		fill_cmd(dst, O_PROBE_STATE, 0, 0);
570998943Sluigi		dst = next_cmd(dst);
571098943Sluigi	}
5711158879Soleg
5712158879Soleg	/* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */
571398943Sluigi	for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
571498943Sluigi		i = F_LEN(src);
571598943Sluigi
5716101116Sluigi		switch (src->opcode) {
5717101116Sluigi		case O_LOG:
5718101116Sluigi		case O_KEEP_STATE:
5719101116Sluigi		case O_LIMIT:
5720136071Sgreen		case O_ALTQ:
5721158879Soleg		case O_TAG:
5722101116Sluigi			break;
5723101116Sluigi		default:
5724117328Sluigi			bcopy(src, dst, i * sizeof(uint32_t));
572598943Sluigi			dst += i;
572698943Sluigi		}
572798943Sluigi	}
572898943Sluigi
572998943Sluigi	/*
5730101116Sluigi	 * put back the have_state command as last opcode
5731101116Sluigi	 */
5732101295Sluigi	if (have_state && have_state->opcode != O_CHECK_STATE) {
5733101116Sluigi		i = F_LEN(have_state);
5734117328Sluigi		bcopy(have_state, dst, i * sizeof(uint32_t));
5735101116Sluigi		dst += i;
5736101116Sluigi	}
5737101116Sluigi	/*
573898943Sluigi	 * start action section
573998943Sluigi	 */
574098943Sluigi	rule->act_ofs = dst - rule->cmd;
574198943Sluigi
5742158879Soleg	/* put back O_LOG, O_ALTQ, O_TAG if necessary */
5743136071Sgreen	if (have_log) {
5744136071Sgreen		i = F_LEN(have_log);
5745136071Sgreen		bcopy(have_log, dst, i * sizeof(uint32_t));
574698943Sluigi		dst += i;
574798943Sluigi	}
5748136071Sgreen	if (have_altq) {
5749136071Sgreen		i = F_LEN(have_altq);
5750136071Sgreen		bcopy(have_altq, dst, i * sizeof(uint32_t));
5751136071Sgreen		dst += i;
5752136071Sgreen	}
5753158879Soleg	if (have_tag) {
5754158879Soleg		i = F_LEN(have_tag);
5755158879Soleg		bcopy(have_tag, dst, i * sizeof(uint32_t));
5756158879Soleg		dst += i;
5757158879Soleg	}
575898943Sluigi	/*
575998943Sluigi	 * copy all other actions
576098943Sluigi	 */
576198943Sluigi	for (src = (ipfw_insn *)actbuf; src != action; src += i) {
576298943Sluigi		i = F_LEN(src);
5763117328Sluigi		bcopy(src, dst, i * sizeof(uint32_t));
576498943Sluigi		dst += i;
576598943Sluigi	}
576698943Sluigi
5767117328Sluigi	rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd);
5768117469Sluigi	i = (char *)dst - (char *)rule;
5769119740Stmm	if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1)
577098943Sluigi		err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
577198943Sluigi	if (!do_quiet)
5772117469Sluigi		show_ipfw(rule, 0, 0);
577398943Sluigi}
577498943Sluigi
577598943Sluigistatic void
5776117328Sluigizero(int ac, char *av[], int optname /* IP_FW_ZERO or IP_FW_RESETLOG */)
577798943Sluigi{
5778170923Smaxim	uint32_t arg, saved_arg;
577998943Sluigi	int failed = EX_OK;
5780117469Sluigi	char const *name = optname == IP_FW_ZERO ?  "ZERO" : "RESETLOG";
5781170923Smaxim	char const *errstr;
578298943Sluigi
578398943Sluigi	av++; ac--;
578498943Sluigi
578598943Sluigi	if (!ac) {
578698943Sluigi		/* clear all entries */
5787117328Sluigi		if (do_cmd(optname, NULL, 0) < 0)
5788117328Sluigi			err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name);
578998943Sluigi		if (!do_quiet)
5790117328Sluigi			printf("%s.\n", optname == IP_FW_ZERO ?
5791117328Sluigi			    "Accounting cleared":"Logging counts reset");
579298943Sluigi
579398943Sluigi		return;
579498943Sluigi	}
579598943Sluigi
579698943Sluigi	while (ac) {
579798943Sluigi		/* Rule number */
579898943Sluigi		if (isdigit(**av)) {
5799170923Smaxim			arg = strtonum(*av, 0, 0xffff, &errstr);
5800170923Smaxim			if (errstr)
5801170923Smaxim				errx(EX_DATAERR,
5802170923Smaxim				    "invalid rule number %s\n", *av);
5803170923Smaxim			saved_arg = arg;
5804170923Smaxim			if (use_set)
5805170923Smaxim				arg |= (1 << 24) | ((use_set - 1) << 16);
580698943Sluigi			av++;
580798943Sluigi			ac--;
5808170923Smaxim			if (do_cmd(optname, &arg, sizeof(arg))) {
5809117328Sluigi				warn("rule %u: setsockopt(IP_FW_%s)",
5810170923Smaxim				    saved_arg, name);
581198943Sluigi				failed = EX_UNAVAILABLE;
581298943Sluigi			} else if (!do_quiet)
5813170923Smaxim				printf("Entry %d %s.\n", saved_arg,
5814117328Sluigi				    optname == IP_FW_ZERO ?
5815117328Sluigi					"cleared" : "logging count reset");
581698943Sluigi		} else {
581798943Sluigi			errx(EX_USAGE, "invalid rule number ``%s''", *av);
581898943Sluigi		}
581998943Sluigi	}
582098943Sluigi	if (failed != EX_OK)
582198943Sluigi		exit(failed);
582298943Sluigi}
582398943Sluigi
582498943Sluigistatic void
5825117544Sluigiflush(int force)
582698943Sluigi{
582798943Sluigi	int cmd = do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH;
582898943Sluigi
5829117544Sluigi	if (!force && !do_quiet) { /* need to ask user */
583098943Sluigi		int c;
583198943Sluigi
583298943Sluigi		printf("Are you sure? [yn] ");
583398943Sluigi		fflush(stdout);
583498943Sluigi		do {
583598943Sluigi			c = toupper(getc(stdin));
583698943Sluigi			while (c != '\n' && getc(stdin) != '\n')
583798943Sluigi				if (feof(stdin))
583898943Sluigi					return; /* and do not flush */
583998943Sluigi		} while (c != 'Y' && c != 'N');
584098943Sluigi		printf("\n");
584198943Sluigi		if (c == 'N')	/* user said no */
584298943Sluigi			return;
584398943Sluigi	}
5844170923Smaxim	/* `ipfw set N flush` - is the same that `ipfw delete set N` */
5845170923Smaxim	if (use_set) {
5846170923Smaxim		uint32_t arg = ((use_set - 1) & 0xffff) | (1 << 24);
5847170923Smaxim		if (do_cmd(IP_FW_DEL, &arg, sizeof(arg)) < 0)
5848170923Smaxim			err(EX_UNAVAILABLE, "setsockopt(IP_FW_DEL)");
5849170923Smaxim	} else if (do_cmd(cmd, NULL, 0) < 0)
585098943Sluigi		err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
585198943Sluigi		    do_pipe ? "DUMMYNET" : "FW");
585298943Sluigi	if (!do_quiet)
585398943Sluigi		printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules");
585498943Sluigi}
585598943Sluigi
5856117469Sluigi/*
5857117544Sluigi * Free a the (locally allocated) copy of command line arguments.
5858117469Sluigi */
5859117544Sluigistatic void
5860117544Sluigifree_args(int ac, char **av)
5861117544Sluigi{
5862117544Sluigi	int i;
5863117544Sluigi
5864117544Sluigi	for (i=0; i < ac; i++)
5865117544Sluigi		free(av[i]);
5866117544Sluigi	free(av);
5867117544Sluigi}
5868117544Sluigi
5869183407Srikstatic void table_list(ipfw_table_entry ent, int need_header);
5870183228Srik
5871117544Sluigi/*
5872130281Sru * This one handles all table-related commands
5873130281Sru * 	ipfw table N add addr[/masklen] [value]
5874130281Sru * 	ipfw table N delete addr[/masklen]
5875183407Srik * 	ipfw table {N | all} flush
5876183407Srik * 	ipfw table {N | all} list
5877130281Sru */
5878130281Srustatic void
5879130281Srutable_handler(int ac, char *av[])
5880130281Sru{
5881130281Sru	ipfw_table_entry ent;
5882130281Sru	int do_add;
5883183407Srik	int is_all;
5884183241Srik	size_t len;
5885130281Sru	char *p;
5886183407Srik	uint32_t a;
5887183241Srik	uint32_t tables_max;
5888130281Sru
5889183263Skeramida	len = sizeof(tables_max);
5890183241Srik	if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len,
5891183241Srik		NULL, 0) == -1) {
5892183241Srik#ifdef IPFW_TABLES_MAX
5893183241Srik		warn("Warn: Failed to get the max tables number via sysctl. "
5894183241Srik		     "Using the compiled in defaults. \nThe reason was");
5895183241Srik		tables_max = IPFW_TABLES_MAX;
5896183241Srik#else
5897183241Srik		errx(1, "Failed sysctlbyname(\"net.inet.ip.fw.tables_max\")");
5898183241Srik#endif
5899183241Srik	}
5900183241Srik
5901130281Sru	ac--; av++;
5902130281Sru	if (ac && isdigit(**av)) {
5903130281Sru		ent.tbl = atoi(*av);
5904183407Srik		is_all = 0;
5905130281Sru		ac--; av++;
5906183407Srik	} else if (ac && _substrcmp(*av, "all") == 0) {
5907183407Srik		ent.tbl = 0;
5908183407Srik		is_all = 1;
5909183407Srik		ac--; av++;
5910130281Sru	} else
5911183407Srik		errx(EX_USAGE, "table number or 'all' keyword required");
5912183241Srik	if (ent.tbl >= tables_max)
5913183241Srik		errx(EX_USAGE, "The table number exceeds the maximum allowed "
5914183241Srik			"value (%d)", tables_max - 1);
5915130281Sru	NEED1("table needs command");
5916183407Srik	if (is_all && _substrcmp(*av, "list") != 0
5917183407Srik		   && _substrcmp(*av, "flush") != 0)
5918183407Srik		errx(EX_USAGE, "table number required");
5919183407Srik
5920140271Sbrooks	if (_substrcmp(*av, "add") == 0 ||
5921140271Sbrooks	    _substrcmp(*av, "delete") == 0) {
5922130281Sru		do_add = **av == 'a';
5923130281Sru		ac--; av++;
5924130281Sru		if (!ac)
5925130281Sru			errx(EX_USAGE, "IP address required");
5926130281Sru		p = strchr(*av, '/');
5927130281Sru		if (p) {
5928130281Sru			*p++ = '\0';
5929130281Sru			ent.masklen = atoi(p);
5930130281Sru			if (ent.masklen > 32)
5931130281Sru				errx(EX_DATAERR, "bad width ``%s''", p);
5932130281Sru		} else
5933130281Sru			ent.masklen = 32;
5934130281Sru		if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0)
5935130298Sru			errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
5936130281Sru		ac--; av++;
5937161424Sjulian		if (do_add && ac) {
5938161424Sjulian			unsigned int tval;
5939161424Sjulian			/* isdigit is a bit of a hack here.. */
5940161424Sjulian			if (strchr(*av, (int)'.') == NULL && isdigit(**av))  {
5941161424Sjulian				ent.value = strtoul(*av, NULL, 0);
5942161424Sjulian			} else {
5943161424Sjulian		        	if (lookup_host(*av, (struct in_addr *)&tval) == 0) {
5944161424Sjulian					/* The value must be stored in host order	 *
5945161424Sjulian					 * so that the values < 65k can be distinguished */
5946161424Sjulian		       			ent.value = ntohl(tval);
5947161424Sjulian				} else {
5948161424Sjulian					errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
5949161424Sjulian				}
5950161424Sjulian			}
5951161424Sjulian		} else
5952130281Sru			ent.value = 0;
5953130281Sru		if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL,
5954157335Sjulian		    &ent, sizeof(ent)) < 0) {
5955155639Sjulian			/* If running silent, don't bomb out on these errors. */
5956155639Sjulian			if (!(do_quiet && (errno == (do_add ? EEXIST : ESRCH))))
5957155639Sjulian				err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
5958155639Sjulian				    do_add ? "ADD" : "DEL");
5959155639Sjulian			/* In silent mode, react to a failed add by deleting */
5960157332Sjulian			if (do_add) {
5961155639Sjulian				do_cmd(IP_FW_TABLE_DEL, &ent, sizeof(ent));
5962155639Sjulian				if (do_cmd(IP_FW_TABLE_ADD,
5963155639Sjulian				    &ent, sizeof(ent)) < 0)
5964155639Sjulian					err(EX_OSERR,
5965155639Sjulian				            "setsockopt(IP_FW_TABLE_ADD)");
5966157332Sjulian			}
5967157335Sjulian		}
5968140271Sbrooks	} else if (_substrcmp(*av, "flush") == 0) {
5969183407Srik		a = is_all ? tables_max : (ent.tbl + 1);
5970183407Srik		do {
5971183407Srik			if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl,
5972183407Srik			    sizeof(ent.tbl)) < 0)
5973183407Srik				err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
5974183407Srik		} while (++ent.tbl < a);
5975140271Sbrooks	} else if (_substrcmp(*av, "list") == 0) {
5976183407Srik		a = is_all ? tables_max : (ent.tbl + 1);
5977183407Srik		do {
5978183415Srik			table_list(ent, is_all);
5979183407Srik		} while (++ent.tbl < a);
5980183228Srik	} else
5981183228Srik		errx(EX_USAGE, "invalid table command %s", *av);
5982183228Srik}
5983183205Srik
5984183228Srikstatic void
5985183407Sriktable_list(ipfw_table_entry ent, int need_header)
5986183228Srik{
5987183228Srik	ipfw_table *tbl;
5988183228Srik	socklen_t l;
5989183228Srik	uint32_t a;
5990183205Srik
5991183228Srik	a = ent.tbl;
5992183228Srik	l = sizeof(a);
5993183228Srik	if (do_cmd(IP_FW_TABLE_GETSIZE, &a, (uintptr_t)&l) < 0)
5994183228Srik		err(EX_OSERR, "getsockopt(IP_FW_TABLE_GETSIZE)");
5995183228Srik
5996183228Srik	/* If a is zero we have nothing to do, the table is empty. */
5997183228Srik	if (a == 0)
5998183228Srik		return;
5999183228Srik
6000183228Srik	l = sizeof(*tbl) + a * sizeof(ipfw_table_entry);
6001187716Sluigi	tbl = safe_calloc(1, l);
6002183228Srik	tbl->tbl = ent.tbl;
6003183228Srik	if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0)
6004183228Srik		err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)");
6005183407Srik	if (tbl->cnt && need_header)
6006183407Srik		printf("---table(%d)---\n", tbl->tbl);
6007183228Srik	for (a = 0; a < tbl->cnt; a++) {
6008183228Srik		unsigned int tval;
6009183228Srik		tval = tbl->ent[a].value;
6010183228Srik		if (do_value_as_ip) {
6011183228Srik			char tbuf[128];
6012183228Srik			strncpy(tbuf, inet_ntoa(*(struct in_addr *)
6013161456Sjulian				&tbl->ent[a].addr), 127);
6014183228Srik			/* inet_ntoa expects network order */
6015183228Srik			tval = htonl(tval);
6016183228Srik			printf("%s/%u %s\n", tbuf, tbl->ent[a].masklen,
6017183228Srik				inet_ntoa(*(struct in_addr *)&tval));
6018183228Srik		} else {
6019183228Srik			printf("%s/%u %u\n",
6020183228Srik				inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
6021183228Srik				tbl->ent[a].masklen, tval);
6022130281Sru		}
6023183228Srik	}
6024183228Srik	free(tbl);
6025130281Sru}
6026130281Sru
6027165648Spisostatic void
6028183206Srikshow_nat(int ac, char **av)
6029183206Srik{
6030165648Spiso	struct cfg_nat *n;
6031165648Spiso	struct cfg_redir *e;
6032165648Spiso	int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
6033176393Spiso	int nat_cnt, redir_cnt, r;
6034165648Spiso	uint8_t *data, *p;
6035183208Srik	char *endptr;
6036165648Spiso
6037165648Spiso	do_rule = 0;
6038165648Spiso	nalloc = 1024;
6039165648Spiso	size = 0;
6040165648Spiso	data = NULL;
6041176445Spiso	frule = 0;
6042182823Srik	lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */
6043165648Spiso	ac--; av++;
6044165648Spiso
6045183889Smaxim	if (test_only)
6046183889Smaxim		return;
6047183889Smaxim
6048165648Spiso	/* Parse parameters. */
6049165648Spiso	for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
6050165648Spiso		if (!strncmp(av[0], "config", strlen(av[0]))) {
6051165648Spiso			cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
6052165648Spiso			continue;
6053165648Spiso		}
6054165648Spiso		/* Convert command line rule #. */
6055165648Spiso		frule = lrule = strtoul(av[0], &endptr, 10);
6056165648Spiso		if (*endptr == '-')
6057165648Spiso			lrule = strtoul(endptr+1, &endptr, 10);
6058165648Spiso		if (lrule == 0)
6059165648Spiso			err(EX_USAGE, "invalid rule number: %s", av[0]);
6060165648Spiso		do_rule = 1;
6061165648Spiso	}
6062165648Spiso
6063165648Spiso	nbytes = nalloc;
6064165648Spiso	while (nbytes >= nalloc) {
6065165648Spiso		nalloc = nalloc * 2;
6066165648Spiso		nbytes = nalloc;
6067187716Sluigi		data = safe_realloc(data, nbytes);
6068165648Spiso		if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
6069165648Spiso			err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
6070165648Spiso			    (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
6071165648Spiso	}
6072165648Spiso	if (nbytes == 0)
6073176445Spiso		exit(0);
6074165648Spiso	if (do_cfg) {
6075165648Spiso		nat_cnt = *((int *)data);
6076165648Spiso		for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
6077165648Spiso			n = (struct cfg_nat *)&data[i];
6078176445Spiso			if (frule <= n->id && lrule >= n->id)
6079176445Spiso				print_nat_config(&data[i]);
6080165648Spiso			i += sizeof(struct cfg_nat);
6081176393Spiso			for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
6082176393Spiso				e = (struct cfg_redir *)&data[i];
6083165648Spiso				i += sizeof(struct cfg_redir) + e->spool_cnt *
6084165648Spiso				    sizeof(struct cfg_spool);
6085176393Spiso			}
6086165648Spiso		}
6087165648Spiso	} else {
6088165648Spiso		for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
6089165648Spiso			p = &data[i];
6090165648Spiso			if (p == data + nbytes)
6091165648Spiso				break;
6092165648Spiso			bcopy(p, &r, sizeof(int));
6093165648Spiso			if (do_rule) {
6094165648Spiso				if (!(frule <= r && lrule >= r))
6095165648Spiso					continue;
6096165648Spiso			}
6097165648Spiso			printf("nat %u: %s\n", r, p+sizeof(int));
6098165648Spiso		}
6099165648Spiso	}
6100165648Spiso}
6101165648Spiso
6102130281Sru/*
6103187713Sluigi * Called with the arguments, including program name because getopt
6104187713Sluigi * wants it to be present.
6105117544Sluigi * Returns 0 if successful, 1 if empty command, errx() in case of errors.
6106117544Sluigi */
610798943Sluigistatic int
6108117328Sluigiipfw_main(int oldac, char **oldav)
610998943Sluigi{
6110117469Sluigi	int ch, ac, save_ac;
6111170923Smaxim	const char *errstr;
6112117469Sluigi	char **av, **save_av;
6113117469Sluigi	int do_acct = 0;		/* Show packet/byte count */
6114117469Sluigi
6115117469Sluigi#define WHITESP		" \t\f\v\n\r"
6116187713Sluigi	if (oldac < 2)
6117187713Sluigi		return 1;	/* need at least one argument */
6118187713Sluigi	if (oldac == 2) {
6119117328Sluigi		/*
6120117328Sluigi		 * If we are called with a single string, try to split it into
6121117328Sluigi		 * arguments for subsequent parsing.
6122117328Sluigi		 * But first, remove spaces after a ',', by copying the string
6123117328Sluigi		 * in-place.
6124117328Sluigi		 */
6125187713Sluigi		char *arg = oldav[1];	/* The string is the first arg. */
6126117328Sluigi		int l = strlen(arg);
6127117328Sluigi		int copy = 0;		/* 1 if we need to copy, 0 otherwise */
6128117328Sluigi		int i, j;
6129117469Sluigi		for (i = j = 0; i < l; i++) {
6130117469Sluigi			if (arg[i] == '#')	/* comment marker */
6131117469Sluigi				break;
6132117328Sluigi			if (copy) {
6133117328Sluigi				arg[j++] = arg[i];
6134117469Sluigi				copy = !index("," WHITESP, arg[i]);
6135117328Sluigi			} else {
6136117469Sluigi				copy = !index(WHITESP, arg[i]);
6137117328Sluigi				if (copy)
6138117328Sluigi					arg[j++] = arg[i];
6139117328Sluigi			}
6140117469Sluigi		}
6141117328Sluigi		if (!copy && j > 0)	/* last char was a 'blank', remove it */
6142117328Sluigi			j--;
6143117328Sluigi		l = j;			/* the new argument length */
6144117328Sluigi		arg[j++] = '\0';
6145117469Sluigi		if (l == 0)		/* empty string! */
6146117544Sluigi			return 1;
6147117328Sluigi
6148117328Sluigi		/*
6149117328Sluigi		 * First, count number of arguments. Because of the previous
6150117469Sluigi		 * processing, this is just the number of blanks plus 1.
6151117328Sluigi		 */
6152117469Sluigi		for (i = 0, ac = 1; i < l; i++)
6153117469Sluigi			if (index(WHITESP, arg[i]) != NULL)
6154117328Sluigi				ac++;
6155117328Sluigi
6156187713Sluigi		/*
6157187713Sluigi		 * Allocate the argument list, including one entry for
6158187713Sluigi		 * the program name because getopt expects it.
6159187713Sluigi		 */
6160187716Sluigi		av = safe_calloc(ac + 1, sizeof(char *));
6161117328Sluigi
6162117328Sluigi		/*
6163187713Sluigi		 * Second, copy arguments from arg[] to av[]. For each one,
6164117328Sluigi		 * j is the initial character, i is the one past the end.
6165117328Sluigi		 */
6166187713Sluigi		for (ac = 1, i = j = 0; i < l; i++)
6167117469Sluigi			if (index(WHITESP, arg[i]) != NULL || i == l-1) {
6168117328Sluigi				if (i == l-1)
6169117328Sluigi					i++;
6170187716Sluigi				av[ac] = safe_calloc(i-j+1, 1);
6171117328Sluigi				bcopy(arg+j, av[ac], i-j);
6172117328Sluigi				ac++;
6173117328Sluigi				j = i + 1;
6174117328Sluigi			}
6175117328Sluigi	} else {
6176117328Sluigi		/*
6177117328Sluigi		 * If an argument ends with ',' join with the next one.
6178117328Sluigi		 */
6179117328Sluigi		int first, i, l;
6180117328Sluigi
6181187716Sluigi		av = safe_calloc(oldac, sizeof(char *));
6182187713Sluigi		for (first = i = ac = 1, l = 0; i < oldac; i++) {
6183117328Sluigi			char *arg = oldav[i];
6184117328Sluigi			int k = strlen(arg);
6185117328Sluigi
6186117328Sluigi			l += k;
6187117328Sluigi			if (arg[k-1] != ',' || i == oldac-1) {
6188117328Sluigi				/* Time to copy. */
6189187716Sluigi				av[ac] = safe_calloc(l+1, 1);
6190117328Sluigi				for (l=0; first <= i; first++) {
6191117328Sluigi					strcat(av[ac]+l, oldav[first]);
6192117328Sluigi					l += strlen(oldav[first]);
6193117328Sluigi				}
6194117328Sluigi				ac++;
6195117328Sluigi				l = 0;
6196117328Sluigi				first = i+1;
6197117328Sluigi			}
6198117328Sluigi		}
6199117328Sluigi	}
6200117328Sluigi
6201187713Sluigi	av[0] = strdup(oldav[0]);	/* copy progname from the caller */
620298943Sluigi	/* Set the force flag for non-interactive processes */
6203123804Smaxim	if (!do_force)
6204123804Smaxim		do_force = !isatty(STDIN_FILENO);
620598943Sluigi
6206117469Sluigi	/* Save arguments for final freeing of memory. */
6207117469Sluigi	save_ac = ac;
6208117469Sluigi	save_av = av;
6209117469Sluigi
6210187713Sluigi	optind = optreset = 1;	/* restart getopt() */
6211176391Sjulian	while ((ch = getopt(ac, av, "abcdefhinNqs:STtv")) != -1)
621298943Sluigi		switch (ch) {
621398943Sluigi		case 'a':
621498943Sluigi			do_acct = 1;
621598943Sluigi			break;
6216117328Sluigi
6217123495Sluigi		case 'b':
6218123495Sluigi			comment_only = 1;
6219123495Sluigi			do_compact = 1;
6220123495Sluigi			break;
6221123495Sluigi
6222102098Sluigi		case 'c':
6223102098Sluigi			do_compact = 1;
6224102098Sluigi			break;
6225117328Sluigi
622698943Sluigi		case 'd':
622798943Sluigi			do_dynamic = 1;
622898943Sluigi			break;
6229117328Sluigi
623098943Sluigi		case 'e':
623198943Sluigi			do_expired = 1;
623298943Sluigi			break;
6233117328Sluigi
623498943Sluigi		case 'f':
623598943Sluigi			do_force = 1;
623698943Sluigi			break;
6237117328Sluigi
6238117328Sluigi		case 'h': /* help */
6239117544Sluigi			free_args(save_ac, save_av);
6240117328Sluigi			help();
6241117328Sluigi			break;	/* NOTREACHED */
6242117328Sluigi
6243176391Sjulian		case 'i':
6244176391Sjulian			do_value_as_ip = 1;
6245176391Sjulian			break;
6246176391Sjulian
6247117328Sluigi		case 'n':
6248117328Sluigi			test_only = 1;
6249117328Sluigi			break;
6250117328Sluigi
625198943Sluigi		case 'N':
625298943Sluigi			do_resolv = 1;
625398943Sluigi			break;
6254117328Sluigi
625598943Sluigi		case 'q':
625698943Sluigi			do_quiet = 1;
625798943Sluigi			break;
6258117328Sluigi
6259117328Sluigi		case 's': /* sort */
6260117328Sluigi			do_sort = atoi(optarg);
6261117328Sluigi			break;
6262117328Sluigi
6263101628Sluigi		case 'S':
6264101628Sluigi			show_sets = 1;
6265101628Sluigi			break;
6266117328Sluigi
626798943Sluigi		case 't':
626898943Sluigi			do_time = 1;
626998943Sluigi			break;
6270117328Sluigi
6271117472Sluigi		case 'T':
6272117472Sluigi			do_time = 2;	/* numeric timestamp */
6273117472Sluigi			break;
6274117472Sluigi
627598943Sluigi		case 'v': /* verbose */
6276117328Sluigi			verbose = 1;
627798943Sluigi			break;
6278117328Sluigi
627998943Sluigi		default:
6280117544Sluigi			free_args(save_ac, save_av);
6281117544Sluigi			return 1;
628298943Sluigi		}
628398943Sluigi
628498943Sluigi	ac -= optind;
628598943Sluigi	av += optind;
628698943Sluigi	NEED1("bad arguments, for usage summary ``ipfw''");
628798943Sluigi
628898943Sluigi	/*
6289117544Sluigi	 * An undocumented behaviour of ipfw1 was to allow rule numbers first,
6290117544Sluigi	 * e.g. "100 add allow ..." instead of "add 100 allow ...".
6291117544Sluigi	 * In case, swap first and second argument to get the normal form.
6292117544Sluigi	 */
6293117544Sluigi	if (ac > 1 && isdigit(*av[0])) {
6294117544Sluigi		char *p = av[0];
6295117544Sluigi
6296117544Sluigi		av[0] = av[1];
6297117544Sluigi		av[1] = p;
6298117544Sluigi	}
6299117544Sluigi
6300117544Sluigi	/*
6301165648Spiso	 * Optional: pipe, queue or nat.
630298943Sluigi	 */
6303165648Spiso	do_nat = 0;
6304117821Smaxim	do_pipe = 0;
6305165648Spiso	if (!strncmp(*av, "nat", strlen(*av)))
6306165648Spiso 	        do_nat = 1;
6307165648Spiso 	else if (!strncmp(*av, "pipe", strlen(*av)))
630898943Sluigi		do_pipe = 1;
6309140271Sbrooks	else if (_substrcmp(*av, "queue") == 0)
631098943Sluigi		do_pipe = 2;
6311170923Smaxim	else if (!strncmp(*av, "set", strlen(*av))) {
6312170923Smaxim		if (ac > 1 && isdigit(av[1][0])) {
6313170923Smaxim			use_set = strtonum(av[1], 0, RESVD_SET, &errstr);
6314170923Smaxim			if (errstr)
6315170923Smaxim				errx(EX_DATAERR,
6316170923Smaxim				    "invalid set number %s\n", av[1]);
6317170923Smaxim			ac -= 2; av += 2; use_set++;
6318170923Smaxim		}
6319170923Smaxim	}
6320170923Smaxim
6321165648Spiso	if (do_pipe || do_nat) {
632298943Sluigi		ac--;
632398943Sluigi		av++;
632498943Sluigi	}
632598943Sluigi	NEED1("missing command");
632698943Sluigi
632798943Sluigi	/*
6328165648Spiso	 * For pipes, queues and nats we normally say 'nat|pipe NN config'
6329165648Spiso	 * but the code is easier to parse as 'nat|pipe config NN'
633098943Sluigi	 * so we swap the two arguments.
633198943Sluigi	 */
6332165648Spiso	if ((do_pipe || do_nat) && ac > 1 && isdigit(*av[0])) {
633398943Sluigi		char *p = av[0];
6334117544Sluigi
633598943Sluigi		av[0] = av[1];
633698943Sluigi		av[1] = p;
633798943Sluigi	}
6338117328Sluigi
6339170923Smaxim	int try_next = 0;
6340170923Smaxim	if (use_set == 0) {
6341170923Smaxim		if (_substrcmp(*av, "add") == 0)
6342170923Smaxim			add(ac, av);
6343170923Smaxim		else if (do_nat && _substrcmp(*av, "show") == 0)
6344170923Smaxim 			show_nat(ac, av);
6345170923Smaxim		else if (do_pipe && _substrcmp(*av, "config") == 0)
6346170923Smaxim			config_pipe(ac, av);
6347170923Smaxim		else if (do_nat && _substrcmp(*av, "config") == 0)
6348170923Smaxim 			config_nat(ac, av);
6349173080Smaxim		else if (_substrcmp(*av, "set") == 0)
6350173080Smaxim			sets_handler(ac, av);
6351173080Smaxim		else if (_substrcmp(*av, "table") == 0)
6352173080Smaxim			table_handler(ac, av);
6353173080Smaxim		else if (_substrcmp(*av, "enable") == 0)
6354173080Smaxim			sysctl_handler(ac, av, 1);
6355173080Smaxim		else if (_substrcmp(*av, "disable") == 0)
6356173080Smaxim			sysctl_handler(ac, av, 0);
6357173080Smaxim		else
6358173080Smaxim			try_next = 1;
6359170923Smaxim	}
6360117469Sluigi
6361170923Smaxim	if (use_set || try_next) {
6362170923Smaxim		if (_substrcmp(*av, "delete") == 0)
6363170923Smaxim			delete(ac, av);
6364170923Smaxim		else if (_substrcmp(*av, "flush") == 0)
6365170923Smaxim			flush(do_force);
6366170923Smaxim		else if (_substrcmp(*av, "zero") == 0)
6367170923Smaxim			zero(ac, av, IP_FW_ZERO);
6368170923Smaxim		else if (_substrcmp(*av, "resetlog") == 0)
6369170923Smaxim			zero(ac, av, IP_FW_RESETLOG);
6370170923Smaxim		else if (_substrcmp(*av, "print") == 0 ||
6371170923Smaxim		         _substrcmp(*av, "list") == 0)
6372170923Smaxim			list(ac, av, do_acct);
6373170923Smaxim		else if (_substrcmp(*av, "show") == 0)
6374170923Smaxim			list(ac, av, 1 /* show counters */);
6375170923Smaxim		else
6376170923Smaxim			errx(EX_USAGE, "bad command `%s'", *av);
6377170923Smaxim	}
6378170923Smaxim
6379117469Sluigi	/* Free memory allocated in the argument parsing. */
6380117544Sluigi	free_args(save_ac, save_av);
638198943Sluigi	return 0;
638298943Sluigi}
638398943Sluigi
638498943Sluigi
638598943Sluigistatic void
6386106505Smaximipfw_readfile(int ac, char *av[])
638798943Sluigi{
638898943Sluigi#define MAX_ARGS	32
638998943Sluigi	char	buf[BUFSIZ];
6390187716Sluigi	char *progname = av[0];		/* original program name */
6391187713Sluigi	const char *cmd = NULL;		/* preprocessor name, if any */
6392187713Sluigi	const char *filename = av[ac-1]; /* file to read */
6393117469Sluigi	int	c, lineno=0;
639498943Sluigi	FILE	*f = NULL;
639598943Sluigi	pid_t	preproc = 0;
639698943Sluigi
6397123804Smaxim	while ((c = getopt(ac, av, "cfNnp:qS")) != -1) {
639898943Sluigi		switch(c) {
6399117469Sluigi		case 'c':
6400117469Sluigi			do_compact = 1;
6401117469Sluigi			break;
6402117469Sluigi
6403123804Smaxim		case 'f':
6404123804Smaxim			do_force = 1;
6405123804Smaxim			break;
6406123804Smaxim
6407117469Sluigi		case 'N':
6408117469Sluigi			do_resolv = 1;
6409117469Sluigi			break;
6410117469Sluigi
6411117328Sluigi		case 'n':
6412117328Sluigi			test_only = 1;
6413117328Sluigi			break;
6414117328Sluigi
641598943Sluigi		case 'p':
6416117469Sluigi			/*
6417187713Sluigi			 * ipfw -p cmd [args] filename
6418187713Sluigi			 *
6419187713Sluigi			 * We are done with getopt(). All arguments
6420187713Sluigi			 * except the filename go to the preprocessor,
6421187713Sluigi			 * so we need to do the following:
6422187713Sluigi			 * - check that a filename is actually present;
6423187713Sluigi			 * - advance av by optind-1 to skip arguments
6424187713Sluigi			 *   already processed;
6425187713Sluigi			 * - decrease ac by optind, to remove the args
6426187713Sluigi			 *   already processed and the final filename;
6427187713Sluigi			 * - set the last entry in av[] to NULL so
6428187713Sluigi			 *   popen() can detect the end of the array;
6429187713Sluigi			 * - set optind=ac to let getopt() terminate.
6430117469Sluigi			 */
6431187713Sluigi			if (optind == ac)
6432162773Smaxim				errx(EX_USAGE, "no filename argument");
6433187713Sluigi			cmd = optarg;
6434117469Sluigi			av[ac-1] = NULL;
6435187713Sluigi			av += optind - 1;
6436187713Sluigi			ac -= optind;
6437187713Sluigi			optind = ac;
643898943Sluigi			break;
643998943Sluigi
644098943Sluigi		case 'q':
6441117469Sluigi			do_quiet = 1;
644298943Sluigi			break;
644398943Sluigi
6444117469Sluigi		case 'S':
6445117469Sluigi			show_sets = 1;
6446117469Sluigi			break;
6447117469Sluigi
644898943Sluigi		default:
644998943Sluigi			errx(EX_USAGE, "bad arguments, for usage"
645098943Sluigi			     " summary ``ipfw''");
645198943Sluigi		}
645298943Sluigi
6453108231Skbyanc	}
6454108231Skbyanc
6455117469Sluigi	if (cmd == NULL && ac != optind + 1) {
6456117469Sluigi		fprintf(stderr, "ac %d, optind %d\n", ac, optind);
6457117469Sluigi		errx(EX_USAGE, "extraneous filename arguments");
6458108231Skbyanc	}
6459108231Skbyanc
6460117469Sluigi	if ((f = fopen(filename, "r")) == NULL)
6461117469Sluigi		err(EX_UNAVAILABLE, "fopen: %s", filename);
646298943Sluigi
6463117469Sluigi	if (cmd != NULL) {			/* pipe through preprocessor */
646498943Sluigi		int pipedes[2];
646598943Sluigi
646698943Sluigi		if (pipe(pipedes) == -1)
646798943Sluigi			err(EX_OSERR, "cannot create pipe");
646898943Sluigi
6469117469Sluigi		preproc = fork();
6470117469Sluigi		if (preproc == -1)
647198943Sluigi			err(EX_OSERR, "cannot fork");
647298943Sluigi
6473117577Sluigi		if (preproc == 0) {
6474117469Sluigi			/*
6475117469Sluigi			 * Child, will run the preprocessor with the
6476117469Sluigi			 * file on stdin and the pipe on stdout.
6477117469Sluigi			 */
647898943Sluigi			if (dup2(fileno(f), 0) == -1
647998943Sluigi			    || dup2(pipedes[1], 1) == -1)
648098943Sluigi				err(EX_OSERR, "dup2()");
648198943Sluigi			fclose(f);
648298943Sluigi			close(pipedes[1]);
648398943Sluigi			close(pipedes[0]);
6484117469Sluigi			execvp(cmd, av);
648598943Sluigi			err(EX_OSERR, "execvp(%s) failed", cmd);
6486117469Sluigi		} else { /* parent, will reopen f as the pipe */
648798943Sluigi			fclose(f);
648898943Sluigi			close(pipedes[1]);
648998943Sluigi			if ((f = fdopen(pipedes[0], "r")) == NULL) {
649098943Sluigi				int savederrno = errno;
649198943Sluigi
649298943Sluigi				(void)kill(preproc, SIGTERM);
649398943Sluigi				errno = savederrno;
649498943Sluigi				err(EX_OSERR, "fdopen()");
649598943Sluigi			}
649698943Sluigi		}
649798943Sluigi	}
649898943Sluigi
6499117469Sluigi	while (fgets(buf, BUFSIZ, f)) {		/* read commands */
6500117469Sluigi		char linename[10];
6501187713Sluigi		char *args[2];
6502117469Sluigi
650398943Sluigi		lineno++;
650498943Sluigi		sprintf(linename, "Line %d", lineno);
6505117328Sluigi		setprogname(linename); /* XXX */
6506187716Sluigi		args[0] = progname;
6507187713Sluigi		args[1] = buf;
6508187713Sluigi		ipfw_main(2, args);
650998943Sluigi	}
651098943Sluigi	fclose(f);
6511117469Sluigi	if (cmd != NULL) {
6512117469Sluigi		int status;
6513117469Sluigi
651498943Sluigi		if (waitpid(preproc, &status, 0) == -1)
651598943Sluigi			errx(EX_OSERR, "waitpid()");
651698943Sluigi		if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
651798943Sluigi			errx(EX_UNAVAILABLE,
651898943Sluigi			    "preprocessor exited with status %d",
651998943Sluigi			    WEXITSTATUS(status));
652098943Sluigi		else if (WIFSIGNALED(status))
652198943Sluigi			errx(EX_UNAVAILABLE,
652298943Sluigi			    "preprocessor exited with signal %d",
652398943Sluigi			    WTERMSIG(status));
652498943Sluigi	}
652598943Sluigi}
652698943Sluigi
652798943Sluigiint
652898943Sluigimain(int ac, char *av[])
652998943Sluigi{
653098943Sluigi	/*
653198943Sluigi	 * If the last argument is an absolute pathname, interpret it
653298943Sluigi	 * as a file to be preprocessed.
653398943Sluigi	 */
653498943Sluigi
653598943Sluigi	if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0)
653698943Sluigi		ipfw_readfile(ac, av);
6537117544Sluigi	else {
6538187713Sluigi		if (ipfw_main(ac, av))
6539117544Sluigi			show_usage();
6540117544Sluigi	}
654198943Sluigi	return EX_OK;
654298943Sluigi}
6543