ipfw2.c revision 172306
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 172306 2007-09-23 16:29:22Z maxim $
2198943Sluigi */
2298943Sluigi
2398943Sluigi#include <sys/param.h>
2498943Sluigi#include <sys/mbuf.h>
2598943Sluigi#include <sys/socket.h>
2698943Sluigi#include <sys/sockio.h>
2798943Sluigi#include <sys/sysctl.h>
2898943Sluigi#include <sys/time.h>
2998943Sluigi#include <sys/wait.h>
30136071Sgreen#include <sys/queue.h>
3198943Sluigi
3298943Sluigi#include <ctype.h>
3398943Sluigi#include <err.h>
3498943Sluigi#include <errno.h>
3598943Sluigi#include <grp.h>
3698943Sluigi#include <limits.h>
3798943Sluigi#include <netdb.h>
3898943Sluigi#include <pwd.h>
3998943Sluigi#include <signal.h>
4098943Sluigi#include <stdio.h>
4198943Sluigi#include <stdlib.h>
4298943Sluigi#include <stdarg.h>
4398943Sluigi#include <string.h>
44117469Sluigi#include <timeconv.h>	/* XXX do we need this ? */
4598943Sluigi#include <unistd.h>
4698943Sluigi#include <sysexits.h>
47136071Sgreen#include <unistd.h>
48136071Sgreen#include <fcntl.h>
4998943Sluigi
50169424Smaxim#include <net/ethernet.h>
5198943Sluigi#include <net/if.h>
52165648Spiso#include <net/if_dl.h>
53136071Sgreen#include <net/pfvar.h>
54145246Sbrooks#include <net/route.h> /* def. of struct route */
5598943Sluigi#include <netinet/in.h>
5698943Sluigi#include <netinet/in_systm.h>
5798943Sluigi#include <netinet/ip.h>
5898943Sluigi#include <netinet/ip_icmp.h>
59145246Sbrooks#include <netinet/icmp6.h>
6098943Sluigi#include <netinet/ip_fw.h>
6198943Sluigi#include <netinet/ip_dummynet.h>
6298943Sluigi#include <netinet/tcp.h>
6398943Sluigi#include <arpa/inet.h>
64165648Spiso#include <alias.h>
6598943Sluigi
66117328Sluigiint
6798943Sluigi		do_resolv,		/* Would try to resolve all */
6898943Sluigi		do_time,		/* Show time stamps */
6998943Sluigi		do_quiet,		/* Be quiet in add and flush */
7098943Sluigi		do_pipe,		/* this cmd refers to a pipe */
71165648Spiso	        do_nat, 		/* Nat configuration. */
7298943Sluigi		do_sort,		/* field to sort results (0 = no) */
7398943Sluigi		do_dynamic,		/* display dynamic rules */
7498943Sluigi		do_expired,		/* display expired dynamic rules */
75102098Sluigi		do_compact,		/* show rules in compact mode */
76123804Smaxim		do_force,		/* do not ask for confirmation */
77170923Smaxim		use_set,		/* work with specified set number */
78101628Sluigi		show_sets,		/* display rule sets */
79117328Sluigi		test_only,		/* only check syntax */
80123495Sluigi		comment_only,		/* only print action and comment */
8198943Sluigi		verbose;
8298943Sluigi
8398943Sluigi#define	IP_MASK_ALL	0xffffffff
84130013Scsjp/*
85130013Scsjp * the following macro returns an error message if we run out of
86130013Scsjp * arguments.
87130013Scsjp */
88130013Scsjp#define NEED1(msg)      {if (!ac) errx(EX_USAGE, msg);}
8998943Sluigi
90159636Soleg#define GET_UINT_ARG(arg, min, max, tok, s_x) do {			\
91159636Soleg	if (!ac)							\
92159636Soleg		errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \
93159636Soleg	if (_substrcmp(*av, "tablearg") == 0) {				\
94159636Soleg		arg = IP_FW_TABLEARG;					\
95159636Soleg		break;							\
96159636Soleg	}								\
97159636Soleg									\
98159636Soleg	{								\
99159636Soleg	long val;							\
100159636Soleg	char *end;							\
101159636Soleg									\
102159636Soleg	val = strtol(*av, &end, 10);					\
103159636Soleg									\
104159636Soleg	if (!isdigit(**av) || *end != '\0' || (val == 0 && errno == EINVAL)) \
105159636Soleg		errx(EX_DATAERR, "%s: invalid argument: %s",		\
106159636Soleg		    match_value(s_x, tok), *av);			\
107159636Soleg									\
108159636Soleg	if (errno == ERANGE || val < min || val > max)			\
109159636Soleg		errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \
110159636Soleg		    match_value(s_x, tok), min, max, *av);		\
111159636Soleg									\
112159636Soleg	if (val == IP_FW_TABLEARG)					\
113159636Soleg		errx(EX_DATAERR, "%s: illegal argument value: %s",	\
114159636Soleg		    match_value(s_x, tok), *av);			\
115159636Soleg	arg = val;							\
116159636Soleg	}								\
117158879Soleg} while (0)
118158879Soleg
119159636Soleg#define PRINT_UINT_ARG(str, arg) do {					\
120159636Soleg	if (str != NULL)						\
121159636Soleg		printf("%s",str);					\
122159636Soleg	if (arg == IP_FW_TABLEARG)					\
123159636Soleg		printf("tablearg");					\
124159636Soleg	else								\
125159636Soleg		printf("%u", (uint32_t)arg);				\
126159636Soleg} while (0)
127159636Soleg
12898943Sluigi/*
129117328Sluigi * _s_x is a structure that stores a string <-> token pairs, used in
130117328Sluigi * various places in the parser. Entries are stored in arrays,
131117328Sluigi * with an entry with s=NULL as terminator.
132117328Sluigi * The search routines are match_token() and match_value().
133117328Sluigi * Often, an element with x=0 contains an error string.
13498943Sluigi *
13598943Sluigi */
13698943Sluigistruct _s_x {
137117469Sluigi	char const *s;
13898943Sluigi	int x;
13998943Sluigi};
14098943Sluigi
14198943Sluigistatic struct _s_x f_tcpflags[] = {
14298943Sluigi	{ "syn", TH_SYN },
14398943Sluigi	{ "fin", TH_FIN },
14498943Sluigi	{ "ack", TH_ACK },
14598943Sluigi	{ "psh", TH_PUSH },
14698943Sluigi	{ "rst", TH_RST },
14798943Sluigi	{ "urg", TH_URG },
14898943Sluigi	{ "tcp flag", 0 },
14998943Sluigi	{ NULL,	0 }
15098943Sluigi};
15198943Sluigi
15298943Sluigistatic struct _s_x f_tcpopts[] = {
15398943Sluigi	{ "mss",	IP_FW_TCPOPT_MSS },
15498943Sluigi	{ "maxseg",	IP_FW_TCPOPT_MSS },
15598943Sluigi	{ "window",	IP_FW_TCPOPT_WINDOW },
15698943Sluigi	{ "sack",	IP_FW_TCPOPT_SACK },
15798943Sluigi	{ "ts",		IP_FW_TCPOPT_TS },
15898943Sluigi	{ "timestamp",	IP_FW_TCPOPT_TS },
15998943Sluigi	{ "cc",		IP_FW_TCPOPT_CC },
16098943Sluigi	{ "tcp option",	0 },
16198943Sluigi	{ NULL,	0 }
16298943Sluigi};
16398943Sluigi
16498943Sluigi/*
16598943Sluigi * IP options span the range 0 to 255 so we need to remap them
16698943Sluigi * (though in fact only the low 5 bits are significant).
16798943Sluigi */
16898943Sluigistatic struct _s_x f_ipopts[] = {
16998943Sluigi	{ "ssrr",	IP_FW_IPOPT_SSRR},
17098943Sluigi	{ "lsrr",	IP_FW_IPOPT_LSRR},
17198943Sluigi	{ "rr",		IP_FW_IPOPT_RR},
17298943Sluigi	{ "ts",		IP_FW_IPOPT_TS},
17398943Sluigi	{ "ip option",	0 },
17498943Sluigi	{ NULL,	0 }
17598943Sluigi};
17698943Sluigi
17798943Sluigistatic struct _s_x f_iptos[] = {
17898943Sluigi	{ "lowdelay",	IPTOS_LOWDELAY},
17998943Sluigi	{ "throughput",	IPTOS_THROUGHPUT},
18098943Sluigi	{ "reliability", IPTOS_RELIABILITY},
18198943Sluigi	{ "mincost",	IPTOS_MINCOST},
18298943Sluigi	{ "congestion",	IPTOS_CE},
18398943Sluigi	{ "ecntransport", IPTOS_ECT},
18498943Sluigi	{ "ip tos option", 0},
18598943Sluigi	{ NULL,	0 }
18698943Sluigi};
18798943Sluigi
18898943Sluigistatic struct _s_x limit_masks[] = {
18998943Sluigi	{"all",		DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
19098943Sluigi	{"src-addr",	DYN_SRC_ADDR},
19198943Sluigi	{"src-port",	DYN_SRC_PORT},
19298943Sluigi	{"dst-addr",	DYN_DST_ADDR},
19398943Sluigi	{"dst-port",	DYN_DST_PORT},
19498943Sluigi	{NULL,		0}
19598943Sluigi};
19698943Sluigi
19798943Sluigi/*
19898943Sluigi * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines
19998943Sluigi * This is only used in this code.
20098943Sluigi */
20198943Sluigi#define IPPROTO_ETHERTYPE	0x1000
20298943Sluigistatic struct _s_x ether_types[] = {
20398943Sluigi    /*
20498943Sluigi     * Note, we cannot use "-:&/" in the names because they are field
20598943Sluigi     * separators in the type specifications. Also, we use s = NULL as
20698943Sluigi     * end-delimiter, because a type of 0 can be legal.
20798943Sluigi     */
20898943Sluigi	{ "ip",		0x0800 },
20998943Sluigi	{ "ipv4",	0x0800 },
21098943Sluigi	{ "ipv6",	0x86dd },
21198943Sluigi	{ "arp",	0x0806 },
21298943Sluigi	{ "rarp",	0x8035 },
21398943Sluigi	{ "vlan",	0x8100 },
21498943Sluigi	{ "loop",	0x9000 },
21598943Sluigi	{ "trail",	0x1000 },
21698943Sluigi	{ "at",		0x809b },
21798943Sluigi	{ "atalk",	0x809b },
21898943Sluigi	{ "aarp",	0x80f3 },
21998943Sluigi	{ "pppoe_disc",	0x8863 },
22098943Sluigi	{ "pppoe_sess",	0x8864 },
22198943Sluigi	{ "ipx_8022",	0x00E0 },
22298943Sluigi	{ "ipx_8023",	0x0000 },
22398943Sluigi	{ "ipx_ii",	0x8137 },
22498943Sluigi	{ "ipx_snap",	0x8137 },
22598943Sluigi	{ "ipx",	0x8137 },
22698943Sluigi	{ "ns",		0x0600 },
22798943Sluigi	{ NULL,		0 }
22898943Sluigi};
22998943Sluigi
23098943Sluigistatic void show_usage(void);
23198943Sluigi
23298943Sluigienum tokens {
23398943Sluigi	TOK_NULL=0,
23498943Sluigi
23598943Sluigi	TOK_OR,
23698943Sluigi	TOK_NOT,
237101641Sluigi	TOK_STARTBRACE,
238101641Sluigi	TOK_ENDBRACE,
23998943Sluigi
24098943Sluigi	TOK_ACCEPT,
24198943Sluigi	TOK_COUNT,
24298943Sluigi	TOK_PIPE,
24398943Sluigi	TOK_QUEUE,
24498943Sluigi	TOK_DIVERT,
24598943Sluigi	TOK_TEE,
246141351Sglebius	TOK_NETGRAPH,
247141351Sglebius	TOK_NGTEE,
24898943Sluigi	TOK_FORWARD,
24998943Sluigi	TOK_SKIPTO,
25098943Sluigi	TOK_DENY,
25198943Sluigi	TOK_REJECT,
25298943Sluigi	TOK_RESET,
25398943Sluigi	TOK_UNREACH,
25498943Sluigi	TOK_CHECKSTATE,
255165648Spiso	TOK_NAT,
25698943Sluigi
257136071Sgreen	TOK_ALTQ,
258136071Sgreen	TOK_LOG,
259158879Soleg	TOK_TAG,
260158879Soleg	TOK_UNTAG,
261136071Sgreen
262158879Soleg	TOK_TAGGED,
26398943Sluigi	TOK_UID,
26498943Sluigi	TOK_GID,
265133600Scsjp	TOK_JAIL,
26698943Sluigi	TOK_IN,
26798943Sluigi	TOK_LIMIT,
26898943Sluigi	TOK_KEEPSTATE,
26998943Sluigi	TOK_LAYER2,
27098943Sluigi	TOK_OUT,
271136073Sgreen	TOK_DIVERTED,
272136073Sgreen	TOK_DIVERTEDLOOPBACK,
273136073Sgreen	TOK_DIVERTEDOUTPUT,
27498943Sluigi	TOK_XMIT,
27598943Sluigi	TOK_RECV,
27698943Sluigi	TOK_VIA,
27798943Sluigi	TOK_FRAG,
27898943Sluigi	TOK_IPOPTS,
27998943Sluigi	TOK_IPLEN,
28098943Sluigi	TOK_IPID,
28198943Sluigi	TOK_IPPRECEDENCE,
28298943Sluigi	TOK_IPTOS,
28398943Sluigi	TOK_IPTTL,
28498943Sluigi	TOK_IPVER,
28598943Sluigi	TOK_ESTAB,
28698943Sluigi	TOK_SETUP,
287136075Sgreen	TOK_TCPDATALEN,
28898943Sluigi	TOK_TCPFLAGS,
28998943Sluigi	TOK_TCPOPTS,
29098943Sluigi	TOK_TCPSEQ,
29198943Sluigi	TOK_TCPACK,
29298943Sluigi	TOK_TCPWIN,
29398943Sluigi	TOK_ICMPTYPES,
294102087Sluigi	TOK_MAC,
295102087Sluigi	TOK_MACTYPE,
296112250Scjc	TOK_VERREVPATH,
297128575Sandre	TOK_VERSRCREACH,
298133387Sandre	TOK_ANTISPOOF,
299117241Sluigi	TOK_IPSEC,
300117469Sluigi	TOK_COMMENT,
30198943Sluigi
30298943Sluigi	TOK_PLR,
303101978Sluigi	TOK_NOERROR,
30498943Sluigi	TOK_BUCKETS,
30598943Sluigi	TOK_DSTIP,
30698943Sluigi	TOK_SRCIP,
30798943Sluigi	TOK_DSTPORT,
30898943Sluigi	TOK_SRCPORT,
30998943Sluigi	TOK_ALL,
31098943Sluigi	TOK_MASK,
31198943Sluigi	TOK_BW,
31298943Sluigi	TOK_DELAY,
31398943Sluigi	TOK_RED,
31498943Sluigi	TOK_GRED,
31598943Sluigi	TOK_DROPTAIL,
31698943Sluigi	TOK_PROTO,
31798943Sluigi	TOK_WEIGHT,
318165648Spiso	TOK_IP,
319165648Spiso	TOK_IF,
320165648Spiso 	TOK_ALOG,
321165648Spiso 	TOK_DENY_INC,
322165648Spiso 	TOK_SAME_PORTS,
323165648Spiso 	TOK_UNREG_ONLY,
324165648Spiso 	TOK_RESET_ADDR,
325165648Spiso 	TOK_ALIAS_REV,
326165648Spiso 	TOK_PROXY_ONLY,
327165648Spiso	TOK_REDIR_ADDR,
328165648Spiso	TOK_REDIR_PORT,
329165648Spiso	TOK_REDIR_PROTO,
330145246Sbrooks
331145246Sbrooks	TOK_IPV6,
332145246Sbrooks	TOK_FLOWID,
333145246Sbrooks	TOK_ICMP6TYPES,
334145246Sbrooks	TOK_EXT6HDR,
335145246Sbrooks	TOK_DSTIP6,
336145246Sbrooks	TOK_SRCIP6,
337146894Smlaier
338146894Smlaier	TOK_IPV4,
339149020Sbz	TOK_UNREACH6,
340149020Sbz	TOK_RESET6,
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 },
413117328Sluigi	{ NULL, 0 }	/* terminator */
41498943Sluigi};
41598943Sluigi
416136071Sgreenstruct _s_x rule_action_params[] = {
417136071Sgreen	{ "altq",		TOK_ALTQ },
418136071Sgreen	{ "log",		TOK_LOG },
419158879Soleg	{ "tag",		TOK_TAG },
420158879Soleg	{ "untag",		TOK_UNTAG },
421136071Sgreen	{ NULL, 0 }	/* terminator */
422136071Sgreen};
423136071Sgreen
42498943Sluigistruct _s_x rule_options[] = {
425158879Soleg	{ "tagged",		TOK_TAGGED },
42698943Sluigi	{ "uid",		TOK_UID },
42798943Sluigi	{ "gid",		TOK_GID },
428133600Scsjp	{ "jail",		TOK_JAIL },
42998943Sluigi	{ "in",			TOK_IN },
43098943Sluigi	{ "limit",		TOK_LIMIT },
43198943Sluigi	{ "keep-state",		TOK_KEEPSTATE },
43298943Sluigi	{ "bridged",		TOK_LAYER2 },
43398943Sluigi	{ "layer2",		TOK_LAYER2 },
43498943Sluigi	{ "out",		TOK_OUT },
435136073Sgreen	{ "diverted",		TOK_DIVERTED },
436136073Sgreen	{ "diverted-loopback",	TOK_DIVERTEDLOOPBACK },
437136073Sgreen	{ "diverted-output",	TOK_DIVERTEDOUTPUT },
43898943Sluigi	{ "xmit",		TOK_XMIT },
43998943Sluigi	{ "recv",		TOK_RECV },
44098943Sluigi	{ "via",		TOK_VIA },
44198943Sluigi	{ "fragment",		TOK_FRAG },
44298943Sluigi	{ "frag",		TOK_FRAG },
44398943Sluigi	{ "ipoptions",		TOK_IPOPTS },
44498943Sluigi	{ "ipopts",		TOK_IPOPTS },
44598943Sluigi	{ "iplen",		TOK_IPLEN },
44698943Sluigi	{ "ipid",		TOK_IPID },
44798943Sluigi	{ "ipprecedence",	TOK_IPPRECEDENCE },
44898943Sluigi	{ "iptos",		TOK_IPTOS },
44998943Sluigi	{ "ipttl",		TOK_IPTTL },
45098943Sluigi	{ "ipversion",		TOK_IPVER },
45198943Sluigi	{ "ipver",		TOK_IPVER },
45298943Sluigi	{ "estab",		TOK_ESTAB },
45398943Sluigi	{ "established",	TOK_ESTAB },
45498943Sluigi	{ "setup",		TOK_SETUP },
455136075Sgreen	{ "tcpdatalen",		TOK_TCPDATALEN },
45698943Sluigi	{ "tcpflags",		TOK_TCPFLAGS },
45798943Sluigi	{ "tcpflgs",		TOK_TCPFLAGS },
45898943Sluigi	{ "tcpoptions",		TOK_TCPOPTS },
45998943Sluigi	{ "tcpopts",		TOK_TCPOPTS },
46098943Sluigi	{ "tcpseq",		TOK_TCPSEQ },
46198943Sluigi	{ "tcpack",		TOK_TCPACK },
46298943Sluigi	{ "tcpwin",		TOK_TCPWIN },
46399909Sluigi	{ "icmptype",		TOK_ICMPTYPES },
46498943Sluigi	{ "icmptypes",		TOK_ICMPTYPES },
465102087Sluigi	{ "dst-ip",		TOK_DSTIP },
466102087Sluigi	{ "src-ip",		TOK_SRCIP },
467102087Sluigi	{ "dst-port",		TOK_DSTPORT },
468102087Sluigi	{ "src-port",		TOK_SRCPORT },
469102087Sluigi	{ "proto",		TOK_PROTO },
470102087Sluigi	{ "MAC",		TOK_MAC },
471102087Sluigi	{ "mac",		TOK_MAC },
472102087Sluigi	{ "mac-type",		TOK_MACTYPE },
473112250Scjc	{ "verrevpath",		TOK_VERREVPATH },
474128575Sandre	{ "versrcreach",	TOK_VERSRCREACH },
475133387Sandre	{ "antispoof",		TOK_ANTISPOOF },
476117241Sluigi	{ "ipsec",		TOK_IPSEC },
477145246Sbrooks	{ "icmp6type",		TOK_ICMP6TYPES },
478145246Sbrooks	{ "icmp6types",		TOK_ICMP6TYPES },
479145246Sbrooks	{ "ext6hdr",		TOK_EXT6HDR},
480145246Sbrooks	{ "flow-id",		TOK_FLOWID},
481145246Sbrooks	{ "ipv6",		TOK_IPV6},
482145246Sbrooks	{ "ip6",		TOK_IPV6},
483146894Smlaier	{ "ipv4",		TOK_IPV4},
484146894Smlaier	{ "ip4",		TOK_IPV4},
485145246Sbrooks	{ "dst-ipv6",		TOK_DSTIP6},
486145246Sbrooks	{ "dst-ip6",		TOK_DSTIP6},
487145246Sbrooks	{ "src-ipv6",		TOK_SRCIP6},
488145246Sbrooks	{ "src-ip6",		TOK_SRCIP6},
489117469Sluigi	{ "//",			TOK_COMMENT },
49098943Sluigi
49198943Sluigi	{ "not",		TOK_NOT },		/* pseudo option */
49298943Sluigi	{ "!", /* escape ? */	TOK_NOT },		/* pseudo option */
49398943Sluigi	{ "or",			TOK_OR },		/* pseudo option */
49498943Sluigi	{ "|", /* escape */	TOK_OR },		/* pseudo option */
495101641Sluigi	{ "{",			TOK_STARTBRACE },	/* pseudo option */
496101641Sluigi	{ "(",			TOK_STARTBRACE },	/* pseudo option */
497101641Sluigi	{ "}",			TOK_ENDBRACE },		/* pseudo option */
498101641Sluigi	{ ")",			TOK_ENDBRACE },		/* pseudo option */
499117328Sluigi	{ NULL, 0 }	/* terminator */
50098943Sluigi};
50198943Sluigi
502153374Sglebius#define	TABLEARG	"tablearg"
503153374Sglebius
504117328Sluigistatic __inline uint64_t
505117328Sluigialign_uint64(uint64_t *pll) {
506117328Sluigi	uint64_t ret;
507115793Sticso
508115793Sticso	bcopy (pll, &ret, sizeof(ret));
509115793Sticso	return ret;
510129389Sstefanf}
511115793Sticso
512117328Sluigi/*
513117328Sluigi * conditionally runs the command.
514117328Sluigi */
515117469Sluigistatic int
516119740Stmmdo_cmd(int optname, void *optval, uintptr_t optlen)
517117328Sluigi{
518117328Sluigi	static int s = -1;	/* the socket */
519117328Sluigi	int i;
520117577Sluigi
521117328Sluigi	if (test_only)
522117328Sluigi		return 0;
523117328Sluigi
524117328Sluigi	if (s == -1)
525117328Sluigi		s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
526117328Sluigi	if (s < 0)
527117328Sluigi		err(EX_UNAVAILABLE, "socket");
528117328Sluigi
529117328Sluigi	if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
530130281Sru	    optname == IP_FW_ADD || optname == IP_FW_TABLE_LIST ||
531165648Spiso	    optname == IP_FW_TABLE_GETSIZE ||
532165648Spiso	    optname == IP_FW_NAT_GET_CONFIG ||
533165648Spiso	    optname == IP_FW_NAT_GET_LOG)
534117328Sluigi		i = getsockopt(s, IPPROTO_IP, optname, optval,
535117328Sluigi			(socklen_t *)optlen);
536117328Sluigi	else
537117328Sluigi		i = setsockopt(s, IPPROTO_IP, optname, optval, optlen);
538117328Sluigi	return i;
539117328Sluigi}
540117328Sluigi
54198943Sluigi/**
54298943Sluigi * match_token takes a table and a string, returns the value associated
543117328Sluigi * with the string (-1 in case of failure).
54498943Sluigi */
54598943Sluigistatic int
54698943Sluigimatch_token(struct _s_x *table, char *string)
54798943Sluigi{
54898943Sluigi	struct _s_x *pt;
549117469Sluigi	uint i = strlen(string);
55098943Sluigi
55198943Sluigi	for (pt = table ; i && pt->s != NULL ; pt++)
55298943Sluigi		if (strlen(pt->s) == i && !bcmp(string, pt->s, i))
55398943Sluigi			return pt->x;
55498943Sluigi	return -1;
555129389Sstefanf}
55698943Sluigi
557117328Sluigi/**
558117328Sluigi * match_value takes a table and a value, returns the string associated
559117328Sluigi * with the value (NULL in case of failure).
560117328Sluigi */
561117469Sluigistatic char const *
562117469Sluigimatch_value(struct _s_x *p, int value)
56398943Sluigi{
56498943Sluigi	for (; p->s != NULL; p++)
56598943Sluigi		if (p->x == value)
56698943Sluigi			return p->s;
56798943Sluigi	return NULL;
56898943Sluigi}
56998943Sluigi
57098943Sluigi/*
571140271Sbrooks * _substrcmp takes two strings and returns 1 if they do not match,
572140271Sbrooks * and 0 if they match exactly or the first string is a sub-string
573140271Sbrooks * of the second.  A warning is printed to stderr in the case that the
574140271Sbrooks * first string is a sub-string of the second.
575140271Sbrooks *
576140271Sbrooks * This function will be removed in the future through the usual
577140271Sbrooks * deprecation process.
578140271Sbrooks */
579140271Sbrooksstatic int
580140271Sbrooks_substrcmp(const char *str1, const char* str2)
581140271Sbrooks{
582140271Sbrooks
583140271Sbrooks	if (strncmp(str1, str2, strlen(str1)) != 0)
584140271Sbrooks		return 1;
585140271Sbrooks
586140271Sbrooks	if (strlen(str1) != strlen(str2))
587140271Sbrooks		warnx("DEPRECATED: '%s' matched '%s' as a sub-string",
588140271Sbrooks		    str1, str2);
589140271Sbrooks	return 0;
590140271Sbrooks}
591140271Sbrooks
592140271Sbrooks/*
593140271Sbrooks * _substrcmp2 takes three strings and returns 1 if the first two do not match,
594140271Sbrooks * and 0 if they match exactly or the second string is a sub-string
595140271Sbrooks * of the first.  A warning is printed to stderr in the case that the
596140271Sbrooks * first string does not match the third.
597140271Sbrooks *
598140271Sbrooks * This function exists to warn about the bizzare construction
599140271Sbrooks * strncmp(str, "by", 2) which is used to allow people to use a shotcut
600140271Sbrooks * for "bytes".  The problem is that in addition to accepting "by",
601140271Sbrooks * "byt", "byte", and "bytes", it also excepts "by_rabid_dogs" and any
602140271Sbrooks * other string beginning with "by".
603140271Sbrooks *
604140271Sbrooks * This function will be removed in the future through the usual
605140271Sbrooks * deprecation process.
606140271Sbrooks */
607140271Sbrooksstatic int
608140271Sbrooks_substrcmp2(const char *str1, const char* str2, const char* str3)
609140271Sbrooks{
610140271Sbrooks
611140271Sbrooks	if (strncmp(str1, str2, strlen(str2)) != 0)
612140271Sbrooks		return 1;
613140271Sbrooks
614140271Sbrooks	if (strcmp(str1, str3) != 0)
615140271Sbrooks		warnx("DEPRECATED: '%s' matched '%s'",
616140271Sbrooks		    str1, str3);
617140271Sbrooks	return 0;
618140271Sbrooks}
619140271Sbrooks
620140271Sbrooks/*
62198943Sluigi * prints one port, symbolic or numeric
62298943Sluigi */
62398943Sluigistatic void
624117328Sluigiprint_port(int proto, uint16_t port)
62598943Sluigi{
62698943Sluigi
62798943Sluigi	if (proto == IPPROTO_ETHERTYPE) {
628117469Sluigi		char const *s;
62998943Sluigi
63098943Sluigi		if (do_resolv && (s = match_value(ether_types, port)) )
63198943Sluigi			printf("%s", s);
63298943Sluigi		else
63398943Sluigi			printf("0x%04x", port);
63498943Sluigi	} else {
63598943Sluigi		struct servent *se = NULL;
63698943Sluigi		if (do_resolv) {
63798943Sluigi			struct protoent *pe = getprotobynumber(proto);
63898943Sluigi
63998943Sluigi			se = getservbyport(htons(port), pe ? pe->p_name : NULL);
64098943Sluigi		}
64198943Sluigi		if (se)
64298943Sluigi			printf("%s", se->s_name);
64398943Sluigi		else
64498943Sluigi			printf("%d", port);
64598943Sluigi	}
64698943Sluigi}
64798943Sluigi
648117328Sluigistruct _s_x _port_name[] = {
649117328Sluigi	{"dst-port",	O_IP_DSTPORT},
650117328Sluigi	{"src-port",	O_IP_SRCPORT},
651117328Sluigi	{"ipid",	O_IPID},
652117328Sluigi	{"iplen",	O_IPLEN},
653117328Sluigi	{"ipttl",	O_IPTTL},
654117328Sluigi	{"mac-type",	O_MAC_TYPE},
655136075Sgreen	{"tcpdatalen",	O_TCPDATALEN},
656158879Soleg	{"tagged",	O_TAGGED},
657117328Sluigi	{NULL,		0}
658117328Sluigi};
659117328Sluigi
66098943Sluigi/*
661117328Sluigi * Print the values in a list 16-bit items of the types above.
66298943Sluigi * XXX todo: add support for mask.
66398943Sluigi */
66498943Sluigistatic void
665102087Sluigiprint_newports(ipfw_insn_u16 *cmd, int proto, int opcode)
66698943Sluigi{
667117328Sluigi	uint16_t *p = cmd->ports;
66898943Sluigi	int i;
669117469Sluigi	char const *sep;
67098943Sluigi
671116690Sluigi	if (opcode != 0) {
672117328Sluigi		sep = match_value(_port_name, opcode);
673117328Sluigi		if (sep == NULL)
674116690Sluigi			sep = "???";
675116690Sluigi		printf (" %s", sep);
676116690Sluigi	}
677116690Sluigi	sep = " ";
67898943Sluigi	for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
67998943Sluigi		printf(sep);
68098943Sluigi		print_port(proto, p[0]);
68198943Sluigi		if (p[0] != p[1]) {
68298943Sluigi			printf("-");
68398943Sluigi			print_port(proto, p[1]);
68498943Sluigi		}
68598943Sluigi		sep = ",";
68698943Sluigi	}
68798943Sluigi}
68898943Sluigi
68998943Sluigi/*
69098943Sluigi * Like strtol, but also translates service names into port numbers
69198943Sluigi * for some protocols.
69298943Sluigi * In particular:
69398943Sluigi *	proto == -1 disables the protocol check;
69498943Sluigi *	proto == IPPROTO_ETHERTYPE looks up an internal table
69598943Sluigi *	proto == <some value in /etc/protocols> matches the values there.
696101628Sluigi * Returns *end == s in case the parameter is not found.
69798943Sluigi */
69898943Sluigistatic int
69998943Sluigistrtoport(char *s, char **end, int base, int proto)
70098943Sluigi{
701101628Sluigi	char *p, *buf;
702101628Sluigi	char *s1;
70398943Sluigi	int i;
70498943Sluigi
705101628Sluigi	*end = s;		/* default - not found */
706117577Sluigi	if (*s == '\0')
707101628Sluigi		return 0;	/* not found */
708106505Smaxim
70998943Sluigi	if (isdigit(*s))
71098943Sluigi		return strtol(s, end, base);
71198943Sluigi
71298943Sluigi	/*
713101628Sluigi	 * find separator. '\\' escapes the next char.
71498943Sluigi	 */
715101628Sluigi	for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++)
716101628Sluigi		if (*s1 == '\\' && s1[1] != '\0')
717101628Sluigi			s1++;
71898943Sluigi
719101628Sluigi	buf = malloc(s1 - s + 1);
720101628Sluigi	if (buf == NULL)
721101628Sluigi		return 0;
722101628Sluigi
723101628Sluigi	/*
724101628Sluigi	 * copy into a buffer skipping backslashes
725101628Sluigi	 */
726101628Sluigi	for (p = s, i = 0; p != s1 ; p++)
727117577Sluigi		if (*p != '\\')
728101628Sluigi			buf[i++] = *p;
729101628Sluigi	buf[i++] = '\0';
730101628Sluigi
73198943Sluigi	if (proto == IPPROTO_ETHERTYPE) {
732101628Sluigi		i = match_token(ether_types, buf);
733101628Sluigi		free(buf);
734101628Sluigi		if (i != -1) {	/* found */
73598943Sluigi			*end = s1;
73698943Sluigi			return i;
73798943Sluigi		}
73898943Sluigi	} else {
73998943Sluigi		struct protoent *pe = NULL;
74098943Sluigi		struct servent *se;
74198943Sluigi
74298943Sluigi		if (proto != 0)
74398943Sluigi			pe = getprotobynumber(proto);
74498943Sluigi		setservent(1);
745101628Sluigi		se = getservbyname(buf, pe ? pe->p_name : NULL);
746101628Sluigi		free(buf);
74798943Sluigi		if (se != NULL) {
74898943Sluigi			*end = s1;
74998943Sluigi			return ntohs(se->s_port);
75098943Sluigi		}
75198943Sluigi	}
752101628Sluigi	return 0;	/* not found */
75398943Sluigi}
75498943Sluigi
75598943Sluigi/*
756136071Sgreen * Map between current altq queue id numbers and names.
757136071Sgreen */
758136071Sgreenstatic int altq_fetched = 0;
759136071Sgreenstatic TAILQ_HEAD(, pf_altq) altq_entries =
760136071Sgreen	TAILQ_HEAD_INITIALIZER(altq_entries);
761136071Sgreen
762136071Sgreenstatic void
763136071Sgreenaltq_set_enabled(int enabled)
764136071Sgreen{
765136071Sgreen	int pffd;
766136071Sgreen
767136071Sgreen	pffd = open("/dev/pf", O_RDWR);
768136071Sgreen	if (pffd == -1)
769136071Sgreen		err(EX_UNAVAILABLE,
770136071Sgreen		    "altq support opening pf(4) control device");
771136071Sgreen	if (enabled) {
772136071Sgreen		if (ioctl(pffd, DIOCSTARTALTQ) != 0 && errno != EEXIST)
773136071Sgreen			err(EX_UNAVAILABLE, "enabling altq");
774136071Sgreen	} else {
775136071Sgreen		if (ioctl(pffd, DIOCSTOPALTQ) != 0 && errno != ENOENT)
776136071Sgreen			err(EX_UNAVAILABLE, "disabling altq");
777136071Sgreen	}
778136071Sgreen	close(pffd);
779136071Sgreen}
780136071Sgreen
781136071Sgreenstatic void
782136071Sgreenaltq_fetch()
783136071Sgreen{
784136071Sgreen	struct pfioc_altq pfioc;
785136071Sgreen	struct pf_altq *altq;
786136071Sgreen	int pffd, mnr;
787136071Sgreen
788136071Sgreen	if (altq_fetched)
789136071Sgreen		return;
790136071Sgreen	altq_fetched = 1;
791136071Sgreen	pffd = open("/dev/pf", O_RDONLY);
792136071Sgreen	if (pffd == -1) {
793136071Sgreen		warn("altq support opening pf(4) control device");
794136071Sgreen		return;
795136071Sgreen	}
796136071Sgreen	bzero(&pfioc, sizeof(pfioc));
797136071Sgreen	if (ioctl(pffd, DIOCGETALTQS, &pfioc) != 0) {
798136071Sgreen		warn("altq support getting queue list");
799136071Sgreen		close(pffd);
800136071Sgreen		return;
801136071Sgreen	}
802136071Sgreen	mnr = pfioc.nr;
803136071Sgreen	for (pfioc.nr = 0; pfioc.nr < mnr; pfioc.nr++) {
804136071Sgreen		if (ioctl(pffd, DIOCGETALTQ, &pfioc) != 0) {
805136071Sgreen			if (errno == EBUSY)
806136071Sgreen				break;
807136071Sgreen			warn("altq support getting queue list");
808136071Sgreen			close(pffd);
809136071Sgreen			return;
810136071Sgreen		}
811136071Sgreen		if (pfioc.altq.qid == 0)
812136071Sgreen			continue;
813136071Sgreen		altq = malloc(sizeof(*altq));
814136071Sgreen		if (altq == NULL)
815136071Sgreen			err(EX_OSERR, "malloc");
816136071Sgreen		*altq = pfioc.altq;
817136071Sgreen		TAILQ_INSERT_TAIL(&altq_entries, altq, entries);
818136071Sgreen	}
819136071Sgreen	close(pffd);
820136071Sgreen}
821136071Sgreen
822136071Sgreenstatic u_int32_t
823136071Sgreenaltq_name_to_qid(const char *name)
824136071Sgreen{
825136071Sgreen	struct pf_altq *altq;
826136071Sgreen
827136071Sgreen	altq_fetch();
828136071Sgreen	TAILQ_FOREACH(altq, &altq_entries, entries)
829136071Sgreen		if (strcmp(name, altq->qname) == 0)
830136071Sgreen			break;
831136071Sgreen	if (altq == NULL)
832136071Sgreen		errx(EX_DATAERR, "altq has no queue named `%s'", name);
833136071Sgreen	return altq->qid;
834136071Sgreen}
835136071Sgreen
836136071Sgreenstatic const char *
837136071Sgreenaltq_qid_to_name(u_int32_t qid)
838136071Sgreen{
839136071Sgreen	struct pf_altq *altq;
840136071Sgreen
841136071Sgreen	altq_fetch();
842136071Sgreen	TAILQ_FOREACH(altq, &altq_entries, entries)
843136071Sgreen		if (qid == altq->qid)
844136071Sgreen			break;
845136071Sgreen	if (altq == NULL)
846136071Sgreen		return NULL;
847136071Sgreen	return altq->qname;
848136071Sgreen}
849136071Sgreen
850136071Sgreenstatic void
851136071Sgreenfill_altq_qid(u_int32_t *qid, const char *av)
852136071Sgreen{
853136071Sgreen	*qid = altq_name_to_qid(av);
854136071Sgreen}
855136071Sgreen
856136071Sgreen/*
857117328Sluigi * Fill the body of the command with the list of port ranges.
85898943Sluigi */
85998943Sluigistatic int
86098943Sluigifill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
86198943Sluigi{
862117328Sluigi	uint16_t a, b, *p = cmd->ports;
86398943Sluigi	int i = 0;
864102087Sluigi	char *s = av;
86598943Sluigi
866102087Sluigi	while (*s) {
86798943Sluigi		a = strtoport(av, &s, 0, proto);
868159636Soleg		if (s == av) 			/* empty or invalid argument */
869159636Soleg			return (0);
870159636Soleg
871159636Soleg		switch (*s) {
872159636Soleg		case '-':			/* a range */
873159636Soleg			av = s + 1;
87498943Sluigi			b = strtoport(av, &s, 0, proto);
875159636Soleg			/* Reject expressions like '1-abc' or '1-2-3'. */
876159636Soleg			if (s == av || (*s != ',' && *s != '\0'))
877159636Soleg				return (0);
87898943Sluigi			p[0] = a;
87998943Sluigi			p[1] = b;
880159636Soleg			break;
881159636Soleg		case ',':			/* comma separated list */
882159636Soleg		case '\0':
88398943Sluigi			p[0] = p[1] = a;
884159636Soleg			break;
885159636Soleg		default:
886159636Soleg			warnx("port list: invalid separator <%c> in <%s>",
887101978Sluigi				*s, av);
888159636Soleg			return (0);
889159636Soleg		}
890159636Soleg
891102087Sluigi		i++;
892102087Sluigi		p += 2;
893159636Soleg		av = s + 1;
89498943Sluigi	}
89598943Sluigi	if (i > 0) {
896159636Soleg		if (i + 1 > F_LEN_MASK)
897102087Sluigi			errx(EX_DATAERR, "too many ports/ranges\n");
898159636Soleg		cmd->o.len |= i + 1;	/* leave F_NOT and F_OR untouched */
89998943Sluigi	}
900159636Soleg	return (i);
90198943Sluigi}
90298943Sluigi
90398943Sluigistatic struct _s_x icmpcodes[] = {
90498943Sluigi      { "net",			ICMP_UNREACH_NET },
90598943Sluigi      { "host",			ICMP_UNREACH_HOST },
90698943Sluigi      { "protocol",		ICMP_UNREACH_PROTOCOL },
90798943Sluigi      { "port",			ICMP_UNREACH_PORT },
90898943Sluigi      { "needfrag",		ICMP_UNREACH_NEEDFRAG },
90998943Sluigi      { "srcfail",		ICMP_UNREACH_SRCFAIL },
91098943Sluigi      { "net-unknown",		ICMP_UNREACH_NET_UNKNOWN },
91198943Sluigi      { "host-unknown",		ICMP_UNREACH_HOST_UNKNOWN },
91298943Sluigi      { "isolated",		ICMP_UNREACH_ISOLATED },
91398943Sluigi      { "net-prohib",		ICMP_UNREACH_NET_PROHIB },
91498943Sluigi      { "host-prohib",		ICMP_UNREACH_HOST_PROHIB },
91598943Sluigi      { "tosnet",		ICMP_UNREACH_TOSNET },
91698943Sluigi      { "toshost",		ICMP_UNREACH_TOSHOST },
91798943Sluigi      { "filter-prohib",	ICMP_UNREACH_FILTER_PROHIB },
91898943Sluigi      { "host-precedence",	ICMP_UNREACH_HOST_PRECEDENCE },
91998943Sluigi      { "precedence-cutoff",	ICMP_UNREACH_PRECEDENCE_CUTOFF },
92098943Sluigi      { NULL, 0 }
92198943Sluigi};
92298943Sluigi
92398943Sluigistatic void
92498943Sluigifill_reject_code(u_short *codep, char *str)
92598943Sluigi{
92698943Sluigi	int val;
92798943Sluigi	char *s;
92898943Sluigi
92998943Sluigi	val = strtoul(str, &s, 0);
93098943Sluigi	if (s == str || *s != '\0' || val >= 0x100)
93198943Sluigi		val = match_token(icmpcodes, str);
932102087Sluigi	if (val < 0)
93398943Sluigi		errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
93498943Sluigi	*codep = val;
93598943Sluigi	return;
93698943Sluigi}
93798943Sluigi
93898943Sluigistatic void
939117328Sluigiprint_reject_code(uint16_t code)
94098943Sluigi{
941117469Sluigi	char const *s = match_value(icmpcodes, code);
94298943Sluigi
94398943Sluigi	if (s != NULL)
94499475Sluigi		printf("unreach %s", s);
94598943Sluigi	else
94699475Sluigi		printf("unreach %u", code);
94798943Sluigi}
94898943Sluigi
949149020Sbzstatic struct _s_x icmp6codes[] = {
950149020Sbz      { "no-route",		ICMP6_DST_UNREACH_NOROUTE },
951149020Sbz      { "admin-prohib",		ICMP6_DST_UNREACH_ADMIN },
952149020Sbz      { "address",		ICMP6_DST_UNREACH_ADDR },
953149020Sbz      { "port",			ICMP6_DST_UNREACH_NOPORT },
954149020Sbz      { NULL, 0 }
955149020Sbz};
956149020Sbz
957149020Sbzstatic void
958149020Sbzfill_unreach6_code(u_short *codep, char *str)
959149020Sbz{
960149020Sbz	int val;
961149020Sbz	char *s;
962149020Sbz
963149020Sbz	val = strtoul(str, &s, 0);
964149020Sbz	if (s == str || *s != '\0' || val >= 0x100)
965149020Sbz		val = match_token(icmp6codes, str);
966149020Sbz	if (val < 0)
967149020Sbz		errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str);
968149020Sbz	*codep = val;
969149020Sbz	return;
970149020Sbz}
971149020Sbz
972149020Sbzstatic void
973149020Sbzprint_unreach6_code(uint16_t code)
974149020Sbz{
975149020Sbz	char const *s = match_value(icmp6codes, code);
976149020Sbz
977149020Sbz	if (s != NULL)
978149020Sbz		printf("unreach6 %s", s);
979149020Sbz	else
980149020Sbz		printf("unreach6 %u", code);
981149020Sbz}
982149020Sbz
98398943Sluigi/*
98498943Sluigi * Returns the number of bits set (from left) in a contiguous bitmask,
98598943Sluigi * or -1 if the mask is not contiguous.
98698943Sluigi * XXX this needs a proper fix.
98798943Sluigi * This effectively works on masks in big-endian (network) format.
98898943Sluigi * when compiled on little endian architectures.
98998943Sluigi *
99098943Sluigi * First bit is bit 7 of the first byte -- note, for MAC addresses,
99198943Sluigi * the first bit on the wire is bit 0 of the first byte.
99298943Sluigi * len is the max length in bits.
99398943Sluigi */
99498943Sluigistatic int
995117577Sluigicontigmask(uint8_t *p, int len)
99698943Sluigi{
99798943Sluigi	int i, n;
998117577Sluigi
99998943Sluigi	for (i=0; i<len ; i++)
100098943Sluigi		if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
100198943Sluigi			break;
100298943Sluigi	for (n=i+1; n < len; n++)
100398943Sluigi		if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
100498943Sluigi			return -1; /* mask not contiguous */
100598943Sluigi	return i;
100698943Sluigi}
100798943Sluigi
100898943Sluigi/*
100998943Sluigi * print flags set/clear in the two bitmasks passed as parameters.
101098943Sluigi * There is a specialized check for f_tcpflags.
101198943Sluigi */
101298943Sluigistatic void
1013117469Sluigiprint_flags(char const *name, ipfw_insn *cmd, struct _s_x *list)
101498943Sluigi{
1015117469Sluigi	char const *comma = "";
101698943Sluigi	int i;
1017117577Sluigi	uint8_t set = cmd->arg1 & 0xff;
1018117577Sluigi	uint8_t clear = (cmd->arg1 >> 8) & 0xff;
101998943Sluigi
102098943Sluigi	if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) {
102198943Sluigi		printf(" setup");
102298943Sluigi		return;
102398943Sluigi	}
102498943Sluigi
102598943Sluigi	printf(" %s ", name);
102698943Sluigi	for (i=0; list[i].x != 0; i++) {
102798943Sluigi		if (set & list[i].x) {
102898943Sluigi			set &= ~list[i].x;
102998943Sluigi			printf("%s%s", comma, list[i].s);
103098943Sluigi			comma = ",";
103198943Sluigi		}
103298943Sluigi		if (clear & list[i].x) {
103398943Sluigi			clear &= ~list[i].x;
103498943Sluigi			printf("%s!%s", comma, list[i].s);
103598943Sluigi			comma = ",";
103698943Sluigi		}
103798943Sluigi	}
103898943Sluigi}
103998943Sluigi
104098943Sluigi/*
104198943Sluigi * Print the ip address contained in a command.
104298943Sluigi */
104398943Sluigistatic void
1044117469Sluigiprint_ip(ipfw_insn_ip *cmd, char const *s)
104598943Sluigi{
104698943Sluigi	struct hostent *he = NULL;
1047117328Sluigi	int len = F_LEN((ipfw_insn *)cmd);
1048117328Sluigi	uint32_t *a = ((ipfw_insn_u32 *)cmd)->d;
104998943Sluigi
1050102087Sluigi	printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
105198943Sluigi
105298943Sluigi	if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) {
105398943Sluigi		printf("me");
105498943Sluigi		return;
105598943Sluigi	}
1056130281Sru	if (cmd->o.opcode == O_IP_SRC_LOOKUP ||
1057130281Sru	    cmd->o.opcode == O_IP_DST_LOOKUP) {
1058130281Sru		printf("table(%u", ((ipfw_insn *)cmd)->arg1);
1059130281Sru		if (len == F_INSN_SIZE(ipfw_insn_u32))
1060130281Sru			printf(",%u", *a);
1061130281Sru		printf(")");
1062130281Sru		return;
1063130281Sru	}
106498943Sluigi	if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) {
1065117328Sluigi		uint32_t x, *map = (uint32_t *)&(cmd->mask);
1066116716Sluigi		int i, j;
106798943Sluigi		char comma = '{';
106898943Sluigi
106998943Sluigi		x = cmd->o.arg1 - 1;
107098943Sluigi		x = htonl( ~x );
107198943Sluigi		cmd->addr.s_addr = htonl(cmd->addr.s_addr);
107298943Sluigi		printf("%s/%d", inet_ntoa(cmd->addr),
1073117577Sluigi			contigmask((uint8_t *)&x, 32));
107498943Sluigi		x = cmd->addr.s_addr = htonl(cmd->addr.s_addr);
107598943Sluigi		x &= 0xff; /* base */
1076116716Sluigi		/*
1077116716Sluigi		 * Print bits and ranges.
1078116716Sluigi		 * Locate first bit set (i), then locate first bit unset (j).
1079116716Sluigi		 * If we have 3+ consecutive bits set, then print them as a
1080116716Sluigi		 * range, otherwise only print the initial bit and rescan.
1081116716Sluigi		 */
108298943Sluigi		for (i=0; i < cmd->o.arg1; i++)
1083117328Sluigi			if (map[i/32] & (1<<(i & 31))) {
1084116716Sluigi				for (j=i+1; j < cmd->o.arg1; j++)
1085117328Sluigi					if (!(map[ j/32] & (1<<(j & 31))))
1086116716Sluigi						break;
108798943Sluigi				printf("%c%d", comma, i+x);
1088116716Sluigi				if (j>i+2) { /* range has at least 3 elements */
1089116716Sluigi					printf("-%d", j-1+x);
1090116716Sluigi					i = j-1;
1091116716Sluigi				}
109298943Sluigi				comma = ',';
109398943Sluigi			}
109498943Sluigi		printf("}");
109598943Sluigi		return;
109698943Sluigi	}
1097117328Sluigi	/*
1098117328Sluigi	 * len == 2 indicates a single IP, whereas lists of 1 or more
1099117328Sluigi	 * addr/mask pairs have len = (2n+1). We convert len to n so we
1100117328Sluigi	 * use that to count the number of entries.
1101117328Sluigi	 */
1102117328Sluigi    for (len = len / 2; len > 0; len--, a += 2) {
1103117328Sluigi	int mb =	/* mask length */
1104117328Sluigi	    (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ?
1105117577Sluigi		32 : contigmask((uint8_t *)&(a[1]), 32);
110698943Sluigi	if (mb == 32 && do_resolv)
1107117328Sluigi		he = gethostbyaddr((char *)&(a[0]), sizeof(u_long), AF_INET);
110898943Sluigi	if (he != NULL)		/* resolved to name */
110998943Sluigi		printf("%s", he->h_name);
111098943Sluigi	else if (mb == 0)	/* any */
111198943Sluigi		printf("any");
111298943Sluigi	else {		/* numeric IP followed by some kind of mask */
1113117328Sluigi		printf("%s", inet_ntoa( *((struct in_addr *)&a[0]) ) );
111498943Sluigi		if (mb < 0)
1115117328Sluigi			printf(":%s", inet_ntoa( *((struct in_addr *)&a[1]) ) );
111698943Sluigi		else if (mb < 32)
111798943Sluigi			printf("/%d", mb);
111898943Sluigi	}
1119117328Sluigi	if (len > 1)
1120117328Sluigi		printf(",");
1121117328Sluigi    }
112298943Sluigi}
112398943Sluigi
112498943Sluigi/*
112598943Sluigi * prints a MAC address/mask pair
112698943Sluigi */
112798943Sluigistatic void
1128117577Sluigiprint_mac(uint8_t *addr, uint8_t *mask)
112998943Sluigi{
113098943Sluigi	int l = contigmask(mask, 48);
113198943Sluigi
113298943Sluigi	if (l == 0)
113398943Sluigi		printf(" any");
113498943Sluigi	else {
113598943Sluigi		printf(" %02x:%02x:%02x:%02x:%02x:%02x",
113698943Sluigi		    addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
113798943Sluigi		if (l == -1)
113898943Sluigi			printf("&%02x:%02x:%02x:%02x:%02x:%02x",
113998943Sluigi			    mask[0], mask[1], mask[2],
114098943Sluigi			    mask[3], mask[4], mask[5]);
114198943Sluigi		else if (l < 48)
114298943Sluigi			printf("/%d", l);
114398943Sluigi	}
114498943Sluigi}
114598943Sluigi
114699475Sluigistatic void
114799475Sluigifill_icmptypes(ipfw_insn_u32 *cmd, char *av)
114899475Sluigi{
1149117328Sluigi	uint8_t type;
115098943Sluigi
115199475Sluigi	cmd->d[0] = 0;
115299475Sluigi	while (*av) {
115399475Sluigi		if (*av == ',')
115499475Sluigi			av++;
115599475Sluigi
115699475Sluigi		type = strtoul(av, &av, 0);
115799475Sluigi
115899475Sluigi		if (*av != ',' && *av != '\0')
115999475Sluigi			errx(EX_DATAERR, "invalid ICMP type");
116099475Sluigi
116199475Sluigi		if (type > 31)
116299475Sluigi			errx(EX_DATAERR, "ICMP type out of range");
116399475Sluigi
116499475Sluigi		cmd->d[0] |= 1 << type;
116599475Sluigi	}
116699475Sluigi	cmd->o.opcode = O_ICMPTYPE;
116799475Sluigi	cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
116899475Sluigi}
116999475Sluigi
117099475Sluigistatic void
117199475Sluigiprint_icmptypes(ipfw_insn_u32 *cmd)
117299475Sluigi{
117399475Sluigi	int i;
117499475Sluigi	char sep= ' ';
117599475Sluigi
117699475Sluigi	printf(" icmptypes");
117799475Sluigi	for (i = 0; i < 32; i++) {
117899475Sluigi		if ( (cmd->d[0] & (1 << (i))) == 0)
117999475Sluigi			continue;
118099475Sluigi		printf("%c%d", sep, i);
118199475Sluigi		sep = ',';
118299475Sluigi	}
118399475Sluigi}
118499475Sluigi
1185145246Sbrooks/*
1186145246Sbrooks * Print the ip address contained in a command.
1187145246Sbrooks */
1188145246Sbrooksstatic void
1189145246Sbrooksprint_ip6(ipfw_insn_ip6 *cmd, char const *s)
1190145246Sbrooks{
1191145246Sbrooks       struct hostent *he = NULL;
1192145246Sbrooks       int len = F_LEN((ipfw_insn *) cmd) - 1;
1193145246Sbrooks       struct in6_addr *a = &(cmd->addr6);
1194145246Sbrooks       char trad[255];
1195145246Sbrooks
1196145246Sbrooks       printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
1197145246Sbrooks
1198145246Sbrooks       if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) {
1199145246Sbrooks               printf("me6");
1200145246Sbrooks               return;
1201145246Sbrooks       }
1202145246Sbrooks       if (cmd->o.opcode == O_IP6) {
1203152923Sume               printf(" ip6");
1204145246Sbrooks               return;
1205145246Sbrooks       }
1206145246Sbrooks
1207145246Sbrooks       /*
1208145246Sbrooks        * len == 4 indicates a single IP, whereas lists of 1 or more
1209145246Sbrooks        * addr/mask pairs have len = (2n+1). We convert len to n so we
1210145246Sbrooks        * use that to count the number of entries.
1211145246Sbrooks        */
1212145246Sbrooks
1213145246Sbrooks       for (len = len / 4; len > 0; len -= 2, a += 2) {
1214145246Sbrooks           int mb =        /* mask length */
1215145246Sbrooks               (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ?
1216145246Sbrooks               128 : contigmask((uint8_t *)&(a[1]), 128);
1217145246Sbrooks
1218145246Sbrooks           if (mb == 128 && do_resolv)
1219145246Sbrooks               he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6);
1220145246Sbrooks           if (he != NULL)             /* resolved to name */
1221145246Sbrooks               printf("%s", he->h_name);
1222145246Sbrooks           else if (mb == 0)           /* any */
1223145246Sbrooks               printf("any");
1224145246Sbrooks           else {          /* numeric IP followed by some kind of mask */
1225145246Sbrooks               if (inet_ntop(AF_INET6,  a, trad, sizeof( trad ) ) == NULL)
1226145246Sbrooks                   printf("Error ntop in print_ip6\n");
1227145246Sbrooks               printf("%s",  trad );
1228145246Sbrooks               if (mb < 0)     /* XXX not really legal... */
1229145246Sbrooks                   printf(":%s",
1230145246Sbrooks                       inet_ntop(AF_INET6, &a[1], trad, sizeof(trad)));
1231145246Sbrooks               else if (mb < 128)
1232145246Sbrooks                   printf("/%d", mb);
1233145246Sbrooks           }
1234145246Sbrooks           if (len > 2)
1235145246Sbrooks               printf(",");
1236145246Sbrooks       }
1237145246Sbrooks}
1238145246Sbrooks
1239145246Sbrooksstatic void
1240145246Sbrooksfill_icmp6types(ipfw_insn_icmp6 *cmd, char *av)
1241145246Sbrooks{
1242145246Sbrooks       uint8_t type;
1243145246Sbrooks
1244162344Sjhay       bzero(cmd, sizeof(*cmd));
1245145246Sbrooks       while (*av) {
1246145246Sbrooks           if (*av == ',')
1247145246Sbrooks               av++;
1248145246Sbrooks           type = strtoul(av, &av, 0);
1249145246Sbrooks           if (*av != ',' && *av != '\0')
1250145246Sbrooks               errx(EX_DATAERR, "invalid ICMP6 type");
1251145246Sbrooks	   /*
1252145246Sbrooks	    * XXX: shouldn't this be 0xFF?  I can't see any reason why
1253145246Sbrooks	    * we shouldn't be able to filter all possiable values
1254145246Sbrooks	    * regardless of the ability of the rest of the kernel to do
1255145246Sbrooks	    * anything useful with them.
1256145246Sbrooks	    */
1257145246Sbrooks           if (type > ICMP6_MAXTYPE)
1258145246Sbrooks               errx(EX_DATAERR, "ICMP6 type out of range");
1259145246Sbrooks           cmd->d[type / 32] |= ( 1 << (type % 32));
1260145246Sbrooks       }
1261145246Sbrooks       cmd->o.opcode = O_ICMP6TYPE;
1262145246Sbrooks       cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6);
1263145246Sbrooks}
1264145246Sbrooks
1265145246Sbrooks
1266145246Sbrooksstatic void
1267145246Sbrooksprint_icmp6types(ipfw_insn_u32 *cmd)
1268145246Sbrooks{
1269145246Sbrooks       int i, j;
1270145246Sbrooks       char sep= ' ';
1271145246Sbrooks
1272152923Sume       printf(" ip6 icmp6types");
1273145246Sbrooks       for (i = 0; i < 7; i++)
1274145246Sbrooks               for (j=0; j < 32; ++j) {
1275145246Sbrooks                       if ( (cmd->d[i] & (1 << (j))) == 0)
1276145246Sbrooks                               continue;
1277145246Sbrooks                       printf("%c%d", sep, (i*32 + j));
1278145246Sbrooks                       sep = ',';
1279145246Sbrooks               }
1280145246Sbrooks}
1281145246Sbrooks
1282145246Sbrooksstatic void
1283145246Sbrooksprint_flow6id( ipfw_insn_u32 *cmd)
1284145246Sbrooks{
1285145246Sbrooks       uint16_t i, limit = cmd->o.arg1;
1286145246Sbrooks       char sep = ',';
1287145246Sbrooks
1288145246Sbrooks       printf(" flow-id ");
1289145246Sbrooks       for( i=0; i < limit; ++i) {
1290145246Sbrooks               if (i == limit - 1)
1291145246Sbrooks                       sep = ' ';
1292145246Sbrooks               printf("%d%c", cmd->d[i], sep);
1293145246Sbrooks       }
1294145246Sbrooks}
1295145246Sbrooks
1296145246Sbrooks/* structure and define for the extension header in ipv6 */
1297145246Sbrooksstatic struct _s_x ext6hdrcodes[] = {
1298145246Sbrooks       { "frag",       EXT_FRAGMENT },
1299145246Sbrooks       { "hopopt",     EXT_HOPOPTS },
1300145246Sbrooks       { "route",      EXT_ROUTING },
1301149020Sbz       { "dstopt",     EXT_DSTOPTS },
1302145246Sbrooks       { "ah",         EXT_AH },
1303145246Sbrooks       { "esp",        EXT_ESP },
1304169245Sbz       { "rthdr0",     EXT_RTHDR0 },
1305169245Sbz       { "rthdr2",     EXT_RTHDR2 },
1306145246Sbrooks       { NULL,         0 }
1307145246Sbrooks};
1308145246Sbrooks
1309145246Sbrooks/* fills command for the extension header filtering */
1310145246Sbrooksint
1311145246Sbrooksfill_ext6hdr( ipfw_insn *cmd, char *av)
1312145246Sbrooks{
1313145246Sbrooks       int tok;
1314145246Sbrooks       char *s = av;
1315145246Sbrooks
1316145246Sbrooks       cmd->arg1 = 0;
1317145246Sbrooks
1318145246Sbrooks       while(s) {
1319145246Sbrooks           av = strsep( &s, ",") ;
1320145246Sbrooks           tok = match_token(ext6hdrcodes, av);
1321145246Sbrooks           switch (tok) {
1322145246Sbrooks           case EXT_FRAGMENT:
1323145246Sbrooks               cmd->arg1 |= EXT_FRAGMENT;
1324145246Sbrooks               break;
1325145246Sbrooks
1326145246Sbrooks           case EXT_HOPOPTS:
1327145246Sbrooks               cmd->arg1 |= EXT_HOPOPTS;
1328145246Sbrooks               break;
1329145246Sbrooks
1330145246Sbrooks           case EXT_ROUTING:
1331145246Sbrooks               cmd->arg1 |= EXT_ROUTING;
1332145246Sbrooks               break;
1333145246Sbrooks
1334149020Sbz           case EXT_DSTOPTS:
1335149020Sbz               cmd->arg1 |= EXT_DSTOPTS;
1336149020Sbz               break;
1337149020Sbz
1338145246Sbrooks           case EXT_AH:
1339145246Sbrooks               cmd->arg1 |= EXT_AH;
1340145246Sbrooks               break;
1341145246Sbrooks
1342145246Sbrooks           case EXT_ESP:
1343145246Sbrooks               cmd->arg1 |= EXT_ESP;
1344145246Sbrooks               break;
1345145246Sbrooks
1346169245Sbz           case EXT_RTHDR0:
1347169245Sbz               cmd->arg1 |= EXT_RTHDR0;
1348169245Sbz               break;
1349169245Sbz
1350169245Sbz           case EXT_RTHDR2:
1351169245Sbz               cmd->arg1 |= EXT_RTHDR2;
1352169245Sbz               break;
1353169245Sbz
1354145246Sbrooks           default:
1355145246Sbrooks               errx( EX_DATAERR, "invalid option for ipv6 exten header" );
1356145246Sbrooks               break;
1357145246Sbrooks           }
1358145246Sbrooks       }
1359145246Sbrooks       if (cmd->arg1 == 0 )
1360145246Sbrooks           return 0;
1361145246Sbrooks       cmd->opcode = O_EXT_HDR;
1362145246Sbrooks       cmd->len |= F_INSN_SIZE( ipfw_insn );
1363145246Sbrooks       return 1;
1364145246Sbrooks}
1365145246Sbrooks
1366145246Sbrooksvoid
1367145246Sbrooksprint_ext6hdr( ipfw_insn *cmd )
1368145246Sbrooks{
1369145246Sbrooks       char sep = ' ';
1370145246Sbrooks
1371145246Sbrooks       printf(" extension header:");
1372145246Sbrooks       if (cmd->arg1 & EXT_FRAGMENT ) {
1373145246Sbrooks           printf("%cfragmentation", sep);
1374145246Sbrooks           sep = ',';
1375145246Sbrooks       }
1376145246Sbrooks       if (cmd->arg1 & EXT_HOPOPTS ) {
1377145246Sbrooks           printf("%chop options", sep);
1378145246Sbrooks           sep = ',';
1379145246Sbrooks       }
1380145246Sbrooks       if (cmd->arg1 & EXT_ROUTING ) {
1381145246Sbrooks           printf("%crouting options", sep);
1382145246Sbrooks           sep = ',';
1383145246Sbrooks       }
1384169245Sbz       if (cmd->arg1 & EXT_RTHDR0 ) {
1385169245Sbz           printf("%crthdr0", sep);
1386169245Sbz           sep = ',';
1387169245Sbz       }
1388169245Sbz       if (cmd->arg1 & EXT_RTHDR2 ) {
1389169245Sbz           printf("%crthdr2", sep);
1390169245Sbz           sep = ',';
1391169245Sbz       }
1392149020Sbz       if (cmd->arg1 & EXT_DSTOPTS ) {
1393149020Sbz           printf("%cdestination options", sep);
1394149020Sbz           sep = ',';
1395149020Sbz       }
1396145246Sbrooks       if (cmd->arg1 & EXT_AH ) {
1397145246Sbrooks           printf("%cauthentication header", sep);
1398145246Sbrooks           sep = ',';
1399145246Sbrooks       }
1400145246Sbrooks       if (cmd->arg1 & EXT_ESP ) {
1401145246Sbrooks           printf("%cencapsulated security payload", sep);
1402145246Sbrooks       }
1403145246Sbrooks}
1404145246Sbrooks
140598943Sluigi/*
140698943Sluigi * show_ipfw() prints the body of an ipfw rule.
140798943Sluigi * Because the standard rule has at least proto src_ip dst_ip, we use
140898943Sluigi * a helper function to produce these entries if not provided explicitly.
1409102087Sluigi * The first argument is the list of fields we have, the second is
1410102087Sluigi * the list of fields we want to be printed.
1411101978Sluigi *
1412102087Sluigi * Special cases if we have provided a MAC header:
1413102087Sluigi *   + if the rule does not contain IP addresses/ports, do not print them;
1414102087Sluigi *   + if the rule does not contain an IP proto, print "all" instead of "ip";
1415102087Sluigi *
1416102087Sluigi * Once we have 'have_options', IP header fields are printed as options.
141798943Sluigi */
1418101978Sluigi#define	HAVE_PROTO	0x0001
1419101978Sluigi#define	HAVE_SRCIP	0x0002
1420101978Sluigi#define	HAVE_DSTIP	0x0004
1421169139Smaxim#define	HAVE_PROTO4	0x0008
1422169139Smaxim#define	HAVE_PROTO6	0x0010
1423102087Sluigi#define	HAVE_OPTIONS	0x8000
142498943Sluigi
1425101978Sluigi#define	HAVE_IP		(HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP)
142698943Sluigistatic void
1427102087Sluigishow_prerequisites(int *flags, int want, int cmd)
142898943Sluigi{
1429123495Sluigi	if (comment_only)
1430123495Sluigi		return;
1431102087Sluigi	if ( (*flags & HAVE_IP) == HAVE_IP)
1432102087Sluigi		*flags |= HAVE_OPTIONS;
1433102087Sluigi
1434102087Sluigi	if ( !(*flags & HAVE_OPTIONS)) {
1435102087Sluigi		if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO))
1436146894Smlaier			if ( (*flags & HAVE_PROTO4))
1437146894Smlaier				printf(" ip4");
1438146894Smlaier			else if ( (*flags & HAVE_PROTO6))
1439146894Smlaier				printf(" ip6");
1440146894Smlaier			else
1441146894Smlaier				printf(" ip");
1442146894Smlaier
1443102087Sluigi		if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
1444102087Sluigi			printf(" from any");
1445102087Sluigi		if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP))
1446102087Sluigi			printf(" to any");
1447102087Sluigi	}
144898943Sluigi	*flags |= want;
144998943Sluigi}
145098943Sluigi
145198943Sluigistatic void
1452112189Smaximshow_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
145398943Sluigi{
1454107291Skeramida	static int twidth = 0;
145598943Sluigi	int l;
1456158879Soleg	ipfw_insn *cmd, *tagptr = NULL;
1457117626Sluigi	char *comment = NULL;	/* ptr to comment if we have one */
145898943Sluigi	int proto = 0;		/* default */
145998943Sluigi	int flags = 0;	/* prerequisites */
146098943Sluigi	ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */
1461136071Sgreen	ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */
146298943Sluigi	int or_block = 0;	/* we are in an or block */
1463117328Sluigi	uint32_t set_disable;
146498943Sluigi
1465115793Sticso	bcopy(&rule->next_rule, &set_disable, sizeof(set_disable));
1466101628Sluigi
1467101628Sluigi	if (set_disable & (1 << rule->set)) { /* disabled */
1468101628Sluigi		if (!show_sets)
1469101628Sluigi			return;
1470101628Sluigi		else
1471101628Sluigi			printf("# DISABLED ");
1472101628Sluigi	}
147398943Sluigi	printf("%05u ", rule->rulenum);
147498943Sluigi
1475117469Sluigi	if (pcwidth>0 || bcwidth>0)
1476115793Sticso		printf("%*llu %*llu ", pcwidth, align_uint64(&rule->pcnt),
1477115793Sticso		    bcwidth, align_uint64(&rule->bcnt));
147898943Sluigi
1479117472Sluigi	if (do_time == 2)
1480117472Sluigi		printf("%10u ", rule->timestamp);
1481117472Sluigi	else if (do_time == 1) {
1482107291Skeramida		char timestr[30];
1483107291Skeramida		time_t t = (time_t)0;
1484107291Skeramida
1485107291Skeramida		if (twidth == 0) {
1486107291Skeramida			strcpy(timestr, ctime(&t));
1487107291Skeramida			*strchr(timestr, '\n') = '\0';
1488107291Skeramida			twidth = strlen(timestr);
1489107291Skeramida		}
149098943Sluigi		if (rule->timestamp) {
1491107291Skeramida			t = _long_to_time(rule->timestamp);
149298943Sluigi
149398943Sluigi			strcpy(timestr, ctime(&t));
149498943Sluigi			*strchr(timestr, '\n') = '\0';
149598943Sluigi			printf("%s ", timestr);
149698943Sluigi		} else {
1497107291Skeramida			printf("%*s", twidth, " ");
149898943Sluigi		}
149998943Sluigi	}
150098943Sluigi
1501101628Sluigi	if (show_sets)
1502101628Sluigi		printf("set %d ", rule->set);
1503101628Sluigi
150498943Sluigi	/*
1505107289Sluigi	 * print the optional "match probability"
1506107289Sluigi	 */
1507107289Sluigi	if (rule->cmd_len > 0) {
1508107289Sluigi		cmd = rule->cmd ;
1509107289Sluigi		if (cmd->opcode == O_PROB) {
1510107289Sluigi			ipfw_insn_u32 *p = (ipfw_insn_u32 *)cmd;
1511107289Sluigi			double d = 1.0 * p->d[0];
1512107289Sluigi
1513107289Sluigi			d = (d / 0x7fffffff);
1514107289Sluigi			printf("prob %f ", d);
1515107289Sluigi		}
1516107289Sluigi	}
1517107289Sluigi
1518107289Sluigi	/*
151998943Sluigi	 * first print actions
152098943Sluigi	 */
152198943Sluigi        for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule);
152298943Sluigi			l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
152398943Sluigi		switch(cmd->opcode) {
152498943Sluigi		case O_CHECK_STATE:
152598943Sluigi			printf("check-state");
1526102087Sluigi			flags = HAVE_IP; /* avoid printing anything else */
152798943Sluigi			break;
152898943Sluigi
152998943Sluigi		case O_ACCEPT:
153098943Sluigi			printf("allow");
153198943Sluigi			break;
153298943Sluigi
153398943Sluigi		case O_COUNT:
153498943Sluigi			printf("count");
153598943Sluigi			break;
153698943Sluigi
153798943Sluigi		case O_DENY:
153898943Sluigi			printf("deny");
153998943Sluigi			break;
154098943Sluigi
154199475Sluigi		case O_REJECT:
154299475Sluigi			if (cmd->arg1 == ICMP_REJECT_RST)
154399475Sluigi				printf("reset");
154499475Sluigi			else if (cmd->arg1 == ICMP_UNREACH_HOST)
154599475Sluigi				printf("reject");
154699475Sluigi			else
154799475Sluigi				print_reject_code(cmd->arg1);
154899475Sluigi			break;
154999475Sluigi
1550149020Sbz		case O_UNREACH6:
1551149020Sbz			if (cmd->arg1 == ICMP6_UNREACH_RST)
1552149020Sbz				printf("reset6");
1553149020Sbz			else
1554149020Sbz				print_unreach6_code(cmd->arg1);
1555149020Sbz			break;
1556149020Sbz
1557159636Soleg		case O_SKIPTO:
1558159636Soleg			PRINT_UINT_ARG("skipto ", cmd->arg1);
155998943Sluigi			break;
156098943Sluigi
156198943Sluigi		case O_PIPE:
1562159636Soleg			PRINT_UINT_ARG("pipe ", cmd->arg1);
1563159636Soleg			break;
1564159636Soleg
156598943Sluigi		case O_QUEUE:
1566159636Soleg			PRINT_UINT_ARG("queue ", cmd->arg1);
1567159636Soleg			break;
1568159636Soleg
156998943Sluigi		case O_DIVERT:
1570159636Soleg			PRINT_UINT_ARG("divert ", cmd->arg1);
1571159636Soleg			break;
1572159636Soleg
157398943Sluigi		case O_TEE:
1574159636Soleg			PRINT_UINT_ARG("tee ", cmd->arg1);
1575159636Soleg			break;
1576159636Soleg
1577141351Sglebius		case O_NETGRAPH:
1578159636Soleg			PRINT_UINT_ARG("netgraph ", cmd->arg1);
1579159636Soleg			break;
1580159636Soleg
1581141351Sglebius		case O_NGTEE:
1582159636Soleg			PRINT_UINT_ARG("ngtee ", cmd->arg1);
1583159636Soleg			break;
1584141351Sglebius
158598943Sluigi		case O_FORWARD_IP:
158698943Sluigi		    {
158798943Sluigi			ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
158898943Sluigi
1589161424Sjulian			if (s->sa.sin_addr.s_addr == INADDR_ANY) {
1590161424Sjulian				printf("fwd tablearg");
1591161424Sjulian			} else {
1592161424Sjulian				printf("fwd %s", inet_ntoa(s->sa.sin_addr));
1593161424Sjulian			}
159498943Sluigi			if (s->sa.sin_port)
1595103241Sluigi				printf(",%d", s->sa.sin_port);
159698943Sluigi		    }
159798943Sluigi			break;
159898943Sluigi
159998943Sluigi		case O_LOG: /* O_LOG is printed last */
160098943Sluigi			logptr = (ipfw_insn_log *)cmd;
160198943Sluigi			break;
160298943Sluigi
1603136071Sgreen		case O_ALTQ: /* O_ALTQ is printed after O_LOG */
1604136071Sgreen			altqptr = (ipfw_insn_altq *)cmd;
1605136071Sgreen			break;
1606136071Sgreen
1607158879Soleg		case O_TAG:
1608158879Soleg			tagptr = cmd;
1609158879Soleg			break;
1610158879Soleg
1611165648Spiso		case O_NAT:
1612165648Spiso 			printf("nat %u", cmd->arg1);
1613165648Spiso 			break;
1614165648Spiso
161598943Sluigi		default:
1616136071Sgreen			printf("** unrecognized action %d len %d ",
161798943Sluigi				cmd->opcode, cmd->len);
161898943Sluigi		}
161998943Sluigi	}
162098943Sluigi	if (logptr) {
162198943Sluigi		if (logptr->max_log > 0)
162299909Sluigi			printf(" log logamount %d", logptr->max_log);
162398943Sluigi		else
162499909Sluigi			printf(" log");
162598943Sluigi	}
1626136071Sgreen	if (altqptr) {
1627136071Sgreen		const char *qname;
1628102087Sluigi
1629136071Sgreen		qname = altq_qid_to_name(altqptr->qid);
1630136071Sgreen		if (qname == NULL)
1631136071Sgreen			printf(" altq ?<%u>", altqptr->qid);
1632136071Sgreen		else
1633136071Sgreen			printf(" altq %s", qname);
1634136071Sgreen	}
1635158879Soleg	if (tagptr) {
1636158879Soleg		if (tagptr->len & F_NOT)
1637159636Soleg			PRINT_UINT_ARG(" untag ", tagptr->arg1);
1638158879Soleg		else
1639159636Soleg			PRINT_UINT_ARG(" tag ", tagptr->arg1);
1640158879Soleg	}
1641136071Sgreen
164298943Sluigi	/*
1643102087Sluigi	 * then print the body.
164498943Sluigi	 */
1645146894Smlaier        for (l = rule->act_ofs, cmd = rule->cmd ;
1646146894Smlaier			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
1647146894Smlaier		if ((cmd->len & F_OR) || (cmd->len & F_NOT))
1648146894Smlaier			continue;
1649146894Smlaier		if (cmd->opcode == O_IP4) {
1650146894Smlaier			flags |= HAVE_PROTO4;
1651146894Smlaier			break;
1652146894Smlaier		} else if (cmd->opcode == O_IP6) {
1653146894Smlaier			flags |= HAVE_PROTO6;
1654146894Smlaier			break;
1655146894Smlaier		}
1656146894Smlaier	}
1657102087Sluigi	if (rule->_pad & 1) {	/* empty rules before options */
1658146894Smlaier		if (!do_compact) {
1659146894Smlaier			show_prerequisites(&flags, HAVE_PROTO, 0);
1660146894Smlaier			printf(" from any to any");
1661146894Smlaier		}
1662102087Sluigi		flags |= HAVE_IP | HAVE_OPTIONS;
1663102087Sluigi	}
1664102087Sluigi
1665123495Sluigi	if (comment_only)
1666123495Sluigi		comment = "...";
1667123495Sluigi
166898943Sluigi        for (l = rule->act_ofs, cmd = rule->cmd ;
166998943Sluigi			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
167099475Sluigi		/* useful alias */
167199475Sluigi		ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
167298943Sluigi
1673123495Sluigi		if (comment_only) {
1674123495Sluigi			if (cmd->opcode != O_NOP)
1675123495Sluigi				continue;
1676123495Sluigi			printf(" // %s\n", (char *)(cmd + 1));
1677123495Sluigi			return;
1678123495Sluigi		}
1679123495Sluigi
1680102087Sluigi		show_prerequisites(&flags, 0, cmd->opcode);
1681102087Sluigi
168298943Sluigi		switch(cmd->opcode) {
1683117577Sluigi		case O_PROB:
1684107289Sluigi			break;	/* done already */
1685107289Sluigi
168698943Sluigi		case O_PROBE_STATE:
168798943Sluigi			break; /* no need to print anything here */
168898943Sluigi
168998943Sluigi		case O_IP_SRC:
1690130281Sru		case O_IP_SRC_LOOKUP:
169198943Sluigi		case O_IP_SRC_MASK:
169298943Sluigi		case O_IP_SRC_ME:
169398943Sluigi		case O_IP_SRC_SET:
1694102087Sluigi			show_prerequisites(&flags, HAVE_PROTO, 0);
169598943Sluigi			if (!(flags & HAVE_SRCIP))
169698943Sluigi				printf(" from");
169798943Sluigi			if ((cmd->len & F_OR) && !or_block)
169898943Sluigi				printf(" {");
1699102087Sluigi			print_ip((ipfw_insn_ip *)cmd,
1700102087Sluigi				(flags & HAVE_OPTIONS) ? " src-ip" : "");
170198943Sluigi			flags |= HAVE_SRCIP;
170298943Sluigi			break;
170398943Sluigi
170498943Sluigi		case O_IP_DST:
1705130281Sru		case O_IP_DST_LOOKUP:
170698943Sluigi		case O_IP_DST_MASK:
170798943Sluigi		case O_IP_DST_ME:
170898943Sluigi		case O_IP_DST_SET:
1709102087Sluigi			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
171098943Sluigi			if (!(flags & HAVE_DSTIP))
171198943Sluigi				printf(" to");
171298943Sluigi			if ((cmd->len & F_OR) && !or_block)
171398943Sluigi				printf(" {");
1714102087Sluigi			print_ip((ipfw_insn_ip *)cmd,
1715102087Sluigi				(flags & HAVE_OPTIONS) ? " dst-ip" : "");
171698943Sluigi			flags |= HAVE_DSTIP;
171798943Sluigi			break;
171898943Sluigi
1719145246Sbrooks		case O_IP6_SRC:
1720145246Sbrooks		case O_IP6_SRC_MASK:
1721145246Sbrooks		case O_IP6_SRC_ME:
1722147105Smlaier			show_prerequisites(&flags, HAVE_PROTO, 0);
1723145246Sbrooks			if (!(flags & HAVE_SRCIP))
1724145246Sbrooks				printf(" from");
1725145246Sbrooks			if ((cmd->len & F_OR) && !or_block)
1726145246Sbrooks				printf(" {");
1727145246Sbrooks			print_ip6((ipfw_insn_ip6 *)cmd,
1728145246Sbrooks			    (flags & HAVE_OPTIONS) ? " src-ip6" : "");
1729145246Sbrooks			flags |= HAVE_SRCIP | HAVE_PROTO;
1730145246Sbrooks			break;
1731145246Sbrooks
1732145246Sbrooks		case O_IP6_DST:
1733145246Sbrooks		case O_IP6_DST_MASK:
1734145246Sbrooks		case O_IP6_DST_ME:
1735145246Sbrooks			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1736145246Sbrooks			if (!(flags & HAVE_DSTIP))
1737145246Sbrooks				printf(" to");
1738145246Sbrooks			if ((cmd->len & F_OR) && !or_block)
1739145246Sbrooks				printf(" {");
1740145246Sbrooks			print_ip6((ipfw_insn_ip6 *)cmd,
1741145246Sbrooks			    (flags & HAVE_OPTIONS) ? " dst-ip6" : "");
1742145246Sbrooks			flags |= HAVE_DSTIP;
1743145246Sbrooks			break;
1744145246Sbrooks
1745145246Sbrooks		case O_FLOW6ID:
1746145246Sbrooks		print_flow6id( (ipfw_insn_u32 *) cmd );
1747145246Sbrooks		flags |= HAVE_OPTIONS;
1748145246Sbrooks		break;
1749145246Sbrooks
175098943Sluigi		case O_IP_DSTPORT:
1751102087Sluigi			show_prerequisites(&flags, HAVE_IP, 0);
175298943Sluigi		case O_IP_SRCPORT:
1753102087Sluigi			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1754101641Sluigi			if ((cmd->len & F_OR) && !or_block)
1755101641Sluigi				printf(" {");
1756172306Smaxim			if (cmd->len & F_NOT)
1757172306Smaxim				printf(" not");
1758102087Sluigi			print_newports((ipfw_insn_u16 *)cmd, proto,
1759102087Sluigi				(flags & HAVE_OPTIONS) ? cmd->opcode : 0);
176098943Sluigi			break;
176198943Sluigi
176298943Sluigi		case O_PROTO: {
1763145246Sbrooks			struct protoent *pe = NULL;
176498943Sluigi
176598943Sluigi			if ((cmd->len & F_OR) && !or_block)
176698943Sluigi				printf(" {");
176798943Sluigi			if (cmd->len & F_NOT)
176898943Sluigi				printf(" not");
176998943Sluigi			proto = cmd->arg1;
1770145567Sbrooks			pe = getprotobynumber(cmd->arg1);
1771146894Smlaier			if ((flags & (HAVE_PROTO4 | HAVE_PROTO6)) &&
1772146894Smlaier			    !(flags & HAVE_PROTO))
1773146894Smlaier				show_prerequisites(&flags,
1774146894Smlaier				    HAVE_IP | HAVE_OPTIONS, 0);
1775102087Sluigi			if (flags & HAVE_OPTIONS)
1776102087Sluigi				printf(" proto");
177798943Sluigi			if (pe)
177898943Sluigi				printf(" %s", pe->p_name);
177998943Sluigi			else
178098943Sluigi				printf(" %u", cmd->arg1);
178198943Sluigi			}
178298943Sluigi			flags |= HAVE_PROTO;
178398943Sluigi			break;
1784106505Smaxim
178598943Sluigi		default: /*options ... */
1786146894Smlaier			if (!(cmd->len & (F_OR|F_NOT)))
1787146894Smlaier				if (((cmd->opcode == O_IP6) &&
1788146894Smlaier				    (flags & HAVE_PROTO6)) ||
1789146894Smlaier				    ((cmd->opcode == O_IP4) &&
1790146894Smlaier				    (flags & HAVE_PROTO4)))
1791146894Smlaier					break;
1792102087Sluigi			show_prerequisites(&flags, HAVE_IP | HAVE_OPTIONS, 0);
179398943Sluigi			if ((cmd->len & F_OR) && !or_block)
179498943Sluigi				printf(" {");
179598943Sluigi			if (cmd->len & F_NOT && cmd->opcode != O_IN)
179698943Sluigi				printf(" not");
179798943Sluigi			switch(cmd->opcode) {
1798169139Smaxim			case O_MACADDR2: {
1799169139Smaxim				ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
1800169139Smaxim
1801169139Smaxim				printf(" MAC");
1802169139Smaxim				print_mac(m->addr, m->mask);
1803169139Smaxim				print_mac(m->addr + 6, m->mask + 6);
1804169139Smaxim				}
1805169139Smaxim				break;
1806169139Smaxim
1807169139Smaxim			case O_MAC_TYPE:
1808169139Smaxim				print_newports((ipfw_insn_u16 *)cmd,
1809169139Smaxim						IPPROTO_ETHERTYPE, cmd->opcode);
1810169139Smaxim				break;
1811169139Smaxim
1812169139Smaxim
181398943Sluigi			case O_FRAG:
181498943Sluigi				printf(" frag");
181598943Sluigi				break;
181698943Sluigi
181798943Sluigi			case O_IN:
181898943Sluigi				printf(cmd->len & F_NOT ? " out" : " in");
181998943Sluigi				break;
182098943Sluigi
1821136073Sgreen			case O_DIVERTED:
1822136073Sgreen				switch (cmd->arg1) {
1823136073Sgreen				case 3:
1824136073Sgreen					printf(" diverted");
1825136073Sgreen					break;
1826136073Sgreen				case 1:
1827136073Sgreen					printf(" diverted-loopback");
1828136073Sgreen					break;
1829136073Sgreen				case 2:
1830136073Sgreen					printf(" diverted-output");
1831136073Sgreen					break;
1832136073Sgreen				default:
1833136073Sgreen					printf(" diverted-?<%u>", cmd->arg1);
1834136073Sgreen					break;
1835136073Sgreen				}
1836136073Sgreen				break;
1837136073Sgreen
183898943Sluigi			case O_LAYER2:
183998943Sluigi				printf(" layer2");
184098943Sluigi				break;
184198943Sluigi			case O_XMIT:
184298943Sluigi			case O_RECV:
1843140423Sglebius			case O_VIA:
1844140423Sglebius			    {
1845117469Sluigi				char const *s;
184698943Sluigi				ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd;
184798943Sluigi
184898943Sluigi				if (cmd->opcode == O_XMIT)
184998943Sluigi					s = "xmit";
185098943Sluigi				else if (cmd->opcode == O_RECV)
185198943Sluigi					s = "recv";
1852117469Sluigi				else /* if (cmd->opcode == O_VIA) */
185398943Sluigi					s = "via";
185498943Sluigi				if (cmdif->name[0] == '\0')
185599475Sluigi					printf(" %s %s", s,
185699475Sluigi					    inet_ntoa(cmdif->p.ip));
1857140423Sglebius				else
1858140423Sglebius					printf(" %s %s", s, cmdif->name);
1859140423Sglebius
186098943Sluigi				break;
1861140423Sglebius			    }
186298943Sluigi			case O_IPID:
1863116690Sluigi				if (F_LEN(cmd) == 1)
1864116690Sluigi				    printf(" ipid %u", cmd->arg1 );
1865116690Sluigi				else
1866116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1867116690Sluigi					O_IPID);
186898943Sluigi				break;
186998943Sluigi
187098943Sluigi			case O_IPTTL:
1871116690Sluigi				if (F_LEN(cmd) == 1)
1872116690Sluigi				    printf(" ipttl %u", cmd->arg1 );
1873116690Sluigi				else
1874116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1875116690Sluigi					O_IPTTL);
187698943Sluigi				break;
187798943Sluigi
187898943Sluigi			case O_IPVER:
187998943Sluigi				printf(" ipver %u", cmd->arg1 );
188098943Sluigi				break;
188198943Sluigi
188299475Sluigi			case O_IPPRECEDENCE:
188399475Sluigi				printf(" ipprecedence %u", (cmd->arg1) >> 5 );
188499475Sluigi				break;
188599475Sluigi
188698943Sluigi			case O_IPLEN:
1887116690Sluigi				if (F_LEN(cmd) == 1)
1888116690Sluigi				    printf(" iplen %u", cmd->arg1 );
1889116690Sluigi				else
1890116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1891116690Sluigi					O_IPLEN);
189298943Sluigi				break;
189398943Sluigi
1894101116Sluigi			case O_IPOPT:
189598943Sluigi				print_flags("ipoptions", cmd, f_ipopts);
189698943Sluigi				break;
189798943Sluigi
189899475Sluigi			case O_IPTOS:
189999475Sluigi				print_flags("iptos", cmd, f_iptos);
190099475Sluigi				break;
190199475Sluigi
190299475Sluigi			case O_ICMPTYPE:
190399475Sluigi				print_icmptypes((ipfw_insn_u32 *)cmd);
190499475Sluigi				break;
190599475Sluigi
190698943Sluigi			case O_ESTAB:
190798943Sluigi				printf(" established");
190898943Sluigi				break;
190998943Sluigi
1910136075Sgreen			case O_TCPDATALEN:
1911136075Sgreen				if (F_LEN(cmd) == 1)
1912136075Sgreen				    printf(" tcpdatalen %u", cmd->arg1 );
1913136075Sgreen				else
1914136075Sgreen				    print_newports((ipfw_insn_u16 *)cmd, 0,
1915136075Sgreen					O_TCPDATALEN);
1916136075Sgreen				break;
1917136075Sgreen
191898943Sluigi			case O_TCPFLAGS:
191998943Sluigi				print_flags("tcpflags", cmd, f_tcpflags);
192098943Sluigi				break;
192198943Sluigi
192298943Sluigi			case O_TCPOPTS:
192398943Sluigi				print_flags("tcpoptions", cmd, f_tcpopts);
192498943Sluigi				break;
192598943Sluigi
192698943Sluigi			case O_TCPWIN:
192798943Sluigi				printf(" tcpwin %d", ntohs(cmd->arg1));
192898943Sluigi				break;
192998943Sluigi
193098943Sluigi			case O_TCPACK:
193198943Sluigi				printf(" tcpack %d", ntohl(cmd32->d[0]));
193298943Sluigi				break;
193398943Sluigi
193498943Sluigi			case O_TCPSEQ:
193598943Sluigi				printf(" tcpseq %d", ntohl(cmd32->d[0]));
193698943Sluigi				break;
193798943Sluigi
193898943Sluigi			case O_UID:
193998943Sluigi			    {
194098943Sluigi				struct passwd *pwd = getpwuid(cmd32->d[0]);
194198943Sluigi
194298943Sluigi				if (pwd)
194398943Sluigi					printf(" uid %s", pwd->pw_name);
194498943Sluigi				else
194598943Sluigi					printf(" uid %u", cmd32->d[0]);
194698943Sluigi			    }
194798943Sluigi				break;
194898943Sluigi
194998943Sluigi			case O_GID:
195098943Sluigi			    {
195198943Sluigi				struct group *grp = getgrgid(cmd32->d[0]);
195298943Sluigi
195398943Sluigi				if (grp)
195498943Sluigi					printf(" gid %s", grp->gr_name);
195598943Sluigi				else
195698943Sluigi					printf(" gid %u", cmd32->d[0]);
195798943Sluigi			    }
195898943Sluigi				break;
195998943Sluigi
1960133600Scsjp			case O_JAIL:
1961133600Scsjp				printf(" jail %d", cmd32->d[0]);
1962133600Scsjp				break;
1963133600Scsjp
1964112250Scjc			case O_VERREVPATH:
1965112250Scjc				printf(" verrevpath");
1966112250Scjc				break;
1967116919Sluigi
1968128575Sandre			case O_VERSRCREACH:
1969128575Sandre				printf(" versrcreach");
1970128575Sandre				break;
1971128575Sandre
1972133387Sandre			case O_ANTISPOOF:
1973133387Sandre				printf(" antispoof");
1974133387Sandre				break;
1975133387Sandre
1976117241Sluigi			case O_IPSEC:
1977117241Sluigi				printf(" ipsec");
1978117241Sluigi				break;
1979117241Sluigi
1980117469Sluigi			case O_NOP:
1981117626Sluigi				comment = (char *)(cmd + 1);
1982117469Sluigi				break;
1983117469Sluigi
198498943Sluigi			case O_KEEP_STATE:
198598943Sluigi				printf(" keep-state");
198698943Sluigi				break;
198798943Sluigi
1988159636Soleg			case O_LIMIT: {
198998943Sluigi				struct _s_x *p = limit_masks;
199098943Sluigi				ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
1991117328Sluigi				uint8_t x = c->limit_mask;
1992117469Sluigi				char const *comma = " ";
199398943Sluigi
199498943Sluigi				printf(" limit");
1995117577Sluigi				for (; p->x != 0 ; p++)
199699909Sluigi					if ((x & p->x) == p->x) {
199798943Sluigi						x &= ~p->x;
199898943Sluigi						printf("%s%s", comma, p->s);
199998943Sluigi						comma = ",";
200098943Sluigi					}
2001159636Soleg				PRINT_UINT_ARG(" ", c->conn_limit);
200298943Sluigi				break;
2003159636Soleg			}
200498943Sluigi
2005146894Smlaier			case O_IP6:
2006152923Sume				printf(" ip6");
2007145246Sbrooks				break;
2008145246Sbrooks
2009146894Smlaier			case O_IP4:
2010152923Sume				printf(" ip4");
2011146894Smlaier				break;
2012146894Smlaier
2013145246Sbrooks			case O_ICMP6TYPE:
2014145246Sbrooks				print_icmp6types((ipfw_insn_u32 *)cmd);
2015145246Sbrooks				break;
2016145246Sbrooks
2017145246Sbrooks			case O_EXT_HDR:
2018145246Sbrooks				print_ext6hdr( (ipfw_insn *) cmd );
2019145246Sbrooks				break;
2020145246Sbrooks
2021158879Soleg			case O_TAGGED:
2022158879Soleg				if (F_LEN(cmd) == 1)
2023159636Soleg					PRINT_UINT_ARG(" tagged ", cmd->arg1);
2024158879Soleg				else
2025159636Soleg					print_newports((ipfw_insn_u16 *)cmd, 0,
2026159636Soleg					    O_TAGGED);
2027158879Soleg				break;
2028158879Soleg
202998943Sluigi			default:
203098943Sluigi				printf(" [opcode %d len %d]",
203198943Sluigi				    cmd->opcode, cmd->len);
203298943Sluigi			}
203398943Sluigi		}
203498943Sluigi		if (cmd->len & F_OR) {
203598943Sluigi			printf(" or");
203698943Sluigi			or_block = 1;
203798943Sluigi		} else if (or_block) {
203898943Sluigi			printf(" }");
203998943Sluigi			or_block = 0;
204098943Sluigi		}
204198943Sluigi	}
2042102087Sluigi	show_prerequisites(&flags, HAVE_IP, 0);
2043117626Sluigi	if (comment)
2044117626Sluigi		printf(" // %s", comment);
204598943Sluigi	printf("\n");
204698943Sluigi}
204798943Sluigi
204898943Sluigistatic void
2049112189Smaximshow_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth)
205098943Sluigi{
205198943Sluigi	struct protoent *pe;
205298943Sluigi	struct in_addr a;
2053115793Sticso	uint16_t rulenum;
2054159160Smlaier	char buf[INET6_ADDRSTRLEN];
205598943Sluigi
205698943Sluigi	if (!do_expired) {
205798943Sluigi		if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT))
205898943Sluigi			return;
205998943Sluigi	}
2060115793Sticso	bcopy(&d->rule, &rulenum, sizeof(rulenum));
2061117328Sluigi	printf("%05d", rulenum);
2062117469Sluigi	if (pcwidth>0 || bcwidth>0)
2063117328Sluigi	    printf(" %*llu %*llu (%ds)", pcwidth,
2064117328Sluigi		align_uint64(&d->pcnt), bcwidth,
2065117328Sluigi		align_uint64(&d->bcnt), d->expire);
206698943Sluigi	switch (d->dyn_type) {
206798943Sluigi	case O_LIMIT_PARENT:
206898943Sluigi		printf(" PARENT %d", d->count);
206998943Sluigi		break;
207098943Sluigi	case O_LIMIT:
207198943Sluigi		printf(" LIMIT");
207298943Sluigi		break;
207398943Sluigi	case O_KEEP_STATE: /* bidir, no mask */
2074106505Smaxim		printf(" STATE");
207598943Sluigi		break;
207698943Sluigi	}
207798943Sluigi
207898943Sluigi	if ((pe = getprotobynumber(d->id.proto)) != NULL)
207998943Sluigi		printf(" %s", pe->p_name);
208098943Sluigi	else
208198943Sluigi		printf(" proto %u", d->id.proto);
208298943Sluigi
2083159160Smlaier	if (d->id.addr_type == 4) {
2084159160Smlaier		a.s_addr = htonl(d->id.src_ip);
2085159160Smlaier		printf(" %s %d", inet_ntoa(a), d->id.src_port);
208698943Sluigi
2087159160Smlaier		a.s_addr = htonl(d->id.dst_ip);
2088159160Smlaier		printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port);
2089159160Smlaier	} else if (d->id.addr_type == 6) {
2090159160Smlaier		printf(" %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf,
2091159160Smlaier		    sizeof(buf)), d->id.src_port);
2092159160Smlaier		printf(" <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf,
2093159160Smlaier		    sizeof(buf)), d->id.dst_port);
2094159160Smlaier	} else
2095159160Smlaier		printf(" UNKNOWN <-> UNKNOWN\n");
2096159160Smlaier
209798943Sluigi	printf("\n");
209898943Sluigi}
209998943Sluigi
2100117469Sluigistatic int
210198943Sluigisort_q(const void *pa, const void *pb)
210298943Sluigi{
210398943Sluigi	int rev = (do_sort < 0);
210498943Sluigi	int field = rev ? -do_sort : do_sort;
210598943Sluigi	long long res = 0;
210698943Sluigi	const struct dn_flow_queue *a = pa;
210798943Sluigi	const struct dn_flow_queue *b = pb;
210898943Sluigi
210998943Sluigi	switch (field) {
211098943Sluigi	case 1: /* pkts */
211198943Sluigi		res = a->len - b->len;
211298943Sluigi		break;
211398943Sluigi	case 2: /* bytes */
211498943Sluigi		res = a->len_bytes - b->len_bytes;
211598943Sluigi		break;
211698943Sluigi
211798943Sluigi	case 3: /* tot pkts */
211898943Sluigi		res = a->tot_pkts - b->tot_pkts;
211998943Sluigi		break;
212098943Sluigi
212198943Sluigi	case 4: /* tot bytes */
212298943Sluigi		res = a->tot_bytes - b->tot_bytes;
212398943Sluigi		break;
212498943Sluigi	}
212598943Sluigi	if (res < 0)
212698943Sluigi		res = -1;
212798943Sluigi	if (res > 0)
212898943Sluigi		res = 1;
212998943Sluigi	return (int)(rev ? res : -res);
213098943Sluigi}
213198943Sluigi
213298943Sluigistatic void
213398943Sluigilist_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
213498943Sluigi{
213598943Sluigi	int l;
2136145246Sbrooks	int index_printed, indexes = 0;
2137145246Sbrooks	char buff[255];
2138145246Sbrooks	struct protoent *pe;
213998943Sluigi
214098943Sluigi	if (fs->rq_elements == 0)
214198943Sluigi		return;
214298943Sluigi
214398943Sluigi	if (do_sort != 0)
214498943Sluigi		heapsort(q, fs->rq_elements, sizeof *q, sort_q);
2145145246Sbrooks
2146145246Sbrooks	/* Print IPv4 flows */
2147145246Sbrooks	index_printed = 0;
214898943Sluigi	for (l = 0; l < fs->rq_elements; l++) {
214998943Sluigi		struct in_addr ina;
215098943Sluigi
2151145246Sbrooks		/* XXX: Should check for IPv4 flows */
2152145246Sbrooks		if (IS_IP6_FLOW_ID(&(q[l].id)))
2153145246Sbrooks			continue;
2154145246Sbrooks
2155145246Sbrooks		if (!index_printed) {
2156145246Sbrooks			index_printed = 1;
2157145246Sbrooks			if (indexes > 0)	/* currently a no-op */
2158145246Sbrooks				printf("\n");
2159145246Sbrooks			indexes++;
2160145246Sbrooks			printf("    "
2161145246Sbrooks			    "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
2162145246Sbrooks			    fs->flow_mask.proto,
2163145246Sbrooks			    fs->flow_mask.src_ip, fs->flow_mask.src_port,
2164145246Sbrooks			    fs->flow_mask.dst_ip, fs->flow_mask.dst_port);
2165145246Sbrooks
2166145246Sbrooks			printf("BKT Prot ___Source IP/port____ "
2167145246Sbrooks			    "____Dest. IP/port____ "
2168145246Sbrooks			    "Tot_pkt/bytes Pkt/Byte Drp\n");
2169145246Sbrooks		}
2170145246Sbrooks
217198943Sluigi		printf("%3d ", q[l].hash_slot);
217298943Sluigi		pe = getprotobynumber(q[l].id.proto);
217398943Sluigi		if (pe)
217498943Sluigi			printf("%-4s ", pe->p_name);
217598943Sluigi		else
217698943Sluigi			printf("%4u ", q[l].id.proto);
2177145246Sbrooks		ina.s_addr = htonl(q[l].id.src_ip);
217898943Sluigi		printf("%15s/%-5d ",
217998943Sluigi		    inet_ntoa(ina), q[l].id.src_port);
218098943Sluigi		ina.s_addr = htonl(q[l].id.dst_ip);
218198943Sluigi		printf("%15s/%-5d ",
218298943Sluigi		    inet_ntoa(ina), q[l].id.dst_port);
218398943Sluigi		printf("%4qu %8qu %2u %4u %3u\n",
218498943Sluigi		    q[l].tot_pkts, q[l].tot_bytes,
218598943Sluigi		    q[l].len, q[l].len_bytes, q[l].drops);
218698943Sluigi		if (verbose)
218798943Sluigi			printf("   S %20qd  F %20qd\n",
218898943Sluigi			    q[l].S, q[l].F);
218998943Sluigi	}
2190145246Sbrooks
2191145246Sbrooks	/* Print IPv6 flows */
2192145246Sbrooks	index_printed = 0;
2193145246Sbrooks	for (l = 0; l < fs->rq_elements; l++) {
2194145246Sbrooks		if (!IS_IP6_FLOW_ID(&(q[l].id)))
2195145246Sbrooks			continue;
2196145246Sbrooks
2197145246Sbrooks		if (!index_printed) {
2198145246Sbrooks			index_printed = 1;
2199145246Sbrooks			if (indexes > 0)
2200145246Sbrooks				printf("\n");
2201145246Sbrooks			indexes++;
2202145246Sbrooks			printf("\n        mask: proto: 0x%02x, flow_id: 0x%08x,  ",
2203145246Sbrooks			    fs->flow_mask.proto, fs->flow_mask.flow_id6);
2204145246Sbrooks			inet_ntop(AF_INET6, &(fs->flow_mask.src_ip6),
2205145246Sbrooks			    buff, sizeof(buff));
2206145246Sbrooks			printf("%s/0x%04x -> ", buff, fs->flow_mask.src_port);
2207145246Sbrooks			inet_ntop( AF_INET6, &(fs->flow_mask.dst_ip6),
2208145246Sbrooks			    buff, sizeof(buff) );
2209145246Sbrooks			printf("%s/0x%04x\n", buff, fs->flow_mask.dst_port);
2210145246Sbrooks
2211145246Sbrooks			printf("BKT ___Prot___ _flow-id_ "
2212145246Sbrooks			    "______________Source IPv6/port_______________ "
2213145246Sbrooks			    "_______________Dest. IPv6/port_______________ "
2214145246Sbrooks			    "Tot_pkt/bytes Pkt/Byte Drp\n");
2215145246Sbrooks		}
2216145246Sbrooks		printf("%3d ", q[l].hash_slot);
2217145246Sbrooks		pe = getprotobynumber(q[l].id.proto);
2218145246Sbrooks		if (pe != NULL)
2219145246Sbrooks			printf("%9s ", pe->p_name);
2220145246Sbrooks		else
2221145246Sbrooks			printf("%9u ", q[l].id.proto);
2222145246Sbrooks		printf("%7d  %39s/%-5d ", q[l].id.flow_id6,
2223145246Sbrooks		    inet_ntop(AF_INET6, &(q[l].id.src_ip6), buff, sizeof(buff)),
2224145246Sbrooks		    q[l].id.src_port);
2225145246Sbrooks		printf(" %39s/%-5d ",
2226145246Sbrooks		    inet_ntop(AF_INET6, &(q[l].id.dst_ip6), buff, sizeof(buff)),
2227145246Sbrooks		    q[l].id.dst_port);
2228145246Sbrooks		printf(" %4qu %8qu %2u %4u %3u\n",
2229145246Sbrooks		    q[l].tot_pkts, q[l].tot_bytes,
2230145246Sbrooks		    q[l].len, q[l].len_bytes, q[l].drops);
2231145246Sbrooks		if (verbose)
2232145246Sbrooks			printf("   S %20qd  F %20qd\n", q[l].S, q[l].F);
2233145246Sbrooks	}
223498943Sluigi}
223598943Sluigi
223698943Sluigistatic void
223798943Sluigiprint_flowset_parms(struct dn_flow_set *fs, char *prefix)
223898943Sluigi{
223998943Sluigi	int l;
224098943Sluigi	char qs[30];
224198943Sluigi	char plr[30];
224298943Sluigi	char red[90];	/* Display RED parameters */
224398943Sluigi
224498943Sluigi	l = fs->qsize;
224598943Sluigi	if (fs->flags_fs & DN_QSIZE_IS_BYTES) {
224698943Sluigi		if (l >= 8192)
224798943Sluigi			sprintf(qs, "%d KB", l / 1024);
224898943Sluigi		else
224998943Sluigi			sprintf(qs, "%d B", l);
225098943Sluigi	} else
225198943Sluigi		sprintf(qs, "%3d sl.", l);
225298943Sluigi	if (fs->plr)
225398943Sluigi		sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
225498943Sluigi	else
225598943Sluigi		plr[0] = '\0';
225698943Sluigi	if (fs->flags_fs & DN_IS_RED)	/* RED parameters */
225798943Sluigi		sprintf(red,
225898943Sluigi		    "\n\t  %cRED w_q %f min_th %d max_th %d max_p %f",
225998943Sluigi		    (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ',
226098943Sluigi		    1.0 * fs->w_q / (double)(1 << SCALE_RED),
226198943Sluigi		    SCALE_VAL(fs->min_th),
226298943Sluigi		    SCALE_VAL(fs->max_th),
226398943Sluigi		    1.0 * fs->max_p / (double)(1 << SCALE_RED));
226498943Sluigi	else
226598943Sluigi		sprintf(red, "droptail");
226698943Sluigi
226798943Sluigi	printf("%s %s%s %d queues (%d buckets) %s\n",
226898943Sluigi	    prefix, qs, plr, fs->rq_elements, fs->rq_size, red);
226998943Sluigi}
227098943Sluigi
227198943Sluigistatic void
2272117469Sluigilist_pipes(void *data, uint nbytes, int ac, char *av[])
227398943Sluigi{
2274117469Sluigi	int rulenum;
227598943Sluigi	void *next = data;
227698943Sluigi	struct dn_pipe *p = (struct dn_pipe *) data;
227798943Sluigi	struct dn_flow_set *fs;
227898943Sluigi	struct dn_flow_queue *q;
227998943Sluigi	int l;
228098943Sluigi
228198943Sluigi	if (ac > 0)
228298943Sluigi		rulenum = strtoul(*av++, NULL, 10);
228398943Sluigi	else
228498943Sluigi		rulenum = 0;
228598943Sluigi	for (; nbytes >= sizeof *p; p = (struct dn_pipe *)next) {
228698943Sluigi		double b = p->bandwidth;
228798943Sluigi		char buf[30];
228898943Sluigi		char prefix[80];
228998943Sluigi
2290161001Sstefanf		if (SLIST_NEXT(p, next) != (struct dn_pipe *)DN_IS_PIPE)
229198943Sluigi			break;	/* done with pipes, now queues */
229298943Sluigi
229398943Sluigi		/*
229498943Sluigi		 * compute length, as pipe have variable size
229598943Sluigi		 */
229698943Sluigi		l = sizeof(*p) + p->fs.rq_elements * sizeof(*q);
2297117469Sluigi		next = (char *)p + l;
229898943Sluigi		nbytes -= l;
229998943Sluigi
2300134225Spjd		if ((rulenum != 0 && rulenum != p->pipe_nr) || do_pipe == 2)
230198943Sluigi			continue;
230298943Sluigi
230398943Sluigi		/*
230498943Sluigi		 * Print rate (or clocking interface)
230598943Sluigi		 */
230698943Sluigi		if (p->if_name[0] != '\0')
230798943Sluigi			sprintf(buf, "%s", p->if_name);
230898943Sluigi		else if (b == 0)
230998943Sluigi			sprintf(buf, "unlimited");
231098943Sluigi		else if (b >= 1000000)
231198943Sluigi			sprintf(buf, "%7.3f Mbit/s", b/1000000);
231298943Sluigi		else if (b >= 1000)
231398943Sluigi			sprintf(buf, "%7.3f Kbit/s", b/1000);
231498943Sluigi		else
231598943Sluigi			sprintf(buf, "%7.3f bit/s ", b);
231698943Sluigi
231798943Sluigi		sprintf(prefix, "%05d: %s %4d ms ",
231898943Sluigi		    p->pipe_nr, buf, p->delay);
231998943Sluigi		print_flowset_parms(&(p->fs), prefix);
232098943Sluigi		if (verbose)
232198943Sluigi			printf("   V %20qd\n", p->V >> MY_M);
2322106505Smaxim
232398943Sluigi		q = (struct dn_flow_queue *)(p+1);
232498943Sluigi		list_queues(&(p->fs), q);
232598943Sluigi	}
232698943Sluigi	for (fs = next; nbytes >= sizeof *fs; fs = next) {
232798943Sluigi		char prefix[80];
232898943Sluigi
2329161001Sstefanf		if (SLIST_NEXT(fs, next) != (struct dn_flow_set *)DN_IS_QUEUE)
233098943Sluigi			break;
233198943Sluigi		l = sizeof(*fs) + fs->rq_elements * sizeof(*q);
2332117469Sluigi		next = (char *)fs + l;
233398943Sluigi		nbytes -= l;
2334134225Spjd
2335134225Spjd		if (rulenum != 0 && ((rulenum != fs->fs_nr && do_pipe == 2) ||
2336134225Spjd		    (rulenum != fs->parent_nr && do_pipe == 1))) {
2337134225Spjd			continue;
2338134225Spjd		}
2339134225Spjd
234098943Sluigi		q = (struct dn_flow_queue *)(fs+1);
234198943Sluigi		sprintf(prefix, "q%05d: weight %d pipe %d ",
234298943Sluigi		    fs->fs_nr, fs->weight, fs->parent_nr);
234398943Sluigi		print_flowset_parms(fs, prefix);
234498943Sluigi		list_queues(fs, q);
234598943Sluigi	}
234698943Sluigi}
234798943Sluigi
2348101978Sluigi/*
2349101978Sluigi * This one handles all set-related commands
2350101978Sluigi * 	ipfw set { show | enable | disable }
2351101978Sluigi * 	ipfw set swap X Y
2352101978Sluigi * 	ipfw set move X to Y
2353101978Sluigi * 	ipfw set move rule X to Y
2354101978Sluigi */
235598943Sluigistatic void
2356101978Sluigisets_handler(int ac, char *av[])
2357101978Sluigi{
2358117328Sluigi	uint32_t set_disable, masks[2];
2359101978Sluigi	int i, nbytes;
2360117328Sluigi	uint16_t rulenum;
2361117328Sluigi	uint8_t cmd, new_set;
2362101978Sluigi
2363101978Sluigi	ac--;
2364101978Sluigi	av++;
2365101978Sluigi
2366101978Sluigi	if (!ac)
2367101978Sluigi		errx(EX_USAGE, "set needs command");
2368140271Sbrooks	if (_substrcmp(*av, "show") == 0) {
2369101978Sluigi		void *data;
2370117469Sluigi		char const *msg;
2371101978Sluigi
2372101978Sluigi		nbytes = sizeof(struct ip_fw);
2373117328Sluigi		if ((data = calloc(1, nbytes)) == NULL)
2374117328Sluigi			err(EX_OSERR, "calloc");
2375119740Stmm		if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0)
2376101978Sluigi			err(EX_OSERR, "getsockopt(IP_FW_GET)");
2377115793Sticso		bcopy(&((struct ip_fw *)data)->next_rule,
2378115793Sticso			&set_disable, sizeof(set_disable));
2379101978Sluigi
2380117655Sluigi		for (i = 0, msg = "disable" ; i < RESVD_SET; i++)
2381117577Sluigi			if ((set_disable & (1<<i))) {
2382101978Sluigi				printf("%s %d", msg, i);
2383101978Sluigi				msg = "";
2384101978Sluigi			}
2385101978Sluigi		msg = (set_disable) ? " enable" : "enable";
2386117655Sluigi		for (i = 0; i < RESVD_SET; i++)
2387117577Sluigi			if (!(set_disable & (1<<i))) {
2388101978Sluigi				printf("%s %d", msg, i);
2389101978Sluigi				msg = "";
2390101978Sluigi			}
2391101978Sluigi		printf("\n");
2392140271Sbrooks	} else if (_substrcmp(*av, "swap") == 0) {
2393101978Sluigi		ac--; av++;
2394101978Sluigi		if (ac != 2)
2395101978Sluigi			errx(EX_USAGE, "set swap needs 2 set numbers\n");
2396101978Sluigi		rulenum = atoi(av[0]);
2397101978Sluigi		new_set = atoi(av[1]);
2398117655Sluigi		if (!isdigit(*(av[0])) || rulenum > RESVD_SET)
2399101978Sluigi			errx(EX_DATAERR, "invalid set number %s\n", av[0]);
2400117655Sluigi		if (!isdigit(*(av[1])) || new_set > RESVD_SET)
2401101978Sluigi			errx(EX_DATAERR, "invalid set number %s\n", av[1]);
2402101978Sluigi		masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
2403117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
2404140271Sbrooks	} else if (_substrcmp(*av, "move") == 0) {
2405101978Sluigi		ac--; av++;
2406140271Sbrooks		if (ac && _substrcmp(*av, "rule") == 0) {
2407101978Sluigi			cmd = 2;
2408101978Sluigi			ac--; av++;
2409101978Sluigi		} else
2410101978Sluigi			cmd = 3;
2411140271Sbrooks		if (ac != 3 || _substrcmp(av[1], "to") != 0)
2412101978Sluigi			errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
2413101978Sluigi		rulenum = atoi(av[0]);
2414101978Sluigi		new_set = atoi(av[2]);
2415117655Sluigi		if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) ||
2416101978Sluigi			(cmd == 2 && rulenum == 65535) )
2417101978Sluigi			errx(EX_DATAERR, "invalid source number %s\n", av[0]);
2418117655Sluigi		if (!isdigit(*(av[2])) || new_set > RESVD_SET)
2419101978Sluigi			errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
2420101978Sluigi		masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
2421117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
2422140271Sbrooks	} else if (_substrcmp(*av, "disable") == 0 ||
2423140271Sbrooks		   _substrcmp(*av, "enable") == 0 ) {
2424140271Sbrooks		int which = _substrcmp(*av, "enable") == 0 ? 1 : 0;
2425101978Sluigi
2426101978Sluigi		ac--; av++;
2427101978Sluigi		masks[0] = masks[1] = 0;
2428101978Sluigi
2429101978Sluigi		while (ac) {
2430101978Sluigi			if (isdigit(**av)) {
2431101978Sluigi				i = atoi(*av);
2432117655Sluigi				if (i < 0 || i > RESVD_SET)
2433101978Sluigi					errx(EX_DATAERR,
2434101978Sluigi					    "invalid set number %d\n", i);
2435101978Sluigi				masks[which] |= (1<<i);
2436140271Sbrooks			} else if (_substrcmp(*av, "disable") == 0)
2437101978Sluigi				which = 0;
2438140271Sbrooks			else if (_substrcmp(*av, "enable") == 0)
2439101978Sluigi				which = 1;
2440101978Sluigi			else
2441101978Sluigi				errx(EX_DATAERR,
2442101978Sluigi					"invalid set command %s\n", *av);
2443101978Sluigi			av++; ac--;
2444101978Sluigi		}
2445101978Sluigi		if ( (masks[0] & masks[1]) != 0 )
2446101978Sluigi			errx(EX_DATAERR,
2447101978Sluigi			    "cannot enable and disable the same set\n");
2448101978Sluigi
2449117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(masks));
2450101978Sluigi		if (i)
2451101978Sluigi			warn("set enable/disable: setsockopt(IP_FW_DEL)");
2452101978Sluigi	} else
2453101978Sluigi		errx(EX_USAGE, "invalid set command %s\n", *av);
2454101978Sluigi}
2455101978Sluigi
2456101978Sluigistatic void
2457109126Sdillonsysctl_handler(int ac, char *av[], int which)
2458109126Sdillon{
2459109126Sdillon	ac--;
2460109126Sdillon	av++;
2461109126Sdillon
2462119668Smaxim	if (ac == 0) {
2463109126Sdillon		warnx("missing keyword to enable/disable\n");
2464140271Sbrooks	} else if (_substrcmp(*av, "firewall") == 0) {
2465116770Sluigi		sysctlbyname("net.inet.ip.fw.enable", NULL, 0,
2466116770Sluigi		    &which, sizeof(which));
2467140271Sbrooks	} else if (_substrcmp(*av, "one_pass") == 0) {
2468116770Sluigi		sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0,
2469116770Sluigi		    &which, sizeof(which));
2470140271Sbrooks	} else if (_substrcmp(*av, "debug") == 0) {
2471116770Sluigi		sysctlbyname("net.inet.ip.fw.debug", NULL, 0,
2472116770Sluigi		    &which, sizeof(which));
2473140271Sbrooks	} else if (_substrcmp(*av, "verbose") == 0) {
2474116770Sluigi		sysctlbyname("net.inet.ip.fw.verbose", NULL, 0,
2475116770Sluigi		    &which, sizeof(which));
2476140271Sbrooks	} else if (_substrcmp(*av, "dyn_keepalive") == 0) {
2477116770Sluigi		sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0,
2478116770Sluigi		    &which, sizeof(which));
2479140271Sbrooks	} else if (_substrcmp(*av, "altq") == 0) {
2480136071Sgreen		altq_set_enabled(which);
2481109126Sdillon	} else {
2482109126Sdillon		warnx("unrecognize enable/disable keyword: %s\n", *av);
2483109126Sdillon	}
2484109126Sdillon}
2485109126Sdillon
2486109126Sdillonstatic void
2487117469Sluigilist(int ac, char *av[], int show_counters)
248898943Sluigi{
248998943Sluigi	struct ip_fw *r;
249098943Sluigi	ipfw_dyn_rule *dynrules, *d;
249198943Sluigi
2492117469Sluigi#define NEXT(r)	((struct ip_fw *)((char *)r + RULESIZE(r)))
2493117469Sluigi	char *lim;
2494117469Sluigi	void *data = NULL;
2495112189Smaxim	int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width;
249698943Sluigi	int exitval = EX_OK;
249798943Sluigi	int lac;
249898943Sluigi	char **lav;
2499117469Sluigi	u_long rnum, last;
250098943Sluigi	char *endptr;
250198943Sluigi	int seen = 0;
2502170923Smaxim	uint8_t set;
250398943Sluigi
250498943Sluigi	const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
250598943Sluigi	int nalloc = 1024;	/* start somewhere... */
250698943Sluigi
2507135036Smaxim	last = 0;
2508135036Smaxim
2509117328Sluigi	if (test_only) {
2510117328Sluigi		fprintf(stderr, "Testing only, list disabled\n");
2511117328Sluigi		return;
2512117328Sluigi	}
2513117328Sluigi
251498943Sluigi	ac--;
251598943Sluigi	av++;
251698943Sluigi
251798943Sluigi	/* get rules or pipes from kernel, resizing array as necessary */
251898943Sluigi	nbytes = nalloc;
251998943Sluigi
252098943Sluigi	while (nbytes >= nalloc) {
252198943Sluigi		nalloc = nalloc * 2 + 200;
252298943Sluigi		nbytes = nalloc;
252398943Sluigi		if ((data = realloc(data, nbytes)) == NULL)
252498943Sluigi			err(EX_OSERR, "realloc");
2525119740Stmm		if (do_cmd(ocmd, data, (uintptr_t)&nbytes) < 0)
252698943Sluigi			err(EX_OSERR, "getsockopt(IP_%s_GET)",
252798943Sluigi				do_pipe ? "DUMMYNET" : "FW");
252898943Sluigi	}
252998943Sluigi
253098943Sluigi	if (do_pipe) {
253198943Sluigi		list_pipes(data, nbytes, ac, av);
253298943Sluigi		goto done;
253398943Sluigi	}
253498943Sluigi
253598943Sluigi	/*
253698943Sluigi	 * Count static rules. They have variable size so we
253798943Sluigi	 * need to scan the list to count them.
253898943Sluigi	 */
2539117469Sluigi	for (nstat = 1, r = data, lim = (char *)data + nbytes;
2540117469Sluigi		    r->rulenum < 65535 && (char *)r < lim;
2541117469Sluigi		    ++nstat, r = NEXT(r) )
254298943Sluigi		; /* nothing */
254398943Sluigi
254498943Sluigi	/*
254598943Sluigi	 * Count dynamic rules. This is easier as they have
254698943Sluigi	 * fixed size.
254798943Sluigi	 */
2548117469Sluigi	r = NEXT(r);
254998943Sluigi	dynrules = (ipfw_dyn_rule *)r ;
2550117469Sluigi	n = (char *)r - (char *)data;
255198943Sluigi	ndyn = (nbytes - n) / sizeof *dynrules;
255298943Sluigi
2553112189Smaxim	/* if showing stats, figure out column widths ahead of time */
2554112189Smaxim	bcwidth = pcwidth = 0;
2555117469Sluigi	if (show_counters) {
2556117469Sluigi		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
2557170923Smaxim			/* skip rules from another set */
2558170923Smaxim			if (use_set && r->set != use_set - 1)
2559170923Smaxim				continue;
2560170923Smaxim
2561112189Smaxim			/* packet counter */
2562115793Sticso			width = snprintf(NULL, 0, "%llu",
2563115793Sticso			    align_uint64(&r->pcnt));
2564112189Smaxim			if (width > pcwidth)
2565112189Smaxim				pcwidth = width;
2566112189Smaxim
2567112189Smaxim			/* byte counter */
2568115793Sticso			width = snprintf(NULL, 0, "%llu",
2569115793Sticso			    align_uint64(&r->bcnt));
2570112189Smaxim			if (width > bcwidth)
2571112189Smaxim				bcwidth = width;
2572112189Smaxim		}
2573112189Smaxim	}
2574112189Smaxim	if (do_dynamic && ndyn) {
2575112189Smaxim		for (n = 0, d = dynrules; n < ndyn; n++, d++) {
2576170923Smaxim			if (use_set) {
2577170923Smaxim				/* skip rules from another set */
2578171989Smaxim				bcopy((char *)&d->rule + sizeof(uint16_t),
2579170923Smaxim				      &set, sizeof(uint8_t));
2580170923Smaxim				if (set != use_set - 1)
2581170923Smaxim					continue;
2582170923Smaxim			}
2583115793Sticso			width = snprintf(NULL, 0, "%llu",
2584115793Sticso			    align_uint64(&d->pcnt));
2585112189Smaxim			if (width > pcwidth)
2586112189Smaxim				pcwidth = width;
2587112189Smaxim
2588115793Sticso			width = snprintf(NULL, 0, "%llu",
2589115793Sticso			    align_uint64(&d->bcnt));
2590112189Smaxim			if (width > bcwidth)
2591112189Smaxim				bcwidth = width;
2592112189Smaxim		}
2593112189Smaxim	}
259498943Sluigi	/* if no rule numbers were specified, list all rules */
259598943Sluigi	if (ac == 0) {
2596170923Smaxim		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
2597170923Smaxim			if (use_set && r->set != use_set - 1)
2598170923Smaxim				continue;
2599112189Smaxim			show_ipfw(r, pcwidth, bcwidth);
2600170923Smaxim		}
260198943Sluigi
260298943Sluigi		if (do_dynamic && ndyn) {
260398943Sluigi			printf("## Dynamic rules (%d):\n", ndyn);
2604170923Smaxim			for (n = 0, d = dynrules; n < ndyn; n++, d++) {
2605170923Smaxim				if (use_set) {
2606171989Smaxim					bcopy((char *)&d->rule + sizeof(uint16_t),
2607170923Smaxim					      &set, sizeof(uint8_t));
2608170923Smaxim					if (set != use_set - 1)
2609170923Smaxim						continue;
2610170923Smaxim				}
2611112189Smaxim				show_dyn_ipfw(d, pcwidth, bcwidth);
261298943Sluigi		}
2613170923Smaxim		}
261498943Sluigi		goto done;
261598943Sluigi	}
261698943Sluigi
261798943Sluigi	/* display specific rules requested on command line */
261898943Sluigi
261998943Sluigi	for (lac = ac, lav = av; lac != 0; lac--) {
262098943Sluigi		/* convert command line rule # */
2621117469Sluigi		last = rnum = strtoul(*lav++, &endptr, 10);
2622117469Sluigi		if (*endptr == '-')
2623117469Sluigi			last = strtoul(endptr+1, &endptr, 10);
262498943Sluigi		if (*endptr) {
262598943Sluigi			exitval = EX_USAGE;
262698943Sluigi			warnx("invalid rule number: %s", *(lav - 1));
262798943Sluigi			continue;
262898943Sluigi		}
2629117469Sluigi		for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) {
2630117469Sluigi			if (r->rulenum > last)
263198943Sluigi				break;
2632170923Smaxim			if (use_set && r->set != use_set - 1)
2633170923Smaxim				continue;
2634117469Sluigi			if (r->rulenum >= rnum && r->rulenum <= last) {
2635112189Smaxim				show_ipfw(r, pcwidth, bcwidth);
263698943Sluigi				seen = 1;
263798943Sluigi			}
263898943Sluigi		}
263998943Sluigi		if (!seen) {
264098943Sluigi			/* give precedence to other error(s) */
264198943Sluigi			if (exitval == EX_OK)
264298943Sluigi				exitval = EX_UNAVAILABLE;
264398943Sluigi			warnx("rule %lu does not exist", rnum);
264498943Sluigi		}
264598943Sluigi	}
264698943Sluigi
264798943Sluigi	if (do_dynamic && ndyn) {
264898943Sluigi		printf("## Dynamic rules:\n");
264998943Sluigi		for (lac = ac, lav = av; lac != 0; lac--) {
2650145246Sbrooks			last = rnum = strtoul(*lav++, &endptr, 10);
2651117469Sluigi			if (*endptr == '-')
2652117469Sluigi				last = strtoul(endptr+1, &endptr, 10);
265398943Sluigi			if (*endptr)
265498943Sluigi				/* already warned */
265598943Sluigi				continue;
265698943Sluigi			for (n = 0, d = dynrules; n < ndyn; n++, d++) {
2657115793Sticso				uint16_t rulenum;
2658115793Sticso
2659115793Sticso				bcopy(&d->rule, &rulenum, sizeof(rulenum));
2660115793Sticso				if (rulenum > rnum)
266198943Sluigi					break;
2662170923Smaxim				if (use_set) {
2663171989Smaxim					bcopy((char *)&d->rule + sizeof(uint16_t),
2664170923Smaxim					      &set, sizeof(uint8_t));
2665170923Smaxim					if (set != use_set - 1)
2666170923Smaxim						continue;
2667170923Smaxim				}
2668117469Sluigi				if (r->rulenum >= rnum && r->rulenum <= last)
2669112189Smaxim					show_dyn_ipfw(d, pcwidth, bcwidth);
267098943Sluigi			}
267198943Sluigi		}
267298943Sluigi	}
267398943Sluigi
267498943Sluigi	ac = 0;
267598943Sluigi
267698943Sluigidone:
267798943Sluigi	free(data);
267898943Sluigi
267998943Sluigi	if (exitval != EX_OK)
268098943Sluigi		exit(exitval);
2681117469Sluigi#undef NEXT
268298943Sluigi}
268398943Sluigi
268498943Sluigistatic void
268598943Sluigishow_usage(void)
268698943Sluigi{
268798943Sluigi	fprintf(stderr, "usage: ipfw [options]\n"
268898943Sluigi"do \"ipfw -h\" or see ipfw manpage for details\n"
268998943Sluigi);
269098943Sluigi	exit(EX_USAGE);
269198943Sluigi}
269298943Sluigi
269398943Sluigistatic void
269498943Sluigihelp(void)
269598943Sluigi{
2696117328Sluigi	fprintf(stderr,
2697117328Sluigi"ipfw syntax summary (but please do read the ipfw(8) manpage):\n"
2698123495Sluigi"ipfw [-abcdefhnNqStTv] <command> where <command> is one of:\n"
2699117328Sluigi"add [num] [set N] [prob x] RULE-BODY\n"
2700117328Sluigi"{pipe|queue} N config PIPE-BODY\n"
2701117328Sluigi"[pipe|queue] {zero|delete|show} [N{,N}]\n"
2702165648Spiso"nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|reset|\n"
2703165648Spiso"		reverse|proxy_only|redirect_addr linkspec|\n"
2704165648Spiso"		redirect_port linkspec|redirect_proto linkspec}\n"
2705117328Sluigi"set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n"
2706170923Smaxim"set N {show|list|zero|resetlog|delete} [N{,N}] | flush\n"
2707130281Sru"table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n"
270898943Sluigi"\n"
2709136071Sgreen"RULE-BODY:	check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n"
2710149020Sbz"ACTION:	check-state | allow | count | deny | unreach{,6} CODE |\n"
2711149020Sbz"               skipto N | {divert|tee} PORT | forward ADDR |\n"
2712165648Spiso"               pipe N | queue N | nat N\n"
2713136071Sgreen"PARAMS: 	[log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n"
271498943Sluigi"ADDR:		[ MAC dst src ether_type ] \n"
2715145246Sbrooks"		[ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
2716145246Sbrooks"		[ ipv6|ip6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n"
2717130281Sru"IPADDR:	[not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n"
2718145246Sbrooks"IP6ADDR:	[not] { any | me | me6 | ip6/bits | IP6LIST }\n"
2719145246Sbrooks"IP6LIST:	{ ip6 | ip6/bits }[,IP6LIST]\n"
2720117544Sluigi"IPLIST:	{ ip | ip/bits | ip:mask }[,IPLIST]\n"
2721117544Sluigi"OPTION_LIST:	OPTION [OPTION_LIST]\n"
2722136247Sgreen"OPTION:	bridged | diverted | diverted-loopback | diverted-output |\n"
2723145246Sbrooks"	{dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n"
2724145246Sbrooks"	{dst-port|src-port} LIST |\n"
2725117328Sluigi"	estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n"
2726117328Sluigi"	iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n"
2727117328Sluigi"	ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n"
2728145246Sbrooks"	icmp6types LIST | ext6hdr LIST | flow-id N[,N] |\n"
2729117328Sluigi"	mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n"
2730117328Sluigi"	setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n"
2731136075Sgreen"	tcpdatalen LIST | verrevpath | versrcreach | antispoof\n"
273298943Sluigi);
273398943Sluigiexit(0);
273498943Sluigi}
273598943Sluigi
273698943Sluigi
273798943Sluigistatic int
273898943Sluigilookup_host (char *host, struct in_addr *ipaddr)
273998943Sluigi{
274098943Sluigi	struct hostent *he;
274198943Sluigi
274298943Sluigi	if (!inet_aton(host, ipaddr)) {
274398943Sluigi		if ((he = gethostbyname(host)) == NULL)
274498943Sluigi			return(-1);
274598943Sluigi		*ipaddr = *(struct in_addr *)he->h_addr_list[0];
274698943Sluigi	}
274798943Sluigi	return(0);
274898943Sluigi}
274998943Sluigi
275098943Sluigi/*
275198943Sluigi * fills the addr and mask fields in the instruction as appropriate from av.
275298943Sluigi * Update length as appropriate.
275398943Sluigi * The following formats are allowed:
275498943Sluigi *	me	returns O_IP_*_ME
275598943Sluigi *	1.2.3.4		single IP address
275698943Sluigi *	1.2.3.4:5.6.7.8	address:mask
275798943Sluigi *	1.2.3.4/24	address/mask
275898943Sluigi *	1.2.3.4/26{1,6,5,4,23}	set of addresses in a subnet
2759117328Sluigi * We can have multiple comma-separated address/mask entries.
276098943Sluigi */
276198943Sluigistatic void
276298943Sluigifill_ip(ipfw_insn_ip *cmd, char *av)
276398943Sluigi{
2764117328Sluigi	int len = 0;
2765117328Sluigi	uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
276698943Sluigi
276798943Sluigi	cmd->o.len &= ~F_LEN_MASK;	/* zero len */
276898943Sluigi
2769140271Sbrooks	if (_substrcmp(av, "any") == 0)
277098943Sluigi		return;
277198943Sluigi
2772140271Sbrooks	if (_substrcmp(av, "me") == 0) {
277398943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn);
277498943Sluigi		return;
277598943Sluigi	}
277698943Sluigi
2777140271Sbrooks	if (strncmp(av, "table(", 6) == 0) {
2778130281Sru		char *p = strchr(av + 6, ',');
2779130281Sru
2780130281Sru		if (p)
2781130281Sru			*p++ = '\0';
2782130281Sru		cmd->o.opcode = O_IP_DST_LOOKUP;
2783130281Sru		cmd->o.arg1 = strtoul(av + 6, NULL, 0);
2784130281Sru		if (p) {
2785130281Sru			cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2786130281Sru			d[0] = strtoul(p, NULL, 0);
2787130281Sru		} else
2788130281Sru			cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2789130281Sru		return;
2790130281Sru	}
2791130281Sru
2792117328Sluigi    while (av) {
2793117328Sluigi	/*
2794117328Sluigi	 * After the address we can have '/' or ':' indicating a mask,
2795117328Sluigi	 * ',' indicating another address follows, '{' indicating a
2796117328Sluigi	 * set of addresses of unspecified size.
2797117328Sluigi	 */
2798165851Smlaier	char *t = NULL, *p = strpbrk(av, "/:,{");
2799117328Sluigi	int masklen;
2800165851Smlaier	char md, nd;
2801117328Sluigi
280298943Sluigi	if (p) {
280398943Sluigi		md = *p;
280498943Sluigi		*p++ = '\0';
2805165851Smlaier		if ((t = strpbrk(p, ",{")) != NULL) {
2806165851Smlaier			nd = *t;
2807165851Smlaier			*t = '\0';
2808165851Smlaier		}
2809117328Sluigi	} else
2810117328Sluigi		md = '\0';
281198943Sluigi
2812117328Sluigi	if (lookup_host(av, (struct in_addr *)&d[0]) != 0)
281398943Sluigi		errx(EX_NOHOST, "hostname ``%s'' unknown", av);
281498943Sluigi	switch (md) {
281598943Sluigi	case ':':
2816117328Sluigi		if (!inet_aton(p, (struct in_addr *)&d[1]))
281798943Sluigi			errx(EX_DATAERR, "bad netmask ``%s''", p);
281898943Sluigi		break;
281998943Sluigi	case '/':
2820117328Sluigi		masklen = atoi(p);
2821117328Sluigi		if (masklen == 0)
2822117328Sluigi			d[1] = htonl(0);	/* mask */
2823117328Sluigi		else if (masklen > 32)
282498943Sluigi			errx(EX_DATAERR, "bad width ``%s''", p);
282598943Sluigi		else
2826117328Sluigi			d[1] = htonl(~0 << (32 - masklen));
282798943Sluigi		break;
2828117328Sluigi	case '{':	/* no mask, assume /24 and put back the '{' */
2829117328Sluigi		d[1] = htonl(~0 << (32 - 24));
2830117328Sluigi		*(--p) = md;
2831117328Sluigi		break;
2832117328Sluigi
2833117328Sluigi	case ',':	/* single address plus continuation */
2834117328Sluigi		*(--p) = md;
2835117328Sluigi		/* FALLTHROUGH */
2836117328Sluigi	case 0:		/* initialization value */
283798943Sluigi	default:
2838117328Sluigi		d[1] = htonl(~0);	/* force /32 */
283998943Sluigi		break;
284098943Sluigi	}
2841117328Sluigi	d[0] &= d[1];		/* mask base address with mask */
2842165851Smlaier	if (t)
2843165851Smlaier		*t = nd;
2844117328Sluigi	/* find next separator */
284598943Sluigi	if (p)
2846117328Sluigi		p = strpbrk(p, ",{");
2847117328Sluigi	if (p && *p == '{') {
2848117328Sluigi		/*
2849117328Sluigi		 * We have a set of addresses. They are stored as follows:
2850117328Sluigi		 *   arg1	is the set size (powers of 2, 2..256)
2851117328Sluigi		 *   addr	is the base address IN HOST FORMAT
2852117328Sluigi		 *   mask..	is an array of arg1 bits (rounded up to
2853117328Sluigi		 *		the next multiple of 32) with bits set
2854117328Sluigi		 *		for each host in the map.
2855117328Sluigi		 */
2856117328Sluigi		uint32_t *map = (uint32_t *)&cmd->mask;
285798943Sluigi		int low, high;
2858117577Sluigi		int i = contigmask((uint8_t *)&(d[1]), 32);
285998943Sluigi
2860117328Sluigi		if (len > 0)
2861117328Sluigi			errx(EX_DATAERR, "address set cannot be in a list");
2862117328Sluigi		if (i < 24 || i > 31)
2863117328Sluigi			errx(EX_DATAERR, "invalid set with mask %d\n", i);
2864117328Sluigi		cmd->o.arg1 = 1<<(32-i);	/* map length		*/
2865117328Sluigi		d[0] = ntohl(d[0]);		/* base addr in host format */
286698943Sluigi		cmd->o.opcode = O_IP_DST_SET;	/* default */
286798943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32;
2868101117Sluigi		for (i = 0; i < (cmd->o.arg1+31)/32 ; i++)
2869117328Sluigi			map[i] = 0;	/* clear map */
287098943Sluigi
2871117328Sluigi		av = p + 1;
2872117328Sluigi		low = d[0] & 0xff;
287398943Sluigi		high = low + cmd->o.arg1 - 1;
2874117328Sluigi		/*
2875117328Sluigi		 * Here, i stores the previous value when we specify a range
2876117328Sluigi		 * of addresses within a mask, e.g. 45-63. i = -1 means we
2877117328Sluigi		 * have no previous value.
2878117328Sluigi		 */
2879116716Sluigi		i = -1;	/* previous value in a range */
288098943Sluigi		while (isdigit(*av)) {
288198943Sluigi			char *s;
2882117328Sluigi			int a = strtol(av, &s, 0);
288398943Sluigi
2884117328Sluigi			if (s == av) { /* no parameter */
2885117328Sluigi			    if (*av != '}')
2886117328Sluigi				errx(EX_DATAERR, "set not closed\n");
2887117328Sluigi			    if (i != -1)
2888117328Sluigi				errx(EX_DATAERR, "incomplete range %d-", i);
2889117328Sluigi			    break;
2890117328Sluigi			}
2891117328Sluigi			if (a < low || a > high)
2892117328Sluigi			    errx(EX_DATAERR, "addr %d out of range [%d-%d]\n",
289398943Sluigi				a, low, high);
289498943Sluigi			a -= low;
2895116716Sluigi			if (i == -1)	/* no previous in range */
2896116716Sluigi			    i = a;
2897116716Sluigi			else {		/* check that range is valid */
2898116716Sluigi			    if (i > a)
2899116716Sluigi				errx(EX_DATAERR, "invalid range %d-%d",
2900116716Sluigi					i+low, a+low);
2901116716Sluigi			    if (*s == '-')
2902116716Sluigi				errx(EX_DATAERR, "double '-' in range");
2903116716Sluigi			}
2904116716Sluigi			for (; i <= a; i++)
2905117328Sluigi			    map[i/32] |= 1<<(i & 31);
2906116716Sluigi			i = -1;
2907116716Sluigi			if (*s == '-')
2908116716Sluigi			    i = a;
2909117328Sluigi			else if (*s == '}')
2910116716Sluigi			    break;
291198943Sluigi			av = s+1;
291298943Sluigi		}
291398943Sluigi		return;
291498943Sluigi	}
2915117328Sluigi	av = p;
2916117328Sluigi	if (av)			/* then *av must be a ',' */
2917117328Sluigi		av++;
291898943Sluigi
2919117328Sluigi	/* Check this entry */
2920117328Sluigi	if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */
2921117328Sluigi		/*
2922117328Sluigi		 * 'any' turns the entire list into a NOP.
2923117328Sluigi		 * 'not any' never matches, so it is removed from the
2924117328Sluigi		 * list unless it is the only item, in which case we
2925117328Sluigi		 * report an error.
2926117328Sluigi		 */
2927117328Sluigi		if (cmd->o.len & F_NOT) {	/* "not any" never matches */
2928117328Sluigi			if (av == NULL && len == 0) /* only this entry */
2929117328Sluigi				errx(EX_DATAERR, "not any never matches");
2930117328Sluigi		}
2931117328Sluigi		/* else do nothing and skip this entry */
2932128067Smaxim		return;
2933117328Sluigi	}
2934117328Sluigi	/* A single IP can be stored in an optimized format */
2935117328Sluigi	if (d[1] == IP_MASK_ALL && av == NULL && len == 0) {
293698943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2937117328Sluigi		return;
2938117328Sluigi	}
2939117328Sluigi	len += 2;	/* two words... */
2940117328Sluigi	d += 2;
2941117328Sluigi    } /* end while */
2942162363Sjhay    if (len + 1 > F_LEN_MASK)
2943162363Sjhay	errx(EX_DATAERR, "address list too long");
2944117328Sluigi    cmd->o.len |= len+1;
294598943Sluigi}
294698943Sluigi
294798943Sluigi
2948145246Sbrooks/* Try to find ipv6 address by hostname */
2949145246Sbrooksstatic int
2950145246Sbrookslookup_host6 (char *host, struct in6_addr *ip6addr)
2951145246Sbrooks{
2952145246Sbrooks	struct hostent *he;
2953145246Sbrooks
2954145246Sbrooks	if (!inet_pton(AF_INET6, host, ip6addr)) {
2955145246Sbrooks		if ((he = gethostbyname2(host, AF_INET6)) == NULL)
2956145246Sbrooks			return(-1);
2957145246Sbrooks		memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr));
2958145246Sbrooks	}
2959145246Sbrooks	return(0);
2960145246Sbrooks}
2961145246Sbrooks
2962145246Sbrooks
2963145246Sbrooks/* n2mask sets n bits of the mask */
2964145246Sbrooksstatic void
2965145246Sbrooksn2mask(struct in6_addr *mask, int n)
2966145246Sbrooks{
2967145246Sbrooks	static int	minimask[9] =
2968145246Sbrooks	    { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
2969145246Sbrooks	u_char		*p;
2970145246Sbrooks
2971145246Sbrooks	memset(mask, 0, sizeof(struct in6_addr));
2972145246Sbrooks	p = (u_char *) mask;
2973145246Sbrooks	for (; n > 0; p++, n -= 8) {
2974145246Sbrooks		if (n >= 8)
2975145246Sbrooks			*p = 0xff;
2976145246Sbrooks		else
2977145246Sbrooks			*p = minimask[n];
2978145246Sbrooks	}
2979145246Sbrooks	return;
2980145246Sbrooks}
2981145246Sbrooks
2982145246Sbrooks
298398943Sluigi/*
2984145246Sbrooks * fill the addr and mask fields in the instruction as appropriate from av.
2985145246Sbrooks * Update length as appropriate.
2986145246Sbrooks * The following formats are allowed:
2987145246Sbrooks *     any     matches any IP6. Actually returns an empty instruction.
2988145246Sbrooks *     me      returns O_IP6_*_ME
2989145246Sbrooks *
2990145246Sbrooks *     03f1::234:123:0342                single IP6 addres
2991145246Sbrooks *     03f1::234:123:0342/24            address/mask
2992145246Sbrooks *     03f1::234:123:0342/24,03f1::234:123:0343/               List of address
2993145246Sbrooks *
2994145246Sbrooks * Set of address (as in ipv6) not supported because ipv6 address
2995145246Sbrooks * are typically random past the initial prefix.
2996145246Sbrooks * Return 1 on success, 0 on failure.
2997145246Sbrooks */
2998145246Sbrooksstatic int
2999145246Sbrooksfill_ip6(ipfw_insn_ip6 *cmd, char *av)
3000145246Sbrooks{
3001145246Sbrooks	int len = 0;
3002145246Sbrooks	struct in6_addr *d = &(cmd->addr6);
3003145246Sbrooks	/*
3004145246Sbrooks	 * Needed for multiple address.
3005145246Sbrooks	 * Note d[1] points to struct in6_add r mask6 of cmd
3006145246Sbrooks	 */
3007145246Sbrooks
3008145246Sbrooks       cmd->o.len &= ~F_LEN_MASK;	/* zero len */
3009145246Sbrooks
3010145246Sbrooks       if (strcmp(av, "any") == 0)
3011145246Sbrooks	       return (1);
3012145246Sbrooks
3013145246Sbrooks
3014145246Sbrooks       if (strcmp(av, "me") == 0) {	/* Set the data for "me" opt*/
3015145246Sbrooks	       cmd->o.len |= F_INSN_SIZE(ipfw_insn);
3016145246Sbrooks	       return (1);
3017145246Sbrooks       }
3018145246Sbrooks
3019145246Sbrooks       if (strcmp(av, "me6") == 0) {	/* Set the data for "me" opt*/
3020145246Sbrooks	       cmd->o.len |= F_INSN_SIZE(ipfw_insn);
3021145246Sbrooks	       return (1);
3022145246Sbrooks       }
3023145246Sbrooks
3024145246Sbrooks       av = strdup(av);
3025145246Sbrooks       while (av) {
3026145246Sbrooks		/*
3027145246Sbrooks		 * After the address we can have '/' indicating a mask,
3028145246Sbrooks		 * or ',' indicating another address follows.
3029145246Sbrooks		 */
3030145246Sbrooks
3031145246Sbrooks		char *p;
3032145246Sbrooks		int masklen;
3033145246Sbrooks		char md = '\0';
3034145246Sbrooks
3035145246Sbrooks		if ((p = strpbrk(av, "/,")) ) {
3036145246Sbrooks			md = *p;	/* save the separator */
3037145246Sbrooks			*p = '\0';	/* terminate address string */
3038145246Sbrooks			p++;		/* and skip past it */
3039145246Sbrooks		}
3040145246Sbrooks		/* now p points to NULL, mask or next entry */
3041145246Sbrooks
3042145246Sbrooks		/* lookup stores address in *d as a side effect */
3043145246Sbrooks		if (lookup_host6(av, d) != 0) {
3044145246Sbrooks			/* XXX: failed. Free memory and go */
3045145246Sbrooks			errx(EX_DATAERR, "bad address \"%s\"", av);
3046145246Sbrooks		}
3047145246Sbrooks		/* next, look at the mask, if any */
3048145246Sbrooks		masklen = (md == '/') ? atoi(p) : 128;
3049145246Sbrooks		if (masklen > 128 || masklen < 0)
3050145246Sbrooks			errx(EX_DATAERR, "bad width \"%s\''", p);
3051145246Sbrooks		else
3052145246Sbrooks			n2mask(&d[1], masklen);
3053145246Sbrooks
3054145246Sbrooks		APPLY_MASK(d, &d[1])   /* mask base address with mask */
3055145246Sbrooks
3056145246Sbrooks		/* find next separator */
3057145246Sbrooks
3058145246Sbrooks		if (md == '/') {	/* find separator past the mask */
3059145246Sbrooks			p = strpbrk(p, ",");
3060145246Sbrooks			if (p != NULL)
3061145246Sbrooks				p++;
3062145246Sbrooks		}
3063145246Sbrooks		av = p;
3064145246Sbrooks
3065145246Sbrooks		/* Check this entry */
3066145246Sbrooks		if (masklen == 0) {
3067145246Sbrooks			/*
3068145246Sbrooks			 * 'any' turns the entire list into a NOP.
3069145246Sbrooks			 * 'not any' never matches, so it is removed from the
3070145246Sbrooks			 * list unless it is the only item, in which case we
3071145246Sbrooks			 * report an error.
3072145246Sbrooks			 */
3073145246Sbrooks			if (cmd->o.len & F_NOT && av == NULL && len == 0)
3074145246Sbrooks				errx(EX_DATAERR, "not any never matches");
3075145246Sbrooks			continue;
3076145246Sbrooks		}
3077145246Sbrooks
3078145246Sbrooks		/*
3079145246Sbrooks		 * A single IP can be stored alone
3080145246Sbrooks		 */
3081145246Sbrooks		if (masklen == 128 && av == NULL && len == 0) {
3082145246Sbrooks			len = F_INSN_SIZE(struct in6_addr);
3083145246Sbrooks			break;
3084145246Sbrooks		}
3085145246Sbrooks
3086145246Sbrooks		/* Update length and pointer to arguments */
3087145246Sbrooks		len += F_INSN_SIZE(struct in6_addr)*2;
3088145246Sbrooks		d += 2;
3089145246Sbrooks	} /* end while */
3090145246Sbrooks
3091145246Sbrooks	/*
3092145246Sbrooks	 * Total length of the command, remember that 1 is the size of
3093145246Sbrooks	 * the base command.
3094145246Sbrooks	 */
3095162363Sjhay	if (len + 1 > F_LEN_MASK)
3096162363Sjhay		errx(EX_DATAERR, "address list too long");
3097145246Sbrooks	cmd->o.len |= len+1;
3098145246Sbrooks	free(av);
3099145246Sbrooks	return (1);
3100145246Sbrooks}
3101145246Sbrooks
3102145246Sbrooks/*
3103145246Sbrooks * fills command for ipv6 flow-id filtering
3104145246Sbrooks * note that the 20 bit flow number is stored in a array of u_int32_t
3105145246Sbrooks * it's supported lists of flow-id, so in the o.arg1 we store how many
3106145246Sbrooks * additional flow-id we want to filter, the basic is 1
3107145246Sbrooks */
3108145246Sbrooksvoid
3109145246Sbrooksfill_flow6( ipfw_insn_u32 *cmd, char *av )
3110145246Sbrooks{
3111145246Sbrooks	u_int32_t type;	 /* Current flow number */
3112145246Sbrooks	u_int16_t nflow = 0;    /* Current flow index */
3113145246Sbrooks	char *s = av;
3114145246Sbrooks	cmd->d[0] = 0;	  /* Initializing the base number*/
3115145246Sbrooks
3116145246Sbrooks	while (s) {
3117145246Sbrooks		av = strsep( &s, ",") ;
3118145246Sbrooks		type = strtoul(av, &av, 0);
3119145246Sbrooks		if (*av != ',' && *av != '\0')
3120145246Sbrooks			errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
3121145246Sbrooks		if (type > 0xfffff)
3122145246Sbrooks			errx(EX_DATAERR, "flow number out of range %s", av);
3123145246Sbrooks		cmd->d[nflow] |= type;
3124145246Sbrooks		nflow++;
3125145246Sbrooks	}
3126145246Sbrooks	if( nflow > 0 ) {
3127145246Sbrooks		cmd->o.opcode = O_FLOW6ID;
3128145246Sbrooks		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow;
3129145246Sbrooks		cmd->o.arg1 = nflow;
3130145246Sbrooks	}
3131145246Sbrooks	else {
3132145246Sbrooks		errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
3133145246Sbrooks	}
3134145246Sbrooks}
3135145246Sbrooks
3136145246Sbrooksstatic ipfw_insn *
3137145246Sbrooksadd_srcip6(ipfw_insn *cmd, char *av)
3138145246Sbrooks{
3139145246Sbrooks
3140145246Sbrooks	fill_ip6((ipfw_insn_ip6 *)cmd, av);
3141145246Sbrooks	if (F_LEN(cmd) == 0)				/* any */
3142145246Sbrooks		;
3143145246Sbrooks	if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) {	/* "me" */
3144145246Sbrooks		cmd->opcode = O_IP6_SRC_ME;
3145145246Sbrooks	} else if (F_LEN(cmd) ==
3146145246Sbrooks	    (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
3147145246Sbrooks		/* single IP, no mask*/
3148145246Sbrooks		cmd->opcode = O_IP6_SRC;
3149145246Sbrooks	} else {					/* addr/mask opt */
3150145246Sbrooks		cmd->opcode = O_IP6_SRC_MASK;
3151145246Sbrooks	}
3152145246Sbrooks	return cmd;
3153145246Sbrooks}
3154145246Sbrooks
3155145246Sbrooksstatic ipfw_insn *
3156145246Sbrooksadd_dstip6(ipfw_insn *cmd, char *av)
3157145246Sbrooks{
3158145246Sbrooks
3159145246Sbrooks	fill_ip6((ipfw_insn_ip6 *)cmd, av);
3160145246Sbrooks	if (F_LEN(cmd) == 0)				/* any */
3161145246Sbrooks		;
3162145246Sbrooks	if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) {	/* "me" */
3163145246Sbrooks		cmd->opcode = O_IP6_DST_ME;
3164145246Sbrooks	} else if (F_LEN(cmd) ==
3165145246Sbrooks	    (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
3166145246Sbrooks		/* single IP, no mask*/
3167145246Sbrooks		cmd->opcode = O_IP6_DST;
3168145246Sbrooks	} else {					/* addr/mask opt */
3169145246Sbrooks		cmd->opcode = O_IP6_DST_MASK;
3170145246Sbrooks	}
3171145246Sbrooks	return cmd;
3172145246Sbrooks}
3173145246Sbrooks
3174145246Sbrooks
3175145246Sbrooks/*
317698943Sluigi * helper function to process a set of flags and set bits in the
317798943Sluigi * appropriate masks.
317898943Sluigi */
317998943Sluigistatic void
318098943Sluigifill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
318198943Sluigi	struct _s_x *flags, char *p)
318298943Sluigi{
3183117328Sluigi	uint8_t set=0, clear=0;
318498943Sluigi
318598943Sluigi	while (p && *p) {
318698943Sluigi		char *q;	/* points to the separator */
318798943Sluigi		int val;
3188117328Sluigi		uint8_t *which;	/* mask we are working on */
318998943Sluigi
319098943Sluigi		if (*p == '!') {
319198943Sluigi			p++;
319298943Sluigi			which = &clear;
319398943Sluigi		} else
319498943Sluigi			which = &set;
319598943Sluigi		q = strchr(p, ',');
319698943Sluigi		if (q)
319798943Sluigi			*q++ = '\0';
319898943Sluigi		val = match_token(flags, p);
319998943Sluigi		if (val <= 0)
320098943Sluigi			errx(EX_DATAERR, "invalid flag %s", p);
3201117328Sluigi		*which |= (uint8_t)val;
320298943Sluigi		p = q;
320398943Sluigi	}
320498943Sluigi        cmd->opcode = opcode;
320598943Sluigi        cmd->len =  (cmd->len & (F_NOT | F_OR)) | 1;
320698943Sluigi        cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
320798943Sluigi}
320898943Sluigi
320998943Sluigi
321098943Sluigistatic void
321198943Sluigidelete(int ac, char *av[])
321298943Sluigi{
3213117328Sluigi	uint32_t rulenum;
3214117469Sluigi	struct dn_pipe p;
321598943Sluigi	int i;
321698943Sluigi	int exitval = EX_OK;
3217101628Sluigi	int do_set = 0;
321898943Sluigi
3219117469Sluigi	memset(&p, 0, sizeof p);
322098943Sluigi
322198943Sluigi	av++; ac--;
3222130013Scsjp	NEED1("missing rule specification");
3223140271Sbrooks	if (ac > 0 && _substrcmp(*av, "set") == 0) {
3224170923Smaxim		/* Do not allow using the following syntax:
3225170923Smaxim		 *	ipfw set N delete set M
3226170923Smaxim		 */
3227170923Smaxim		if (use_set)
3228170923Smaxim			errx(EX_DATAERR, "invalid syntax");
3229101978Sluigi		do_set = 1;	/* delete set */
3230101628Sluigi		ac--; av++;
3231101978Sluigi	}
323298943Sluigi
323398943Sluigi	/* Rule number */
323498943Sluigi	while (ac && isdigit(**av)) {
323598943Sluigi		i = atoi(*av); av++; ac--;
3236165648Spiso		if (do_nat) {
3237165648Spiso			exitval = do_cmd(IP_FW_NAT_DEL, &i, sizeof i);
3238165648Spiso			if (exitval) {
3239165648Spiso				exitval = EX_UNAVAILABLE;
3240165648Spiso				warn("rule %u not available", i);
3241165648Spiso			}
3242165648Spiso 		} else if (do_pipe) {
324398943Sluigi			if (do_pipe == 1)
3244117469Sluigi				p.pipe_nr = i;
324598943Sluigi			else
3246117469Sluigi				p.fs.fs_nr = i;
3247117469Sluigi			i = do_cmd(IP_DUMMYNET_DEL, &p, sizeof p);
324898943Sluigi			if (i) {
324998943Sluigi				exitval = 1;
325098943Sluigi				warn("rule %u: setsockopt(IP_DUMMYNET_DEL)",
3251117469Sluigi				    do_pipe == 1 ? p.pipe_nr : p.fs.fs_nr);
325298943Sluigi			}
325398943Sluigi		} else {
3254170923Smaxim			if (use_set)
3255170923Smaxim				rulenum = (i & 0xffff) | (5 << 24) |
3256170923Smaxim				    ((use_set - 1) << 16);
3257170923Smaxim			else
3258101978Sluigi			rulenum =  (i & 0xffff) | (do_set << 24);
3259117328Sluigi			i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum);
326098943Sluigi			if (i) {
326198943Sluigi				exitval = EX_UNAVAILABLE;
326298943Sluigi				warn("rule %u: setsockopt(IP_FW_DEL)",
326398943Sluigi				    rulenum);
326498943Sluigi			}
326598943Sluigi		}
326698943Sluigi	}
326798943Sluigi	if (exitval != EX_OK)
326898943Sluigi		exit(exitval);
326998943Sluigi}
327098943Sluigi
327198943Sluigi
327298943Sluigi/*
327398943Sluigi * fill the interface structure. We do not check the name as we can
327498943Sluigi * create interfaces dynamically, so checking them at insert time
327598943Sluigi * makes relatively little sense.
3276121816Sbrooks * Interface names containing '*', '?', or '[' are assumed to be shell
3277121816Sbrooks * patterns which match interfaces.
327898943Sluigi */
327998943Sluigistatic void
328098943Sluigifill_iface(ipfw_insn_if *cmd, char *arg)
328198943Sluigi{
328298943Sluigi	cmd->name[0] = '\0';
328398943Sluigi	cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
328498943Sluigi
328598943Sluigi	/* Parse the interface or address */
3286140271Sbrooks	if (strcmp(arg, "any") == 0)
328798943Sluigi		cmd->o.len = 0;		/* effectively ignore this command */
328898943Sluigi	else if (!isdigit(*arg)) {
3289121816Sbrooks		strlcpy(cmd->name, arg, sizeof(cmd->name));
3290121816Sbrooks		cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
329198943Sluigi	} else if (!inet_aton(arg, &cmd->p.ip))
329298943Sluigi		errx(EX_DATAERR, "bad ip address ``%s''", arg);
329398943Sluigi}
329498943Sluigi
3295165648Spiso/*
3296165648Spiso * Search for interface with name "ifn", and fill n accordingly:
3297165648Spiso *
3298165648Spiso * n->ip        ip address of interface "ifn"
3299165648Spiso * n->if_name   copy of interface name "ifn"
3300165648Spiso */
330198943Sluigistatic void
3302165648Spisoset_addr_dynamic(const char *ifn, struct cfg_nat *n)
3303165648Spiso{
3304165648Spiso	size_t needed;
3305165648Spiso	int mib[6];
3306165648Spiso	char *buf, *lim, *next;
3307165648Spiso	struct if_msghdr *ifm;
3308165648Spiso	struct ifa_msghdr *ifam;
3309165648Spiso	struct sockaddr_dl *sdl;
3310165648Spiso	struct sockaddr_in *sin;
3311165648Spiso	int ifIndex, ifMTU;
3312165648Spiso
3313165648Spiso	mib[0] = CTL_NET;
3314165648Spiso	mib[1] = PF_ROUTE;
3315165648Spiso	mib[2] = 0;
3316165648Spiso	mib[3] = AF_INET;
3317165648Spiso	mib[4] = NET_RT_IFLIST;
3318165648Spiso	mib[5] = 0;
3319165648Spiso/*
3320165648Spiso * Get interface data.
3321165648Spiso */
3322165648Spiso	if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
3323165648Spiso		err(1, "iflist-sysctl-estimate");
3324165648Spiso	if ((buf = malloc(needed)) == NULL)
3325165648Spiso		errx(1, "malloc failed");
3326165648Spiso	if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
3327165648Spiso		err(1, "iflist-sysctl-get");
3328165648Spiso	lim = buf + needed;
3329165648Spiso/*
3330165648Spiso * Loop through interfaces until one with
3331165648Spiso * given name is found. This is done to
3332165648Spiso * find correct interface index for routing
3333165648Spiso * message processing.
3334165648Spiso */
3335165648Spiso	ifIndex	= 0;
3336165648Spiso	next = buf;
3337165648Spiso	while (next < lim) {
3338165648Spiso		ifm = (struct if_msghdr *)next;
3339165648Spiso		next += ifm->ifm_msglen;
3340165648Spiso		if (ifm->ifm_version != RTM_VERSION) {
3341165648Spiso			if (verbose)
3342165648Spiso				warnx("routing message version %d "
3343165648Spiso				    "not understood", ifm->ifm_version);
3344165648Spiso			continue;
3345165648Spiso		}
3346165648Spiso		if (ifm->ifm_type == RTM_IFINFO) {
3347165648Spiso			sdl = (struct sockaddr_dl *)(ifm + 1);
3348165648Spiso			if (strlen(ifn) == sdl->sdl_nlen &&
3349165648Spiso			    strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
3350165648Spiso				ifIndex = ifm->ifm_index;
3351165648Spiso				ifMTU = ifm->ifm_data.ifi_mtu;
3352165648Spiso				break;
3353165648Spiso			}
3354165648Spiso		}
3355165648Spiso	}
3356165648Spiso	if (!ifIndex)
3357165648Spiso		errx(1, "unknown interface name %s", ifn);
3358165648Spiso/*
3359165648Spiso * Get interface address.
3360165648Spiso */
3361165648Spiso	sin = NULL;
3362165648Spiso	while (next < lim) {
3363165648Spiso		ifam = (struct ifa_msghdr *)next;
3364165648Spiso		next += ifam->ifam_msglen;
3365165648Spiso		if (ifam->ifam_version != RTM_VERSION) {
3366165648Spiso			if (verbose)
3367165648Spiso				warnx("routing message version %d "
3368165648Spiso				    "not understood", ifam->ifam_version);
3369165648Spiso			continue;
3370165648Spiso		}
3371165648Spiso		if (ifam->ifam_type != RTM_NEWADDR)
3372165648Spiso			break;
3373165648Spiso		if (ifam->ifam_addrs & RTA_IFA) {
3374165648Spiso			int i;
3375165648Spiso			char *cp = (char *)(ifam + 1);
3376165648Spiso
3377165648Spiso			for (i = 1; i < RTA_IFA; i <<= 1) {
3378165648Spiso				if (ifam->ifam_addrs & i)
3379165648Spiso					cp += SA_SIZE((struct sockaddr *)cp);
3380165648Spiso			}
3381165648Spiso			if (((struct sockaddr *)cp)->sa_family == AF_INET) {
3382165648Spiso				sin = (struct sockaddr_in *)cp;
3383165648Spiso				break;
3384165648Spiso			}
3385165648Spiso		}
3386165648Spiso	}
3387165648Spiso	if (sin == NULL)
3388165648Spiso		errx(1, "%s: cannot get interface address", ifn);
3389165648Spiso
3390165648Spiso	n->ip = sin->sin_addr;
3391165648Spiso	strncpy(n->if_name, ifn, IF_NAMESIZE);
3392165648Spiso
3393165648Spiso	free(buf);
3394165648Spiso}
3395165648Spiso
3396165648Spiso/*
3397165648Spiso * XXX - The following functions, macros and definitions come from natd.c:
3398165648Spiso * it would be better to move them outside natd.c, in a file
3399165648Spiso * (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live
3400165648Spiso * with it.
3401165648Spiso */
3402165648Spiso
3403165648Spiso/*
3404165648Spiso * Definition of a port range, and macros to deal with values.
3405165648Spiso * FORMAT:  HI 16-bits == first port in range, 0 == all ports.
3406165648Spiso *          LO 16-bits == number of ports in range
3407165648Spiso * NOTES:   - Port values are not stored in network byte order.
3408165648Spiso */
3409165648Spiso
3410165648Spiso#define port_range u_long
3411165648Spiso
3412165648Spiso#define GETLOPORT(x)     ((x) >> 0x10)
3413165648Spiso#define GETNUMPORTS(x)   ((x) & 0x0000ffff)
3414165648Spiso#define GETHIPORT(x)     (GETLOPORT((x)) + GETNUMPORTS((x)))
3415165648Spiso
3416165648Spiso/* Set y to be the low-port value in port_range variable x. */
3417165648Spiso#define SETLOPORT(x,y)   ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
3418165648Spiso
3419165648Spiso/* Set y to be the number of ports in port_range variable x. */
3420165648Spiso#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
3421165648Spiso
3422165648Spisostatic void
3423165648SpisoStrToAddr (const char* str, struct in_addr* addr)
3424165648Spiso{
3425165648Spiso	struct hostent* hp;
3426165648Spiso
3427165648Spiso	if (inet_aton (str, addr))
3428165648Spiso		return;
3429165648Spiso
3430165648Spiso	hp = gethostbyname (str);
3431165648Spiso	if (!hp)
3432165648Spiso		errx (1, "unknown host %s", str);
3433165648Spiso
3434165648Spiso	memcpy (addr, hp->h_addr, sizeof (struct in_addr));
3435165648Spiso}
3436165648Spiso
3437165648Spisostatic int
3438165648SpisoStrToPortRange (const char* str, const char* proto, port_range *portRange)
3439165648Spiso{
3440165648Spiso	char*           sep;
3441165648Spiso	struct servent*	sp;
3442165648Spiso	char*		end;
3443165648Spiso	u_short         loPort;
3444165648Spiso	u_short         hiPort;
3445165648Spiso
3446165648Spiso	/* First see if this is a service, return corresponding port if so. */
3447165648Spiso	sp = getservbyname (str,proto);
3448165648Spiso	if (sp) {
3449165648Spiso	        SETLOPORT(*portRange, ntohs(sp->s_port));
3450165648Spiso		SETNUMPORTS(*portRange, 1);
3451165648Spiso		return 0;
3452165648Spiso	}
3453165648Spiso
3454165648Spiso	/* Not a service, see if it's a single port or port range. */
3455165648Spiso	sep = strchr (str, '-');
3456165648Spiso	if (sep == NULL) {
3457165648Spiso	        SETLOPORT(*portRange, strtol(str, &end, 10));
3458165648Spiso		if (end != str) {
3459165648Spiso		        /* Single port. */
3460165648Spiso		        SETNUMPORTS(*portRange, 1);
3461165648Spiso			return 0;
3462165648Spiso		}
3463165648Spiso
3464165648Spiso		/* Error in port range field. */
3465165648Spiso		errx (EX_DATAERR, "%s/%s: unknown service", str, proto);
3466165648Spiso	}
3467165648Spiso
3468165648Spiso	/* Port range, get the values and sanity check. */
3469165648Spiso	sscanf (str, "%hu-%hu", &loPort, &hiPort);
3470165648Spiso	SETLOPORT(*portRange, loPort);
3471165648Spiso	SETNUMPORTS(*portRange, 0);	/* Error by default */
3472165648Spiso	if (loPort <= hiPort)
3473165648Spiso	        SETNUMPORTS(*portRange, hiPort - loPort + 1);
3474165648Spiso
3475165648Spiso	if (GETNUMPORTS(*portRange) == 0)
3476165648Spiso	        errx (EX_DATAERR, "invalid port range %s", str);
3477165648Spiso
3478165648Spiso	return 0;
3479165648Spiso}
3480165648Spiso
3481165648Spisostatic int
3482165648SpisoStrToProto (const char* str)
3483165648Spiso{
3484165648Spiso	if (!strcmp (str, "tcp"))
3485165648Spiso		return IPPROTO_TCP;
3486165648Spiso
3487165648Spiso	if (!strcmp (str, "udp"))
3488165648Spiso		return IPPROTO_UDP;
3489165648Spiso
3490165648Spiso	errx (EX_DATAERR, "unknown protocol %s. Expected tcp or udp", str);
3491165648Spiso}
3492165648Spiso
3493165648Spisostatic int
3494165648SpisoStrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
3495165648Spiso		       port_range *portRange)
3496165648Spiso{
3497165648Spiso	char*	ptr;
3498165648Spiso
3499165648Spiso	ptr = strchr (str, ':');
3500165648Spiso	if (!ptr)
3501165648Spiso		errx (EX_DATAERR, "%s is missing port number", str);
3502165648Spiso
3503165648Spiso	*ptr = '\0';
3504165648Spiso	++ptr;
3505165648Spiso
3506165648Spiso	StrToAddr (str, addr);
3507165648Spiso	return StrToPortRange (ptr, proto, portRange);
3508165648Spiso}
3509165648Spiso
3510165648Spiso/* End of stuff taken from natd.c. */
3511165648Spiso
3512165648Spiso#define INC_ARGCV() do {        \
3513165648Spiso	(*_av)++;               \
3514165648Spiso	(*_ac)--;               \
3515165648Spiso	av = *_av;              \
3516165648Spiso	ac = *_ac;              \
3517165648Spiso} while(0)
3518165648Spiso
3519165648Spiso/*
3520165648Spiso * The next 3 functions add support for the addr, port and proto redirect and
3521165648Spiso * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect()
3522165648Spiso * and SetupProtoRedirect() from natd.c.
3523165648Spiso *
3524165648Spiso * Every setup_* function fills at least one redirect entry
3525165648Spiso * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool)
3526165648Spiso * in buf.
3527165648Spiso *
3528165648Spiso * The format of data in buf is:
3529165648Spiso *
3530165648Spiso *
3531165648Spiso *     cfg_nat    cfg_redir    cfg_spool    ......  cfg_spool
3532165648Spiso *
3533165648Spiso *    -------------------------------------        ------------
3534165648Spiso *   |          | .....X ... |          |         |           |  .....
3535165648Spiso *    ------------------------------------- ...... ------------
3536165648Spiso *                     ^
3537165648Spiso *                spool_cnt       n=0       ......   n=(X-1)
3538165648Spiso *
3539165648Spiso * len points to the amount of available space in buf
3540165648Spiso * space counts the memory consumed by every function
3541165648Spiso *
3542165648Spiso * XXX - Every function get all the argv params so it
3543165648Spiso * has to check, in optional parameters, that the next
3544165648Spiso * args is a valid option for the redir entry and not
3545165648Spiso * another token. Only redir_port and redir_proto are
3546165648Spiso * affected by this.
3547165648Spiso */
3548165648Spiso
3549165648Spisostatic int
3550165648Spisosetup_redir_addr(char *spool_buf, int len,
3551165648Spiso		 int *_ac, char ***_av)
3552165648Spiso{
3553165648Spiso	char **av, *sep; /* Token separator. */
3554165648Spiso	/* Temporary buffer used to hold server pool ip's. */
3555165648Spiso	char tmp_spool_buf[NAT_BUF_LEN];
3556165648Spiso	int ac, i, space, lsnat;
3557165648Spiso	struct cfg_redir *r;
3558165648Spiso	struct cfg_spool *tmp;
3559165648Spiso
3560165648Spiso	av = *_av;
3561165648Spiso	ac = *_ac;
3562165648Spiso	space = 0;
3563165648Spiso	lsnat = 0;
3564165648Spiso	if (len >= SOF_REDIR) {
3565165648Spiso		r = (struct cfg_redir *)spool_buf;
3566165648Spiso		/* Skip cfg_redir at beginning of buf. */
3567165648Spiso		spool_buf = &spool_buf[SOF_REDIR];
3568165648Spiso		space = SOF_REDIR;
3569165648Spiso		len -= SOF_REDIR;
3570165648Spiso	} else
3571165648Spiso		goto nospace;
3572165648Spiso	r->mode = REDIR_ADDR;
3573165648Spiso	/* Extract local address. */
3574165648Spiso	if (ac == 0)
3575165648Spiso		errx(EX_DATAERR, "redirect_addr: missing local address");
3576165648Spiso	sep = strchr(*av, ',');
3577165648Spiso	if (sep) {		/* LSNAT redirection syntax. */
3578165648Spiso		r->laddr.s_addr = INADDR_NONE;
3579165648Spiso		/* Preserve av, copy spool servers to tmp_spool_buf. */
3580165648Spiso		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
3581165648Spiso		lsnat = 1;
3582165648Spiso	} else
3583165648Spiso		StrToAddr(*av, &r->laddr);
3584165648Spiso	INC_ARGCV();
3585165648Spiso
3586165648Spiso	/* Extract public address. */
3587165648Spiso	if (ac == 0)
3588165648Spiso		errx(EX_DATAERR, "redirect_addr: missing public address");
3589165648Spiso	StrToAddr(*av, &r->paddr);
3590165648Spiso	INC_ARGCV();
3591165648Spiso
3592165648Spiso	/* Setup LSNAT server pool. */
3593165648Spiso	if (sep) {
3594165648Spiso		sep = strtok(tmp_spool_buf, ",");
3595165648Spiso		while (sep != NULL) {
3596165648Spiso			tmp = (struct cfg_spool *)spool_buf;
3597165648Spiso			if (len < SOF_SPOOL)
3598165648Spiso				goto nospace;
3599165648Spiso			len -= SOF_SPOOL;
3600165648Spiso			space += SOF_SPOOL;
3601165648Spiso			StrToAddr(sep, &tmp->addr);
3602165648Spiso			tmp->port = ~0;
3603165648Spiso			r->spool_cnt++;
3604165648Spiso			/* Point to the next possible cfg_spool. */
3605165648Spiso			spool_buf = &spool_buf[SOF_SPOOL];
3606165648Spiso			sep = strtok(NULL, ",");
3607165648Spiso		}
3608165648Spiso	}
3609165648Spiso	return(space);
3610165648Spisonospace:
3611165648Spiso	errx(EX_DATAERR, "redirect_addr: buf is too small\n");
3612165648Spiso}
3613165648Spiso
3614165648Spisostatic int
3615165648Spisosetup_redir_port(char *spool_buf, int len,
3616165648Spiso		 int *_ac, char ***_av)
3617165648Spiso{
3618165648Spiso	char **av, *sep, *protoName;
3619165648Spiso	char tmp_spool_buf[NAT_BUF_LEN];
3620165648Spiso	int ac, space, lsnat;
3621165648Spiso	struct cfg_redir *r;
3622165648Spiso	struct cfg_spool *tmp;
3623165648Spiso	u_short numLocalPorts;
3624165648Spiso	port_range portRange;
3625165648Spiso
3626165648Spiso	av = *_av;
3627165648Spiso	ac = *_ac;
3628165648Spiso	space = 0;
3629165648Spiso	lsnat = 0;
3630165648Spiso	numLocalPorts = 0;
3631165648Spiso
3632165648Spiso	if (len >= SOF_REDIR) {
3633165648Spiso		r = (struct cfg_redir *)spool_buf;
3634165648Spiso		/* Skip cfg_redir at beginning of buf. */
3635165648Spiso		spool_buf = &spool_buf[SOF_REDIR];
3636165648Spiso		space = SOF_REDIR;
3637165648Spiso		len -= SOF_REDIR;
3638165648Spiso	} else
3639165648Spiso		goto nospace;
3640165648Spiso	r->mode = REDIR_PORT;
3641165648Spiso	/*
3642165648Spiso	 * Extract protocol.
3643165648Spiso	 */
3644165648Spiso	if (ac == 0)
3645165648Spiso		errx (EX_DATAERR, "redirect_port: missing protocol");
3646165648Spiso	r->proto = StrToProto(*av);
3647165648Spiso	protoName = *av;
3648165648Spiso	INC_ARGCV();
3649165648Spiso
3650165648Spiso	/*
3651165648Spiso	 * Extract local address.
3652165648Spiso	 */
3653165648Spiso	if (ac == 0)
3654165648Spiso		errx (EX_DATAERR, "redirect_port: missing local address");
3655165648Spiso
3656165648Spiso	sep = strchr(*av, ',');
3657165648Spiso	/* LSNAT redirection syntax. */
3658165648Spiso	if (sep) {
3659165648Spiso		r->laddr.s_addr = INADDR_NONE;
3660165648Spiso		r->lport = ~0;
3661165648Spiso		numLocalPorts = 1;
3662165648Spiso		/* Preserve av, copy spool servers to tmp_spool_buf. */
3663165648Spiso		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
3664165648Spiso		lsnat = 1;
3665165648Spiso	} else {
3666165648Spiso		if (StrToAddrAndPortRange (*av, &r->laddr, protoName,
3667165648Spiso		    &portRange) != 0)
3668165648Spiso			errx(EX_DATAERR, "redirect_port:"
3669165648Spiso			    "invalid local port range");
3670165648Spiso
3671165648Spiso		r->lport = GETLOPORT(portRange);
3672165648Spiso		numLocalPorts = GETNUMPORTS(portRange);
3673165648Spiso	}
3674165648Spiso	INC_ARGCV();
3675165648Spiso
3676165648Spiso	/*
3677165648Spiso	 * Extract public port and optionally address.
3678165648Spiso	 */
3679165648Spiso	if (ac == 0)
3680165648Spiso		errx (EX_DATAERR, "redirect_port: missing public port");
3681165648Spiso
3682165648Spiso	sep = strchr (*av, ':');
3683165648Spiso	if (sep) {
3684165648Spiso	        if (StrToAddrAndPortRange (*av, &r->paddr, protoName,
3685165648Spiso		    &portRange) != 0)
3686165648Spiso		        errx(EX_DATAERR, "redirect_port:"
3687165648Spiso			    "invalid public port range");
3688165648Spiso	} else {
3689165648Spiso		r->paddr.s_addr = INADDR_ANY;
3690165648Spiso		if (StrToPortRange (*av, protoName, &portRange) != 0)
3691165648Spiso		        errx(EX_DATAERR, "redirect_port:"
3692165648Spiso			    "invalid public port range");
3693165648Spiso	}
3694165648Spiso
3695165648Spiso	r->pport = GETLOPORT(portRange);
3696165648Spiso	r->pport_cnt = GETNUMPORTS(portRange);
3697165648Spiso	INC_ARGCV();
3698165648Spiso
3699165648Spiso	/*
3700165648Spiso	 * Extract remote address and optionally port.
3701165648Spiso	 */
3702165648Spiso	/*
3703165648Spiso	 * NB: isalpha(**av) => we've to check that next parameter is really an
3704165648Spiso	 * option for this redirect entry, else stop here processing arg[cv].
3705165648Spiso	 */
3706165648Spiso	if (ac != 0 && !isalpha(**av)) {
3707165648Spiso		sep = strchr (*av, ':');
3708165648Spiso		if (sep) {
3709165648Spiso		        if (StrToAddrAndPortRange (*av, &r->raddr, protoName,
3710165648Spiso			    &portRange) != 0)
3711165648Spiso				errx(EX_DATAERR, "redirect_port:"
3712165648Spiso				    "invalid remote port range");
3713165648Spiso		} else {
3714165648Spiso		        SETLOPORT(portRange, 0);
3715165648Spiso			SETNUMPORTS(portRange, 1);
3716165648Spiso			StrToAddr (*av, &r->raddr);
3717165648Spiso		}
3718165648Spiso		INC_ARGCV();
3719165648Spiso	} else {
3720165648Spiso		SETLOPORT(portRange, 0);
3721165648Spiso		SETNUMPORTS(portRange, 1);
3722165648Spiso		r->raddr.s_addr = INADDR_ANY;
3723165648Spiso	}
3724165648Spiso	r->rport = GETLOPORT(portRange);
3725165648Spiso	r->rport_cnt = GETNUMPORTS(portRange);
3726165648Spiso
3727165648Spiso	/*
3728165648Spiso	 * Make sure port ranges match up, then add the redirect ports.
3729165648Spiso	 */
3730165648Spiso	if (numLocalPorts != r->pport_cnt)
3731165648Spiso	        errx(EX_DATAERR, "redirect_port:"
3732165648Spiso		    "port ranges must be equal in size");
3733165648Spiso
3734165648Spiso	/* Remote port range is allowed to be '0' which means all ports. */
3735165648Spiso	if (r->rport_cnt != numLocalPorts &&
3736165648Spiso	    (r->rport_cnt != 1 || r->rport != 0))
3737165648Spiso	        errx(EX_DATAERR, "redirect_port: remote port must"
3738165648Spiso		    "be 0 or equal to local port range in size");
3739165648Spiso
3740165648Spiso	/*
3741165648Spiso	 * Setup LSNAT server pool.
3742165648Spiso	 */
3743165648Spiso	if (lsnat) {
3744165648Spiso		sep = strtok(tmp_spool_buf, ",");
3745165648Spiso		while (sep != NULL) {
3746165648Spiso			tmp = (struct cfg_spool *)spool_buf;
3747165648Spiso			if (len < SOF_SPOOL)
3748165648Spiso				goto nospace;
3749165648Spiso			len -= SOF_SPOOL;
3750165648Spiso			space += SOF_SPOOL;
3751165648Spiso			if (StrToAddrAndPortRange(sep, &tmp->addr, protoName,
3752165648Spiso			    &portRange) != 0)
3753165648Spiso				errx(EX_DATAERR, "redirect_port:"
3754165648Spiso				    "invalid local port range");
3755165648Spiso			if (GETNUMPORTS(portRange) != 1)
3756165648Spiso				errx(EX_DATAERR, "redirect_port: local port"
3757165648Spiso				    "must be single in this context");
3758165648Spiso			tmp->port = GETLOPORT(portRange);
3759165648Spiso			r->spool_cnt++;
3760165648Spiso			/* Point to the next possible cfg_spool. */
3761165648Spiso			spool_buf = &spool_buf[SOF_SPOOL];
3762165648Spiso			sep = strtok(NULL, ",");
3763165648Spiso		}
3764165648Spiso	}
3765165648Spiso	return (space);
3766165648Spisonospace:
3767165648Spiso	errx(EX_DATAERR, "redirect_port: buf is too small\n");
3768165648Spiso}
3769165648Spiso
3770165648Spisostatic int
3771165648Spisosetup_redir_proto(char *spool_buf, int len,
3772165648Spiso		 int *_ac, char ***_av)
3773165648Spiso{
3774165648Spiso	char **av;
3775165648Spiso	int ac, i, space;
3776165648Spiso	struct protoent *protoent;
3777165648Spiso	struct cfg_redir *r;
3778165648Spiso
3779165648Spiso	av = *_av;
3780165648Spiso	ac = *_ac;
3781165648Spiso	if (len >= SOF_REDIR) {
3782165648Spiso		r = (struct cfg_redir *)spool_buf;
3783165648Spiso		/* Skip cfg_redir at beginning of buf. */
3784165648Spiso		spool_buf = &spool_buf[SOF_REDIR];
3785165648Spiso		space = SOF_REDIR;
3786165648Spiso		len -= SOF_REDIR;
3787165648Spiso	} else
3788165648Spiso		goto nospace;
3789165648Spiso	r->mode = REDIR_PROTO;
3790165648Spiso	/*
3791165648Spiso	 * Extract protocol.
3792165648Spiso	 */
3793165648Spiso	if (ac == 0)
3794165648Spiso		errx(EX_DATAERR, "redirect_proto: missing protocol");
3795165648Spiso
3796165648Spiso	protoent = getprotobyname(*av);
3797165648Spiso	if (protoent == NULL)
3798165648Spiso		errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av);
3799165648Spiso	else
3800165648Spiso		r->proto = protoent->p_proto;
3801165648Spiso
3802165648Spiso	INC_ARGCV();
3803165648Spiso
3804165648Spiso	/*
3805165648Spiso	 * Extract local address.
3806165648Spiso	 */
3807165648Spiso	if (ac == 0)
3808165648Spiso		errx(EX_DATAERR, "redirect_proto: missing local address");
3809165648Spiso	else
3810165648Spiso		StrToAddr(*av, &r->laddr);
3811165648Spiso
3812165648Spiso	INC_ARGCV();
3813165648Spiso
3814165648Spiso	/*
3815165648Spiso	 * Extract optional public address.
3816165648Spiso	 */
3817165648Spiso	if (ac == 0) {
3818165648Spiso		r->paddr.s_addr = INADDR_ANY;
3819165648Spiso		r->raddr.s_addr = INADDR_ANY;
3820165648Spiso	} else {
3821165648Spiso		/* see above in setup_redir_port() */
3822165648Spiso		if (!isalpha(**av)) {
3823165648Spiso			StrToAddr(*av, &r->paddr);
3824165648Spiso			INC_ARGCV();
3825165648Spiso
3826165648Spiso			/*
3827165648Spiso			 * Extract optional remote address.
3828165648Spiso			 */
3829165648Spiso			/* see above in setup_redir_port() */
3830165648Spiso			if (ac!=0 && !isalpha(**av)) {
3831165648Spiso				StrToAddr(*av, &r->raddr);
3832165648Spiso				INC_ARGCV();
3833165648Spiso			}
3834165648Spiso		}
3835165648Spiso	}
3836165648Spiso	return (space);
3837165648Spisonospace:
3838165648Spiso	errx(EX_DATAERR, "redirect_proto: buf is too small\n");
3839165648Spiso}
3840165648Spiso
3841165648Spisostatic void
3842165648Spisoshow_nat(int ac, char **av);
3843165648Spiso
3844165648Spisostatic void
3845165648Spisoprint_nat_config(char *buf) {
3846165648Spiso	struct cfg_nat *n;
3847165648Spiso	int i, cnt, flag, off;
3848165648Spiso	struct cfg_redir *t;
3849165648Spiso	struct cfg_spool *s;
3850165648Spiso	struct protoent *p;
3851165648Spiso
3852165648Spiso	n = (struct cfg_nat *)buf;
3853165648Spiso	flag = 1;
3854165648Spiso	off  = sizeof(*n);
3855165648Spiso	printf("ipfw nat %u config", n->id);
3856165648Spiso	if (strlen(n->if_name) != 0)
3857165648Spiso		printf(" if %s", n->if_name);
3858165648Spiso	else if (n->ip.s_addr != 0)
3859165648Spiso		printf(" ip %s", inet_ntoa(n->ip));
3860165648Spiso	while (n->mode != 0) {
3861165648Spiso		if (n->mode & PKT_ALIAS_LOG) {
3862165648Spiso			printf(" log");
3863165648Spiso			n->mode &= ~PKT_ALIAS_LOG;
3864165648Spiso		} else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
3865165648Spiso			printf(" deny_in");
3866165648Spiso			n->mode &= ~PKT_ALIAS_DENY_INCOMING;
3867165648Spiso		} else if (n->mode & PKT_ALIAS_SAME_PORTS) {
3868165648Spiso			printf(" same_ports");
3869165648Spiso			n->mode &= ~PKT_ALIAS_SAME_PORTS;
3870165648Spiso		} else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
3871165648Spiso			printf(" unreg_only");
3872165648Spiso			n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
3873165648Spiso		} else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
3874165648Spiso			printf(" reset");
3875165648Spiso			n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
3876165648Spiso		} else if (n->mode & PKT_ALIAS_REVERSE) {
3877165648Spiso			printf(" reverse");
3878165648Spiso			n->mode &= ~PKT_ALIAS_REVERSE;
3879165648Spiso		} else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
3880165648Spiso			printf(" proxy_only");
3881165648Spiso			n->mode &= ~PKT_ALIAS_PROXY_ONLY;
3882165648Spiso		}
3883165648Spiso	}
3884165648Spiso	/* Print all the redirect's data configuration. */
3885165648Spiso	for (cnt = 0; cnt < n->redir_cnt; cnt++) {
3886165648Spiso		t = (struct cfg_redir *)&buf[off];
3887165648Spiso		off += SOF_REDIR;
3888165648Spiso		switch (t->mode) {
3889165648Spiso		case REDIR_ADDR:
3890165648Spiso			printf(" redirect_addr");
3891165648Spiso			if (t->spool_cnt == 0)
3892165648Spiso				printf(" %s", inet_ntoa(t->laddr));
3893165648Spiso			else
3894165648Spiso				for (i = 0; i < t->spool_cnt; i++) {
3895165648Spiso					s = (struct cfg_spool *)&buf[off];
3896165648Spiso					if (i)
3897165648Spiso						printf(",");
3898165648Spiso					else
3899165648Spiso						printf(" ");
3900165648Spiso					printf("%s", inet_ntoa(s->addr));
3901165648Spiso					off += SOF_SPOOL;
3902165648Spiso				}
3903165648Spiso			printf(" %s", inet_ntoa(t->paddr));
3904165648Spiso			break;
3905165648Spiso		case REDIR_PORT:
3906165648Spiso			p = getprotobynumber(t->proto);
3907165648Spiso			printf(" redirect_port %s ", p->p_name);
3908165648Spiso			if (!t->spool_cnt) {
3909165648Spiso				printf("%s:%u", inet_ntoa(t->laddr), t->lport);
3910165648Spiso				if (t->pport_cnt > 1)
3911165648Spiso					printf("-%u", t->lport +
3912165648Spiso					    t->pport_cnt - 1);
3913165648Spiso			} else
3914165648Spiso				for (i=0; i < t->spool_cnt; i++) {
3915165648Spiso					s = (struct cfg_spool *)&buf[off];
3916165648Spiso					if (i)
3917165648Spiso						printf(",");
3918165648Spiso					printf("%s:%u", inet_ntoa(s->addr),
3919165648Spiso					    s->port);
3920165648Spiso					off += SOF_SPOOL;
3921165648Spiso				}
3922165648Spiso
3923165648Spiso			printf(" ");
3924165648Spiso			if (t->paddr.s_addr)
3925165648Spiso				printf("%s:", inet_ntoa(t->paddr));
3926165648Spiso			printf("%u", t->pport);
3927165648Spiso			if (!t->spool_cnt && t->pport_cnt > 1)
3928165648Spiso				printf("-%u", t->pport + t->pport_cnt - 1);
3929165648Spiso
3930165648Spiso			if (t->raddr.s_addr) {
3931165648Spiso				printf(" %s", inet_ntoa(t->raddr));
3932165648Spiso				if (t->rport) {
3933165648Spiso					printf(":%u", t->rport);
3934165648Spiso					if (!t->spool_cnt && t->rport_cnt > 1)
3935165648Spiso						printf("-%u", t->rport +
3936165648Spiso						    t->rport_cnt - 1);
3937165648Spiso				}
3938165648Spiso			}
3939165648Spiso			break;
3940165648Spiso		case REDIR_PROTO:
3941165648Spiso			p = getprotobynumber(t->proto);
3942165648Spiso			printf(" redirect_proto %s %s", p->p_name,
3943165648Spiso			    inet_ntoa(t->laddr));
3944165648Spiso			if (t->paddr.s_addr != 0) {
3945165648Spiso				printf(" %s", inet_ntoa(t->paddr));
3946165648Spiso				if (t->raddr.s_addr)
3947165648Spiso					printf(" %s", inet_ntoa(t->raddr));
3948165648Spiso			}
3949165648Spiso			break;
3950165648Spiso		default:
3951165648Spiso			errx(EX_DATAERR, "unknown redir mode");
3952165648Spiso			break;
3953165648Spiso		}
3954165648Spiso	}
3955165648Spiso	printf("\n");
3956165648Spiso}
3957165648Spiso
3958165648Spisostatic void
3959165648Spisoconfig_nat(int ac, char **av)
3960165648Spiso{
3961165648Spiso	struct cfg_nat *n;              /* Nat instance configuration. */
3962165648Spiso	struct in_addr ip;
3963165648Spiso	int i, len, off, tok;
3964165648Spiso	char *id, buf[NAT_BUF_LEN]; 	/* Buffer for serialized data. */
3965165648Spiso
3966165648Spiso	len = NAT_BUF_LEN;
3967165648Spiso	/* Offset in buf: save space for n at the beginning. */
3968165648Spiso	off = sizeof(*n);
3969165648Spiso	memset(buf, 0, sizeof(buf));
3970165648Spiso	n = (struct cfg_nat *)buf;
3971165648Spiso
3972165648Spiso	av++; ac--;
3973165648Spiso	/* Nat id. */
3974165648Spiso	if (ac && isdigit(**av)) {
3975165648Spiso		id = *av;
3976165648Spiso		i = atoi(*av);
3977165648Spiso		ac--; av++;
3978165648Spiso		n->id = i;
3979165648Spiso	} else
3980165648Spiso		errx(EX_DATAERR, "missing nat id");
3981165648Spiso	if (ac == 0)
3982165648Spiso		errx(EX_DATAERR, "missing option");
3983165648Spiso
3984165648Spiso	while (ac > 0) {
3985165648Spiso		tok = match_token(nat_params, *av);
3986165648Spiso		ac--; av++;
3987165648Spiso		switch (tok) {
3988165648Spiso		case TOK_IP:
3989165648Spiso			if (ac == 0)
3990165648Spiso				errx(EX_DATAERR, "missing option");
3991165648Spiso			if (!inet_aton(av[0], &(n->ip)))
3992165648Spiso				errx(EX_DATAERR, "bad ip address ``%s''",
3993165648Spiso				    av[0]);
3994165648Spiso			ac--; av++;
3995165648Spiso			break;
3996165648Spiso		case TOK_IF:
3997165648Spiso			set_addr_dynamic(av[0], n);
3998165648Spiso			ac--; av++;
3999165648Spiso			break;
4000165648Spiso		case TOK_ALOG:
4001165648Spiso			n->mode |= PKT_ALIAS_LOG;
4002165648Spiso			break;
4003165648Spiso		case TOK_DENY_INC:
4004165648Spiso			n->mode |= PKT_ALIAS_DENY_INCOMING;
4005165648Spiso			break;
4006165648Spiso		case TOK_SAME_PORTS:
4007165648Spiso			n->mode |= PKT_ALIAS_SAME_PORTS;
4008165648Spiso			break;
4009165648Spiso		case TOK_UNREG_ONLY:
4010165648Spiso			n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
4011165648Spiso			break;
4012165648Spiso		case TOK_RESET_ADDR:
4013165648Spiso			n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
4014165648Spiso			break;
4015165648Spiso		case TOK_ALIAS_REV:
4016165648Spiso			n->mode |= PKT_ALIAS_REVERSE;
4017165648Spiso			break;
4018165648Spiso		case TOK_PROXY_ONLY:
4019165648Spiso			n->mode |= PKT_ALIAS_PROXY_ONLY;
4020165648Spiso			break;
4021165648Spiso			/*
4022165648Spiso			 * All the setup_redir_* functions work directly in the final
4023165648Spiso			 * buffer, see above for details.
4024165648Spiso			 */
4025165648Spiso		case TOK_REDIR_ADDR:
4026165648Spiso		case TOK_REDIR_PORT:
4027165648Spiso		case TOK_REDIR_PROTO:
4028165648Spiso			switch (tok) {
4029165648Spiso			case TOK_REDIR_ADDR:
4030165648Spiso				i = setup_redir_addr(&buf[off], len, &ac, &av);
4031165648Spiso				break;
4032165648Spiso			case TOK_REDIR_PORT:
4033165648Spiso				i = setup_redir_port(&buf[off], len, &ac, &av);
4034165648Spiso				break;
4035165648Spiso			case TOK_REDIR_PROTO:
4036165648Spiso				i = setup_redir_proto(&buf[off], len, &ac, &av);
4037165648Spiso				break;
4038165648Spiso			}
4039165648Spiso			n->redir_cnt++;
4040165648Spiso			off += i;
4041165648Spiso			len -= i;
4042165648Spiso			break;
4043165648Spiso		default:
4044165648Spiso			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
4045165648Spiso		}
4046165648Spiso	}
4047165648Spiso
4048165648Spiso	i = do_cmd(IP_FW_NAT_CFG, buf, off);
4049165648Spiso	if (i)
4050165648Spiso		err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
4051165648Spiso
4052165648Spiso	/* After every modification, we show the resultant rule. */
4053165648Spiso	int _ac = 3;
4054165648Spiso	char *_av[] = {"show", "config", id};
4055165648Spiso	show_nat(_ac, _av);
4056165648Spiso}
4057165648Spiso
4058165648Spisostatic void
405998943Sluigiconfig_pipe(int ac, char **av)
406098943Sluigi{
4061117469Sluigi	struct dn_pipe p;
406298943Sluigi	int i;
406398943Sluigi	char *end;
406498943Sluigi	void *par = NULL;
406598943Sluigi
4066117469Sluigi	memset(&p, 0, sizeof p);
406798943Sluigi
406898943Sluigi	av++; ac--;
406998943Sluigi	/* Pipe number */
407098943Sluigi	if (ac && isdigit(**av)) {
407198943Sluigi		i = atoi(*av); av++; ac--;
407298943Sluigi		if (do_pipe == 1)
4073117469Sluigi			p.pipe_nr = i;
407498943Sluigi		else
4075117469Sluigi			p.fs.fs_nr = i;
407698943Sluigi	}
407799475Sluigi	while (ac > 0) {
407898943Sluigi		double d;
407998943Sluigi		int tok = match_token(dummynet_params, *av);
408098943Sluigi		ac--; av++;
408198943Sluigi
408298943Sluigi		switch(tok) {
4083101978Sluigi		case TOK_NOERROR:
4084117469Sluigi			p.fs.flags_fs |= DN_NOERROR;
4085101978Sluigi			break;
4086101978Sluigi
408798943Sluigi		case TOK_PLR:
408898943Sluigi			NEED1("plr needs argument 0..1\n");
408998943Sluigi			d = strtod(av[0], NULL);
409098943Sluigi			if (d > 1)
409198943Sluigi				d = 1;
409298943Sluigi			else if (d < 0)
409398943Sluigi				d = 0;
4094117469Sluigi			p.fs.plr = (int)(d*0x7fffffff);
409598943Sluigi			ac--; av++;
409698943Sluigi			break;
409798943Sluigi
409898943Sluigi		case TOK_QUEUE:
409998943Sluigi			NEED1("queue needs queue size\n");
410098943Sluigi			end = NULL;
4101117469Sluigi			p.fs.qsize = strtoul(av[0], &end, 0);
410298943Sluigi			if (*end == 'K' || *end == 'k') {
4103117469Sluigi				p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
4104117469Sluigi				p.fs.qsize *= 1024;
4105140271Sbrooks			} else if (*end == 'B' ||
4106140271Sbrooks			    _substrcmp2(end, "by", "bytes") == 0) {
4107117469Sluigi				p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
410898943Sluigi			}
410998943Sluigi			ac--; av++;
411098943Sluigi			break;
411198943Sluigi
411298943Sluigi		case TOK_BUCKETS:
411398943Sluigi			NEED1("buckets needs argument\n");
4114117469Sluigi			p.fs.rq_size = strtoul(av[0], NULL, 0);
411598943Sluigi			ac--; av++;
411698943Sluigi			break;
411798943Sluigi
411898943Sluigi		case TOK_MASK:
411998943Sluigi			NEED1("mask needs mask specifier\n");
412098943Sluigi			/*
412198943Sluigi			 * per-flow queue, mask is dst_ip, dst_port,
412298943Sluigi			 * src_ip, src_port, proto measured in bits
412398943Sluigi			 */
412498943Sluigi			par = NULL;
412598943Sluigi
4126145246Sbrooks			bzero(&p.fs.flow_mask, sizeof(p.fs.flow_mask));
412798943Sluigi			end = NULL;
412898943Sluigi
412998943Sluigi			while (ac >= 1) {
4130117328Sluigi			    uint32_t *p32 = NULL;
4131117328Sluigi			    uint16_t *p16 = NULL;
4132145246Sbrooks			    uint32_t *p20 = NULL;
4133145246Sbrooks			    struct in6_addr *pa6 = NULL;
4134145246Sbrooks			    uint32_t a;
413598943Sluigi
413698943Sluigi			    tok = match_token(dummynet_params, *av);
413798943Sluigi			    ac--; av++;
413898943Sluigi			    switch(tok) {
413998943Sluigi			    case TOK_ALL:
414098943Sluigi				    /*
414198943Sluigi				     * special case, all bits significant
414298943Sluigi				     */
4143117469Sluigi				    p.fs.flow_mask.dst_ip = ~0;
4144117469Sluigi				    p.fs.flow_mask.src_ip = ~0;
4145117469Sluigi				    p.fs.flow_mask.dst_port = ~0;
4146117469Sluigi				    p.fs.flow_mask.src_port = ~0;
4147117469Sluigi				    p.fs.flow_mask.proto = ~0;
4148145246Sbrooks				    n2mask(&(p.fs.flow_mask.dst_ip6), 128);
4149145246Sbrooks				    n2mask(&(p.fs.flow_mask.src_ip6), 128);
4150145246Sbrooks				    p.fs.flow_mask.flow_id6 = ~0;
4151117469Sluigi				    p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
415298943Sluigi				    goto end_mask;
415398943Sluigi
415498943Sluigi			    case TOK_DSTIP:
4155117469Sluigi				    p32 = &p.fs.flow_mask.dst_ip;
415698943Sluigi				    break;
415798943Sluigi
415898943Sluigi			    case TOK_SRCIP:
4159117469Sluigi				    p32 = &p.fs.flow_mask.src_ip;
416098943Sluigi				    break;
416198943Sluigi
4162145246Sbrooks			    case TOK_DSTIP6:
4163145246Sbrooks				    pa6 = &(p.fs.flow_mask.dst_ip6);
4164145246Sbrooks				    break;
4165145246Sbrooks
4166145246Sbrooks			    case TOK_SRCIP6:
4167145246Sbrooks				    pa6 = &(p.fs.flow_mask.src_ip6);
4168145246Sbrooks				    break;
4169145246Sbrooks
4170145246Sbrooks			    case TOK_FLOWID:
4171145246Sbrooks				    p20 = &p.fs.flow_mask.flow_id6;
4172145246Sbrooks				    break;
4173145246Sbrooks
417498943Sluigi			    case TOK_DSTPORT:
4175117469Sluigi				    p16 = &p.fs.flow_mask.dst_port;
417698943Sluigi				    break;
417798943Sluigi
417898943Sluigi			    case TOK_SRCPORT:
4179117469Sluigi				    p16 = &p.fs.flow_mask.src_port;
418098943Sluigi				    break;
418198943Sluigi
418298943Sluigi			    case TOK_PROTO:
418398943Sluigi				    break;
418498943Sluigi
418598943Sluigi			    default:
418698943Sluigi				    ac++; av--; /* backtrack */
418798943Sluigi				    goto end_mask;
418898943Sluigi			    }
418998943Sluigi			    if (ac < 1)
419098943Sluigi				    errx(EX_USAGE, "mask: value missing");
419198943Sluigi			    if (*av[0] == '/') {
419298943Sluigi				    a = strtoul(av[0]+1, &end, 0);
4193145246Sbrooks				    if (pa6 == NULL)
4194145246Sbrooks					    a = (a == 32) ? ~0 : (1 << a) - 1;
4195106505Smaxim			    } else
419699909Sluigi				    a = strtoul(av[0], &end, 0);
419798943Sluigi			    if (p32 != NULL)
419898943Sluigi				    *p32 = a;
419998943Sluigi			    else if (p16 != NULL) {
4200139821Sbrooks				    if (a > 0xFFFF)
420198943Sluigi					    errx(EX_DATAERR,
4202144687Sbrooks						"port mask must be 16 bit");
4203117328Sluigi				    *p16 = (uint16_t)a;
4204145246Sbrooks			    } else if (p20 != NULL) {
4205145246Sbrooks				    if (a > 0xfffff)
4206145246Sbrooks					errx(EX_DATAERR,
4207145246Sbrooks					    "flow_id mask must be 20 bit");
4208145246Sbrooks				    *p20 = (uint32_t)a;
4209145246Sbrooks			    } else if (pa6 != NULL) {
4210145246Sbrooks				    if (a < 0 || a > 128)
4211145246Sbrooks					errx(EX_DATAERR,
4212145246Sbrooks					    "in6addr invalid mask len");
4213145246Sbrooks				    else
4214145246Sbrooks					n2mask(pa6, a);
421598943Sluigi			    } else {
4216139821Sbrooks				    if (a > 0xFF)
421798943Sluigi					    errx(EX_DATAERR,
4218144687Sbrooks						"proto mask must be 8 bit");
4219117469Sluigi				    p.fs.flow_mask.proto = (uint8_t)a;
422098943Sluigi			    }
422198943Sluigi			    if (a != 0)
4222117469Sluigi				    p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
422398943Sluigi			    ac--; av++;
422498943Sluigi			} /* end while, config masks */
422598943Sluigiend_mask:
422698943Sluigi			break;
422798943Sluigi
422898943Sluigi		case TOK_RED:
422998943Sluigi		case TOK_GRED:
423098943Sluigi			NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
4231117469Sluigi			p.fs.flags_fs |= DN_IS_RED;
423298943Sluigi			if (tok == TOK_GRED)
4233117469Sluigi				p.fs.flags_fs |= DN_IS_GENTLE_RED;
423498943Sluigi			/*
423598943Sluigi			 * the format for parameters is w_q/min_th/max_th/max_p
423698943Sluigi			 */
423798943Sluigi			if ((end = strsep(&av[0], "/"))) {
423898943Sluigi			    double w_q = strtod(end, NULL);
423998943Sluigi			    if (w_q > 1 || w_q <= 0)
424098943Sluigi				errx(EX_DATAERR, "0 < w_q <= 1");
4241117469Sluigi			    p.fs.w_q = (int) (w_q * (1 << SCALE_RED));
424298943Sluigi			}
424398943Sluigi			if ((end = strsep(&av[0], "/"))) {
4244117469Sluigi			    p.fs.min_th = strtoul(end, &end, 0);
424598943Sluigi			    if (*end == 'K' || *end == 'k')
4246117469Sluigi				p.fs.min_th *= 1024;
424798943Sluigi			}
424898943Sluigi			if ((end = strsep(&av[0], "/"))) {
4249117469Sluigi			    p.fs.max_th = strtoul(end, &end, 0);
425098943Sluigi			    if (*end == 'K' || *end == 'k')
4251117469Sluigi				p.fs.max_th *= 1024;
425298943Sluigi			}
425398943Sluigi			if ((end = strsep(&av[0], "/"))) {
425498943Sluigi			    double max_p = strtod(end, NULL);
425598943Sluigi			    if (max_p > 1 || max_p <= 0)
425698943Sluigi				errx(EX_DATAERR, "0 < max_p <= 1");
4257117469Sluigi			    p.fs.max_p = (int)(max_p * (1 << SCALE_RED));
425898943Sluigi			}
425998943Sluigi			ac--; av++;
426098943Sluigi			break;
426198943Sluigi
426298943Sluigi		case TOK_DROPTAIL:
4263117469Sluigi			p.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
426498943Sluigi			break;
4265106505Smaxim
426698943Sluigi		case TOK_BW:
426798943Sluigi			NEED1("bw needs bandwidth or interface\n");
426898943Sluigi			if (do_pipe != 1)
426998943Sluigi			    errx(EX_DATAERR, "bandwidth only valid for pipes");
427098943Sluigi			/*
427198943Sluigi			 * set clocking interface or bandwidth value
427298943Sluigi			 */
427398943Sluigi			if (av[0][0] >= 'a' && av[0][0] <= 'z') {
4274117469Sluigi			    int l = sizeof(p.if_name)-1;
427598943Sluigi			    /* interface name */
4276117469Sluigi			    strncpy(p.if_name, av[0], l);
4277117469Sluigi			    p.if_name[l] = '\0';
4278117469Sluigi			    p.bandwidth = 0;
427998943Sluigi			} else {
4280117469Sluigi			    p.if_name[0] = '\0';
4281117469Sluigi			    p.bandwidth = strtoul(av[0], &end, 0);
428298943Sluigi			    if (*end == 'K' || *end == 'k') {
428398943Sluigi				end++;
4284117469Sluigi				p.bandwidth *= 1000;
428598943Sluigi			    } else if (*end == 'M') {
428698943Sluigi				end++;
4287117469Sluigi				p.bandwidth *= 1000000;
428898943Sluigi			    }
4289161550Sdwmalone			    if ((*end == 'B' &&
4290161550Sdwmalone				  _substrcmp2(end, "Bi", "Bit/s") != 0) ||
4291140271Sbrooks			        _substrcmp2(end, "by", "bytes") == 0)
4292117469Sluigi				p.bandwidth *= 8;
4293117469Sluigi			    if (p.bandwidth < 0)
429498943Sluigi				errx(EX_DATAERR, "bandwidth too large");
429598943Sluigi			}
429698943Sluigi			ac--; av++;
429798943Sluigi			break;
429898943Sluigi
429998943Sluigi		case TOK_DELAY:
430098943Sluigi			if (do_pipe != 1)
430198943Sluigi				errx(EX_DATAERR, "delay only valid for pipes");
430298943Sluigi			NEED1("delay needs argument 0..10000ms\n");
4303117469Sluigi			p.delay = strtoul(av[0], NULL, 0);
430498943Sluigi			ac--; av++;
430598943Sluigi			break;
430698943Sluigi
430798943Sluigi		case TOK_WEIGHT:
430898943Sluigi			if (do_pipe == 1)
430998943Sluigi				errx(EX_DATAERR,"weight only valid for queues");
431098943Sluigi			NEED1("weight needs argument 0..100\n");
4311117469Sluigi			p.fs.weight = strtoul(av[0], &end, 0);
431298943Sluigi			ac--; av++;
431398943Sluigi			break;
431498943Sluigi
431598943Sluigi		case TOK_PIPE:
431698943Sluigi			if (do_pipe == 1)
431798943Sluigi				errx(EX_DATAERR,"pipe only valid for queues");
431898943Sluigi			NEED1("pipe needs pipe_number\n");
4319117469Sluigi			p.fs.parent_nr = strtoul(av[0], &end, 0);
432098943Sluigi			ac--; av++;
432198943Sluigi			break;
432298943Sluigi
432398943Sluigi		default:
4324124924Smaxim			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
432598943Sluigi		}
432698943Sluigi	}
432798943Sluigi	if (do_pipe == 1) {
4328117469Sluigi		if (p.pipe_nr == 0)
432998943Sluigi			errx(EX_DATAERR, "pipe_nr must be > 0");
4330117469Sluigi		if (p.delay > 10000)
433198943Sluigi			errx(EX_DATAERR, "delay must be < 10000");
433298943Sluigi	} else { /* do_pipe == 2, queue */
4333117469Sluigi		if (p.fs.parent_nr == 0)
433498943Sluigi			errx(EX_DATAERR, "pipe must be > 0");
4335117469Sluigi		if (p.fs.weight >100)
433698943Sluigi			errx(EX_DATAERR, "weight must be <= 100");
433798943Sluigi	}
4338117469Sluigi	if (p.fs.flags_fs & DN_QSIZE_IS_BYTES) {
4339117469Sluigi		if (p.fs.qsize > 1024*1024)
434098943Sluigi			errx(EX_DATAERR, "queue size must be < 1MB");
434198943Sluigi	} else {
4342117469Sluigi		if (p.fs.qsize > 100)
434398943Sluigi			errx(EX_DATAERR, "2 <= queue size <= 100");
434498943Sluigi	}
4345117469Sluigi	if (p.fs.flags_fs & DN_IS_RED) {
434698943Sluigi		size_t len;
434798943Sluigi		int lookup_depth, avg_pkt_size;
434898943Sluigi		double s, idle, weight, w_q;
4349117469Sluigi		struct clockinfo ck;
435098943Sluigi		int t;
435198943Sluigi
4352117469Sluigi		if (p.fs.min_th >= p.fs.max_th)
435398943Sluigi		    errx(EX_DATAERR, "min_th %d must be < than max_th %d",
4354117469Sluigi			p.fs.min_th, p.fs.max_th);
4355117469Sluigi		if (p.fs.max_th == 0)
435698943Sluigi		    errx(EX_DATAERR, "max_th must be > 0");
435798943Sluigi
435898943Sluigi		len = sizeof(int);
435998943Sluigi		if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
436098943Sluigi			&lookup_depth, &len, NULL, 0) == -1)
436198943Sluigi
436298943Sluigi		    errx(1, "sysctlbyname(\"%s\")",
436398943Sluigi			"net.inet.ip.dummynet.red_lookup_depth");
436498943Sluigi		if (lookup_depth == 0)
436598943Sluigi		    errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth"
436698943Sluigi			" must be greater than zero");
436798943Sluigi
436898943Sluigi		len = sizeof(int);
436998943Sluigi		if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
437098943Sluigi			&avg_pkt_size, &len, NULL, 0) == -1)
437198943Sluigi
437298943Sluigi		    errx(1, "sysctlbyname(\"%s\")",
437398943Sluigi			"net.inet.ip.dummynet.red_avg_pkt_size");
437498943Sluigi		if (avg_pkt_size == 0)
437598943Sluigi			errx(EX_DATAERR,
437698943Sluigi			    "net.inet.ip.dummynet.red_avg_pkt_size must"
437798943Sluigi			    " be greater than zero");
437898943Sluigi
437998943Sluigi		len = sizeof(struct clockinfo);
4380117469Sluigi		if (sysctlbyname("kern.clockrate", &ck, &len, NULL, 0) == -1)
438198943Sluigi			errx(1, "sysctlbyname(\"%s\")", "kern.clockrate");
438298943Sluigi
438398943Sluigi		/*
438498943Sluigi		 * Ticks needed for sending a medium-sized packet.
438598943Sluigi		 * Unfortunately, when we are configuring a WF2Q+ queue, we
438698943Sluigi		 * do not have bandwidth information, because that is stored
438798943Sluigi		 * in the parent pipe, and also we have multiple queues
438898943Sluigi		 * competing for it. So we set s=0, which is not very
438998943Sluigi		 * correct. But on the other hand, why do we want RED with
439098943Sluigi		 * WF2Q+ ?
439198943Sluigi		 */
4392117469Sluigi		if (p.bandwidth==0) /* this is a WF2Q+ queue */
439398943Sluigi			s = 0;
439498943Sluigi		else
4395117469Sluigi			s = ck.hz * avg_pkt_size * 8 / p.bandwidth;
439698943Sluigi
439798943Sluigi		/*
439898943Sluigi		 * max idle time (in ticks) before avg queue size becomes 0.
439998943Sluigi		 * NOTA:  (3/w_q) is approx the value x so that
440098943Sluigi		 * (1-w_q)^x < 10^-3.
440198943Sluigi		 */
4402117469Sluigi		w_q = ((double)p.fs.w_q) / (1 << SCALE_RED);
440398943Sluigi		idle = s * 3. / w_q;
4404117469Sluigi		p.fs.lookup_step = (int)idle / lookup_depth;
4405117469Sluigi		if (!p.fs.lookup_step)
4406117469Sluigi			p.fs.lookup_step = 1;
440798943Sluigi		weight = 1 - w_q;
4408117469Sluigi		for (t = p.fs.lookup_step; t > 0; --t)
440998943Sluigi			weight *= weight;
4410117469Sluigi		p.fs.lookup_weight = (int)(weight * (1 << SCALE_RED));
441198943Sluigi	}
4412117469Sluigi	i = do_cmd(IP_DUMMYNET_CONFIGURE, &p, sizeof p);
441398943Sluigi	if (i)
441498943Sluigi		err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
441598943Sluigi}
441698943Sluigi
441798943Sluigistatic void
4418169424Smaximget_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask)
441998943Sluigi{
442098943Sluigi	int i, l;
4421169424Smaxim	char *ap, *ptr, *optr;
4422169424Smaxim	struct ether_addr *mac;
4423169424Smaxim	const char *macset = "0123456789abcdefABCDEF:";
442498943Sluigi
4425169424Smaxim	if (strcmp(p, "any") == 0) {
4426169424Smaxim		for (i = 0; i < ETHER_ADDR_LEN; i++)
4427169424Smaxim			addr[i] = mask[i] = 0;
442898943Sluigi		return;
4429169424Smaxim	}
443098943Sluigi
4431169424Smaxim	optr = ptr = strdup(p);
4432169424Smaxim	if ((ap = strsep(&ptr, "&/")) != NULL && *ap != 0) {
4433169424Smaxim		l = strlen(ap);
4434169424Smaxim		if (strspn(ap, macset) != l || (mac = ether_aton(ap)) == NULL)
4435169424Smaxim			errx(EX_DATAERR, "Incorrect MAC address");
4436169424Smaxim		bcopy(mac, addr, ETHER_ADDR_LEN);
4437169424Smaxim	} else
4438169424Smaxim		errx(EX_DATAERR, "Incorrect MAC address");
4439169424Smaxim
4440169424Smaxim	if (ptr != NULL) { /* we have mask? */
4441169424Smaxim		if (p[ptr - optr - 1] == '/') { /* mask len */
4442169424Smaxim			l = strtol(ptr, &ap, 10);
4443169424Smaxim			if (*ap != 0 || l > ETHER_ADDR_LEN * 8 || l < 0)
4444169424Smaxim				errx(EX_DATAERR, "Incorrect mask length");
4445169424Smaxim			for (i = 0; l > 0 && i < ETHER_ADDR_LEN; l -= 8, i++)
4446169424Smaxim				mask[i] = (l >= 8) ? 0xff: (~0) << (8 - l);
4447169424Smaxim		} else { /* mask */
4448169424Smaxim			l = strlen(ptr);
4449169424Smaxim			if (strspn(ptr, macset) != l ||
4450169424Smaxim			    (mac = ether_aton(ptr)) == NULL)
4451169424Smaxim				errx(EX_DATAERR, "Incorrect mask");
4452169424Smaxim			bcopy(mac, mask, ETHER_ADDR_LEN);
445398943Sluigi		}
4454169424Smaxim	} else { /* default mask: ff:ff:ff:ff:ff:ff */
4455169424Smaxim		for (i = 0; i < ETHER_ADDR_LEN; i++)
445698943Sluigi			mask[i] = 0xff;
445798943Sluigi	}
4458169424Smaxim	for (i = 0; i < ETHER_ADDR_LEN; i++)
445998943Sluigi		addr[i] &= mask[i];
4460169424Smaxim
4461169424Smaxim	free(optr);
446298943Sluigi}
446398943Sluigi
446498943Sluigi/*
446598943Sluigi * helper function, updates the pointer to cmd with the length
446698943Sluigi * of the current command, and also cleans up the first word of
446798943Sluigi * the new command in case it has been clobbered before.
446898943Sluigi */
446998943Sluigistatic ipfw_insn *
447098943Sluiginext_cmd(ipfw_insn *cmd)
447198943Sluigi{
447298943Sluigi	cmd += F_LEN(cmd);
447398943Sluigi	bzero(cmd, sizeof(*cmd));
447498943Sluigi	return cmd;
447598943Sluigi}
447698943Sluigi
447798943Sluigi/*
4478117469Sluigi * Takes arguments and copies them into a comment
4479117469Sluigi */
4480117469Sluigistatic void
4481117469Sluigifill_comment(ipfw_insn *cmd, int ac, char **av)
4482117469Sluigi{
4483117469Sluigi	int i, l;
4484117469Sluigi	char *p = (char *)(cmd + 1);
4485117577Sluigi
4486117469Sluigi	cmd->opcode = O_NOP;
4487117469Sluigi	cmd->len =  (cmd->len & (F_NOT | F_OR));
4488117469Sluigi
4489117469Sluigi	/* Compute length of comment string. */
4490117469Sluigi	for (i = 0, l = 0; i < ac; i++)
4491117469Sluigi		l += strlen(av[i]) + 1;
4492117469Sluigi	if (l == 0)
4493117469Sluigi		return;
4494117469Sluigi	if (l > 84)
4495117469Sluigi		errx(EX_DATAERR,
4496117469Sluigi		    "comment too long (max 80 chars)");
4497117469Sluigi	l = 1 + (l+3)/4;
4498117469Sluigi	cmd->len =  (cmd->len & (F_NOT | F_OR)) | l;
4499117469Sluigi	for (i = 0; i < ac; i++) {
4500117469Sluigi		strcpy(p, av[i]);
4501117469Sluigi		p += strlen(av[i]);
4502117469Sluigi		*p++ = ' ';
4503117469Sluigi	}
4504117469Sluigi	*(--p) = '\0';
4505117469Sluigi}
4506117577Sluigi
4507117469Sluigi/*
450898943Sluigi * A function to fill simple commands of size 1.
450998943Sluigi * Existing flags are preserved.
451098943Sluigi */
451198943Sluigistatic void
4512117328Sluigifill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg)
451398943Sluigi{
451498943Sluigi	cmd->opcode = opcode;
451598943Sluigi	cmd->len =  ((cmd->len | flags) & (F_NOT | F_OR)) | 1;
451698943Sluigi	cmd->arg1 = arg;
451798943Sluigi}
451898943Sluigi
451998943Sluigi/*
452098943Sluigi * Fetch and add the MAC address and type, with masks. This generates one or
452198943Sluigi * two microinstructions, and returns the pointer to the last one.
452298943Sluigi */
452398943Sluigistatic ipfw_insn *
452498943Sluigiadd_mac(ipfw_insn *cmd, int ac, char *av[])
452598943Sluigi{
4526102087Sluigi	ipfw_insn_mac *mac;
452798943Sluigi
4528102087Sluigi	if (ac < 2)
4529102098Sluigi		errx(EX_DATAERR, "MAC dst src");
453098943Sluigi
453198943Sluigi	cmd->opcode = O_MACADDR2;
453298943Sluigi	cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
453398943Sluigi
453498943Sluigi	mac = (ipfw_insn_mac *)cmd;
4535101978Sluigi	get_mac_addr_mask(av[0], mac->addr, mac->mask);	/* dst */
4536169424Smaxim	get_mac_addr_mask(av[1], &(mac->addr[ETHER_ADDR_LEN]),
4537169424Smaxim	    &(mac->mask[ETHER_ADDR_LEN])); /* src */
4538102087Sluigi	return cmd;
4539102087Sluigi}
454098943Sluigi
4541102087Sluigistatic ipfw_insn *
4542102087Sluigiadd_mactype(ipfw_insn *cmd, int ac, char *av)
4543102087Sluigi{
4544102087Sluigi	if (ac < 1)
4545102087Sluigi		errx(EX_DATAERR, "missing MAC type");
4546102087Sluigi	if (strcmp(av, "any") != 0) { /* we have a non-null type */
4547102087Sluigi		fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE);
454898943Sluigi		cmd->opcode = O_MAC_TYPE;
4549102087Sluigi		return cmd;
4550102087Sluigi	} else
4551102087Sluigi		return NULL;
4552102087Sluigi}
455398943Sluigi
4554102087Sluigistatic ipfw_insn *
4555152923Sumeadd_proto0(ipfw_insn *cmd, char *av, u_char *protop)
4556102087Sluigi{
4557102087Sluigi	struct protoent *pe;
4558152923Sume	char *ep;
4559152923Sume	int proto;
4560102087Sluigi
4561156315Sume	proto = strtol(av, &ep, 10);
4562156315Sume	if (*ep != '\0' || proto <= 0) {
4563152923Sume		if ((pe = getprotobyname(av)) == NULL)
4564152923Sume			return NULL;
4565152923Sume		proto = pe->p_proto;
4566152923Sume	}
4567145246Sbrooks
4568152923Sume	fill_cmd(cmd, O_PROTO, 0, proto);
4569152923Sume	*protop = proto;
4570152923Sume	return cmd;
4571152923Sume}
4572152923Sume
4573152923Sumestatic ipfw_insn *
4574152923Sumeadd_proto(ipfw_insn *cmd, char *av, u_char *protop)
4575152923Sume{
4576152923Sume	u_char proto = IPPROTO_IP;
4577152923Sume
4578156315Sume	if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
4579146894Smlaier		; /* do not set O_IP4 nor O_IP6 */
4580152923Sume	else if (strcmp(av, "ip4") == 0)
4581152923Sume		/* explicit "just IPv4" rule */
4582152923Sume		fill_cmd(cmd, O_IP4, 0, 0);
4583152923Sume	else if (strcmp(av, "ip6") == 0) {
4584152923Sume		/* explicit "just IPv6" rule */
4585152923Sume		proto = IPPROTO_IPV6;
4586152923Sume		fill_cmd(cmd, O_IP6, 0, 0);
4587152923Sume	} else
4588152923Sume		return add_proto0(cmd, av, protop);
4589152923Sume
4590152923Sume	*protop = proto;
4591152923Sume	return cmd;
4592152923Sume}
4593152923Sume
4594152923Sumestatic ipfw_insn *
4595152923Sumeadd_proto_compat(ipfw_insn *cmd, char *av, u_char *protop)
4596152923Sume{
4597152923Sume	u_char proto = IPPROTO_IP;
4598152923Sume
4599152923Sume	if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
4600152923Sume		; /* do not set O_IP4 nor O_IP6 */
4601146894Smlaier	else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0)
4602146894Smlaier		/* explicit "just IPv4" rule */
4603146894Smlaier		fill_cmd(cmd, O_IP4, 0, 0);
4604146894Smlaier	else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) {
4605146894Smlaier		/* explicit "just IPv6" rule */
4606152923Sume		proto = IPPROTO_IPV6;
4607146894Smlaier		fill_cmd(cmd, O_IP6, 0, 0);
4608152923Sume	} else
4609152923Sume		return add_proto0(cmd, av, protop);
4610145246Sbrooks
4611152923Sume	*protop = proto;
461298943Sluigi	return cmd;
461398943Sluigi}
461498943Sluigi
4615102087Sluigistatic ipfw_insn *
4616102087Sluigiadd_srcip(ipfw_insn *cmd, char *av)
4617102087Sluigi{
4618102087Sluigi	fill_ip((ipfw_insn_ip *)cmd, av);
4619102087Sluigi	if (cmd->opcode == O_IP_DST_SET)			/* set */
4620102087Sluigi		cmd->opcode = O_IP_SRC_SET;
4621130281Sru	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
4622130281Sru		cmd->opcode = O_IP_SRC_LOOKUP;
4623102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
4624102087Sluigi		cmd->opcode = O_IP_SRC_ME;
4625102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
4626102087Sluigi		cmd->opcode = O_IP_SRC;
4627117328Sluigi	else							/* addr/mask */
4628102087Sluigi		cmd->opcode = O_IP_SRC_MASK;
4629102087Sluigi	return cmd;
4630102087Sluigi}
4631102087Sluigi
4632102087Sluigistatic ipfw_insn *
4633102087Sluigiadd_dstip(ipfw_insn *cmd, char *av)
4634102087Sluigi{
4635102087Sluigi	fill_ip((ipfw_insn_ip *)cmd, av);
4636102087Sluigi	if (cmd->opcode == O_IP_DST_SET)			/* set */
4637102087Sluigi		;
4638130281Sru	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
4639130281Sru		;
4640102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
4641102087Sluigi		cmd->opcode = O_IP_DST_ME;
4642102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
4643102087Sluigi		cmd->opcode = O_IP_DST;
4644117328Sluigi	else							/* addr/mask */
4645102087Sluigi		cmd->opcode = O_IP_DST_MASK;
4646102087Sluigi	return cmd;
4647102087Sluigi}
4648102087Sluigi
4649102087Sluigistatic ipfw_insn *
4650102087Sluigiadd_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
4651102087Sluigi{
4652140271Sbrooks	if (_substrcmp(av, "any") == 0) {
4653102087Sluigi		return NULL;
4654102087Sluigi	} else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) {
4655102087Sluigi		/* XXX todo: check that we have a protocol with ports */
4656102087Sluigi		cmd->opcode = opcode;
4657102087Sluigi		return cmd;
4658102087Sluigi	}
4659102087Sluigi	return NULL;
4660102087Sluigi}
4661102087Sluigi
4662145246Sbrooksstatic ipfw_insn *
4663145246Sbrooksadd_src(ipfw_insn *cmd, char *av, u_char proto)
4664145246Sbrooks{
4665145246Sbrooks	struct in6_addr a;
4666158553Smlaier	char *host, *ch;
4667158553Smlaier	ipfw_insn *ret = NULL;
4668145246Sbrooks
4669158553Smlaier	if ((host = strdup(av)) == NULL)
4670158553Smlaier		return NULL;
4671158553Smlaier	if ((ch = strrchr(host, '/')) != NULL)
4672158553Smlaier		*ch = '\0';
4673158553Smlaier
4674145246Sbrooks	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
4675158553Smlaier	    inet_pton(AF_INET6, host, &a))
4676158553Smlaier		ret = add_srcip6(cmd, av);
4677145246Sbrooks	/* XXX: should check for IPv4, not !IPv6 */
4678161483Sdwmalone	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
4679161483Sdwmalone	    !inet_pton(AF_INET6, host, &a)))
4680158553Smlaier		ret = add_srcip(cmd, av);
4681161483Sdwmalone	if (ret == NULL && strcmp(av, "any") != 0)
4682158553Smlaier		ret = cmd;
4683145246Sbrooks
4684158553Smlaier	free(host);
4685158553Smlaier	return ret;
4686145246Sbrooks}
4687145246Sbrooks
4688145246Sbrooksstatic ipfw_insn *
4689145246Sbrooksadd_dst(ipfw_insn *cmd, char *av, u_char proto)
4690145246Sbrooks{
4691145246Sbrooks	struct in6_addr a;
4692158553Smlaier	char *host, *ch;
4693158553Smlaier	ipfw_insn *ret = NULL;
4694145246Sbrooks
4695158553Smlaier	if ((host = strdup(av)) == NULL)
4696158553Smlaier		return NULL;
4697158553Smlaier	if ((ch = strrchr(host, '/')) != NULL)
4698158553Smlaier		*ch = '\0';
4699158553Smlaier
4700145246Sbrooks	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
4701158553Smlaier	    inet_pton(AF_INET6, host, &a))
4702158553Smlaier		ret = add_dstip6(cmd, av);
4703145246Sbrooks	/* XXX: should check for IPv4, not !IPv6 */
4704161483Sdwmalone	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
4705161483Sdwmalone	    !inet_pton(AF_INET6, host, &a)))
4706158553Smlaier		ret = add_dstip(cmd, av);
4707161483Sdwmalone	if (ret == NULL && strcmp(av, "any") != 0)
4708158553Smlaier		ret = cmd;
4709145246Sbrooks
4710158553Smlaier	free(host);
4711158553Smlaier	return ret;
4712145246Sbrooks}
4713145246Sbrooks
471498943Sluigi/*
471598943Sluigi * Parse arguments and assemble the microinstructions which make up a rule.
471698943Sluigi * Rules are added into the 'rulebuf' and then copied in the correct order
471798943Sluigi * into the actual rule.
471898943Sluigi *
4719136071Sgreen * The syntax for a rule starts with the action, followed by
4720136071Sgreen * optional action parameters, and the various match patterns.
4721108533Sschweikh * In the assembled microcode, the first opcode must be an O_PROBE_STATE
472298943Sluigi * (generated if the rule includes a keep-state option), then the
4723136071Sgreen * various match patterns, log/altq actions, and the actual action.
4724106505Smaxim *
472598943Sluigi */
472698943Sluigistatic void
472798943Sluigiadd(int ac, char *av[])
472898943Sluigi{
472998943Sluigi	/*
473098943Sluigi	 * rules are added into the 'rulebuf' and then copied in
473198943Sluigi	 * the correct order into the actual rule.
473298943Sluigi	 * Some things that need to go out of order (prob, action etc.)
473398943Sluigi	 * go into actbuf[].
473498943Sluigi	 */
4735117328Sluigi	static uint32_t rulebuf[255], actbuf[255], cmdbuf[255];
473698943Sluigi
4737117469Sluigi	ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
4738102087Sluigi	ipfw_insn *first_cmd;	/* first match pattern */
473998943Sluigi
474098943Sluigi	struct ip_fw *rule;
474198943Sluigi
474298943Sluigi	/*
474398943Sluigi	 * various flags used to record that we entered some fields.
474498943Sluigi	 */
4745101116Sluigi	ipfw_insn *have_state = NULL;	/* check-state or keep-state */
4746158879Soleg	ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL;
4747134475Smaxim	size_t len;
474898943Sluigi
474998943Sluigi	int i;
475098943Sluigi
475198943Sluigi	int open_par = 0;	/* open parenthesis ( */
475298943Sluigi
475398943Sluigi	/* proto is here because it is used to fetch ports */
475498943Sluigi	u_char proto = IPPROTO_IP;	/* default protocol */
475598943Sluigi
4756107289Sluigi	double match_prob = 1; /* match probability, default is always match */
4757107289Sluigi
475898943Sluigi	bzero(actbuf, sizeof(actbuf));		/* actions go here */
475998943Sluigi	bzero(cmdbuf, sizeof(cmdbuf));
476098943Sluigi	bzero(rulebuf, sizeof(rulebuf));
476198943Sluigi
476298943Sluigi	rule = (struct ip_fw *)rulebuf;
476398943Sluigi	cmd = (ipfw_insn *)cmdbuf;
476498943Sluigi	action = (ipfw_insn *)actbuf;
476598943Sluigi
476698943Sluigi	av++; ac--;
476798943Sluigi
476898943Sluigi	/* [rule N]	-- Rule number optional */
476998943Sluigi	if (ac && isdigit(**av)) {
477098943Sluigi		rule->rulenum = atoi(*av);
477198943Sluigi		av++;
477298943Sluigi		ac--;
477398943Sluigi	}
477498943Sluigi
4775117655Sluigi	/* [set N]	-- set number (0..RESVD_SET), optional */
4776140271Sbrooks	if (ac > 1 && _substrcmp(*av, "set") == 0) {
4777101628Sluigi		int set = strtoul(av[1], NULL, 10);
4778117655Sluigi		if (set < 0 || set > RESVD_SET)
4779101628Sluigi			errx(EX_DATAERR, "illegal set %s", av[1]);
4780101628Sluigi		rule->set = set;
4781101628Sluigi		av += 2; ac -= 2;
4782101628Sluigi	}
4783101628Sluigi
478498943Sluigi	/* [prob D]	-- match probability, optional */
4785140271Sbrooks	if (ac > 1 && _substrcmp(*av, "prob") == 0) {
4786107289Sluigi		match_prob = strtod(av[1], NULL);
478798943Sluigi
4788107289Sluigi		if (match_prob <= 0 || match_prob > 1)
478998943Sluigi			errx(EX_DATAERR, "illegal match prob. %s", av[1]);
479098943Sluigi		av += 2; ac -= 2;
479198943Sluigi	}
479298943Sluigi
479398943Sluigi	/* action	-- mandatory */
479498943Sluigi	NEED1("missing action");
479598943Sluigi	i = match_token(rule_actions, *av);
479698943Sluigi	ac--; av++;
479798943Sluigi	action->len = 1;	/* default */
479898943Sluigi	switch(i) {
479998943Sluigi	case TOK_CHECKSTATE:
4800101116Sluigi		have_state = action;
480198943Sluigi		action->opcode = O_CHECK_STATE;
480298943Sluigi		break;
480398943Sluigi
480498943Sluigi	case TOK_ACCEPT:
480598943Sluigi		action->opcode = O_ACCEPT;
480698943Sluigi		break;
480798943Sluigi
480898943Sluigi	case TOK_DENY:
480998943Sluigi		action->opcode = O_DENY;
481099475Sluigi		action->arg1 = 0;
481198943Sluigi		break;
481298943Sluigi
481399475Sluigi	case TOK_REJECT:
481499475Sluigi		action->opcode = O_REJECT;
481599475Sluigi		action->arg1 = ICMP_UNREACH_HOST;
481699475Sluigi		break;
481799475Sluigi
481899475Sluigi	case TOK_RESET:
481999475Sluigi		action->opcode = O_REJECT;
482099475Sluigi		action->arg1 = ICMP_REJECT_RST;
482199475Sluigi		break;
482299475Sluigi
4823149020Sbz	case TOK_RESET6:
4824149020Sbz		action->opcode = O_UNREACH6;
4825149020Sbz		action->arg1 = ICMP6_UNREACH_RST;
4826149020Sbz		break;
4827149020Sbz
482899475Sluigi	case TOK_UNREACH:
482999475Sluigi		action->opcode = O_REJECT;
483099475Sluigi		NEED1("missing reject code");
483199475Sluigi		fill_reject_code(&action->arg1, *av);
483299475Sluigi		ac--; av++;
483399475Sluigi		break;
483499475Sluigi
4835149020Sbz	case TOK_UNREACH6:
4836149020Sbz		action->opcode = O_UNREACH6;
4837149020Sbz		NEED1("missing unreach code");
4838149020Sbz		fill_unreach6_code(&action->arg1, *av);
4839149020Sbz		ac--; av++;
4840149020Sbz		break;
4841149020Sbz
484298943Sluigi	case TOK_COUNT:
484398943Sluigi		action->opcode = O_COUNT;
484498943Sluigi		break;
484598943Sluigi
484698943Sluigi	case TOK_QUEUE:
4847153374Sglebius		action->opcode = O_QUEUE;
4848153374Sglebius		goto chkarg;
484998943Sluigi	case TOK_PIPE:
4850153374Sglebius		action->opcode = O_PIPE;
4851153374Sglebius		goto chkarg;
485298943Sluigi	case TOK_SKIPTO:
4853153374Sglebius		action->opcode = O_SKIPTO;
4854153374Sglebius		goto chkarg;
4855153374Sglebius	case TOK_NETGRAPH:
4856153374Sglebius		action->opcode = O_NETGRAPH;
4857153374Sglebius		goto chkarg;
4858153374Sglebius	case TOK_NGTEE:
4859153374Sglebius		action->opcode = O_NGTEE;
4860153374Sglebius		goto chkarg;
486198943Sluigi	case TOK_DIVERT:
4862153374Sglebius		action->opcode = O_DIVERT;
4863153374Sglebius		goto chkarg;
486498943Sluigi	case TOK_TEE:
4865153374Sglebius		action->opcode = O_TEE;
4866153374Sglebiuschkarg:
4867153374Sglebius		if (!ac)
4868153374Sglebius			errx(EX_USAGE, "missing argument for %s", *(av - 1));
4869153374Sglebius		if (isdigit(**av)) {
4870153374Sglebius			action->arg1 = strtoul(*av, NULL, 10);
4871153374Sglebius			if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG)
4872153374Sglebius				errx(EX_DATAERR, "illegal argument for %s",
4873153374Sglebius				    *(av - 1));
4874153374Sglebius		} else if (_substrcmp(*av, TABLEARG) == 0) {
4875153374Sglebius			action->arg1 = IP_FW_TABLEARG;
4876153374Sglebius		} else if (i == TOK_DIVERT || i == TOK_TEE) {
487798943Sluigi			struct servent *s;
487898943Sluigi			setservent(1);
487998943Sluigi			s = getservbyname(av[0], "divert");
488098943Sluigi			if (s != NULL)
488198943Sluigi				action->arg1 = ntohs(s->s_port);
488298943Sluigi			else
488398943Sluigi				errx(EX_DATAERR, "illegal divert/tee port");
4884153374Sglebius		} else
4885153374Sglebius			errx(EX_DATAERR, "illegal argument for %s", *(av - 1));
488698943Sluigi		ac--; av++;
488798943Sluigi		break;
488898943Sluigi
488998943Sluigi	case TOK_FORWARD: {
489098943Sluigi		ipfw_insn_sa *p = (ipfw_insn_sa *)action;
489198943Sluigi		char *s, *end;
489298943Sluigi
489398943Sluigi		NEED1("missing forward address[:port]");
489498943Sluigi
489598943Sluigi		action->opcode = O_FORWARD_IP;
489698943Sluigi		action->len = F_INSN_SIZE(ipfw_insn_sa);
489798943Sluigi
489898943Sluigi		p->sa.sin_len = sizeof(struct sockaddr_in);
489998943Sluigi		p->sa.sin_family = AF_INET;
490098943Sluigi		p->sa.sin_port = 0;
490198943Sluigi		/*
490298943Sluigi		 * locate the address-port separator (':' or ',')
490398943Sluigi		 */
490498943Sluigi		s = strchr(*av, ':');
490598943Sluigi		if (s == NULL)
490698943Sluigi			s = strchr(*av, ',');
490798943Sluigi		if (s != NULL) {
490898943Sluigi			*(s++) = '\0';
490998943Sluigi			i = strtoport(s, &end, 0 /* base */, 0 /* proto */);
491098943Sluigi			if (s == end)
491198943Sluigi				errx(EX_DATAERR,
491298943Sluigi				    "illegal forwarding port ``%s''", s);
4913103241Sluigi			p->sa.sin_port = (u_short)i;
491498943Sluigi		}
4915161456Sjulian		if (_substrcmp(*av, "tablearg") == 0)
4916161456Sjulian			p->sa.sin_addr.s_addr = INADDR_ANY;
4917161456Sjulian		else
4918161424Sjulian			lookup_host(*av, &(p->sa.sin_addr));
491998943Sluigi		ac--; av++;
492098943Sluigi		break;
4921161424Sjulian	    }
4922117469Sluigi	case TOK_COMMENT:
4923117469Sluigi		/* pretend it is a 'count' rule followed by the comment */
4924117469Sluigi		action->opcode = O_COUNT;
4925117469Sluigi		ac++; av--;	/* go back... */
4926117469Sluigi		break;
4927117469Sluigi
4928165648Spiso	case TOK_NAT:
4929165648Spiso 		action->opcode = O_NAT;
4930165648Spiso 		action->len = F_INSN_SIZE(ipfw_insn_nat);
4931165648Spiso 		NEED1("missing nat number");
4932165648Spiso 	        action->arg1 = strtoul(*av, NULL, 10);
4933165648Spiso 		ac--; av++;
4934165648Spiso 		break;
4935165648Spiso
493698943Sluigi	default:
4937102087Sluigi		errx(EX_DATAERR, "invalid action %s\n", av[-1]);
493898943Sluigi	}
493998943Sluigi	action = next_cmd(action);
494098943Sluigi
494198943Sluigi	/*
4942136071Sgreen	 * [altq queuename] -- altq tag, optional
494398943Sluigi	 * [log [logamount N]]	-- log, optional
494498943Sluigi	 *
4945136071Sgreen	 * If they exist, it go first in the cmdbuf, but then it is
494698943Sluigi	 * skipped in the copy section to the end of the buffer.
494798943Sluigi	 */
4948136071Sgreen	while (ac != 0 && (i = match_token(rule_action_params, *av)) != -1) {
4949136071Sgreen		ac--; av++;
4950136071Sgreen		switch (i) {
4951136071Sgreen		case TOK_LOG:
4952136071Sgreen		    {
4953136071Sgreen			ipfw_insn_log *c = (ipfw_insn_log *)cmd;
4954136071Sgreen			int l;
495598943Sluigi
4956136071Sgreen			if (have_log)
4957136071Sgreen				errx(EX_DATAERR,
4958136071Sgreen				    "log cannot be specified more than once");
4959136071Sgreen			have_log = (ipfw_insn *)c;
4960136071Sgreen			cmd->len = F_INSN_SIZE(ipfw_insn_log);
4961136071Sgreen			cmd->opcode = O_LOG;
4962140271Sbrooks			if (ac && _substrcmp(*av, "logamount") == 0) {
4963136071Sgreen				ac--; av++;
4964136071Sgreen				NEED1("logamount requires argument");
4965136071Sgreen				l = atoi(*av);
4966136071Sgreen				if (l < 0)
4967136071Sgreen					errx(EX_DATAERR,
4968136071Sgreen					    "logamount must be positive");
4969136071Sgreen				c->max_log = l;
4970136071Sgreen				ac--; av++;
4971136071Sgreen			} else {
4972136071Sgreen				len = sizeof(c->max_log);
4973136071Sgreen				if (sysctlbyname("net.inet.ip.fw.verbose_limit",
4974136071Sgreen				    &c->max_log, &len, NULL, 0) == -1)
4975136071Sgreen					errx(1, "sysctlbyname(\"%s\")",
4976136071Sgreen					    "net.inet.ip.fw.verbose_limit");
4977136071Sgreen			}
4978136071Sgreen		    }
4979136071Sgreen			break;
4980136071Sgreen
4981136071Sgreen		case TOK_ALTQ:
4982136071Sgreen		    {
4983136071Sgreen			ipfw_insn_altq *a = (ipfw_insn_altq *)cmd;
4984136071Sgreen
4985136071Sgreen			NEED1("missing altq queue name");
4986136071Sgreen			if (have_altq)
4987136071Sgreen				errx(EX_DATAERR,
4988136071Sgreen				    "altq cannot be specified more than once");
4989136071Sgreen			have_altq = (ipfw_insn *)a;
4990136071Sgreen			cmd->len = F_INSN_SIZE(ipfw_insn_altq);
4991136071Sgreen			cmd->opcode = O_ALTQ;
4992136071Sgreen			fill_altq_qid(&a->qid, *av);
499398943Sluigi			ac--; av++;
4994136071Sgreen		    }
4995136071Sgreen			break;
4996136071Sgreen
4997158879Soleg		case TOK_TAG:
4998159636Soleg		case TOK_UNTAG: {
4999159636Soleg			uint16_t tag;
5000159636Soleg
5001158879Soleg			if (have_tag)
5002159636Soleg				errx(EX_USAGE, "tag and untag cannot be "
5003159636Soleg				    "specified more than once");
5004159636Soleg			GET_UINT_ARG(tag, 1, 65534, i, rule_action_params);
5005158879Soleg			have_tag = cmd;
5006159636Soleg			fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag);
5007158879Soleg			ac--; av++;
5008158879Soleg			break;
5009159636Soleg		}
5010158879Soleg
5011136071Sgreen		default:
5012136071Sgreen			abort();
501398943Sluigi		}
501498943Sluigi		cmd = next_cmd(cmd);
501598943Sluigi	}
501698943Sluigi
5017101116Sluigi	if (have_state)	/* must be a check-state, we are done */
501898943Sluigi		goto done;
501998943Sluigi
502098943Sluigi#define OR_START(target)					\
502198943Sluigi	if (ac && (*av[0] == '(' || *av[0] == '{')) {		\
502298943Sluigi		if (open_par)					\
502398943Sluigi			errx(EX_USAGE, "nested \"(\" not allowed\n"); \
5024101641Sluigi		prev = NULL;					\
502598943Sluigi		open_par = 1;					\
502698943Sluigi		if ( (av[0])[1] == '\0') {			\
502798943Sluigi			ac--; av++;				\
502898943Sluigi		} else						\
502998943Sluigi			(*av)++;				\
503098943Sluigi	}							\
503198943Sluigi	target:							\
503298943Sluigi
503398943Sluigi
503498943Sluigi#define	CLOSE_PAR						\
503598943Sluigi	if (open_par) {						\
503698943Sluigi		if (ac && (					\
5037140271Sbrooks		    strcmp(*av, ")") == 0 ||			\
5038140271Sbrooks		    strcmp(*av, "}") == 0)) {			\
5039101641Sluigi			prev = NULL;				\
504098943Sluigi			open_par = 0;				\
504198943Sluigi			ac--; av++;				\
504298943Sluigi		} else						\
504398943Sluigi			errx(EX_USAGE, "missing \")\"\n");	\
504498943Sluigi	}
5045106505Smaxim
504698943Sluigi#define NOT_BLOCK						\
5047140271Sbrooks	if (ac && _substrcmp(*av, "not") == 0) {		\
504898943Sluigi		if (cmd->len & F_NOT)				\
504998943Sluigi			errx(EX_USAGE, "double \"not\" not allowed\n"); \
505098943Sluigi		cmd->len |= F_NOT;				\
505198943Sluigi		ac--; av++;					\
505298943Sluigi	}
505398943Sluigi
505498943Sluigi#define OR_BLOCK(target)					\
5055140271Sbrooks	if (ac && _substrcmp(*av, "or") == 0) {		\
505698943Sluigi		if (prev == NULL || open_par == 0)		\
505798943Sluigi			errx(EX_DATAERR, "invalid OR block");	\
505898943Sluigi		prev->len |= F_OR;				\
505998943Sluigi		ac--; av++;					\
506098943Sluigi		goto target;					\
506198943Sluigi	}							\
506298943Sluigi	CLOSE_PAR;
506398943Sluigi
5064102087Sluigi	first_cmd = cmd;
5065102098Sluigi
5066102098Sluigi#if 0
506798943Sluigi	/*
5068102087Sluigi	 * MAC addresses, optional.
5069102087Sluigi	 * If we have this, we skip the part "proto from src to dst"
5070102087Sluigi	 * and jump straight to the option parsing.
5071102087Sluigi	 */
5072102087Sluigi	NOT_BLOCK;
5073102087Sluigi	NEED1("missing protocol");
5074140271Sbrooks	if (_substrcmp(*av, "MAC") == 0 ||
5075140271Sbrooks	    _substrcmp(*av, "mac") == 0) {
5076102087Sluigi		ac--; av++;	/* the "MAC" keyword */
5077102087Sluigi		add_mac(cmd, ac, av); /* exits in case of errors */
5078102087Sluigi		cmd = next_cmd(cmd);
5079102087Sluigi		ac -= 2; av += 2;	/* dst-mac and src-mac */
5080102087Sluigi		NOT_BLOCK;
5081102087Sluigi		NEED1("missing mac type");
5082102087Sluigi		if (add_mactype(cmd, ac, av[0]))
5083102087Sluigi			cmd = next_cmd(cmd);
5084102087Sluigi		ac--; av++;	/* any or mac-type */
5085102087Sluigi		goto read_options;
5086102087Sluigi	}
5087102098Sluigi#endif
5088102087Sluigi
5089102087Sluigi	/*
509098943Sluigi	 * protocol, mandatory
509198943Sluigi	 */
509298943Sluigi    OR_START(get_proto);
509398943Sluigi	NOT_BLOCK;
509498943Sluigi	NEED1("missing protocol");
5095152923Sume	if (add_proto_compat(cmd, *av, &proto)) {
5096102087Sluigi		av++; ac--;
5097147105Smlaier		if (F_LEN(cmd) != 0) {
5098102087Sluigi			prev = cmd;
5099102087Sluigi			cmd = next_cmd(cmd);
5100102087Sluigi		}
5101102098Sluigi	} else if (first_cmd != cmd) {
5102116438Smaxim		errx(EX_DATAERR, "invalid protocol ``%s''", *av);
5103102098Sluigi	} else
5104102098Sluigi		goto read_options;
510598943Sluigi    OR_BLOCK(get_proto);
510698943Sluigi
510798943Sluigi	/*
5108102087Sluigi	 * "from", mandatory
510998943Sluigi	 */
5110140271Sbrooks	if (!ac || _substrcmp(*av, "from") != 0)
511198943Sluigi		errx(EX_USAGE, "missing ``from''");
511298943Sluigi	ac--; av++;
511398943Sluigi
511498943Sluigi	/*
511598943Sluigi	 * source IP, mandatory
511698943Sluigi	 */
511798943Sluigi    OR_START(source_ip);
511898943Sluigi	NOT_BLOCK;	/* optional "not" */
511998943Sluigi	NEED1("missing source address");
5120145246Sbrooks	if (add_src(cmd, *av, proto)) {
5121102087Sluigi		ac--; av++;
5122102087Sluigi		if (F_LEN(cmd) != 0) {	/* ! any */
5123102087Sluigi			prev = cmd;
5124102087Sluigi			cmd = next_cmd(cmd);
5125102087Sluigi		}
5126145246Sbrooks	} else
5127145246Sbrooks		errx(EX_USAGE, "bad source address %s", *av);
512898943Sluigi    OR_BLOCK(source_ip);
512998943Sluigi
513098943Sluigi	/*
513198943Sluigi	 * source ports, optional
513298943Sluigi	 */
513398943Sluigi	NOT_BLOCK;	/* optional "not" */
5134101641Sluigi	if (ac) {
5135140271Sbrooks		if (_substrcmp(*av, "any") == 0 ||
5136102087Sluigi		    add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
5137102087Sluigi			ac--; av++;
5138102087Sluigi			if (F_LEN(cmd) != 0)
5139102087Sluigi				cmd = next_cmd(cmd);
5140101641Sluigi		}
514198943Sluigi	}
514298943Sluigi
514398943Sluigi	/*
5144102087Sluigi	 * "to", mandatory
514598943Sluigi	 */
5146140271Sbrooks	if (!ac || _substrcmp(*av, "to") != 0)
514798943Sluigi		errx(EX_USAGE, "missing ``to''");
514898943Sluigi	av++; ac--;
514998943Sluigi
515098943Sluigi	/*
515198943Sluigi	 * destination, mandatory
515298943Sluigi	 */
515398943Sluigi    OR_START(dest_ip);
515498943Sluigi	NOT_BLOCK;	/* optional "not" */
515598943Sluigi	NEED1("missing dst address");
5156145246Sbrooks	if (add_dst(cmd, *av, proto)) {
5157102087Sluigi		ac--; av++;
5158102087Sluigi		if (F_LEN(cmd) != 0) {	/* ! any */
5159102087Sluigi			prev = cmd;
5160102087Sluigi			cmd = next_cmd(cmd);
5161102087Sluigi		}
5162145246Sbrooks	} else
5163145246Sbrooks		errx( EX_USAGE, "bad destination address %s", *av);
516498943Sluigi    OR_BLOCK(dest_ip);
516598943Sluigi
516698943Sluigi	/*
516798943Sluigi	 * dest. ports, optional
516898943Sluigi	 */
516998943Sluigi	NOT_BLOCK;	/* optional "not" */
5170101641Sluigi	if (ac) {
5171140271Sbrooks		if (_substrcmp(*av, "any") == 0 ||
5172102087Sluigi		    add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
5173102087Sluigi			ac--; av++;
5174102087Sluigi			if (F_LEN(cmd) != 0)
5175102087Sluigi				cmd = next_cmd(cmd);
5176101641Sluigi		}
517798943Sluigi	}
517898943Sluigi
517998943Sluigiread_options:
5180102087Sluigi	if (ac && first_cmd == cmd) {
5181102087Sluigi		/*
5182102087Sluigi		 * nothing specified so far, store in the rule to ease
5183102087Sluigi		 * printout later.
5184102087Sluigi		 */
5185102087Sluigi		 rule->_pad = 1;
5186102087Sluigi	}
518798943Sluigi	prev = NULL;
518898943Sluigi	while (ac) {
5189101641Sluigi		char *s;
5190101641Sluigi		ipfw_insn_u32 *cmd32;	/* alias for cmd */
519198943Sluigi
5192101641Sluigi		s = *av;
5193101641Sluigi		cmd32 = (ipfw_insn_u32 *)cmd;
5194101641Sluigi
519598943Sluigi		if (*s == '!') {	/* alternate syntax for NOT */
519698943Sluigi			if (cmd->len & F_NOT)
519798943Sluigi				errx(EX_USAGE, "double \"not\" not allowed\n");
519898943Sluigi			cmd->len = F_NOT;
519998943Sluigi			s++;
520098943Sluigi		}
520198943Sluigi		i = match_token(rule_options, s);
520298943Sluigi		ac--; av++;
520398943Sluigi		switch(i) {
520498943Sluigi		case TOK_NOT:
520598943Sluigi			if (cmd->len & F_NOT)
520698943Sluigi				errx(EX_USAGE, "double \"not\" not allowed\n");
520798943Sluigi			cmd->len = F_NOT;
520898943Sluigi			break;
520998943Sluigi
521098943Sluigi		case TOK_OR:
5211101641Sluigi			if (open_par == 0 || prev == NULL)
521298943Sluigi				errx(EX_USAGE, "invalid \"or\" block\n");
521398943Sluigi			prev->len |= F_OR;
521498943Sluigi			break;
5215101641Sluigi
5216101641Sluigi		case TOK_STARTBRACE:
5217101641Sluigi			if (open_par)
5218101641Sluigi				errx(EX_USAGE, "+nested \"(\" not allowed\n");
5219101641Sluigi			open_par = 1;
5220101641Sluigi			break;
5221101641Sluigi
5222101641Sluigi		case TOK_ENDBRACE:
5223101641Sluigi			if (!open_par)
5224101641Sluigi				errx(EX_USAGE, "+missing \")\"\n");
5225101641Sluigi			open_par = 0;
5226102087Sluigi			prev = NULL;
5227101641Sluigi        		break;
5228101641Sluigi
522998943Sluigi		case TOK_IN:
523098943Sluigi			fill_cmd(cmd, O_IN, 0, 0);
523198943Sluigi			break;
523298943Sluigi
523398943Sluigi		case TOK_OUT:
523498943Sluigi			cmd->len ^= F_NOT; /* toggle F_NOT */
523598943Sluigi			fill_cmd(cmd, O_IN, 0, 0);
523698943Sluigi			break;
523798943Sluigi
5238136073Sgreen		case TOK_DIVERTED:
5239136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 3);
5240136073Sgreen			break;
5241136073Sgreen
5242136073Sgreen		case TOK_DIVERTEDLOOPBACK:
5243136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 1);
5244136073Sgreen			break;
5245136073Sgreen
5246136073Sgreen		case TOK_DIVERTEDOUTPUT:
5247136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 2);
5248136073Sgreen			break;
5249136073Sgreen
525098943Sluigi		case TOK_FRAG:
525198943Sluigi			fill_cmd(cmd, O_FRAG, 0, 0);
525298943Sluigi			break;
525398943Sluigi
525498943Sluigi		case TOK_LAYER2:
525598943Sluigi			fill_cmd(cmd, O_LAYER2, 0, 0);
525698943Sluigi			break;
525798943Sluigi
525898943Sluigi		case TOK_XMIT:
525998943Sluigi		case TOK_RECV:
526098943Sluigi		case TOK_VIA:
526198943Sluigi			NEED1("recv, xmit, via require interface name"
526298943Sluigi				" or address");
526398943Sluigi			fill_iface((ipfw_insn_if *)cmd, av[0]);
526498943Sluigi			ac--; av++;
526598943Sluigi			if (F_LEN(cmd) == 0)	/* not a valid address */
526698943Sluigi				break;
526798943Sluigi			if (i == TOK_XMIT)
526898943Sluigi				cmd->opcode = O_XMIT;
526998943Sluigi			else if (i == TOK_RECV)
527098943Sluigi				cmd->opcode = O_RECV;
527198943Sluigi			else if (i == TOK_VIA)
527298943Sluigi				cmd->opcode = O_VIA;
527398943Sluigi			break;
527498943Sluigi
527599475Sluigi		case TOK_ICMPTYPES:
527699475Sluigi			NEED1("icmptypes requires list of types");
527799475Sluigi			fill_icmptypes((ipfw_insn_u32 *)cmd, *av);
527899475Sluigi			av++; ac--;
527999475Sluigi			break;
5280145246Sbrooks
5281145246Sbrooks		case TOK_ICMP6TYPES:
5282145246Sbrooks			NEED1("icmptypes requires list of types");
5283145246Sbrooks			fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av);
5284145246Sbrooks			av++; ac--;
5285145246Sbrooks			break;
528699475Sluigi
528798943Sluigi		case TOK_IPTTL:
528898943Sluigi			NEED1("ipttl requires TTL");
5289116690Sluigi			if (strpbrk(*av, "-,")) {
5290116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPTTL))
5291116690Sluigi				errx(EX_DATAERR, "invalid ipttl %s", *av);
5292116690Sluigi			} else
5293116690Sluigi			    fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
529498943Sluigi			ac--; av++;
529598943Sluigi			break;
529698943Sluigi
529798943Sluigi		case TOK_IPID:
5298116690Sluigi			NEED1("ipid requires id");
5299116690Sluigi			if (strpbrk(*av, "-,")) {
5300116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPID))
5301116690Sluigi				errx(EX_DATAERR, "invalid ipid %s", *av);
5302116690Sluigi			} else
5303116690Sluigi			    fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0));
530498943Sluigi			ac--; av++;
530598943Sluigi			break;
530698943Sluigi
530798943Sluigi		case TOK_IPLEN:
530898943Sluigi			NEED1("iplen requires length");
5309116690Sluigi			if (strpbrk(*av, "-,")) {
5310116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPLEN))
5311116690Sluigi				errx(EX_DATAERR, "invalid ip len %s", *av);
5312116690Sluigi			} else
5313116690Sluigi			    fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0));
531498943Sluigi			ac--; av++;
531598943Sluigi			break;
531698943Sluigi
531798943Sluigi		case TOK_IPVER:
531898943Sluigi			NEED1("ipver requires version");
531998943Sluigi			fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0));
532098943Sluigi			ac--; av++;
532198943Sluigi			break;
532298943Sluigi
532399475Sluigi		case TOK_IPPRECEDENCE:
532499475Sluigi			NEED1("ipprecedence requires value");
532599475Sluigi			fill_cmd(cmd, O_IPPRECEDENCE, 0,
532699475Sluigi			    (strtoul(*av, NULL, 0) & 7) << 5);
532799475Sluigi			ac--; av++;
532899475Sluigi			break;
532999475Sluigi
533098943Sluigi		case TOK_IPOPTS:
533198943Sluigi			NEED1("missing argument for ipoptions");
5332101116Sluigi			fill_flags(cmd, O_IPOPT, f_ipopts, *av);
533398943Sluigi			ac--; av++;
533498943Sluigi			break;
533598943Sluigi
533699475Sluigi		case TOK_IPTOS:
533799475Sluigi			NEED1("missing argument for iptos");
5338101116Sluigi			fill_flags(cmd, O_IPTOS, f_iptos, *av);
533999475Sluigi			ac--; av++;
534099475Sluigi			break;
534199475Sluigi
534298943Sluigi		case TOK_UID:
534398943Sluigi			NEED1("uid requires argument");
534498943Sluigi		    {
534598943Sluigi			char *end;
534698943Sluigi			uid_t uid;
534798943Sluigi			struct passwd *pwd;
534898943Sluigi
534998943Sluigi			cmd->opcode = O_UID;
535098943Sluigi			uid = strtoul(*av, &end, 0);
535198943Sluigi			pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av);
535298943Sluigi			if (pwd == NULL)
535398943Sluigi				errx(EX_DATAERR, "uid \"%s\" nonexistent", *av);
5354106504Smaxim			cmd32->d[0] = pwd->pw_uid;
5355135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
535698943Sluigi			ac--; av++;
535798943Sluigi		    }
535898943Sluigi			break;
535998943Sluigi
536098943Sluigi		case TOK_GID:
536198943Sluigi			NEED1("gid requires argument");
536298943Sluigi		    {
536398943Sluigi			char *end;
536498943Sluigi			gid_t gid;
536598943Sluigi			struct group *grp;
536698943Sluigi
536798943Sluigi			cmd->opcode = O_GID;
536898943Sluigi			gid = strtoul(*av, &end, 0);
536998943Sluigi			grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av);
537098943Sluigi			if (grp == NULL)
537198943Sluigi				errx(EX_DATAERR, "gid \"%s\" nonexistent", *av);
5372106504Smaxim			cmd32->d[0] = grp->gr_gid;
5373135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
537498943Sluigi			ac--; av++;
537598943Sluigi		    }
537698943Sluigi			break;
537798943Sluigi
5378133600Scsjp		case TOK_JAIL:
5379133600Scsjp			NEED1("jail requires argument");
5380133600Scsjp		    {
5381133600Scsjp			char *end;
5382133600Scsjp			int jid;
5383133600Scsjp
5384133600Scsjp			cmd->opcode = O_JAIL;
5385133600Scsjp			jid = (int)strtol(*av, &end, 0);
5386133600Scsjp			if (jid < 0 || *end != '\0')
5387133600Scsjp				errx(EX_DATAERR, "jail requires prison ID");
5388135554Scsjp			cmd32->d[0] = (uint32_t)jid;
5389135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
5390133600Scsjp			ac--; av++;
5391133600Scsjp		    }
5392133600Scsjp			break;
5393133600Scsjp
539498943Sluigi		case TOK_ESTAB:
539598943Sluigi			fill_cmd(cmd, O_ESTAB, 0, 0);
539698943Sluigi			break;
539798943Sluigi
539898943Sluigi		case TOK_SETUP:
539998943Sluigi			fill_cmd(cmd, O_TCPFLAGS, 0,
540098943Sluigi				(TH_SYN) | ( (TH_ACK) & 0xff) <<8 );
540198943Sluigi			break;
540298943Sluigi
5403136075Sgreen		case TOK_TCPDATALEN:
5404136075Sgreen			NEED1("tcpdatalen requires length");
5405136075Sgreen			if (strpbrk(*av, "-,")) {
5406136075Sgreen			    if (!add_ports(cmd, *av, 0, O_TCPDATALEN))
5407136075Sgreen				errx(EX_DATAERR, "invalid tcpdata len %s", *av);
5408136075Sgreen			} else
5409136075Sgreen			    fill_cmd(cmd, O_TCPDATALEN, 0,
5410136075Sgreen				    strtoul(*av, NULL, 0));
5411136075Sgreen			ac--; av++;
5412136075Sgreen			break;
5413136075Sgreen
541498943Sluigi		case TOK_TCPOPTS:
541598943Sluigi			NEED1("missing argument for tcpoptions");
541698943Sluigi			fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av);
541798943Sluigi			ac--; av++;
541898943Sluigi			break;
541998943Sluigi
542098943Sluigi		case TOK_TCPSEQ:
542198943Sluigi		case TOK_TCPACK:
542298943Sluigi			NEED1("tcpseq/tcpack requires argument");
542398943Sluigi			cmd->len = F_INSN_SIZE(ipfw_insn_u32);
542498943Sluigi			cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK;
542598943Sluigi			cmd32->d[0] = htonl(strtoul(*av, NULL, 0));
542698943Sluigi			ac--; av++;
542798943Sluigi			break;
542898943Sluigi
542998943Sluigi		case TOK_TCPWIN:
543098943Sluigi			NEED1("tcpwin requires length");
543198943Sluigi			fill_cmd(cmd, O_TCPWIN, 0,
543298943Sluigi			    htons(strtoul(*av, NULL, 0)));
543398943Sluigi			ac--; av++;
543498943Sluigi			break;
543598943Sluigi
543698943Sluigi		case TOK_TCPFLAGS:
543798943Sluigi			NEED1("missing argument for tcpflags");
543898943Sluigi			cmd->opcode = O_TCPFLAGS;
543998943Sluigi			fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av);
544098943Sluigi			ac--; av++;
544198943Sluigi			break;
544298943Sluigi
544398943Sluigi		case TOK_KEEPSTATE:
5444101641Sluigi			if (open_par)
5445101641Sluigi				errx(EX_USAGE, "keep-state cannot be part "
5446101641Sluigi				    "of an or block");
544799909Sluigi			if (have_state)
5448101116Sluigi				errx(EX_USAGE, "only one of keep-state "
544999909Sluigi					"and limit is allowed");
5450101116Sluigi			have_state = cmd;
545198943Sluigi			fill_cmd(cmd, O_KEEP_STATE, 0, 0);
545298943Sluigi			break;
545398943Sluigi
5454159636Soleg		case TOK_LIMIT: {
5455159636Soleg			ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
5456159636Soleg			int val;
5457159636Soleg
5458101641Sluigi			if (open_par)
5459159636Soleg				errx(EX_USAGE,
5460159636Soleg				    "limit cannot be part of an or block");
546199909Sluigi			if (have_state)
5462168819Smaxim				errx(EX_USAGE, "only one of keep-state and "
5463159636Soleg				    "limit is allowed");
5464101116Sluigi			have_state = cmd;
546598943Sluigi
546698943Sluigi			cmd->len = F_INSN_SIZE(ipfw_insn_limit);
546798943Sluigi			cmd->opcode = O_LIMIT;
5468159636Soleg			c->limit_mask = c->conn_limit = 0;
546998943Sluigi
5470159636Soleg			while (ac > 0) {
5471159636Soleg				if ((val = match_token(limit_masks, *av)) <= 0)
547298943Sluigi					break;
547398943Sluigi				c->limit_mask |= val;
547498943Sluigi				ac--; av++;
547598943Sluigi			}
5476159636Soleg
547798943Sluigi			if (c->limit_mask == 0)
5478159636Soleg				errx(EX_USAGE, "limit: missing limit mask");
5479159636Soleg
5480159636Soleg			GET_UINT_ARG(c->conn_limit, 1, 65534, TOK_LIMIT,
5481159636Soleg			    rule_options);
5482159636Soleg
548398943Sluigi			ac--; av++;
548498943Sluigi			break;
5485159636Soleg		}
548698943Sluigi
5487102087Sluigi		case TOK_PROTO:
5488102087Sluigi			NEED1("missing protocol");
5489145246Sbrooks			if (add_proto(cmd, *av, &proto)) {
5490102087Sluigi				ac--; av++;
5491102098Sluigi			} else
5492116438Smaxim				errx(EX_DATAERR, "invalid protocol ``%s''",
5493116438Smaxim				    *av);
5494102087Sluigi			break;
5495106505Smaxim
5496102087Sluigi		case TOK_SRCIP:
5497102087Sluigi			NEED1("missing source IP");
5498102087Sluigi			if (add_srcip(cmd, *av)) {
5499102087Sluigi				ac--; av++;
5500102087Sluigi			}
5501102087Sluigi			break;
5502102087Sluigi
5503102087Sluigi		case TOK_DSTIP:
5504102087Sluigi			NEED1("missing destination IP");
5505102087Sluigi			if (add_dstip(cmd, *av)) {
5506102087Sluigi				ac--; av++;
5507102087Sluigi			}
5508102087Sluigi			break;
5509102087Sluigi
5510145246Sbrooks		case TOK_SRCIP6:
5511145246Sbrooks			NEED1("missing source IP6");
5512145246Sbrooks			if (add_srcip6(cmd, *av)) {
5513145246Sbrooks				ac--; av++;
5514145246Sbrooks			}
5515145246Sbrooks			break;
5516145246Sbrooks
5517145246Sbrooks		case TOK_DSTIP6:
5518145246Sbrooks			NEED1("missing destination IP6");
5519145246Sbrooks			if (add_dstip6(cmd, *av)) {
5520145246Sbrooks				ac--; av++;
5521145246Sbrooks			}
5522145246Sbrooks			break;
5523145246Sbrooks
5524102087Sluigi		case TOK_SRCPORT:
5525102087Sluigi			NEED1("missing source port");
5526140271Sbrooks			if (_substrcmp(*av, "any") == 0 ||
5527102087Sluigi			    add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
5528102087Sluigi				ac--; av++;
5529102087Sluigi			} else
5530102087Sluigi				errx(EX_DATAERR, "invalid source port %s", *av);
5531102087Sluigi			break;
5532102087Sluigi
5533102087Sluigi		case TOK_DSTPORT:
5534102087Sluigi			NEED1("missing destination port");
5535140271Sbrooks			if (_substrcmp(*av, "any") == 0 ||
5536102087Sluigi			    add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
5537102087Sluigi				ac--; av++;
5538102087Sluigi			} else
5539102087Sluigi				errx(EX_DATAERR, "invalid destination port %s",
5540102087Sluigi				    *av);
5541102087Sluigi			break;
5542102087Sluigi
5543102087Sluigi		case TOK_MAC:
5544102087Sluigi			if (add_mac(cmd, ac, av)) {
5545102087Sluigi				ac -= 2; av += 2;
5546102087Sluigi			}
5547102087Sluigi			break;
5548102087Sluigi
5549102087Sluigi		case TOK_MACTYPE:
5550102087Sluigi			NEED1("missing mac type");
5551102087Sluigi			if (!add_mactype(cmd, ac, *av))
5552116438Smaxim				errx(EX_DATAERR, "invalid mac type %s", *av);
5553102087Sluigi			ac--; av++;
5554102087Sluigi			break;
5555102087Sluigi
5556112250Scjc		case TOK_VERREVPATH:
5557112250Scjc			fill_cmd(cmd, O_VERREVPATH, 0, 0);
5558112250Scjc			break;
5559116919Sluigi
5560128575Sandre		case TOK_VERSRCREACH:
5561128575Sandre			fill_cmd(cmd, O_VERSRCREACH, 0, 0);
5562128575Sandre			break;
5563128575Sandre
5564133387Sandre		case TOK_ANTISPOOF:
5565133387Sandre			fill_cmd(cmd, O_ANTISPOOF, 0, 0);
5566133387Sandre			break;
5567133387Sandre
5568117241Sluigi		case TOK_IPSEC:
5569117241Sluigi			fill_cmd(cmd, O_IPSEC, 0, 0);
5570117241Sluigi			break;
5571117241Sluigi
5572145246Sbrooks		case TOK_IPV6:
5573145246Sbrooks			fill_cmd(cmd, O_IP6, 0, 0);
5574145246Sbrooks			break;
5575145246Sbrooks
5576146894Smlaier		case TOK_IPV4:
5577146894Smlaier			fill_cmd(cmd, O_IP4, 0, 0);
5578146894Smlaier			break;
5579146894Smlaier
5580145246Sbrooks		case TOK_EXT6HDR:
5581145246Sbrooks			fill_ext6hdr( cmd, *av );
5582145246Sbrooks			ac--; av++;
5583145246Sbrooks			break;
5584145246Sbrooks
5585145246Sbrooks		case TOK_FLOWID:
5586145246Sbrooks			if (proto != IPPROTO_IPV6 )
5587145246Sbrooks				errx( EX_USAGE, "flow-id filter is active "
5588145246Sbrooks				    "only for ipv6 protocol\n");
5589145246Sbrooks			fill_flow6( (ipfw_insn_u32 *) cmd, *av );
5590145246Sbrooks			ac--; av++;
5591145246Sbrooks			break;
5592145246Sbrooks
5593117469Sluigi		case TOK_COMMENT:
5594117469Sluigi			fill_comment(cmd, ac, av);
5595117469Sluigi			av += ac;
5596117469Sluigi			ac = 0;
5597117469Sluigi			break;
5598117469Sluigi
5599158879Soleg		case TOK_TAGGED:
5600159636Soleg			if (ac > 0 && strpbrk(*av, "-,")) {
5601158879Soleg				if (!add_ports(cmd, *av, 0, O_TAGGED))
5602159636Soleg					errx(EX_DATAERR, "tagged: invalid tag"
5603159636Soleg					    " list: %s", *av);
5604158879Soleg			}
5605159636Soleg			else {
5606159636Soleg				uint16_t tag;
5607159636Soleg
5608159636Soleg				GET_UINT_ARG(tag, 1, 65534, TOK_TAGGED,
5609159636Soleg				    rule_options);
5610159636Soleg				fill_cmd(cmd, O_TAGGED, 0, tag);
5611159636Soleg			}
5612158879Soleg			ac--; av++;
5613158879Soleg			break;
5614158879Soleg
561598943Sluigi		default:
561698943Sluigi			errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
561798943Sluigi		}
561898943Sluigi		if (F_LEN(cmd) > 0) {	/* prepare to advance */
561998943Sluigi			prev = cmd;
562098943Sluigi			cmd = next_cmd(cmd);
562198943Sluigi		}
562298943Sluigi	}
562398943Sluigi
562498943Sluigidone:
562598943Sluigi	/*
562698943Sluigi	 * Now copy stuff into the rule.
562798943Sluigi	 * If we have a keep-state option, the first instruction
562898943Sluigi	 * must be a PROBE_STATE (which is generated here).
562998943Sluigi	 * If we have a LOG option, it was stored as the first command,
563098943Sluigi	 * and now must be moved to the top of the action part.
563198943Sluigi	 */
563298943Sluigi	dst = (ipfw_insn *)rule->cmd;
563398943Sluigi
563498943Sluigi	/*
5635107289Sluigi	 * First thing to write into the command stream is the match probability.
5636107289Sluigi	 */
5637107289Sluigi	if (match_prob != 1) { /* 1 means always match */
5638107289Sluigi		dst->opcode = O_PROB;
5639107289Sluigi		dst->len = 2;
5640107289Sluigi		*((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff);
5641107289Sluigi		dst += dst->len;
5642107289Sluigi	}
5643107289Sluigi
5644107289Sluigi	/*
564598943Sluigi	 * generate O_PROBE_STATE if necessary
564698943Sluigi	 */
5647101116Sluigi	if (have_state && have_state->opcode != O_CHECK_STATE) {
564898943Sluigi		fill_cmd(dst, O_PROBE_STATE, 0, 0);
564998943Sluigi		dst = next_cmd(dst);
565098943Sluigi	}
5651158879Soleg
5652158879Soleg	/* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */
565398943Sluigi	for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
565498943Sluigi		i = F_LEN(src);
565598943Sluigi
5656101116Sluigi		switch (src->opcode) {
5657101116Sluigi		case O_LOG:
5658101116Sluigi		case O_KEEP_STATE:
5659101116Sluigi		case O_LIMIT:
5660136071Sgreen		case O_ALTQ:
5661158879Soleg		case O_TAG:
5662101116Sluigi			break;
5663101116Sluigi		default:
5664117328Sluigi			bcopy(src, dst, i * sizeof(uint32_t));
566598943Sluigi			dst += i;
566698943Sluigi		}
566798943Sluigi	}
566898943Sluigi
566998943Sluigi	/*
5670101116Sluigi	 * put back the have_state command as last opcode
5671101116Sluigi	 */
5672101295Sluigi	if (have_state && have_state->opcode != O_CHECK_STATE) {
5673101116Sluigi		i = F_LEN(have_state);
5674117328Sluigi		bcopy(have_state, dst, i * sizeof(uint32_t));
5675101116Sluigi		dst += i;
5676101116Sluigi	}
5677101116Sluigi	/*
567898943Sluigi	 * start action section
567998943Sluigi	 */
568098943Sluigi	rule->act_ofs = dst - rule->cmd;
568198943Sluigi
5682158879Soleg	/* put back O_LOG, O_ALTQ, O_TAG if necessary */
5683136071Sgreen	if (have_log) {
5684136071Sgreen		i = F_LEN(have_log);
5685136071Sgreen		bcopy(have_log, dst, i * sizeof(uint32_t));
568698943Sluigi		dst += i;
568798943Sluigi	}
5688136071Sgreen	if (have_altq) {
5689136071Sgreen		i = F_LEN(have_altq);
5690136071Sgreen		bcopy(have_altq, dst, i * sizeof(uint32_t));
5691136071Sgreen		dst += i;
5692136071Sgreen	}
5693158879Soleg	if (have_tag) {
5694158879Soleg		i = F_LEN(have_tag);
5695158879Soleg		bcopy(have_tag, dst, i * sizeof(uint32_t));
5696158879Soleg		dst += i;
5697158879Soleg	}
569898943Sluigi	/*
569998943Sluigi	 * copy all other actions
570098943Sluigi	 */
570198943Sluigi	for (src = (ipfw_insn *)actbuf; src != action; src += i) {
570298943Sluigi		i = F_LEN(src);
5703117328Sluigi		bcopy(src, dst, i * sizeof(uint32_t));
570498943Sluigi		dst += i;
570598943Sluigi	}
570698943Sluigi
5707117328Sluigi	rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd);
5708117469Sluigi	i = (char *)dst - (char *)rule;
5709119740Stmm	if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1)
571098943Sluigi		err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
571198943Sluigi	if (!do_quiet)
5712117469Sluigi		show_ipfw(rule, 0, 0);
571398943Sluigi}
571498943Sluigi
571598943Sluigistatic void
5716117328Sluigizero(int ac, char *av[], int optname /* IP_FW_ZERO or IP_FW_RESETLOG */)
571798943Sluigi{
5718170923Smaxim	uint32_t arg, saved_arg;
571998943Sluigi	int failed = EX_OK;
5720117469Sluigi	char const *name = optname == IP_FW_ZERO ?  "ZERO" : "RESETLOG";
5721170923Smaxim	char const *errstr;
572298943Sluigi
572398943Sluigi	av++; ac--;
572498943Sluigi
572598943Sluigi	if (!ac) {
572698943Sluigi		/* clear all entries */
5727117328Sluigi		if (do_cmd(optname, NULL, 0) < 0)
5728117328Sluigi			err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name);
572998943Sluigi		if (!do_quiet)
5730117328Sluigi			printf("%s.\n", optname == IP_FW_ZERO ?
5731117328Sluigi			    "Accounting cleared":"Logging counts reset");
573298943Sluigi
573398943Sluigi		return;
573498943Sluigi	}
573598943Sluigi
573698943Sluigi	while (ac) {
573798943Sluigi		/* Rule number */
573898943Sluigi		if (isdigit(**av)) {
5739170923Smaxim			arg = strtonum(*av, 0, 0xffff, &errstr);
5740170923Smaxim			if (errstr)
5741170923Smaxim				errx(EX_DATAERR,
5742170923Smaxim				    "invalid rule number %s\n", *av);
5743170923Smaxim			saved_arg = arg;
5744170923Smaxim			if (use_set)
5745170923Smaxim				arg |= (1 << 24) | ((use_set - 1) << 16);
574698943Sluigi			av++;
574798943Sluigi			ac--;
5748170923Smaxim			if (do_cmd(optname, &arg, sizeof(arg))) {
5749117328Sluigi				warn("rule %u: setsockopt(IP_FW_%s)",
5750170923Smaxim				    saved_arg, name);
575198943Sluigi				failed = EX_UNAVAILABLE;
575298943Sluigi			} else if (!do_quiet)
5753170923Smaxim				printf("Entry %d %s.\n", saved_arg,
5754117328Sluigi				    optname == IP_FW_ZERO ?
5755117328Sluigi					"cleared" : "logging count reset");
575698943Sluigi		} else {
575798943Sluigi			errx(EX_USAGE, "invalid rule number ``%s''", *av);
575898943Sluigi		}
575998943Sluigi	}
576098943Sluigi	if (failed != EX_OK)
576198943Sluigi		exit(failed);
576298943Sluigi}
576398943Sluigi
576498943Sluigistatic void
5765117544Sluigiflush(int force)
576698943Sluigi{
576798943Sluigi	int cmd = do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH;
576898943Sluigi
5769117544Sluigi	if (!force && !do_quiet) { /* need to ask user */
577098943Sluigi		int c;
577198943Sluigi
577298943Sluigi		printf("Are you sure? [yn] ");
577398943Sluigi		fflush(stdout);
577498943Sluigi		do {
577598943Sluigi			c = toupper(getc(stdin));
577698943Sluigi			while (c != '\n' && getc(stdin) != '\n')
577798943Sluigi				if (feof(stdin))
577898943Sluigi					return; /* and do not flush */
577998943Sluigi		} while (c != 'Y' && c != 'N');
578098943Sluigi		printf("\n");
578198943Sluigi		if (c == 'N')	/* user said no */
578298943Sluigi			return;
578398943Sluigi	}
5784170923Smaxim	/* `ipfw set N flush` - is the same that `ipfw delete set N` */
5785170923Smaxim	if (use_set) {
5786170923Smaxim		uint32_t arg = ((use_set - 1) & 0xffff) | (1 << 24);
5787170923Smaxim		if (do_cmd(IP_FW_DEL, &arg, sizeof(arg)) < 0)
5788170923Smaxim			err(EX_UNAVAILABLE, "setsockopt(IP_FW_DEL)");
5789170923Smaxim	} else if (do_cmd(cmd, NULL, 0) < 0)
579098943Sluigi		err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
579198943Sluigi		    do_pipe ? "DUMMYNET" : "FW");
579298943Sluigi	if (!do_quiet)
579398943Sluigi		printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules");
579498943Sluigi}
579598943Sluigi
5796117469Sluigi/*
5797117544Sluigi * Free a the (locally allocated) copy of command line arguments.
5798117469Sluigi */
5799117544Sluigistatic void
5800117544Sluigifree_args(int ac, char **av)
5801117544Sluigi{
5802117544Sluigi	int i;
5803117544Sluigi
5804117544Sluigi	for (i=0; i < ac; i++)
5805117544Sluigi		free(av[i]);
5806117544Sluigi	free(av);
5807117544Sluigi}
5808117544Sluigi
5809117544Sluigi/*
5810130281Sru * This one handles all table-related commands
5811130281Sru * 	ipfw table N add addr[/masklen] [value]
5812130281Sru * 	ipfw table N delete addr[/masklen]
5813130281Sru * 	ipfw table N flush
5814130281Sru * 	ipfw table N list
5815130281Sru */
5816130281Srustatic void
5817130281Srutable_handler(int ac, char *av[])
5818130281Sru{
5819130281Sru	ipfw_table_entry ent;
5820130281Sru	ipfw_table *tbl;
5821130281Sru	int do_add;
5822130281Sru	char *p;
5823130281Sru	socklen_t l;
5824130281Sru	uint32_t a;
5825130281Sru
5826130281Sru	ac--; av++;
5827130281Sru	if (ac && isdigit(**av)) {
5828130281Sru		ent.tbl = atoi(*av);
5829130281Sru		ac--; av++;
5830130281Sru	} else
5831130281Sru		errx(EX_USAGE, "table number required");
5832130281Sru	NEED1("table needs command");
5833140271Sbrooks	if (_substrcmp(*av, "add") == 0 ||
5834140271Sbrooks	    _substrcmp(*av, "delete") == 0) {
5835130281Sru		do_add = **av == 'a';
5836130281Sru		ac--; av++;
5837130281Sru		if (!ac)
5838130281Sru			errx(EX_USAGE, "IP address required");
5839130281Sru		p = strchr(*av, '/');
5840130281Sru		if (p) {
5841130281Sru			*p++ = '\0';
5842130281Sru			ent.masklen = atoi(p);
5843130281Sru			if (ent.masklen > 32)
5844130281Sru				errx(EX_DATAERR, "bad width ``%s''", p);
5845130281Sru		} else
5846130281Sru			ent.masklen = 32;
5847130281Sru		if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0)
5848130298Sru			errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
5849130281Sru		ac--; av++;
5850161424Sjulian		if (do_add && ac) {
5851161424Sjulian			unsigned int tval;
5852161424Sjulian			/* isdigit is a bit of a hack here.. */
5853161424Sjulian			if (strchr(*av, (int)'.') == NULL && isdigit(**av))  {
5854161424Sjulian				ent.value = strtoul(*av, NULL, 0);
5855161424Sjulian			} else {
5856161424Sjulian		        	if (lookup_host(*av, (struct in_addr *)&tval) == 0) {
5857161424Sjulian					/* The value must be stored in host order	 *
5858161424Sjulian					 * so that the values < 65k can be distinguished */
5859161424Sjulian		       			ent.value = ntohl(tval);
5860161424Sjulian				} else {
5861161424Sjulian					errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
5862161424Sjulian				}
5863161424Sjulian			}
5864161424Sjulian		} else
5865130281Sru			ent.value = 0;
5866130281Sru		if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL,
5867157335Sjulian		    &ent, sizeof(ent)) < 0) {
5868155639Sjulian			/* If running silent, don't bomb out on these errors. */
5869155639Sjulian			if (!(do_quiet && (errno == (do_add ? EEXIST : ESRCH))))
5870155639Sjulian				err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
5871155639Sjulian				    do_add ? "ADD" : "DEL");
5872155639Sjulian			/* In silent mode, react to a failed add by deleting */
5873157332Sjulian			if (do_add) {
5874155639Sjulian				do_cmd(IP_FW_TABLE_DEL, &ent, sizeof(ent));
5875155639Sjulian				if (do_cmd(IP_FW_TABLE_ADD,
5876155639Sjulian				    &ent, sizeof(ent)) < 0)
5877155639Sjulian					err(EX_OSERR,
5878155639Sjulian				            "setsockopt(IP_FW_TABLE_ADD)");
5879157332Sjulian			}
5880157335Sjulian		}
5881140271Sbrooks	} else if (_substrcmp(*av, "flush") == 0) {
5882130281Sru		if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl, sizeof(ent.tbl)) < 0)
5883130281Sru			err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
5884140271Sbrooks	} else if (_substrcmp(*av, "list") == 0) {
5885130281Sru		a = ent.tbl;
5886130281Sru		l = sizeof(a);
5887130281Sru		if (do_cmd(IP_FW_TABLE_GETSIZE, &a, (uintptr_t)&l) < 0)
5888130281Sru			err(EX_OSERR, "getsockopt(IP_FW_TABLE_GETSIZE)");
5889130281Sru		l = sizeof(*tbl) + a * sizeof(ipfw_table_entry);
5890130281Sru		tbl = malloc(l);
5891130281Sru		if (tbl == NULL)
5892130281Sru			err(EX_OSERR, "malloc");
5893130281Sru		tbl->tbl = ent.tbl;
5894130281Sru		if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0)
5895130281Sru			err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)");
5896130281Sru		for (a = 0; a < tbl->cnt; a++) {
5897161424Sjulian			/* Heuristic to print it the right way */
5898161456Sjulian			/* values < 64k are printed as numbers */
5899161424Sjulian			unsigned int tval;
5900161424Sjulian			tval = tbl->ent[a].value;
5901161424Sjulian			if (tval > 0xffff) {
5902161424Sjulian			    char tbuf[128];
5903161456Sjulian			    strncpy(tbuf, inet_ntoa(*(struct in_addr *)
5904161456Sjulian				&tbl->ent[a].addr), 127);
5905161424Sjulian			    /* inet_ntoa expects host order */
5906161424Sjulian			    tval = htonl(tval);
5907161456Sjulian			    printf("%s/%u %s\n", tbuf, tbl->ent[a].masklen,
5908161456Sjulian			        inet_ntoa(*(struct in_addr *)&tval));
5909161424Sjulian			} else {
5910161424Sjulian			    printf("%s/%u %u\n",
5911161456Sjulian			        inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
5912161456Sjulian			        tbl->ent[a].masklen, tbl->ent[a].value);
5913161424Sjulian			}
5914130281Sru		}
5915130281Sru	} else
5916130281Sru		errx(EX_USAGE, "invalid table command %s", *av);
5917130281Sru}
5918130281Sru
5919165648Spisostatic void
5920165648Spisoshow_nat(int ac, char **av) {
5921165648Spiso	struct cfg_nat *n;
5922165648Spiso	struct cfg_redir *e;
5923165648Spiso	int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
5924165648Spiso	int nat_cnt, r;
5925165648Spiso	uint8_t *data, *p;
5926165648Spiso	char **lav, *endptr;
5927165648Spiso
5928165648Spiso	do_rule = 0;
5929165648Spiso	nalloc = 1024;
5930165648Spiso	size = 0;
5931165648Spiso	data = NULL;
5932165648Spiso	ac--; av++;
5933165648Spiso
5934165648Spiso	/* Parse parameters. */
5935165648Spiso	for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
5936165648Spiso		if (!strncmp(av[0], "config", strlen(av[0]))) {
5937165648Spiso			cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
5938165648Spiso			continue;
5939165648Spiso		}
5940165648Spiso		/* Convert command line rule #. */
5941165648Spiso		frule = lrule = strtoul(av[0], &endptr, 10);
5942165648Spiso		if (*endptr == '-')
5943165648Spiso			lrule = strtoul(endptr+1, &endptr, 10);
5944165648Spiso		if (lrule == 0)
5945165648Spiso			err(EX_USAGE, "invalid rule number: %s", av[0]);
5946165648Spiso		do_rule = 1;
5947165648Spiso	}
5948165648Spiso
5949165648Spiso	nbytes = nalloc;
5950165648Spiso	while (nbytes >= nalloc) {
5951165648Spiso		nalloc = nalloc * 2;
5952165648Spiso		nbytes = nalloc;
5953165648Spiso		if ((data = realloc(data, nbytes)) == NULL)
5954165648Spiso			err(EX_OSERR, "realloc");
5955165648Spiso		if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
5956165648Spiso			err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
5957165648Spiso			    (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
5958165648Spiso	}
5959165648Spiso	if (nbytes == 0)
5960165648Spiso		exit(0);
5961165648Spiso	if (do_cfg) {
5962165648Spiso		nat_cnt = *((int *)data);
5963165648Spiso		for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
5964165648Spiso			n = (struct cfg_nat *)&data[i];
5965165648Spiso			if (do_rule) {
5966165648Spiso				if (!(frule <= n->id && lrule >= n->id))
5967165648Spiso					continue;
5968165648Spiso			}
5969165648Spiso			print_nat_config(&data[i]);
5970165648Spiso			i += sizeof(struct cfg_nat);
5971165648Spiso			e = (struct cfg_redir *)&data[i];
5972165648Spiso			if (e->mode == REDIR_ADDR || e->mode == REDIR_PORT ||
5973165648Spiso			    e->mode == REDIR_PROTO)
5974165648Spiso				i += sizeof(struct cfg_redir) + e->spool_cnt *
5975165648Spiso				    sizeof(struct cfg_spool);
5976165648Spiso		}
5977165648Spiso	} else {
5978165648Spiso		for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
5979165648Spiso			p = &data[i];
5980165648Spiso			if (p == data + nbytes)
5981165648Spiso				break;
5982165648Spiso			bcopy(p, &r, sizeof(int));
5983165648Spiso			if (do_rule) {
5984165648Spiso				if (!(frule <= r && lrule >= r))
5985165648Spiso					continue;
5986165648Spiso			}
5987165648Spiso			printf("nat %u: %s\n", r, p+sizeof(int));
5988165648Spiso		}
5989165648Spiso	}
5990165648Spiso}
5991165648Spiso
5992130281Sru/*
5993117544Sluigi * Called with the arguments (excluding program name).
5994117544Sluigi * Returns 0 if successful, 1 if empty command, errx() in case of errors.
5995117544Sluigi */
599698943Sluigistatic int
5997117328Sluigiipfw_main(int oldac, char **oldav)
599898943Sluigi{
5999117469Sluigi	int ch, ac, save_ac;
6000170923Smaxim	const char *errstr;
6001117469Sluigi	char **av, **save_av;
6002117469Sluigi	int do_acct = 0;		/* Show packet/byte count */
6003117469Sluigi
6004117469Sluigi#define WHITESP		" \t\f\v\n\r"
6005117544Sluigi	if (oldac == 0)
6006117544Sluigi		return 1;
6007117544Sluigi	else if (oldac == 1) {
6008117328Sluigi		/*
6009117328Sluigi		 * If we are called with a single string, try to split it into
6010117328Sluigi		 * arguments for subsequent parsing.
6011117328Sluigi		 * But first, remove spaces after a ',', by copying the string
6012117328Sluigi		 * in-place.
6013117328Sluigi		 */
6014117469Sluigi		char *arg = oldav[0];	/* The string... */
6015117328Sluigi		int l = strlen(arg);
6016117328Sluigi		int copy = 0;		/* 1 if we need to copy, 0 otherwise */
6017117328Sluigi		int i, j;
6018117469Sluigi		for (i = j = 0; i < l; i++) {
6019117469Sluigi			if (arg[i] == '#')	/* comment marker */
6020117469Sluigi				break;
6021117328Sluigi			if (copy) {
6022117328Sluigi				arg[j++] = arg[i];
6023117469Sluigi				copy = !index("," WHITESP, arg[i]);
6024117328Sluigi			} else {
6025117469Sluigi				copy = !index(WHITESP, arg[i]);
6026117328Sluigi				if (copy)
6027117328Sluigi					arg[j++] = arg[i];
6028117328Sluigi			}
6029117469Sluigi		}
6030117328Sluigi		if (!copy && j > 0)	/* last char was a 'blank', remove it */
6031117328Sluigi			j--;
6032117328Sluigi		l = j;			/* the new argument length */
6033117328Sluigi		arg[j++] = '\0';
6034117469Sluigi		if (l == 0)		/* empty string! */
6035117544Sluigi			return 1;
6036117328Sluigi
6037117328Sluigi		/*
6038117328Sluigi		 * First, count number of arguments. Because of the previous
6039117469Sluigi		 * processing, this is just the number of blanks plus 1.
6040117328Sluigi		 */
6041117469Sluigi		for (i = 0, ac = 1; i < l; i++)
6042117469Sluigi			if (index(WHITESP, arg[i]) != NULL)
6043117328Sluigi				ac++;
6044117328Sluigi
6045117328Sluigi		av = calloc(ac, sizeof(char *));
6046117328Sluigi
6047117328Sluigi		/*
6048117328Sluigi		 * Second, copy arguments from cmd[] to av[]. For each one,
6049117328Sluigi		 * j is the initial character, i is the one past the end.
6050117328Sluigi		 */
6051117469Sluigi		for (ac = 0, i = j = 0; i < l; i++)
6052117469Sluigi			if (index(WHITESP, arg[i]) != NULL || i == l-1) {
6053117328Sluigi				if (i == l-1)
6054117328Sluigi					i++;
6055117328Sluigi				av[ac] = calloc(i-j+1, 1);
6056117328Sluigi				bcopy(arg+j, av[ac], i-j);
6057117328Sluigi				ac++;
6058117328Sluigi				j = i + 1;
6059117328Sluigi			}
6060117328Sluigi	} else {
6061117328Sluigi		/*
6062117328Sluigi		 * If an argument ends with ',' join with the next one.
6063117328Sluigi		 */
6064117328Sluigi		int first, i, l;
6065117328Sluigi
6066117328Sluigi		av = calloc(oldac, sizeof(char *));
6067117328Sluigi		for (first = i = ac = 0, l = 0; i < oldac; i++) {
6068117328Sluigi			char *arg = oldav[i];
6069117328Sluigi			int k = strlen(arg);
6070117328Sluigi
6071117328Sluigi			l += k;
6072117328Sluigi			if (arg[k-1] != ',' || i == oldac-1) {
6073117328Sluigi				/* Time to copy. */
6074117328Sluigi				av[ac] = calloc(l+1, 1);
6075117328Sluigi				for (l=0; first <= i; first++) {
6076117328Sluigi					strcat(av[ac]+l, oldav[first]);
6077117328Sluigi					l += strlen(oldav[first]);
6078117328Sluigi				}
6079117328Sluigi				ac++;
6080117328Sluigi				l = 0;
6081117328Sluigi				first = i+1;
6082117328Sluigi			}
6083117328Sluigi		}
6084117328Sluigi	}
6085117328Sluigi
608698943Sluigi	/* Set the force flag for non-interactive processes */
6087123804Smaxim	if (!do_force)
6088123804Smaxim		do_force = !isatty(STDIN_FILENO);
608998943Sluigi
6090117469Sluigi	/* Save arguments for final freeing of memory. */
6091117469Sluigi	save_ac = ac;
6092117469Sluigi	save_av = av;
6093117469Sluigi
6094117469Sluigi	optind = optreset = 0;
6095123495Sluigi	while ((ch = getopt(ac, av, "abcdefhnNqs:STtv")) != -1)
609698943Sluigi		switch (ch) {
609798943Sluigi		case 'a':
609898943Sluigi			do_acct = 1;
609998943Sluigi			break;
6100117328Sluigi
6101123495Sluigi		case 'b':
6102123495Sluigi			comment_only = 1;
6103123495Sluigi			do_compact = 1;
6104123495Sluigi			break;
6105123495Sluigi
6106102098Sluigi		case 'c':
6107102098Sluigi			do_compact = 1;
6108102098Sluigi			break;
6109117328Sluigi
611098943Sluigi		case 'd':
611198943Sluigi			do_dynamic = 1;
611298943Sluigi			break;
6113117328Sluigi
611498943Sluigi		case 'e':
611598943Sluigi			do_expired = 1;
611698943Sluigi			break;
6117117328Sluigi
611898943Sluigi		case 'f':
611998943Sluigi			do_force = 1;
612098943Sluigi			break;
6121117328Sluigi
6122117328Sluigi		case 'h': /* help */
6123117544Sluigi			free_args(save_ac, save_av);
6124117328Sluigi			help();
6125117328Sluigi			break;	/* NOTREACHED */
6126117328Sluigi
6127117328Sluigi		case 'n':
6128117328Sluigi			test_only = 1;
6129117328Sluigi			break;
6130117328Sluigi
613198943Sluigi		case 'N':
613298943Sluigi			do_resolv = 1;
613398943Sluigi			break;
6134117328Sluigi
613598943Sluigi		case 'q':
613698943Sluigi			do_quiet = 1;
613798943Sluigi			break;
6138117328Sluigi
6139117328Sluigi		case 's': /* sort */
6140117328Sluigi			do_sort = atoi(optarg);
6141117328Sluigi			break;
6142117328Sluigi
6143101628Sluigi		case 'S':
6144101628Sluigi			show_sets = 1;
6145101628Sluigi			break;
6146117328Sluigi
614798943Sluigi		case 't':
614898943Sluigi			do_time = 1;
614998943Sluigi			break;
6150117328Sluigi
6151117472Sluigi		case 'T':
6152117472Sluigi			do_time = 2;	/* numeric timestamp */
6153117472Sluigi			break;
6154117472Sluigi
615598943Sluigi		case 'v': /* verbose */
6156117328Sluigi			verbose = 1;
615798943Sluigi			break;
6158117328Sluigi
615998943Sluigi		default:
6160117544Sluigi			free_args(save_ac, save_av);
6161117544Sluigi			return 1;
616298943Sluigi		}
616398943Sluigi
616498943Sluigi	ac -= optind;
616598943Sluigi	av += optind;
616698943Sluigi	NEED1("bad arguments, for usage summary ``ipfw''");
616798943Sluigi
616898943Sluigi	/*
6169117544Sluigi	 * An undocumented behaviour of ipfw1 was to allow rule numbers first,
6170117544Sluigi	 * e.g. "100 add allow ..." instead of "add 100 allow ...".
6171117544Sluigi	 * In case, swap first and second argument to get the normal form.
6172117544Sluigi	 */
6173117544Sluigi	if (ac > 1 && isdigit(*av[0])) {
6174117544Sluigi		char *p = av[0];
6175117544Sluigi
6176117544Sluigi		av[0] = av[1];
6177117544Sluigi		av[1] = p;
6178117544Sluigi	}
6179117544Sluigi
6180117544Sluigi	/*
6181165648Spiso	 * Optional: pipe, queue or nat.
618298943Sluigi	 */
6183165648Spiso	do_nat = 0;
6184117821Smaxim	do_pipe = 0;
6185165648Spiso	if (!strncmp(*av, "nat", strlen(*av)))
6186165648Spiso 	        do_nat = 1;
6187165648Spiso 	else if (!strncmp(*av, "pipe", strlen(*av)))
618898943Sluigi		do_pipe = 1;
6189140271Sbrooks	else if (_substrcmp(*av, "queue") == 0)
619098943Sluigi		do_pipe = 2;
6191170923Smaxim	else if (!strncmp(*av, "set", strlen(*av))) {
6192170923Smaxim		if (ac > 1 && isdigit(av[1][0])) {
6193170923Smaxim			use_set = strtonum(av[1], 0, RESVD_SET, &errstr);
6194170923Smaxim			if (errstr)
6195170923Smaxim				errx(EX_DATAERR,
6196170923Smaxim				    "invalid set number %s\n", av[1]);
6197170923Smaxim			ac -= 2; av += 2; use_set++;
6198170923Smaxim		}
6199170923Smaxim	}
6200170923Smaxim
6201165648Spiso	if (do_pipe || do_nat) {
620298943Sluigi		ac--;
620398943Sluigi		av++;
620498943Sluigi	}
620598943Sluigi	NEED1("missing command");
620698943Sluigi
620798943Sluigi	/*
6208165648Spiso	 * For pipes, queues and nats we normally say 'nat|pipe NN config'
6209165648Spiso	 * but the code is easier to parse as 'nat|pipe config NN'
621098943Sluigi	 * so we swap the two arguments.
621198943Sluigi	 */
6212165648Spiso	if ((do_pipe || do_nat) && ac > 1 && isdigit(*av[0])) {
621398943Sluigi		char *p = av[0];
6214117544Sluigi
621598943Sluigi		av[0] = av[1];
621698943Sluigi		av[1] = p;
621798943Sluigi	}
6218117328Sluigi
6219170923Smaxim	int try_next = 0;
6220170923Smaxim	if (use_set == 0) {
6221170923Smaxim		if (_substrcmp(*av, "add") == 0)
6222170923Smaxim			add(ac, av);
6223170923Smaxim		else if (do_nat && _substrcmp(*av, "show") == 0)
6224170923Smaxim 			show_nat(ac, av);
6225170923Smaxim		else if (do_pipe && _substrcmp(*av, "config") == 0)
6226170923Smaxim			config_pipe(ac, av);
6227170923Smaxim		else if (do_nat && _substrcmp(*av, "config") == 0)
6228170923Smaxim 			config_nat(ac, av);
6229170923Smaxim			else if (_substrcmp(*av, "set") == 0)
6230170923Smaxim				sets_handler(ac, av);
6231170923Smaxim			else if (_substrcmp(*av, "table") == 0)
6232170923Smaxim				table_handler(ac, av);
6233170923Smaxim			else if (_substrcmp(*av, "enable") == 0)
6234170923Smaxim				sysctl_handler(ac, av, 1);
6235170923Smaxim			else if (_substrcmp(*av, "disable") == 0)
6236170923Smaxim				sysctl_handler(ac, av, 0);
6237170923Smaxim			else
6238170923Smaxim				try_next = 1;
6239170923Smaxim	}
6240117469Sluigi
6241170923Smaxim	if (use_set || try_next) {
6242170923Smaxim		if (_substrcmp(*av, "delete") == 0)
6243170923Smaxim			delete(ac, av);
6244170923Smaxim		else if (_substrcmp(*av, "flush") == 0)
6245170923Smaxim			flush(do_force);
6246170923Smaxim		else if (_substrcmp(*av, "zero") == 0)
6247170923Smaxim			zero(ac, av, IP_FW_ZERO);
6248170923Smaxim		else if (_substrcmp(*av, "resetlog") == 0)
6249170923Smaxim			zero(ac, av, IP_FW_RESETLOG);
6250170923Smaxim		else if (_substrcmp(*av, "print") == 0 ||
6251170923Smaxim		         _substrcmp(*av, "list") == 0)
6252170923Smaxim			list(ac, av, do_acct);
6253170923Smaxim		else if (_substrcmp(*av, "show") == 0)
6254170923Smaxim			list(ac, av, 1 /* show counters */);
6255170923Smaxim		else
6256170923Smaxim			errx(EX_USAGE, "bad command `%s'", *av);
6257170923Smaxim	}
6258170923Smaxim
6259117469Sluigi	/* Free memory allocated in the argument parsing. */
6260117544Sluigi	free_args(save_ac, save_av);
626198943Sluigi	return 0;
626298943Sluigi}
626398943Sluigi
626498943Sluigi
626598943Sluigistatic void
6266106505Smaximipfw_readfile(int ac, char *av[])
626798943Sluigi{
626898943Sluigi#define MAX_ARGS	32
626998943Sluigi	char	buf[BUFSIZ];
6270117469Sluigi	char	*cmd = NULL, *filename = av[ac-1];
6271117469Sluigi	int	c, lineno=0;
627298943Sluigi	FILE	*f = NULL;
627398943Sluigi	pid_t	preproc = 0;
627498943Sluigi
6275117469Sluigi	filename = av[ac-1];
6276117469Sluigi
6277123804Smaxim	while ((c = getopt(ac, av, "cfNnp:qS")) != -1) {
627898943Sluigi		switch(c) {
6279117469Sluigi		case 'c':
6280117469Sluigi			do_compact = 1;
6281117469Sluigi			break;
6282117469Sluigi
6283123804Smaxim		case 'f':
6284123804Smaxim			do_force = 1;
6285123804Smaxim			break;
6286123804Smaxim
6287117469Sluigi		case 'N':
6288117469Sluigi			do_resolv = 1;
6289117469Sluigi			break;
6290117469Sluigi
6291117328Sluigi		case 'n':
6292117328Sluigi			test_only = 1;
6293117328Sluigi			break;
6294117328Sluigi
629598943Sluigi		case 'p':
629698943Sluigi			cmd = optarg;
6297117469Sluigi			/*
6298117469Sluigi			 * Skip previous args and delete last one, so we
6299117469Sluigi			 * pass all but the last argument to the preprocessor
6300117469Sluigi			 * via av[optind-1]
6301117469Sluigi			 */
6302117469Sluigi			av += optind - 1;
6303117469Sluigi			ac -= optind - 1;
6304162773Smaxim			if (ac < 2)
6305162773Smaxim				errx(EX_USAGE, "no filename argument");
6306117469Sluigi			av[ac-1] = NULL;
6307117469Sluigi			fprintf(stderr, "command is %s\n", av[0]);
630898943Sluigi			break;
630998943Sluigi
631098943Sluigi		case 'q':
6311117469Sluigi			do_quiet = 1;
631298943Sluigi			break;
631398943Sluigi
6314117469Sluigi		case 'S':
6315117469Sluigi			show_sets = 1;
6316117469Sluigi			break;
6317117469Sluigi
631898943Sluigi		default:
631998943Sluigi			errx(EX_USAGE, "bad arguments, for usage"
632098943Sluigi			     " summary ``ipfw''");
632198943Sluigi		}
632298943Sluigi
6323117469Sluigi		if (cmd != NULL)
6324108231Skbyanc			break;
6325108231Skbyanc	}
6326108231Skbyanc
6327117469Sluigi	if (cmd == NULL && ac != optind + 1) {
6328117469Sluigi		fprintf(stderr, "ac %d, optind %d\n", ac, optind);
6329117469Sluigi		errx(EX_USAGE, "extraneous filename arguments");
6330108231Skbyanc	}
6331108231Skbyanc
6332117469Sluigi	if ((f = fopen(filename, "r")) == NULL)
6333117469Sluigi		err(EX_UNAVAILABLE, "fopen: %s", filename);
633498943Sluigi
6335117469Sluigi	if (cmd != NULL) {			/* pipe through preprocessor */
633698943Sluigi		int pipedes[2];
633798943Sluigi
633898943Sluigi		if (pipe(pipedes) == -1)
633998943Sluigi			err(EX_OSERR, "cannot create pipe");
634098943Sluigi
6341117469Sluigi		preproc = fork();
6342117469Sluigi		if (preproc == -1)
634398943Sluigi			err(EX_OSERR, "cannot fork");
634498943Sluigi
6345117577Sluigi		if (preproc == 0) {
6346117469Sluigi			/*
6347117469Sluigi			 * Child, will run the preprocessor with the
6348117469Sluigi			 * file on stdin and the pipe on stdout.
6349117469Sluigi			 */
635098943Sluigi			if (dup2(fileno(f), 0) == -1
635198943Sluigi			    || dup2(pipedes[1], 1) == -1)
635298943Sluigi				err(EX_OSERR, "dup2()");
635398943Sluigi			fclose(f);
635498943Sluigi			close(pipedes[1]);
635598943Sluigi			close(pipedes[0]);
6356117469Sluigi			execvp(cmd, av);
635798943Sluigi			err(EX_OSERR, "execvp(%s) failed", cmd);
6358117469Sluigi		} else { /* parent, will reopen f as the pipe */
635998943Sluigi			fclose(f);
636098943Sluigi			close(pipedes[1]);
636198943Sluigi			if ((f = fdopen(pipedes[0], "r")) == NULL) {
636298943Sluigi				int savederrno = errno;
636398943Sluigi
636498943Sluigi				(void)kill(preproc, SIGTERM);
636598943Sluigi				errno = savederrno;
636698943Sluigi				err(EX_OSERR, "fdopen()");
636798943Sluigi			}
636898943Sluigi		}
636998943Sluigi	}
637098943Sluigi
6371117469Sluigi	while (fgets(buf, BUFSIZ, f)) {		/* read commands */
6372117469Sluigi		char linename[10];
6373117469Sluigi		char *args[1];
6374117469Sluigi
637598943Sluigi		lineno++;
637698943Sluigi		sprintf(linename, "Line %d", lineno);
6377117328Sluigi		setprogname(linename); /* XXX */
6378117469Sluigi		args[0] = buf;
6379117469Sluigi		ipfw_main(1, args);
638098943Sluigi	}
638198943Sluigi	fclose(f);
6382117469Sluigi	if (cmd != NULL) {
6383117469Sluigi		int status;
6384117469Sluigi
638598943Sluigi		if (waitpid(preproc, &status, 0) == -1)
638698943Sluigi			errx(EX_OSERR, "waitpid()");
638798943Sluigi		if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
638898943Sluigi			errx(EX_UNAVAILABLE,
638998943Sluigi			    "preprocessor exited with status %d",
639098943Sluigi			    WEXITSTATUS(status));
639198943Sluigi		else if (WIFSIGNALED(status))
639298943Sluigi			errx(EX_UNAVAILABLE,
639398943Sluigi			    "preprocessor exited with signal %d",
639498943Sluigi			    WTERMSIG(status));
639598943Sluigi	}
639698943Sluigi}
639798943Sluigi
639898943Sluigiint
639998943Sluigimain(int ac, char *av[])
640098943Sluigi{
640198943Sluigi	/*
640298943Sluigi	 * If the last argument is an absolute pathname, interpret it
640398943Sluigi	 * as a file to be preprocessed.
640498943Sluigi	 */
640598943Sluigi
640698943Sluigi	if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0)
640798943Sluigi		ipfw_readfile(ac, av);
6408117544Sluigi	else {
6409117544Sluigi		if (ipfw_main(ac-1, av+1))
6410117544Sluigi			show_usage();
6411117544Sluigi	}
641298943Sluigi	return EX_OK;
641398943Sluigi}
6414