ipfw2.c revision 178888
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 178888 2008-05-09 23:03:00Z julian $
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
50175659Srwatson#define IPFW_INTERNAL	/* Access to protected structures in ip_fw.h. */
51175659Srwatson
52169424Smaxim#include <net/ethernet.h>
5398943Sluigi#include <net/if.h>
54165648Spiso#include <net/if_dl.h>
55136071Sgreen#include <net/pfvar.h>
56145246Sbrooks#include <net/route.h> /* def. of struct route */
5798943Sluigi#include <netinet/in.h>
5898943Sluigi#include <netinet/in_systm.h>
5998943Sluigi#include <netinet/ip.h>
6098943Sluigi#include <netinet/ip_icmp.h>
61145246Sbrooks#include <netinet/icmp6.h>
6298943Sluigi#include <netinet/ip_fw.h>
6398943Sluigi#include <netinet/ip_dummynet.h>
6498943Sluigi#include <netinet/tcp.h>
6598943Sluigi#include <arpa/inet.h>
66165648Spiso#include <alias.h>
6798943Sluigi
68117328Sluigiint
69176391Sjulian		do_value_as_ip,		/* show table value as IP */
7098943Sluigi		do_resolv,		/* Would try to resolve all */
7198943Sluigi		do_time,		/* Show time stamps */
7298943Sluigi		do_quiet,		/* Be quiet in add and flush */
7398943Sluigi		do_pipe,		/* this cmd refers to a pipe */
74165648Spiso	        do_nat, 		/* Nat configuration. */
7598943Sluigi		do_sort,		/* field to sort results (0 = no) */
7698943Sluigi		do_dynamic,		/* display dynamic rules */
7798943Sluigi		do_expired,		/* display expired dynamic rules */
78102098Sluigi		do_compact,		/* show rules in compact mode */
79123804Smaxim		do_force,		/* do not ask for confirmation */
80170923Smaxim		use_set,		/* work with specified set number */
81101628Sluigi		show_sets,		/* display rule sets */
82117328Sluigi		test_only,		/* only check syntax */
83123495Sluigi		comment_only,		/* only print action and comment */
8498943Sluigi		verbose;
8598943Sluigi
8698943Sluigi#define	IP_MASK_ALL	0xffffffff
87130013Scsjp/*
88130013Scsjp * the following macro returns an error message if we run out of
89130013Scsjp * arguments.
90130013Scsjp */
91130013Scsjp#define NEED1(msg)      {if (!ac) errx(EX_USAGE, msg);}
9298943Sluigi
93159636Soleg#define GET_UINT_ARG(arg, min, max, tok, s_x) do {			\
94159636Soleg	if (!ac)							\
95159636Soleg		errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \
96159636Soleg	if (_substrcmp(*av, "tablearg") == 0) {				\
97159636Soleg		arg = IP_FW_TABLEARG;					\
98159636Soleg		break;							\
99159636Soleg	}								\
100159636Soleg									\
101159636Soleg	{								\
102159636Soleg	long val;							\
103159636Soleg	char *end;							\
104159636Soleg									\
105159636Soleg	val = strtol(*av, &end, 10);					\
106159636Soleg									\
107159636Soleg	if (!isdigit(**av) || *end != '\0' || (val == 0 && errno == EINVAL)) \
108159636Soleg		errx(EX_DATAERR, "%s: invalid argument: %s",		\
109159636Soleg		    match_value(s_x, tok), *av);			\
110159636Soleg									\
111159636Soleg	if (errno == ERANGE || val < min || val > max)			\
112159636Soleg		errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \
113159636Soleg		    match_value(s_x, tok), min, max, *av);		\
114159636Soleg									\
115159636Soleg	if (val == IP_FW_TABLEARG)					\
116159636Soleg		errx(EX_DATAERR, "%s: illegal argument value: %s",	\
117159636Soleg		    match_value(s_x, tok), *av);			\
118159636Soleg	arg = val;							\
119159636Soleg	}								\
120158879Soleg} while (0)
121158879Soleg
122159636Soleg#define PRINT_UINT_ARG(str, arg) do {					\
123159636Soleg	if (str != NULL)						\
124159636Soleg		printf("%s",str);					\
125159636Soleg	if (arg == IP_FW_TABLEARG)					\
126159636Soleg		printf("tablearg");					\
127159636Soleg	else								\
128159636Soleg		printf("%u", (uint32_t)arg);				\
129159636Soleg} while (0)
130159636Soleg
13198943Sluigi/*
132117328Sluigi * _s_x is a structure that stores a string <-> token pairs, used in
133117328Sluigi * various places in the parser. Entries are stored in arrays,
134117328Sluigi * with an entry with s=NULL as terminator.
135117328Sluigi * The search routines are match_token() and match_value().
136117328Sluigi * Often, an element with x=0 contains an error string.
13798943Sluigi *
13898943Sluigi */
13998943Sluigistruct _s_x {
140117469Sluigi	char const *s;
14198943Sluigi	int x;
14298943Sluigi};
14398943Sluigi
14498943Sluigistatic struct _s_x f_tcpflags[] = {
14598943Sluigi	{ "syn", TH_SYN },
14698943Sluigi	{ "fin", TH_FIN },
14798943Sluigi	{ "ack", TH_ACK },
14898943Sluigi	{ "psh", TH_PUSH },
14998943Sluigi	{ "rst", TH_RST },
15098943Sluigi	{ "urg", TH_URG },
15198943Sluigi	{ "tcp flag", 0 },
15298943Sluigi	{ NULL,	0 }
15398943Sluigi};
15498943Sluigi
15598943Sluigistatic struct _s_x f_tcpopts[] = {
15698943Sluigi	{ "mss",	IP_FW_TCPOPT_MSS },
15798943Sluigi	{ "maxseg",	IP_FW_TCPOPT_MSS },
15898943Sluigi	{ "window",	IP_FW_TCPOPT_WINDOW },
15998943Sluigi	{ "sack",	IP_FW_TCPOPT_SACK },
16098943Sluigi	{ "ts",		IP_FW_TCPOPT_TS },
16198943Sluigi	{ "timestamp",	IP_FW_TCPOPT_TS },
16298943Sluigi	{ "cc",		IP_FW_TCPOPT_CC },
16398943Sluigi	{ "tcp option",	0 },
16498943Sluigi	{ NULL,	0 }
16598943Sluigi};
16698943Sluigi
16798943Sluigi/*
16898943Sluigi * IP options span the range 0 to 255 so we need to remap them
16998943Sluigi * (though in fact only the low 5 bits are significant).
17098943Sluigi */
17198943Sluigistatic struct _s_x f_ipopts[] = {
17298943Sluigi	{ "ssrr",	IP_FW_IPOPT_SSRR},
17398943Sluigi	{ "lsrr",	IP_FW_IPOPT_LSRR},
17498943Sluigi	{ "rr",		IP_FW_IPOPT_RR},
17598943Sluigi	{ "ts",		IP_FW_IPOPT_TS},
17698943Sluigi	{ "ip option",	0 },
17798943Sluigi	{ NULL,	0 }
17898943Sluigi};
17998943Sluigi
18098943Sluigistatic struct _s_x f_iptos[] = {
18198943Sluigi	{ "lowdelay",	IPTOS_LOWDELAY},
18298943Sluigi	{ "throughput",	IPTOS_THROUGHPUT},
18398943Sluigi	{ "reliability", IPTOS_RELIABILITY},
18498943Sluigi	{ "mincost",	IPTOS_MINCOST},
185172801Srpaulo	{ "congestion",	IPTOS_ECN_CE},
186172801Srpaulo	{ "ecntransport", IPTOS_ECN_ECT0},
18798943Sluigi	{ "ip tos option", 0},
18898943Sluigi	{ NULL,	0 }
18998943Sluigi};
19098943Sluigi
19198943Sluigistatic struct _s_x limit_masks[] = {
19298943Sluigi	{"all",		DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
19398943Sluigi	{"src-addr",	DYN_SRC_ADDR},
19498943Sluigi	{"src-port",	DYN_SRC_PORT},
19598943Sluigi	{"dst-addr",	DYN_DST_ADDR},
19698943Sluigi	{"dst-port",	DYN_DST_PORT},
19798943Sluigi	{NULL,		0}
19898943Sluigi};
19998943Sluigi
20098943Sluigi/*
20198943Sluigi * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines
20298943Sluigi * This is only used in this code.
20398943Sluigi */
20498943Sluigi#define IPPROTO_ETHERTYPE	0x1000
20598943Sluigistatic struct _s_x ether_types[] = {
20698943Sluigi    /*
20798943Sluigi     * Note, we cannot use "-:&/" in the names because they are field
20898943Sluigi     * separators in the type specifications. Also, we use s = NULL as
20998943Sluigi     * end-delimiter, because a type of 0 can be legal.
21098943Sluigi     */
21198943Sluigi	{ "ip",		0x0800 },
21298943Sluigi	{ "ipv4",	0x0800 },
21398943Sluigi	{ "ipv6",	0x86dd },
21498943Sluigi	{ "arp",	0x0806 },
21598943Sluigi	{ "rarp",	0x8035 },
21698943Sluigi	{ "vlan",	0x8100 },
21798943Sluigi	{ "loop",	0x9000 },
21898943Sluigi	{ "trail",	0x1000 },
21998943Sluigi	{ "at",		0x809b },
22098943Sluigi	{ "atalk",	0x809b },
22198943Sluigi	{ "aarp",	0x80f3 },
22298943Sluigi	{ "pppoe_disc",	0x8863 },
22398943Sluigi	{ "pppoe_sess",	0x8864 },
22498943Sluigi	{ "ipx_8022",	0x00E0 },
22598943Sluigi	{ "ipx_8023",	0x0000 },
22698943Sluigi	{ "ipx_ii",	0x8137 },
22798943Sluigi	{ "ipx_snap",	0x8137 },
22898943Sluigi	{ "ipx",	0x8137 },
22998943Sluigi	{ "ns",		0x0600 },
23098943Sluigi	{ NULL,		0 }
23198943Sluigi};
23298943Sluigi
23398943Sluigistatic void show_usage(void);
23498943Sluigi
23598943Sluigienum tokens {
23698943Sluigi	TOK_NULL=0,
23798943Sluigi
23898943Sluigi	TOK_OR,
23998943Sluigi	TOK_NOT,
240101641Sluigi	TOK_STARTBRACE,
241101641Sluigi	TOK_ENDBRACE,
24298943Sluigi
24398943Sluigi	TOK_ACCEPT,
24498943Sluigi	TOK_COUNT,
24598943Sluigi	TOK_PIPE,
24698943Sluigi	TOK_QUEUE,
24798943Sluigi	TOK_DIVERT,
24898943Sluigi	TOK_TEE,
249141351Sglebius	TOK_NETGRAPH,
250141351Sglebius	TOK_NGTEE,
25198943Sluigi	TOK_FORWARD,
25298943Sluigi	TOK_SKIPTO,
25398943Sluigi	TOK_DENY,
25498943Sluigi	TOK_REJECT,
25598943Sluigi	TOK_RESET,
25698943Sluigi	TOK_UNREACH,
25798943Sluigi	TOK_CHECKSTATE,
258165648Spiso	TOK_NAT,
25998943Sluigi
260136071Sgreen	TOK_ALTQ,
261136071Sgreen	TOK_LOG,
262158879Soleg	TOK_TAG,
263158879Soleg	TOK_UNTAG,
264136071Sgreen
265158879Soleg	TOK_TAGGED,
26698943Sluigi	TOK_UID,
26798943Sluigi	TOK_GID,
268133600Scsjp	TOK_JAIL,
26998943Sluigi	TOK_IN,
27098943Sluigi	TOK_LIMIT,
27198943Sluigi	TOK_KEEPSTATE,
27298943Sluigi	TOK_LAYER2,
27398943Sluigi	TOK_OUT,
274136073Sgreen	TOK_DIVERTED,
275136073Sgreen	TOK_DIVERTEDLOOPBACK,
276136073Sgreen	TOK_DIVERTEDOUTPUT,
27798943Sluigi	TOK_XMIT,
27898943Sluigi	TOK_RECV,
27998943Sluigi	TOK_VIA,
28098943Sluigi	TOK_FRAG,
28198943Sluigi	TOK_IPOPTS,
28298943Sluigi	TOK_IPLEN,
28398943Sluigi	TOK_IPID,
28498943Sluigi	TOK_IPPRECEDENCE,
28598943Sluigi	TOK_IPTOS,
28698943Sluigi	TOK_IPTTL,
28798943Sluigi	TOK_IPVER,
28898943Sluigi	TOK_ESTAB,
28998943Sluigi	TOK_SETUP,
290136075Sgreen	TOK_TCPDATALEN,
29198943Sluigi	TOK_TCPFLAGS,
29298943Sluigi	TOK_TCPOPTS,
29398943Sluigi	TOK_TCPSEQ,
29498943Sluigi	TOK_TCPACK,
29598943Sluigi	TOK_TCPWIN,
29698943Sluigi	TOK_ICMPTYPES,
297102087Sluigi	TOK_MAC,
298102087Sluigi	TOK_MACTYPE,
299112250Scjc	TOK_VERREVPATH,
300128575Sandre	TOK_VERSRCREACH,
301133387Sandre	TOK_ANTISPOOF,
302117241Sluigi	TOK_IPSEC,
303117469Sluigi	TOK_COMMENT,
30498943Sluigi
30598943Sluigi	TOK_PLR,
306101978Sluigi	TOK_NOERROR,
30798943Sluigi	TOK_BUCKETS,
30898943Sluigi	TOK_DSTIP,
30998943Sluigi	TOK_SRCIP,
31098943Sluigi	TOK_DSTPORT,
31198943Sluigi	TOK_SRCPORT,
31298943Sluigi	TOK_ALL,
31398943Sluigi	TOK_MASK,
31498943Sluigi	TOK_BW,
31598943Sluigi	TOK_DELAY,
31698943Sluigi	TOK_RED,
31798943Sluigi	TOK_GRED,
31898943Sluigi	TOK_DROPTAIL,
31998943Sluigi	TOK_PROTO,
32098943Sluigi	TOK_WEIGHT,
321165648Spiso	TOK_IP,
322165648Spiso	TOK_IF,
323165648Spiso 	TOK_ALOG,
324165648Spiso 	TOK_DENY_INC,
325165648Spiso 	TOK_SAME_PORTS,
326165648Spiso 	TOK_UNREG_ONLY,
327165648Spiso 	TOK_RESET_ADDR,
328165648Spiso 	TOK_ALIAS_REV,
329165648Spiso 	TOK_PROXY_ONLY,
330165648Spiso	TOK_REDIR_ADDR,
331165648Spiso	TOK_REDIR_PORT,
332165648Spiso	TOK_REDIR_PROTO,
333145246Sbrooks
334145246Sbrooks	TOK_IPV6,
335145246Sbrooks	TOK_FLOWID,
336145246Sbrooks	TOK_ICMP6TYPES,
337145246Sbrooks	TOK_EXT6HDR,
338145246Sbrooks	TOK_DSTIP6,
339145246Sbrooks	TOK_SRCIP6,
340146894Smlaier
341146894Smlaier	TOK_IPV4,
342149020Sbz	TOK_UNREACH6,
343149020Sbz	TOK_RESET6,
344178888Sjulian
345178888Sjulian	TOK_FIB,
346178888Sjulian	TOK_SETFIB,
34798943Sluigi};
34898943Sluigi
34998943Sluigistruct _s_x dummynet_params[] = {
35098943Sluigi	{ "plr",		TOK_PLR },
351101978Sluigi	{ "noerror",		TOK_NOERROR },
35298943Sluigi	{ "buckets",		TOK_BUCKETS },
35398943Sluigi	{ "dst-ip",		TOK_DSTIP },
35498943Sluigi	{ "src-ip",		TOK_SRCIP },
35598943Sluigi	{ "dst-port",		TOK_DSTPORT },
35698943Sluigi	{ "src-port",		TOK_SRCPORT },
35798943Sluigi	{ "proto",		TOK_PROTO },
35898943Sluigi	{ "weight",		TOK_WEIGHT },
35998943Sluigi	{ "all",		TOK_ALL },
36098943Sluigi	{ "mask",		TOK_MASK },
36198943Sluigi	{ "droptail",		TOK_DROPTAIL },
36298943Sluigi	{ "red",		TOK_RED },
36398943Sluigi	{ "gred",		TOK_GRED },
36498943Sluigi	{ "bw",			TOK_BW },
36598943Sluigi	{ "bandwidth",		TOK_BW },
36698943Sluigi	{ "delay",		TOK_DELAY },
36799475Sluigi	{ "pipe",		TOK_PIPE },
36898943Sluigi	{ "queue",		TOK_QUEUE },
369145246Sbrooks	{ "flow-id",		TOK_FLOWID},
370145246Sbrooks	{ "dst-ipv6",		TOK_DSTIP6},
371145246Sbrooks	{ "dst-ip6",		TOK_DSTIP6},
372145246Sbrooks	{ "src-ipv6",		TOK_SRCIP6},
373145246Sbrooks	{ "src-ip6",		TOK_SRCIP6},
37498943Sluigi	{ "dummynet-params",	TOK_NULL },
375117328Sluigi	{ NULL, 0 }	/* terminator */
37698943Sluigi};
37798943Sluigi
378165648Spisostruct _s_x nat_params[] = {
379165648Spiso	{ "ip",	                TOK_IP },
380165648Spiso	{ "if",	                TOK_IF },
381165648Spiso 	{ "log",                TOK_ALOG },
382165648Spiso 	{ "deny_in",	        TOK_DENY_INC },
383165648Spiso 	{ "same_ports",	        TOK_SAME_PORTS },
384165648Spiso 	{ "unreg_only",	        TOK_UNREG_ONLY },
385165648Spiso 	{ "reset",	        TOK_RESET_ADDR },
386165648Spiso 	{ "reverse",	        TOK_ALIAS_REV },
387165648Spiso 	{ "proxy_only",	        TOK_PROXY_ONLY },
388165648Spiso	{ "redirect_addr",	TOK_REDIR_ADDR },
389165648Spiso	{ "redirect_port",	TOK_REDIR_PORT },
390165648Spiso	{ "redirect_proto",	TOK_REDIR_PROTO },
391165648Spiso 	{ NULL, 0 }	/* terminator */
392165648Spiso};
393165648Spiso
39498943Sluigistruct _s_x rule_actions[] = {
39598943Sluigi	{ "accept",		TOK_ACCEPT },
39698943Sluigi	{ "pass",		TOK_ACCEPT },
39798943Sluigi	{ "allow",		TOK_ACCEPT },
39898943Sluigi	{ "permit",		TOK_ACCEPT },
39998943Sluigi	{ "count",		TOK_COUNT },
40098943Sluigi	{ "pipe",		TOK_PIPE },
40198943Sluigi	{ "queue",		TOK_QUEUE },
40298943Sluigi	{ "divert",		TOK_DIVERT },
40398943Sluigi	{ "tee",		TOK_TEE },
404141351Sglebius	{ "netgraph",		TOK_NETGRAPH },
405141351Sglebius	{ "ngtee",		TOK_NGTEE },
40698943Sluigi	{ "fwd",		TOK_FORWARD },
40798943Sluigi	{ "forward",		TOK_FORWARD },
40898943Sluigi	{ "skipto",		TOK_SKIPTO },
40998943Sluigi	{ "deny",		TOK_DENY },
41098943Sluigi	{ "drop",		TOK_DENY },
41198943Sluigi	{ "reject",		TOK_REJECT },
412149020Sbz	{ "reset6",		TOK_RESET6 },
41398943Sluigi	{ "reset",		TOK_RESET },
414149020Sbz	{ "unreach6",		TOK_UNREACH6 },
41599475Sluigi	{ "unreach",		TOK_UNREACH },
41698943Sluigi	{ "check-state",	TOK_CHECKSTATE },
417117469Sluigi	{ "//",			TOK_COMMENT },
418165648Spiso	{ "nat",                TOK_NAT },
419178888Sjulian	{ "setfib",		TOK_SETFIB },
420117328Sluigi	{ NULL, 0 }	/* terminator */
42198943Sluigi};
42298943Sluigi
423136071Sgreenstruct _s_x rule_action_params[] = {
424136071Sgreen	{ "altq",		TOK_ALTQ },
425136071Sgreen	{ "log",		TOK_LOG },
426158879Soleg	{ "tag",		TOK_TAG },
427158879Soleg	{ "untag",		TOK_UNTAG },
428136071Sgreen	{ NULL, 0 }	/* terminator */
429136071Sgreen};
430136071Sgreen
43198943Sluigistruct _s_x rule_options[] = {
432158879Soleg	{ "tagged",		TOK_TAGGED },
43398943Sluigi	{ "uid",		TOK_UID },
43498943Sluigi	{ "gid",		TOK_GID },
435133600Scsjp	{ "jail",		TOK_JAIL },
43698943Sluigi	{ "in",			TOK_IN },
43798943Sluigi	{ "limit",		TOK_LIMIT },
43898943Sluigi	{ "keep-state",		TOK_KEEPSTATE },
43998943Sluigi	{ "bridged",		TOK_LAYER2 },
44098943Sluigi	{ "layer2",		TOK_LAYER2 },
44198943Sluigi	{ "out",		TOK_OUT },
442136073Sgreen	{ "diverted",		TOK_DIVERTED },
443136073Sgreen	{ "diverted-loopback",	TOK_DIVERTEDLOOPBACK },
444136073Sgreen	{ "diverted-output",	TOK_DIVERTEDOUTPUT },
44598943Sluigi	{ "xmit",		TOK_XMIT },
44698943Sluigi	{ "recv",		TOK_RECV },
44798943Sluigi	{ "via",		TOK_VIA },
44898943Sluigi	{ "fragment",		TOK_FRAG },
44998943Sluigi	{ "frag",		TOK_FRAG },
450178888Sjulian	{ "fib",		TOK_FIB },
45198943Sluigi	{ "ipoptions",		TOK_IPOPTS },
45298943Sluigi	{ "ipopts",		TOK_IPOPTS },
45398943Sluigi	{ "iplen",		TOK_IPLEN },
45498943Sluigi	{ "ipid",		TOK_IPID },
45598943Sluigi	{ "ipprecedence",	TOK_IPPRECEDENCE },
45698943Sluigi	{ "iptos",		TOK_IPTOS },
45798943Sluigi	{ "ipttl",		TOK_IPTTL },
45898943Sluigi	{ "ipversion",		TOK_IPVER },
45998943Sluigi	{ "ipver",		TOK_IPVER },
46098943Sluigi	{ "estab",		TOK_ESTAB },
46198943Sluigi	{ "established",	TOK_ESTAB },
46298943Sluigi	{ "setup",		TOK_SETUP },
463136075Sgreen	{ "tcpdatalen",		TOK_TCPDATALEN },
46498943Sluigi	{ "tcpflags",		TOK_TCPFLAGS },
46598943Sluigi	{ "tcpflgs",		TOK_TCPFLAGS },
46698943Sluigi	{ "tcpoptions",		TOK_TCPOPTS },
46798943Sluigi	{ "tcpopts",		TOK_TCPOPTS },
46898943Sluigi	{ "tcpseq",		TOK_TCPSEQ },
46998943Sluigi	{ "tcpack",		TOK_TCPACK },
47098943Sluigi	{ "tcpwin",		TOK_TCPWIN },
47199909Sluigi	{ "icmptype",		TOK_ICMPTYPES },
47298943Sluigi	{ "icmptypes",		TOK_ICMPTYPES },
473102087Sluigi	{ "dst-ip",		TOK_DSTIP },
474102087Sluigi	{ "src-ip",		TOK_SRCIP },
475102087Sluigi	{ "dst-port",		TOK_DSTPORT },
476102087Sluigi	{ "src-port",		TOK_SRCPORT },
477102087Sluigi	{ "proto",		TOK_PROTO },
478102087Sluigi	{ "MAC",		TOK_MAC },
479102087Sluigi	{ "mac",		TOK_MAC },
480102087Sluigi	{ "mac-type",		TOK_MACTYPE },
481112250Scjc	{ "verrevpath",		TOK_VERREVPATH },
482128575Sandre	{ "versrcreach",	TOK_VERSRCREACH },
483133387Sandre	{ "antispoof",		TOK_ANTISPOOF },
484117241Sluigi	{ "ipsec",		TOK_IPSEC },
485145246Sbrooks	{ "icmp6type",		TOK_ICMP6TYPES },
486145246Sbrooks	{ "icmp6types",		TOK_ICMP6TYPES },
487145246Sbrooks	{ "ext6hdr",		TOK_EXT6HDR},
488145246Sbrooks	{ "flow-id",		TOK_FLOWID},
489145246Sbrooks	{ "ipv6",		TOK_IPV6},
490145246Sbrooks	{ "ip6",		TOK_IPV6},
491146894Smlaier	{ "ipv4",		TOK_IPV4},
492146894Smlaier	{ "ip4",		TOK_IPV4},
493145246Sbrooks	{ "dst-ipv6",		TOK_DSTIP6},
494145246Sbrooks	{ "dst-ip6",		TOK_DSTIP6},
495145246Sbrooks	{ "src-ipv6",		TOK_SRCIP6},
496145246Sbrooks	{ "src-ip6",		TOK_SRCIP6},
497117469Sluigi	{ "//",			TOK_COMMENT },
49898943Sluigi
49998943Sluigi	{ "not",		TOK_NOT },		/* pseudo option */
50098943Sluigi	{ "!", /* escape ? */	TOK_NOT },		/* pseudo option */
50198943Sluigi	{ "or",			TOK_OR },		/* pseudo option */
50298943Sluigi	{ "|", /* escape */	TOK_OR },		/* pseudo option */
503101641Sluigi	{ "{",			TOK_STARTBRACE },	/* pseudo option */
504101641Sluigi	{ "(",			TOK_STARTBRACE },	/* pseudo option */
505101641Sluigi	{ "}",			TOK_ENDBRACE },		/* pseudo option */
506101641Sluigi	{ ")",			TOK_ENDBRACE },		/* pseudo option */
507117328Sluigi	{ NULL, 0 }	/* terminator */
50898943Sluigi};
50998943Sluigi
510153374Sglebius#define	TABLEARG	"tablearg"
511153374Sglebius
512117328Sluigistatic __inline uint64_t
513117328Sluigialign_uint64(uint64_t *pll) {
514117328Sluigi	uint64_t ret;
515115793Sticso
516115793Sticso	bcopy (pll, &ret, sizeof(ret));
517115793Sticso	return ret;
518129389Sstefanf}
519115793Sticso
520117328Sluigi/*
521117328Sluigi * conditionally runs the command.
522117328Sluigi */
523117469Sluigistatic int
524119740Stmmdo_cmd(int optname, void *optval, uintptr_t optlen)
525117328Sluigi{
526117328Sluigi	static int s = -1;	/* the socket */
527117328Sluigi	int i;
528117577Sluigi
529117328Sluigi	if (test_only)
530117328Sluigi		return 0;
531117328Sluigi
532117328Sluigi	if (s == -1)
533117328Sluigi		s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
534117328Sluigi	if (s < 0)
535117328Sluigi		err(EX_UNAVAILABLE, "socket");
536117328Sluigi
537117328Sluigi	if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
538130281Sru	    optname == IP_FW_ADD || optname == IP_FW_TABLE_LIST ||
539165648Spiso	    optname == IP_FW_TABLE_GETSIZE ||
540165648Spiso	    optname == IP_FW_NAT_GET_CONFIG ||
541165648Spiso	    optname == IP_FW_NAT_GET_LOG)
542117328Sluigi		i = getsockopt(s, IPPROTO_IP, optname, optval,
543117328Sluigi			(socklen_t *)optlen);
544117328Sluigi	else
545117328Sluigi		i = setsockopt(s, IPPROTO_IP, optname, optval, optlen);
546117328Sluigi	return i;
547117328Sluigi}
548117328Sluigi
54998943Sluigi/**
55098943Sluigi * match_token takes a table and a string, returns the value associated
551117328Sluigi * with the string (-1 in case of failure).
55298943Sluigi */
55398943Sluigistatic int
55498943Sluigimatch_token(struct _s_x *table, char *string)
55598943Sluigi{
55698943Sluigi	struct _s_x *pt;
557117469Sluigi	uint i = strlen(string);
55898943Sluigi
55998943Sluigi	for (pt = table ; i && pt->s != NULL ; pt++)
56098943Sluigi		if (strlen(pt->s) == i && !bcmp(string, pt->s, i))
56198943Sluigi			return pt->x;
56298943Sluigi	return -1;
563129389Sstefanf}
56498943Sluigi
565117328Sluigi/**
566117328Sluigi * match_value takes a table and a value, returns the string associated
567117328Sluigi * with the value (NULL in case of failure).
568117328Sluigi */
569117469Sluigistatic char const *
570117469Sluigimatch_value(struct _s_x *p, int value)
57198943Sluigi{
57298943Sluigi	for (; p->s != NULL; p++)
57398943Sluigi		if (p->x == value)
57498943Sluigi			return p->s;
57598943Sluigi	return NULL;
57698943Sluigi}
57798943Sluigi
57898943Sluigi/*
579140271Sbrooks * _substrcmp takes two strings and returns 1 if they do not match,
580140271Sbrooks * and 0 if they match exactly or the first string is a sub-string
581140271Sbrooks * of the second.  A warning is printed to stderr in the case that the
582140271Sbrooks * first string is a sub-string of the second.
583140271Sbrooks *
584140271Sbrooks * This function will be removed in the future through the usual
585140271Sbrooks * deprecation process.
586140271Sbrooks */
587140271Sbrooksstatic int
588140271Sbrooks_substrcmp(const char *str1, const char* str2)
589140271Sbrooks{
590140271Sbrooks
591140271Sbrooks	if (strncmp(str1, str2, strlen(str1)) != 0)
592140271Sbrooks		return 1;
593140271Sbrooks
594140271Sbrooks	if (strlen(str1) != strlen(str2))
595140271Sbrooks		warnx("DEPRECATED: '%s' matched '%s' as a sub-string",
596140271Sbrooks		    str1, str2);
597140271Sbrooks	return 0;
598140271Sbrooks}
599140271Sbrooks
600140271Sbrooks/*
601140271Sbrooks * _substrcmp2 takes three strings and returns 1 if the first two do not match,
602140271Sbrooks * and 0 if they match exactly or the second string is a sub-string
603140271Sbrooks * of the first.  A warning is printed to stderr in the case that the
604140271Sbrooks * first string does not match the third.
605140271Sbrooks *
606140271Sbrooks * This function exists to warn about the bizzare construction
607140271Sbrooks * strncmp(str, "by", 2) which is used to allow people to use a shotcut
608140271Sbrooks * for "bytes".  The problem is that in addition to accepting "by",
609140271Sbrooks * "byt", "byte", and "bytes", it also excepts "by_rabid_dogs" and any
610140271Sbrooks * other string beginning with "by".
611140271Sbrooks *
612140271Sbrooks * This function will be removed in the future through the usual
613140271Sbrooks * deprecation process.
614140271Sbrooks */
615140271Sbrooksstatic int
616140271Sbrooks_substrcmp2(const char *str1, const char* str2, const char* str3)
617140271Sbrooks{
618140271Sbrooks
619140271Sbrooks	if (strncmp(str1, str2, strlen(str2)) != 0)
620140271Sbrooks		return 1;
621140271Sbrooks
622140271Sbrooks	if (strcmp(str1, str3) != 0)
623140271Sbrooks		warnx("DEPRECATED: '%s' matched '%s'",
624140271Sbrooks		    str1, str3);
625140271Sbrooks	return 0;
626140271Sbrooks}
627140271Sbrooks
628140271Sbrooks/*
62998943Sluigi * prints one port, symbolic or numeric
63098943Sluigi */
63198943Sluigistatic void
632117328Sluigiprint_port(int proto, uint16_t port)
63398943Sluigi{
63498943Sluigi
63598943Sluigi	if (proto == IPPROTO_ETHERTYPE) {
636117469Sluigi		char const *s;
63798943Sluigi
63898943Sluigi		if (do_resolv && (s = match_value(ether_types, port)) )
63998943Sluigi			printf("%s", s);
64098943Sluigi		else
64198943Sluigi			printf("0x%04x", port);
64298943Sluigi	} else {
64398943Sluigi		struct servent *se = NULL;
64498943Sluigi		if (do_resolv) {
64598943Sluigi			struct protoent *pe = getprotobynumber(proto);
64698943Sluigi
64798943Sluigi			se = getservbyport(htons(port), pe ? pe->p_name : NULL);
64898943Sluigi		}
64998943Sluigi		if (se)
65098943Sluigi			printf("%s", se->s_name);
65198943Sluigi		else
65298943Sluigi			printf("%d", port);
65398943Sluigi	}
65498943Sluigi}
65598943Sluigi
656117328Sluigistruct _s_x _port_name[] = {
657117328Sluigi	{"dst-port",	O_IP_DSTPORT},
658117328Sluigi	{"src-port",	O_IP_SRCPORT},
659117328Sluigi	{"ipid",	O_IPID},
660117328Sluigi	{"iplen",	O_IPLEN},
661117328Sluigi	{"ipttl",	O_IPTTL},
662117328Sluigi	{"mac-type",	O_MAC_TYPE},
663136075Sgreen	{"tcpdatalen",	O_TCPDATALEN},
664158879Soleg	{"tagged",	O_TAGGED},
665117328Sluigi	{NULL,		0}
666117328Sluigi};
667117328Sluigi
66898943Sluigi/*
669117328Sluigi * Print the values in a list 16-bit items of the types above.
67098943Sluigi * XXX todo: add support for mask.
67198943Sluigi */
67298943Sluigistatic void
673102087Sluigiprint_newports(ipfw_insn_u16 *cmd, int proto, int opcode)
67498943Sluigi{
675117328Sluigi	uint16_t *p = cmd->ports;
67698943Sluigi	int i;
677117469Sluigi	char const *sep;
67898943Sluigi
679116690Sluigi	if (opcode != 0) {
680117328Sluigi		sep = match_value(_port_name, opcode);
681117328Sluigi		if (sep == NULL)
682116690Sluigi			sep = "???";
683116690Sluigi		printf (" %s", sep);
684116690Sluigi	}
685116690Sluigi	sep = " ";
68698943Sluigi	for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
68798943Sluigi		printf(sep);
68898943Sluigi		print_port(proto, p[0]);
68998943Sluigi		if (p[0] != p[1]) {
69098943Sluigi			printf("-");
69198943Sluigi			print_port(proto, p[1]);
69298943Sluigi		}
69398943Sluigi		sep = ",";
69498943Sluigi	}
69598943Sluigi}
69698943Sluigi
69798943Sluigi/*
69898943Sluigi * Like strtol, but also translates service names into port numbers
69998943Sluigi * for some protocols.
70098943Sluigi * In particular:
70198943Sluigi *	proto == -1 disables the protocol check;
70298943Sluigi *	proto == IPPROTO_ETHERTYPE looks up an internal table
70398943Sluigi *	proto == <some value in /etc/protocols> matches the values there.
704101628Sluigi * Returns *end == s in case the parameter is not found.
70598943Sluigi */
70698943Sluigistatic int
70798943Sluigistrtoport(char *s, char **end, int base, int proto)
70898943Sluigi{
709101628Sluigi	char *p, *buf;
710101628Sluigi	char *s1;
71198943Sluigi	int i;
71298943Sluigi
713101628Sluigi	*end = s;		/* default - not found */
714117577Sluigi	if (*s == '\0')
715101628Sluigi		return 0;	/* not found */
716106505Smaxim
71798943Sluigi	if (isdigit(*s))
71898943Sluigi		return strtol(s, end, base);
71998943Sluigi
72098943Sluigi	/*
721101628Sluigi	 * find separator. '\\' escapes the next char.
72298943Sluigi	 */
723101628Sluigi	for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++)
724101628Sluigi		if (*s1 == '\\' && s1[1] != '\0')
725101628Sluigi			s1++;
72698943Sluigi
727101628Sluigi	buf = malloc(s1 - s + 1);
728101628Sluigi	if (buf == NULL)
729101628Sluigi		return 0;
730101628Sluigi
731101628Sluigi	/*
732101628Sluigi	 * copy into a buffer skipping backslashes
733101628Sluigi	 */
734101628Sluigi	for (p = s, i = 0; p != s1 ; p++)
735117577Sluigi		if (*p != '\\')
736101628Sluigi			buf[i++] = *p;
737101628Sluigi	buf[i++] = '\0';
738101628Sluigi
73998943Sluigi	if (proto == IPPROTO_ETHERTYPE) {
740101628Sluigi		i = match_token(ether_types, buf);
741101628Sluigi		free(buf);
742101628Sluigi		if (i != -1) {	/* found */
74398943Sluigi			*end = s1;
74498943Sluigi			return i;
74598943Sluigi		}
74698943Sluigi	} else {
74798943Sluigi		struct protoent *pe = NULL;
74898943Sluigi		struct servent *se;
74998943Sluigi
75098943Sluigi		if (proto != 0)
75198943Sluigi			pe = getprotobynumber(proto);
75298943Sluigi		setservent(1);
753101628Sluigi		se = getservbyname(buf, pe ? pe->p_name : NULL);
754101628Sluigi		free(buf);
75598943Sluigi		if (se != NULL) {
75698943Sluigi			*end = s1;
75798943Sluigi			return ntohs(se->s_port);
75898943Sluigi		}
75998943Sluigi	}
760101628Sluigi	return 0;	/* not found */
76198943Sluigi}
76298943Sluigi
76398943Sluigi/*
764136071Sgreen * Map between current altq queue id numbers and names.
765136071Sgreen */
766136071Sgreenstatic int altq_fetched = 0;
767136071Sgreenstatic TAILQ_HEAD(, pf_altq) altq_entries =
768136071Sgreen	TAILQ_HEAD_INITIALIZER(altq_entries);
769136071Sgreen
770136071Sgreenstatic void
771136071Sgreenaltq_set_enabled(int enabled)
772136071Sgreen{
773136071Sgreen	int pffd;
774136071Sgreen
775136071Sgreen	pffd = open("/dev/pf", O_RDWR);
776136071Sgreen	if (pffd == -1)
777136071Sgreen		err(EX_UNAVAILABLE,
778136071Sgreen		    "altq support opening pf(4) control device");
779136071Sgreen	if (enabled) {
780136071Sgreen		if (ioctl(pffd, DIOCSTARTALTQ) != 0 && errno != EEXIST)
781136071Sgreen			err(EX_UNAVAILABLE, "enabling altq");
782136071Sgreen	} else {
783136071Sgreen		if (ioctl(pffd, DIOCSTOPALTQ) != 0 && errno != ENOENT)
784136071Sgreen			err(EX_UNAVAILABLE, "disabling altq");
785136071Sgreen	}
786136071Sgreen	close(pffd);
787136071Sgreen}
788136071Sgreen
789136071Sgreenstatic void
790136071Sgreenaltq_fetch()
791136071Sgreen{
792136071Sgreen	struct pfioc_altq pfioc;
793136071Sgreen	struct pf_altq *altq;
794136071Sgreen	int pffd, mnr;
795136071Sgreen
796136071Sgreen	if (altq_fetched)
797136071Sgreen		return;
798136071Sgreen	altq_fetched = 1;
799136071Sgreen	pffd = open("/dev/pf", O_RDONLY);
800136071Sgreen	if (pffd == -1) {
801136071Sgreen		warn("altq support opening pf(4) control device");
802136071Sgreen		return;
803136071Sgreen	}
804136071Sgreen	bzero(&pfioc, sizeof(pfioc));
805136071Sgreen	if (ioctl(pffd, DIOCGETALTQS, &pfioc) != 0) {
806136071Sgreen		warn("altq support getting queue list");
807136071Sgreen		close(pffd);
808136071Sgreen		return;
809136071Sgreen	}
810136071Sgreen	mnr = pfioc.nr;
811136071Sgreen	for (pfioc.nr = 0; pfioc.nr < mnr; pfioc.nr++) {
812136071Sgreen		if (ioctl(pffd, DIOCGETALTQ, &pfioc) != 0) {
813136071Sgreen			if (errno == EBUSY)
814136071Sgreen				break;
815136071Sgreen			warn("altq support getting queue list");
816136071Sgreen			close(pffd);
817136071Sgreen			return;
818136071Sgreen		}
819136071Sgreen		if (pfioc.altq.qid == 0)
820136071Sgreen			continue;
821136071Sgreen		altq = malloc(sizeof(*altq));
822136071Sgreen		if (altq == NULL)
823136071Sgreen			err(EX_OSERR, "malloc");
824136071Sgreen		*altq = pfioc.altq;
825136071Sgreen		TAILQ_INSERT_TAIL(&altq_entries, altq, entries);
826136071Sgreen	}
827136071Sgreen	close(pffd);
828136071Sgreen}
829136071Sgreen
830136071Sgreenstatic u_int32_t
831136071Sgreenaltq_name_to_qid(const char *name)
832136071Sgreen{
833136071Sgreen	struct pf_altq *altq;
834136071Sgreen
835136071Sgreen	altq_fetch();
836136071Sgreen	TAILQ_FOREACH(altq, &altq_entries, entries)
837136071Sgreen		if (strcmp(name, altq->qname) == 0)
838136071Sgreen			break;
839136071Sgreen	if (altq == NULL)
840136071Sgreen		errx(EX_DATAERR, "altq has no queue named `%s'", name);
841136071Sgreen	return altq->qid;
842136071Sgreen}
843136071Sgreen
844136071Sgreenstatic const char *
845136071Sgreenaltq_qid_to_name(u_int32_t qid)
846136071Sgreen{
847136071Sgreen	struct pf_altq *altq;
848136071Sgreen
849136071Sgreen	altq_fetch();
850136071Sgreen	TAILQ_FOREACH(altq, &altq_entries, entries)
851136071Sgreen		if (qid == altq->qid)
852136071Sgreen			break;
853136071Sgreen	if (altq == NULL)
854136071Sgreen		return NULL;
855136071Sgreen	return altq->qname;
856136071Sgreen}
857136071Sgreen
858136071Sgreenstatic void
859136071Sgreenfill_altq_qid(u_int32_t *qid, const char *av)
860136071Sgreen{
861136071Sgreen	*qid = altq_name_to_qid(av);
862136071Sgreen}
863136071Sgreen
864136071Sgreen/*
865117328Sluigi * Fill the body of the command with the list of port ranges.
86698943Sluigi */
86798943Sluigistatic int
86898943Sluigifill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
86998943Sluigi{
870117328Sluigi	uint16_t a, b, *p = cmd->ports;
87198943Sluigi	int i = 0;
872102087Sluigi	char *s = av;
87398943Sluigi
874102087Sluigi	while (*s) {
87598943Sluigi		a = strtoport(av, &s, 0, proto);
876159636Soleg		if (s == av) 			/* empty or invalid argument */
877159636Soleg			return (0);
878159636Soleg
879159636Soleg		switch (*s) {
880159636Soleg		case '-':			/* a range */
881159636Soleg			av = s + 1;
88298943Sluigi			b = strtoport(av, &s, 0, proto);
883159636Soleg			/* Reject expressions like '1-abc' or '1-2-3'. */
884159636Soleg			if (s == av || (*s != ',' && *s != '\0'))
885159636Soleg				return (0);
88698943Sluigi			p[0] = a;
88798943Sluigi			p[1] = b;
888159636Soleg			break;
889159636Soleg		case ',':			/* comma separated list */
890159636Soleg		case '\0':
89198943Sluigi			p[0] = p[1] = a;
892159636Soleg			break;
893159636Soleg		default:
894159636Soleg			warnx("port list: invalid separator <%c> in <%s>",
895101978Sluigi				*s, av);
896159636Soleg			return (0);
897159636Soleg		}
898159636Soleg
899102087Sluigi		i++;
900102087Sluigi		p += 2;
901159636Soleg		av = s + 1;
90298943Sluigi	}
90398943Sluigi	if (i > 0) {
904159636Soleg		if (i + 1 > F_LEN_MASK)
905102087Sluigi			errx(EX_DATAERR, "too many ports/ranges\n");
906159636Soleg		cmd->o.len |= i + 1;	/* leave F_NOT and F_OR untouched */
90798943Sluigi	}
908159636Soleg	return (i);
90998943Sluigi}
91098943Sluigi
91198943Sluigistatic struct _s_x icmpcodes[] = {
91298943Sluigi      { "net",			ICMP_UNREACH_NET },
91398943Sluigi      { "host",			ICMP_UNREACH_HOST },
91498943Sluigi      { "protocol",		ICMP_UNREACH_PROTOCOL },
91598943Sluigi      { "port",			ICMP_UNREACH_PORT },
91698943Sluigi      { "needfrag",		ICMP_UNREACH_NEEDFRAG },
91798943Sluigi      { "srcfail",		ICMP_UNREACH_SRCFAIL },
91898943Sluigi      { "net-unknown",		ICMP_UNREACH_NET_UNKNOWN },
91998943Sluigi      { "host-unknown",		ICMP_UNREACH_HOST_UNKNOWN },
92098943Sluigi      { "isolated",		ICMP_UNREACH_ISOLATED },
92198943Sluigi      { "net-prohib",		ICMP_UNREACH_NET_PROHIB },
92298943Sluigi      { "host-prohib",		ICMP_UNREACH_HOST_PROHIB },
92398943Sluigi      { "tosnet",		ICMP_UNREACH_TOSNET },
92498943Sluigi      { "toshost",		ICMP_UNREACH_TOSHOST },
92598943Sluigi      { "filter-prohib",	ICMP_UNREACH_FILTER_PROHIB },
92698943Sluigi      { "host-precedence",	ICMP_UNREACH_HOST_PRECEDENCE },
92798943Sluigi      { "precedence-cutoff",	ICMP_UNREACH_PRECEDENCE_CUTOFF },
92898943Sluigi      { NULL, 0 }
92998943Sluigi};
93098943Sluigi
93198943Sluigistatic void
93298943Sluigifill_reject_code(u_short *codep, char *str)
93398943Sluigi{
93498943Sluigi	int val;
93598943Sluigi	char *s;
93698943Sluigi
93798943Sluigi	val = strtoul(str, &s, 0);
93898943Sluigi	if (s == str || *s != '\0' || val >= 0x100)
93998943Sluigi		val = match_token(icmpcodes, str);
940102087Sluigi	if (val < 0)
94198943Sluigi		errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
94298943Sluigi	*codep = val;
94398943Sluigi	return;
94498943Sluigi}
94598943Sluigi
94698943Sluigistatic void
947117328Sluigiprint_reject_code(uint16_t code)
94898943Sluigi{
949117469Sluigi	char const *s = match_value(icmpcodes, code);
95098943Sluigi
95198943Sluigi	if (s != NULL)
95299475Sluigi		printf("unreach %s", s);
95398943Sluigi	else
95499475Sluigi		printf("unreach %u", code);
95598943Sluigi}
95698943Sluigi
957149020Sbzstatic struct _s_x icmp6codes[] = {
958149020Sbz      { "no-route",		ICMP6_DST_UNREACH_NOROUTE },
959149020Sbz      { "admin-prohib",		ICMP6_DST_UNREACH_ADMIN },
960149020Sbz      { "address",		ICMP6_DST_UNREACH_ADDR },
961149020Sbz      { "port",			ICMP6_DST_UNREACH_NOPORT },
962149020Sbz      { NULL, 0 }
963149020Sbz};
964149020Sbz
965149020Sbzstatic void
966149020Sbzfill_unreach6_code(u_short *codep, char *str)
967149020Sbz{
968149020Sbz	int val;
969149020Sbz	char *s;
970149020Sbz
971149020Sbz	val = strtoul(str, &s, 0);
972149020Sbz	if (s == str || *s != '\0' || val >= 0x100)
973149020Sbz		val = match_token(icmp6codes, str);
974149020Sbz	if (val < 0)
975149020Sbz		errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str);
976149020Sbz	*codep = val;
977149020Sbz	return;
978149020Sbz}
979149020Sbz
980149020Sbzstatic void
981149020Sbzprint_unreach6_code(uint16_t code)
982149020Sbz{
983149020Sbz	char const *s = match_value(icmp6codes, code);
984149020Sbz
985149020Sbz	if (s != NULL)
986149020Sbz		printf("unreach6 %s", s);
987149020Sbz	else
988149020Sbz		printf("unreach6 %u", code);
989149020Sbz}
990149020Sbz
99198943Sluigi/*
99298943Sluigi * Returns the number of bits set (from left) in a contiguous bitmask,
99398943Sluigi * or -1 if the mask is not contiguous.
99498943Sluigi * XXX this needs a proper fix.
99598943Sluigi * This effectively works on masks in big-endian (network) format.
99698943Sluigi * when compiled on little endian architectures.
99798943Sluigi *
99898943Sluigi * First bit is bit 7 of the first byte -- note, for MAC addresses,
99998943Sluigi * the first bit on the wire is bit 0 of the first byte.
100098943Sluigi * len is the max length in bits.
100198943Sluigi */
100298943Sluigistatic int
1003117577Sluigicontigmask(uint8_t *p, int len)
100498943Sluigi{
100598943Sluigi	int i, n;
1006117577Sluigi
100798943Sluigi	for (i=0; i<len ; i++)
100898943Sluigi		if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
100998943Sluigi			break;
101098943Sluigi	for (n=i+1; n < len; n++)
101198943Sluigi		if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
101298943Sluigi			return -1; /* mask not contiguous */
101398943Sluigi	return i;
101498943Sluigi}
101598943Sluigi
101698943Sluigi/*
101798943Sluigi * print flags set/clear in the two bitmasks passed as parameters.
101898943Sluigi * There is a specialized check for f_tcpflags.
101998943Sluigi */
102098943Sluigistatic void
1021117469Sluigiprint_flags(char const *name, ipfw_insn *cmd, struct _s_x *list)
102298943Sluigi{
1023117469Sluigi	char const *comma = "";
102498943Sluigi	int i;
1025117577Sluigi	uint8_t set = cmd->arg1 & 0xff;
1026117577Sluigi	uint8_t clear = (cmd->arg1 >> 8) & 0xff;
102798943Sluigi
102898943Sluigi	if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) {
102998943Sluigi		printf(" setup");
103098943Sluigi		return;
103198943Sluigi	}
103298943Sluigi
103398943Sluigi	printf(" %s ", name);
103498943Sluigi	for (i=0; list[i].x != 0; i++) {
103598943Sluigi		if (set & list[i].x) {
103698943Sluigi			set &= ~list[i].x;
103798943Sluigi			printf("%s%s", comma, list[i].s);
103898943Sluigi			comma = ",";
103998943Sluigi		}
104098943Sluigi		if (clear & list[i].x) {
104198943Sluigi			clear &= ~list[i].x;
104298943Sluigi			printf("%s!%s", comma, list[i].s);
104398943Sluigi			comma = ",";
104498943Sluigi		}
104598943Sluigi	}
104698943Sluigi}
104798943Sluigi
104898943Sluigi/*
104998943Sluigi * Print the ip address contained in a command.
105098943Sluigi */
105198943Sluigistatic void
1052117469Sluigiprint_ip(ipfw_insn_ip *cmd, char const *s)
105398943Sluigi{
105498943Sluigi	struct hostent *he = NULL;
1055117328Sluigi	int len = F_LEN((ipfw_insn *)cmd);
1056117328Sluigi	uint32_t *a = ((ipfw_insn_u32 *)cmd)->d;
105798943Sluigi
1058102087Sluigi	printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
105998943Sluigi
106098943Sluigi	if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) {
106198943Sluigi		printf("me");
106298943Sluigi		return;
106398943Sluigi	}
1064130281Sru	if (cmd->o.opcode == O_IP_SRC_LOOKUP ||
1065130281Sru	    cmd->o.opcode == O_IP_DST_LOOKUP) {
1066130281Sru		printf("table(%u", ((ipfw_insn *)cmd)->arg1);
1067130281Sru		if (len == F_INSN_SIZE(ipfw_insn_u32))
1068130281Sru			printf(",%u", *a);
1069130281Sru		printf(")");
1070130281Sru		return;
1071130281Sru	}
107298943Sluigi	if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) {
1073117328Sluigi		uint32_t x, *map = (uint32_t *)&(cmd->mask);
1074116716Sluigi		int i, j;
107598943Sluigi		char comma = '{';
107698943Sluigi
107798943Sluigi		x = cmd->o.arg1 - 1;
107898943Sluigi		x = htonl( ~x );
107998943Sluigi		cmd->addr.s_addr = htonl(cmd->addr.s_addr);
108098943Sluigi		printf("%s/%d", inet_ntoa(cmd->addr),
1081117577Sluigi			contigmask((uint8_t *)&x, 32));
108298943Sluigi		x = cmd->addr.s_addr = htonl(cmd->addr.s_addr);
108398943Sluigi		x &= 0xff; /* base */
1084116716Sluigi		/*
1085116716Sluigi		 * Print bits and ranges.
1086116716Sluigi		 * Locate first bit set (i), then locate first bit unset (j).
1087116716Sluigi		 * If we have 3+ consecutive bits set, then print them as a
1088116716Sluigi		 * range, otherwise only print the initial bit and rescan.
1089116716Sluigi		 */
109098943Sluigi		for (i=0; i < cmd->o.arg1; i++)
1091117328Sluigi			if (map[i/32] & (1<<(i & 31))) {
1092116716Sluigi				for (j=i+1; j < cmd->o.arg1; j++)
1093117328Sluigi					if (!(map[ j/32] & (1<<(j & 31))))
1094116716Sluigi						break;
109598943Sluigi				printf("%c%d", comma, i+x);
1096116716Sluigi				if (j>i+2) { /* range has at least 3 elements */
1097116716Sluigi					printf("-%d", j-1+x);
1098116716Sluigi					i = j-1;
1099116716Sluigi				}
110098943Sluigi				comma = ',';
110198943Sluigi			}
110298943Sluigi		printf("}");
110398943Sluigi		return;
110498943Sluigi	}
1105117328Sluigi	/*
1106117328Sluigi	 * len == 2 indicates a single IP, whereas lists of 1 or more
1107117328Sluigi	 * addr/mask pairs have len = (2n+1). We convert len to n so we
1108117328Sluigi	 * use that to count the number of entries.
1109117328Sluigi	 */
1110117328Sluigi    for (len = len / 2; len > 0; len--, a += 2) {
1111117328Sluigi	int mb =	/* mask length */
1112117328Sluigi	    (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ?
1113117577Sluigi		32 : contigmask((uint8_t *)&(a[1]), 32);
111498943Sluigi	if (mb == 32 && do_resolv)
1115117328Sluigi		he = gethostbyaddr((char *)&(a[0]), sizeof(u_long), AF_INET);
111698943Sluigi	if (he != NULL)		/* resolved to name */
111798943Sluigi		printf("%s", he->h_name);
111898943Sluigi	else if (mb == 0)	/* any */
111998943Sluigi		printf("any");
112098943Sluigi	else {		/* numeric IP followed by some kind of mask */
1121117328Sluigi		printf("%s", inet_ntoa( *((struct in_addr *)&a[0]) ) );
112298943Sluigi		if (mb < 0)
1123117328Sluigi			printf(":%s", inet_ntoa( *((struct in_addr *)&a[1]) ) );
112498943Sluigi		else if (mb < 32)
112598943Sluigi			printf("/%d", mb);
112698943Sluigi	}
1127117328Sluigi	if (len > 1)
1128117328Sluigi		printf(",");
1129117328Sluigi    }
113098943Sluigi}
113198943Sluigi
113298943Sluigi/*
113398943Sluigi * prints a MAC address/mask pair
113498943Sluigi */
113598943Sluigistatic void
1136117577Sluigiprint_mac(uint8_t *addr, uint8_t *mask)
113798943Sluigi{
113898943Sluigi	int l = contigmask(mask, 48);
113998943Sluigi
114098943Sluigi	if (l == 0)
114198943Sluigi		printf(" any");
114298943Sluigi	else {
114398943Sluigi		printf(" %02x:%02x:%02x:%02x:%02x:%02x",
114498943Sluigi		    addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
114598943Sluigi		if (l == -1)
114698943Sluigi			printf("&%02x:%02x:%02x:%02x:%02x:%02x",
114798943Sluigi			    mask[0], mask[1], mask[2],
114898943Sluigi			    mask[3], mask[4], mask[5]);
114998943Sluigi		else if (l < 48)
115098943Sluigi			printf("/%d", l);
115198943Sluigi	}
115298943Sluigi}
115398943Sluigi
115499475Sluigistatic void
115599475Sluigifill_icmptypes(ipfw_insn_u32 *cmd, char *av)
115699475Sluigi{
1157117328Sluigi	uint8_t type;
115898943Sluigi
115999475Sluigi	cmd->d[0] = 0;
116099475Sluigi	while (*av) {
116199475Sluigi		if (*av == ',')
116299475Sluigi			av++;
116399475Sluigi
116499475Sluigi		type = strtoul(av, &av, 0);
116599475Sluigi
116699475Sluigi		if (*av != ',' && *av != '\0')
116799475Sluigi			errx(EX_DATAERR, "invalid ICMP type");
116899475Sluigi
116999475Sluigi		if (type > 31)
117099475Sluigi			errx(EX_DATAERR, "ICMP type out of range");
117199475Sluigi
117299475Sluigi		cmd->d[0] |= 1 << type;
117399475Sluigi	}
117499475Sluigi	cmd->o.opcode = O_ICMPTYPE;
117599475Sluigi	cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
117699475Sluigi}
117799475Sluigi
117899475Sluigistatic void
117999475Sluigiprint_icmptypes(ipfw_insn_u32 *cmd)
118099475Sluigi{
118199475Sluigi	int i;
118299475Sluigi	char sep= ' ';
118399475Sluigi
118499475Sluigi	printf(" icmptypes");
118599475Sluigi	for (i = 0; i < 32; i++) {
118699475Sluigi		if ( (cmd->d[0] & (1 << (i))) == 0)
118799475Sluigi			continue;
118899475Sluigi		printf("%c%d", sep, i);
118999475Sluigi		sep = ',';
119099475Sluigi	}
119199475Sluigi}
119299475Sluigi
1193145246Sbrooks/*
1194145246Sbrooks * Print the ip address contained in a command.
1195145246Sbrooks */
1196145246Sbrooksstatic void
1197145246Sbrooksprint_ip6(ipfw_insn_ip6 *cmd, char const *s)
1198145246Sbrooks{
1199145246Sbrooks       struct hostent *he = NULL;
1200145246Sbrooks       int len = F_LEN((ipfw_insn *) cmd) - 1;
1201145246Sbrooks       struct in6_addr *a = &(cmd->addr6);
1202145246Sbrooks       char trad[255];
1203145246Sbrooks
1204145246Sbrooks       printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
1205145246Sbrooks
1206145246Sbrooks       if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) {
1207145246Sbrooks               printf("me6");
1208145246Sbrooks               return;
1209145246Sbrooks       }
1210145246Sbrooks       if (cmd->o.opcode == O_IP6) {
1211152923Sume               printf(" ip6");
1212145246Sbrooks               return;
1213145246Sbrooks       }
1214145246Sbrooks
1215145246Sbrooks       /*
1216145246Sbrooks        * len == 4 indicates a single IP, whereas lists of 1 or more
1217145246Sbrooks        * addr/mask pairs have len = (2n+1). We convert len to n so we
1218145246Sbrooks        * use that to count the number of entries.
1219145246Sbrooks        */
1220145246Sbrooks
1221145246Sbrooks       for (len = len / 4; len > 0; len -= 2, a += 2) {
1222145246Sbrooks           int mb =        /* mask length */
1223145246Sbrooks               (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ?
1224145246Sbrooks               128 : contigmask((uint8_t *)&(a[1]), 128);
1225145246Sbrooks
1226145246Sbrooks           if (mb == 128 && do_resolv)
1227145246Sbrooks               he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6);
1228145246Sbrooks           if (he != NULL)             /* resolved to name */
1229145246Sbrooks               printf("%s", he->h_name);
1230145246Sbrooks           else if (mb == 0)           /* any */
1231145246Sbrooks               printf("any");
1232145246Sbrooks           else {          /* numeric IP followed by some kind of mask */
1233145246Sbrooks               if (inet_ntop(AF_INET6,  a, trad, sizeof( trad ) ) == NULL)
1234145246Sbrooks                   printf("Error ntop in print_ip6\n");
1235145246Sbrooks               printf("%s",  trad );
1236145246Sbrooks               if (mb < 0)     /* XXX not really legal... */
1237145246Sbrooks                   printf(":%s",
1238145246Sbrooks                       inet_ntop(AF_INET6, &a[1], trad, sizeof(trad)));
1239145246Sbrooks               else if (mb < 128)
1240145246Sbrooks                   printf("/%d", mb);
1241145246Sbrooks           }
1242145246Sbrooks           if (len > 2)
1243145246Sbrooks               printf(",");
1244145246Sbrooks       }
1245145246Sbrooks}
1246145246Sbrooks
1247145246Sbrooksstatic void
1248145246Sbrooksfill_icmp6types(ipfw_insn_icmp6 *cmd, char *av)
1249145246Sbrooks{
1250145246Sbrooks       uint8_t type;
1251145246Sbrooks
1252162344Sjhay       bzero(cmd, sizeof(*cmd));
1253145246Sbrooks       while (*av) {
1254145246Sbrooks           if (*av == ',')
1255145246Sbrooks               av++;
1256145246Sbrooks           type = strtoul(av, &av, 0);
1257145246Sbrooks           if (*av != ',' && *av != '\0')
1258145246Sbrooks               errx(EX_DATAERR, "invalid ICMP6 type");
1259145246Sbrooks	   /*
1260145246Sbrooks	    * XXX: shouldn't this be 0xFF?  I can't see any reason why
1261145246Sbrooks	    * we shouldn't be able to filter all possiable values
1262145246Sbrooks	    * regardless of the ability of the rest of the kernel to do
1263145246Sbrooks	    * anything useful with them.
1264145246Sbrooks	    */
1265145246Sbrooks           if (type > ICMP6_MAXTYPE)
1266145246Sbrooks               errx(EX_DATAERR, "ICMP6 type out of range");
1267145246Sbrooks           cmd->d[type / 32] |= ( 1 << (type % 32));
1268145246Sbrooks       }
1269145246Sbrooks       cmd->o.opcode = O_ICMP6TYPE;
1270145246Sbrooks       cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6);
1271145246Sbrooks}
1272145246Sbrooks
1273145246Sbrooks
1274145246Sbrooksstatic void
1275145246Sbrooksprint_icmp6types(ipfw_insn_u32 *cmd)
1276145246Sbrooks{
1277145246Sbrooks       int i, j;
1278145246Sbrooks       char sep= ' ';
1279145246Sbrooks
1280152923Sume       printf(" ip6 icmp6types");
1281145246Sbrooks       for (i = 0; i < 7; i++)
1282145246Sbrooks               for (j=0; j < 32; ++j) {
1283145246Sbrooks                       if ( (cmd->d[i] & (1 << (j))) == 0)
1284145246Sbrooks                               continue;
1285145246Sbrooks                       printf("%c%d", sep, (i*32 + j));
1286145246Sbrooks                       sep = ',';
1287145246Sbrooks               }
1288145246Sbrooks}
1289145246Sbrooks
1290145246Sbrooksstatic void
1291145246Sbrooksprint_flow6id( ipfw_insn_u32 *cmd)
1292145246Sbrooks{
1293145246Sbrooks       uint16_t i, limit = cmd->o.arg1;
1294145246Sbrooks       char sep = ',';
1295145246Sbrooks
1296145246Sbrooks       printf(" flow-id ");
1297145246Sbrooks       for( i=0; i < limit; ++i) {
1298145246Sbrooks               if (i == limit - 1)
1299145246Sbrooks                       sep = ' ';
1300145246Sbrooks               printf("%d%c", cmd->d[i], sep);
1301145246Sbrooks       }
1302145246Sbrooks}
1303145246Sbrooks
1304145246Sbrooks/* structure and define for the extension header in ipv6 */
1305145246Sbrooksstatic struct _s_x ext6hdrcodes[] = {
1306145246Sbrooks       { "frag",       EXT_FRAGMENT },
1307145246Sbrooks       { "hopopt",     EXT_HOPOPTS },
1308145246Sbrooks       { "route",      EXT_ROUTING },
1309149020Sbz       { "dstopt",     EXT_DSTOPTS },
1310145246Sbrooks       { "ah",         EXT_AH },
1311145246Sbrooks       { "esp",        EXT_ESP },
1312169245Sbz       { "rthdr0",     EXT_RTHDR0 },
1313169245Sbz       { "rthdr2",     EXT_RTHDR2 },
1314145246Sbrooks       { NULL,         0 }
1315145246Sbrooks};
1316145246Sbrooks
1317145246Sbrooks/* fills command for the extension header filtering */
1318145246Sbrooksint
1319145246Sbrooksfill_ext6hdr( ipfw_insn *cmd, char *av)
1320145246Sbrooks{
1321145246Sbrooks       int tok;
1322145246Sbrooks       char *s = av;
1323145246Sbrooks
1324145246Sbrooks       cmd->arg1 = 0;
1325145246Sbrooks
1326145246Sbrooks       while(s) {
1327145246Sbrooks           av = strsep( &s, ",") ;
1328145246Sbrooks           tok = match_token(ext6hdrcodes, av);
1329145246Sbrooks           switch (tok) {
1330145246Sbrooks           case EXT_FRAGMENT:
1331145246Sbrooks               cmd->arg1 |= EXT_FRAGMENT;
1332145246Sbrooks               break;
1333145246Sbrooks
1334145246Sbrooks           case EXT_HOPOPTS:
1335145246Sbrooks               cmd->arg1 |= EXT_HOPOPTS;
1336145246Sbrooks               break;
1337145246Sbrooks
1338145246Sbrooks           case EXT_ROUTING:
1339145246Sbrooks               cmd->arg1 |= EXT_ROUTING;
1340145246Sbrooks               break;
1341145246Sbrooks
1342149020Sbz           case EXT_DSTOPTS:
1343149020Sbz               cmd->arg1 |= EXT_DSTOPTS;
1344149020Sbz               break;
1345149020Sbz
1346145246Sbrooks           case EXT_AH:
1347145246Sbrooks               cmd->arg1 |= EXT_AH;
1348145246Sbrooks               break;
1349145246Sbrooks
1350145246Sbrooks           case EXT_ESP:
1351145246Sbrooks               cmd->arg1 |= EXT_ESP;
1352145246Sbrooks               break;
1353145246Sbrooks
1354169245Sbz           case EXT_RTHDR0:
1355169245Sbz               cmd->arg1 |= EXT_RTHDR0;
1356169245Sbz               break;
1357169245Sbz
1358169245Sbz           case EXT_RTHDR2:
1359169245Sbz               cmd->arg1 |= EXT_RTHDR2;
1360169245Sbz               break;
1361169245Sbz
1362145246Sbrooks           default:
1363145246Sbrooks               errx( EX_DATAERR, "invalid option for ipv6 exten header" );
1364145246Sbrooks               break;
1365145246Sbrooks           }
1366145246Sbrooks       }
1367145246Sbrooks       if (cmd->arg1 == 0 )
1368145246Sbrooks           return 0;
1369145246Sbrooks       cmd->opcode = O_EXT_HDR;
1370145246Sbrooks       cmd->len |= F_INSN_SIZE( ipfw_insn );
1371145246Sbrooks       return 1;
1372145246Sbrooks}
1373145246Sbrooks
1374145246Sbrooksvoid
1375145246Sbrooksprint_ext6hdr( ipfw_insn *cmd )
1376145246Sbrooks{
1377145246Sbrooks       char sep = ' ';
1378145246Sbrooks
1379145246Sbrooks       printf(" extension header:");
1380145246Sbrooks       if (cmd->arg1 & EXT_FRAGMENT ) {
1381145246Sbrooks           printf("%cfragmentation", sep);
1382145246Sbrooks           sep = ',';
1383145246Sbrooks       }
1384145246Sbrooks       if (cmd->arg1 & EXT_HOPOPTS ) {
1385145246Sbrooks           printf("%chop options", sep);
1386145246Sbrooks           sep = ',';
1387145246Sbrooks       }
1388145246Sbrooks       if (cmd->arg1 & EXT_ROUTING ) {
1389145246Sbrooks           printf("%crouting options", sep);
1390145246Sbrooks           sep = ',';
1391145246Sbrooks       }
1392169245Sbz       if (cmd->arg1 & EXT_RTHDR0 ) {
1393169245Sbz           printf("%crthdr0", sep);
1394169245Sbz           sep = ',';
1395169245Sbz       }
1396169245Sbz       if (cmd->arg1 & EXT_RTHDR2 ) {
1397169245Sbz           printf("%crthdr2", sep);
1398169245Sbz           sep = ',';
1399169245Sbz       }
1400149020Sbz       if (cmd->arg1 & EXT_DSTOPTS ) {
1401149020Sbz           printf("%cdestination options", sep);
1402149020Sbz           sep = ',';
1403149020Sbz       }
1404145246Sbrooks       if (cmd->arg1 & EXT_AH ) {
1405145246Sbrooks           printf("%cauthentication header", sep);
1406145246Sbrooks           sep = ',';
1407145246Sbrooks       }
1408145246Sbrooks       if (cmd->arg1 & EXT_ESP ) {
1409145246Sbrooks           printf("%cencapsulated security payload", sep);
1410145246Sbrooks       }
1411145246Sbrooks}
1412145246Sbrooks
141398943Sluigi/*
141498943Sluigi * show_ipfw() prints the body of an ipfw rule.
141598943Sluigi * Because the standard rule has at least proto src_ip dst_ip, we use
141698943Sluigi * a helper function to produce these entries if not provided explicitly.
1417102087Sluigi * The first argument is the list of fields we have, the second is
1418102087Sluigi * the list of fields we want to be printed.
1419101978Sluigi *
1420102087Sluigi * Special cases if we have provided a MAC header:
1421102087Sluigi *   + if the rule does not contain IP addresses/ports, do not print them;
1422102087Sluigi *   + if the rule does not contain an IP proto, print "all" instead of "ip";
1423102087Sluigi *
1424102087Sluigi * Once we have 'have_options', IP header fields are printed as options.
142598943Sluigi */
1426101978Sluigi#define	HAVE_PROTO	0x0001
1427101978Sluigi#define	HAVE_SRCIP	0x0002
1428101978Sluigi#define	HAVE_DSTIP	0x0004
1429169139Smaxim#define	HAVE_PROTO4	0x0008
1430169139Smaxim#define	HAVE_PROTO6	0x0010
1431102087Sluigi#define	HAVE_OPTIONS	0x8000
143298943Sluigi
1433101978Sluigi#define	HAVE_IP		(HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP)
143498943Sluigistatic void
1435102087Sluigishow_prerequisites(int *flags, int want, int cmd)
143698943Sluigi{
1437123495Sluigi	if (comment_only)
1438123495Sluigi		return;
1439102087Sluigi	if ( (*flags & HAVE_IP) == HAVE_IP)
1440102087Sluigi		*flags |= HAVE_OPTIONS;
1441102087Sluigi
1442102087Sluigi	if ( !(*flags & HAVE_OPTIONS)) {
1443102087Sluigi		if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO))
1444146894Smlaier			if ( (*flags & HAVE_PROTO4))
1445146894Smlaier				printf(" ip4");
1446146894Smlaier			else if ( (*flags & HAVE_PROTO6))
1447146894Smlaier				printf(" ip6");
1448146894Smlaier			else
1449146894Smlaier				printf(" ip");
1450146894Smlaier
1451102087Sluigi		if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
1452102087Sluigi			printf(" from any");
1453102087Sluigi		if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP))
1454102087Sluigi			printf(" to any");
1455102087Sluigi	}
145698943Sluigi	*flags |= want;
145798943Sluigi}
145898943Sluigi
145998943Sluigistatic void
1460112189Smaximshow_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
146198943Sluigi{
1462107291Skeramida	static int twidth = 0;
146398943Sluigi	int l;
1464158879Soleg	ipfw_insn *cmd, *tagptr = NULL;
1465117626Sluigi	char *comment = NULL;	/* ptr to comment if we have one */
146698943Sluigi	int proto = 0;		/* default */
146798943Sluigi	int flags = 0;	/* prerequisites */
146898943Sluigi	ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */
1469136071Sgreen	ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */
147098943Sluigi	int or_block = 0;	/* we are in an or block */
1471117328Sluigi	uint32_t set_disable;
147298943Sluigi
1473115793Sticso	bcopy(&rule->next_rule, &set_disable, sizeof(set_disable));
1474101628Sluigi
1475101628Sluigi	if (set_disable & (1 << rule->set)) { /* disabled */
1476101628Sluigi		if (!show_sets)
1477101628Sluigi			return;
1478101628Sluigi		else
1479101628Sluigi			printf("# DISABLED ");
1480101628Sluigi	}
148198943Sluigi	printf("%05u ", rule->rulenum);
148298943Sluigi
1483117469Sluigi	if (pcwidth>0 || bcwidth>0)
1484115793Sticso		printf("%*llu %*llu ", pcwidth, align_uint64(&rule->pcnt),
1485115793Sticso		    bcwidth, align_uint64(&rule->bcnt));
148698943Sluigi
1487117472Sluigi	if (do_time == 2)
1488117472Sluigi		printf("%10u ", rule->timestamp);
1489117472Sluigi	else if (do_time == 1) {
1490107291Skeramida		char timestr[30];
1491107291Skeramida		time_t t = (time_t)0;
1492107291Skeramida
1493107291Skeramida		if (twidth == 0) {
1494107291Skeramida			strcpy(timestr, ctime(&t));
1495107291Skeramida			*strchr(timestr, '\n') = '\0';
1496107291Skeramida			twidth = strlen(timestr);
1497107291Skeramida		}
149898943Sluigi		if (rule->timestamp) {
1499107291Skeramida			t = _long_to_time(rule->timestamp);
150098943Sluigi
150198943Sluigi			strcpy(timestr, ctime(&t));
150298943Sluigi			*strchr(timestr, '\n') = '\0';
150398943Sluigi			printf("%s ", timestr);
150498943Sluigi		} else {
1505107291Skeramida			printf("%*s", twidth, " ");
150698943Sluigi		}
150798943Sluigi	}
150898943Sluigi
1509101628Sluigi	if (show_sets)
1510101628Sluigi		printf("set %d ", rule->set);
1511101628Sluigi
151298943Sluigi	/*
1513107289Sluigi	 * print the optional "match probability"
1514107289Sluigi	 */
1515107289Sluigi	if (rule->cmd_len > 0) {
1516107289Sluigi		cmd = rule->cmd ;
1517107289Sluigi		if (cmd->opcode == O_PROB) {
1518107289Sluigi			ipfw_insn_u32 *p = (ipfw_insn_u32 *)cmd;
1519107289Sluigi			double d = 1.0 * p->d[0];
1520107289Sluigi
1521107289Sluigi			d = (d / 0x7fffffff);
1522107289Sluigi			printf("prob %f ", d);
1523107289Sluigi		}
1524107289Sluigi	}
1525107289Sluigi
1526107289Sluigi	/*
152798943Sluigi	 * first print actions
152898943Sluigi	 */
152998943Sluigi        for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule);
153098943Sluigi			l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
153198943Sluigi		switch(cmd->opcode) {
153298943Sluigi		case O_CHECK_STATE:
153398943Sluigi			printf("check-state");
1534102087Sluigi			flags = HAVE_IP; /* avoid printing anything else */
153598943Sluigi			break;
153698943Sluigi
153798943Sluigi		case O_ACCEPT:
153898943Sluigi			printf("allow");
153998943Sluigi			break;
154098943Sluigi
154198943Sluigi		case O_COUNT:
154298943Sluigi			printf("count");
154398943Sluigi			break;
154498943Sluigi
154598943Sluigi		case O_DENY:
154698943Sluigi			printf("deny");
154798943Sluigi			break;
154898943Sluigi
154999475Sluigi		case O_REJECT:
155099475Sluigi			if (cmd->arg1 == ICMP_REJECT_RST)
155199475Sluigi				printf("reset");
155299475Sluigi			else if (cmd->arg1 == ICMP_UNREACH_HOST)
155399475Sluigi				printf("reject");
155499475Sluigi			else
155599475Sluigi				print_reject_code(cmd->arg1);
155699475Sluigi			break;
155799475Sluigi
1558149020Sbz		case O_UNREACH6:
1559149020Sbz			if (cmd->arg1 == ICMP6_UNREACH_RST)
1560149020Sbz				printf("reset6");
1561149020Sbz			else
1562149020Sbz				print_unreach6_code(cmd->arg1);
1563149020Sbz			break;
1564149020Sbz
1565159636Soleg		case O_SKIPTO:
1566159636Soleg			PRINT_UINT_ARG("skipto ", cmd->arg1);
156798943Sluigi			break;
156898943Sluigi
156998943Sluigi		case O_PIPE:
1570159636Soleg			PRINT_UINT_ARG("pipe ", cmd->arg1);
1571159636Soleg			break;
1572159636Soleg
157398943Sluigi		case O_QUEUE:
1574159636Soleg			PRINT_UINT_ARG("queue ", cmd->arg1);
1575159636Soleg			break;
1576159636Soleg
157798943Sluigi		case O_DIVERT:
1578159636Soleg			PRINT_UINT_ARG("divert ", cmd->arg1);
1579159636Soleg			break;
1580159636Soleg
158198943Sluigi		case O_TEE:
1582159636Soleg			PRINT_UINT_ARG("tee ", cmd->arg1);
1583159636Soleg			break;
1584159636Soleg
1585141351Sglebius		case O_NETGRAPH:
1586159636Soleg			PRINT_UINT_ARG("netgraph ", cmd->arg1);
1587159636Soleg			break;
1588159636Soleg
1589141351Sglebius		case O_NGTEE:
1590159636Soleg			PRINT_UINT_ARG("ngtee ", cmd->arg1);
1591159636Soleg			break;
1592141351Sglebius
159398943Sluigi		case O_FORWARD_IP:
159498943Sluigi		    {
159598943Sluigi			ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
159698943Sluigi
1597161424Sjulian			if (s->sa.sin_addr.s_addr == INADDR_ANY) {
1598161424Sjulian				printf("fwd tablearg");
1599161424Sjulian			} else {
1600161424Sjulian				printf("fwd %s", inet_ntoa(s->sa.sin_addr));
1601161424Sjulian			}
160298943Sluigi			if (s->sa.sin_port)
1603103241Sluigi				printf(",%d", s->sa.sin_port);
160498943Sluigi		    }
160598943Sluigi			break;
160698943Sluigi
160798943Sluigi		case O_LOG: /* O_LOG is printed last */
160898943Sluigi			logptr = (ipfw_insn_log *)cmd;
160998943Sluigi			break;
161098943Sluigi
1611136071Sgreen		case O_ALTQ: /* O_ALTQ is printed after O_LOG */
1612136071Sgreen			altqptr = (ipfw_insn_altq *)cmd;
1613136071Sgreen			break;
1614136071Sgreen
1615158879Soleg		case O_TAG:
1616158879Soleg			tagptr = cmd;
1617158879Soleg			break;
1618158879Soleg
1619165648Spiso		case O_NAT:
1620176517Spiso			PRINT_UINT_ARG("nat ", cmd->arg1);
1621165648Spiso 			break;
1622165648Spiso
1623178888Sjulian		case O_SETFIB:
1624178888Sjulian			PRINT_UINT_ARG("setfib ", cmd->arg1);
1625178888Sjulian 			break;
1626178888Sjulian
162798943Sluigi		default:
1628136071Sgreen			printf("** unrecognized action %d len %d ",
162998943Sluigi				cmd->opcode, cmd->len);
163098943Sluigi		}
163198943Sluigi	}
163298943Sluigi	if (logptr) {
163398943Sluigi		if (logptr->max_log > 0)
163499909Sluigi			printf(" log logamount %d", logptr->max_log);
163598943Sluigi		else
163699909Sluigi			printf(" log");
163798943Sluigi	}
1638136071Sgreen	if (altqptr) {
1639136071Sgreen		const char *qname;
1640102087Sluigi
1641136071Sgreen		qname = altq_qid_to_name(altqptr->qid);
1642136071Sgreen		if (qname == NULL)
1643136071Sgreen			printf(" altq ?<%u>", altqptr->qid);
1644136071Sgreen		else
1645136071Sgreen			printf(" altq %s", qname);
1646136071Sgreen	}
1647158879Soleg	if (tagptr) {
1648158879Soleg		if (tagptr->len & F_NOT)
1649159636Soleg			PRINT_UINT_ARG(" untag ", tagptr->arg1);
1650158879Soleg		else
1651159636Soleg			PRINT_UINT_ARG(" tag ", tagptr->arg1);
1652158879Soleg	}
1653136071Sgreen
165498943Sluigi	/*
1655102087Sluigi	 * then print the body.
165698943Sluigi	 */
1657146894Smlaier        for (l = rule->act_ofs, cmd = rule->cmd ;
1658146894Smlaier			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
1659146894Smlaier		if ((cmd->len & F_OR) || (cmd->len & F_NOT))
1660146894Smlaier			continue;
1661146894Smlaier		if (cmd->opcode == O_IP4) {
1662146894Smlaier			flags |= HAVE_PROTO4;
1663146894Smlaier			break;
1664146894Smlaier		} else if (cmd->opcode == O_IP6) {
1665146894Smlaier			flags |= HAVE_PROTO6;
1666146894Smlaier			break;
1667146894Smlaier		}
1668146894Smlaier	}
1669102087Sluigi	if (rule->_pad & 1) {	/* empty rules before options */
1670146894Smlaier		if (!do_compact) {
1671146894Smlaier			show_prerequisites(&flags, HAVE_PROTO, 0);
1672146894Smlaier			printf(" from any to any");
1673146894Smlaier		}
1674102087Sluigi		flags |= HAVE_IP | HAVE_OPTIONS;
1675102087Sluigi	}
1676102087Sluigi
1677123495Sluigi	if (comment_only)
1678123495Sluigi		comment = "...";
1679123495Sluigi
168098943Sluigi        for (l = rule->act_ofs, cmd = rule->cmd ;
168198943Sluigi			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
168299475Sluigi		/* useful alias */
168399475Sluigi		ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
168498943Sluigi
1685123495Sluigi		if (comment_only) {
1686123495Sluigi			if (cmd->opcode != O_NOP)
1687123495Sluigi				continue;
1688123495Sluigi			printf(" // %s\n", (char *)(cmd + 1));
1689123495Sluigi			return;
1690123495Sluigi		}
1691123495Sluigi
1692102087Sluigi		show_prerequisites(&flags, 0, cmd->opcode);
1693102087Sluigi
169498943Sluigi		switch(cmd->opcode) {
1695117577Sluigi		case O_PROB:
1696107289Sluigi			break;	/* done already */
1697107289Sluigi
169898943Sluigi		case O_PROBE_STATE:
169998943Sluigi			break; /* no need to print anything here */
170098943Sluigi
170198943Sluigi		case O_IP_SRC:
1702130281Sru		case O_IP_SRC_LOOKUP:
170398943Sluigi		case O_IP_SRC_MASK:
170498943Sluigi		case O_IP_SRC_ME:
170598943Sluigi		case O_IP_SRC_SET:
1706102087Sluigi			show_prerequisites(&flags, HAVE_PROTO, 0);
170798943Sluigi			if (!(flags & HAVE_SRCIP))
170898943Sluigi				printf(" from");
170998943Sluigi			if ((cmd->len & F_OR) && !or_block)
171098943Sluigi				printf(" {");
1711102087Sluigi			print_ip((ipfw_insn_ip *)cmd,
1712102087Sluigi				(flags & HAVE_OPTIONS) ? " src-ip" : "");
171398943Sluigi			flags |= HAVE_SRCIP;
171498943Sluigi			break;
171598943Sluigi
171698943Sluigi		case O_IP_DST:
1717130281Sru		case O_IP_DST_LOOKUP:
171898943Sluigi		case O_IP_DST_MASK:
171998943Sluigi		case O_IP_DST_ME:
172098943Sluigi		case O_IP_DST_SET:
1721102087Sluigi			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
172298943Sluigi			if (!(flags & HAVE_DSTIP))
172398943Sluigi				printf(" to");
172498943Sluigi			if ((cmd->len & F_OR) && !or_block)
172598943Sluigi				printf(" {");
1726102087Sluigi			print_ip((ipfw_insn_ip *)cmd,
1727102087Sluigi				(flags & HAVE_OPTIONS) ? " dst-ip" : "");
172898943Sluigi			flags |= HAVE_DSTIP;
172998943Sluigi			break;
173098943Sluigi
1731145246Sbrooks		case O_IP6_SRC:
1732145246Sbrooks		case O_IP6_SRC_MASK:
1733145246Sbrooks		case O_IP6_SRC_ME:
1734147105Smlaier			show_prerequisites(&flags, HAVE_PROTO, 0);
1735145246Sbrooks			if (!(flags & HAVE_SRCIP))
1736145246Sbrooks				printf(" from");
1737145246Sbrooks			if ((cmd->len & F_OR) && !or_block)
1738145246Sbrooks				printf(" {");
1739145246Sbrooks			print_ip6((ipfw_insn_ip6 *)cmd,
1740145246Sbrooks			    (flags & HAVE_OPTIONS) ? " src-ip6" : "");
1741145246Sbrooks			flags |= HAVE_SRCIP | HAVE_PROTO;
1742145246Sbrooks			break;
1743145246Sbrooks
1744145246Sbrooks		case O_IP6_DST:
1745145246Sbrooks		case O_IP6_DST_MASK:
1746145246Sbrooks		case O_IP6_DST_ME:
1747145246Sbrooks			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1748145246Sbrooks			if (!(flags & HAVE_DSTIP))
1749145246Sbrooks				printf(" to");
1750145246Sbrooks			if ((cmd->len & F_OR) && !or_block)
1751145246Sbrooks				printf(" {");
1752145246Sbrooks			print_ip6((ipfw_insn_ip6 *)cmd,
1753145246Sbrooks			    (flags & HAVE_OPTIONS) ? " dst-ip6" : "");
1754145246Sbrooks			flags |= HAVE_DSTIP;
1755145246Sbrooks			break;
1756145246Sbrooks
1757145246Sbrooks		case O_FLOW6ID:
1758145246Sbrooks		print_flow6id( (ipfw_insn_u32 *) cmd );
1759145246Sbrooks		flags |= HAVE_OPTIONS;
1760145246Sbrooks		break;
1761145246Sbrooks
176298943Sluigi		case O_IP_DSTPORT:
1763102087Sluigi			show_prerequisites(&flags, HAVE_IP, 0);
176498943Sluigi		case O_IP_SRCPORT:
1765102087Sluigi			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1766101641Sluigi			if ((cmd->len & F_OR) && !or_block)
1767101641Sluigi				printf(" {");
1768172306Smaxim			if (cmd->len & F_NOT)
1769172306Smaxim				printf(" not");
1770102087Sluigi			print_newports((ipfw_insn_u16 *)cmd, proto,
1771102087Sluigi				(flags & HAVE_OPTIONS) ? cmd->opcode : 0);
177298943Sluigi			break;
177398943Sluigi
177498943Sluigi		case O_PROTO: {
1775145246Sbrooks			struct protoent *pe = NULL;
177698943Sluigi
177798943Sluigi			if ((cmd->len & F_OR) && !or_block)
177898943Sluigi				printf(" {");
177998943Sluigi			if (cmd->len & F_NOT)
178098943Sluigi				printf(" not");
178198943Sluigi			proto = cmd->arg1;
1782145567Sbrooks			pe = getprotobynumber(cmd->arg1);
1783146894Smlaier			if ((flags & (HAVE_PROTO4 | HAVE_PROTO6)) &&
1784146894Smlaier			    !(flags & HAVE_PROTO))
1785146894Smlaier				show_prerequisites(&flags,
1786146894Smlaier				    HAVE_IP | HAVE_OPTIONS, 0);
1787102087Sluigi			if (flags & HAVE_OPTIONS)
1788102087Sluigi				printf(" proto");
178998943Sluigi			if (pe)
179098943Sluigi				printf(" %s", pe->p_name);
179198943Sluigi			else
179298943Sluigi				printf(" %u", cmd->arg1);
179398943Sluigi			}
179498943Sluigi			flags |= HAVE_PROTO;
179598943Sluigi			break;
1796106505Smaxim
179798943Sluigi		default: /*options ... */
1798146894Smlaier			if (!(cmd->len & (F_OR|F_NOT)))
1799146894Smlaier				if (((cmd->opcode == O_IP6) &&
1800146894Smlaier				    (flags & HAVE_PROTO6)) ||
1801146894Smlaier				    ((cmd->opcode == O_IP4) &&
1802146894Smlaier				    (flags & HAVE_PROTO4)))
1803146894Smlaier					break;
1804102087Sluigi			show_prerequisites(&flags, HAVE_IP | HAVE_OPTIONS, 0);
180598943Sluigi			if ((cmd->len & F_OR) && !or_block)
180698943Sluigi				printf(" {");
180798943Sluigi			if (cmd->len & F_NOT && cmd->opcode != O_IN)
180898943Sluigi				printf(" not");
180998943Sluigi			switch(cmd->opcode) {
1810169139Smaxim			case O_MACADDR2: {
1811169139Smaxim				ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
1812169139Smaxim
1813169139Smaxim				printf(" MAC");
1814169139Smaxim				print_mac(m->addr, m->mask);
1815169139Smaxim				print_mac(m->addr + 6, m->mask + 6);
1816169139Smaxim				}
1817169139Smaxim				break;
1818169139Smaxim
1819169139Smaxim			case O_MAC_TYPE:
1820169139Smaxim				print_newports((ipfw_insn_u16 *)cmd,
1821169139Smaxim						IPPROTO_ETHERTYPE, cmd->opcode);
1822169139Smaxim				break;
1823169139Smaxim
1824169139Smaxim
182598943Sluigi			case O_FRAG:
182698943Sluigi				printf(" frag");
182798943Sluigi				break;
182898943Sluigi
1829178888Sjulian			case O_FIB:
1830178888Sjulian				printf(" fib %u", cmd->arg1 );
1831178888Sjulian				break;
1832178888Sjulian
183398943Sluigi			case O_IN:
183498943Sluigi				printf(cmd->len & F_NOT ? " out" : " in");
183598943Sluigi				break;
183698943Sluigi
1837136073Sgreen			case O_DIVERTED:
1838136073Sgreen				switch (cmd->arg1) {
1839136073Sgreen				case 3:
1840136073Sgreen					printf(" diverted");
1841136073Sgreen					break;
1842136073Sgreen				case 1:
1843136073Sgreen					printf(" diverted-loopback");
1844136073Sgreen					break;
1845136073Sgreen				case 2:
1846136073Sgreen					printf(" diverted-output");
1847136073Sgreen					break;
1848136073Sgreen				default:
1849136073Sgreen					printf(" diverted-?<%u>", cmd->arg1);
1850136073Sgreen					break;
1851136073Sgreen				}
1852136073Sgreen				break;
1853136073Sgreen
185498943Sluigi			case O_LAYER2:
185598943Sluigi				printf(" layer2");
185698943Sluigi				break;
185798943Sluigi			case O_XMIT:
185898943Sluigi			case O_RECV:
1859140423Sglebius			case O_VIA:
1860140423Sglebius			    {
1861117469Sluigi				char const *s;
186298943Sluigi				ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd;
186398943Sluigi
186498943Sluigi				if (cmd->opcode == O_XMIT)
186598943Sluigi					s = "xmit";
186698943Sluigi				else if (cmd->opcode == O_RECV)
186798943Sluigi					s = "recv";
1868117469Sluigi				else /* if (cmd->opcode == O_VIA) */
186998943Sluigi					s = "via";
187098943Sluigi				if (cmdif->name[0] == '\0')
187199475Sluigi					printf(" %s %s", s,
187299475Sluigi					    inet_ntoa(cmdif->p.ip));
1873140423Sglebius				else
1874140423Sglebius					printf(" %s %s", s, cmdif->name);
1875140423Sglebius
187698943Sluigi				break;
1877140423Sglebius			    }
187898943Sluigi			case O_IPID:
1879116690Sluigi				if (F_LEN(cmd) == 1)
1880116690Sluigi				    printf(" ipid %u", cmd->arg1 );
1881116690Sluigi				else
1882116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1883116690Sluigi					O_IPID);
188498943Sluigi				break;
188598943Sluigi
188698943Sluigi			case O_IPTTL:
1887116690Sluigi				if (F_LEN(cmd) == 1)
1888116690Sluigi				    printf(" ipttl %u", cmd->arg1 );
1889116690Sluigi				else
1890116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1891116690Sluigi					O_IPTTL);
189298943Sluigi				break;
189398943Sluigi
189498943Sluigi			case O_IPVER:
189598943Sluigi				printf(" ipver %u", cmd->arg1 );
189698943Sluigi				break;
189798943Sluigi
189899475Sluigi			case O_IPPRECEDENCE:
189999475Sluigi				printf(" ipprecedence %u", (cmd->arg1) >> 5 );
190099475Sluigi				break;
190199475Sluigi
190298943Sluigi			case O_IPLEN:
1903116690Sluigi				if (F_LEN(cmd) == 1)
1904116690Sluigi				    printf(" iplen %u", cmd->arg1 );
1905116690Sluigi				else
1906116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1907116690Sluigi					O_IPLEN);
190898943Sluigi				break;
190998943Sluigi
1910101116Sluigi			case O_IPOPT:
191198943Sluigi				print_flags("ipoptions", cmd, f_ipopts);
191298943Sluigi				break;
191398943Sluigi
191499475Sluigi			case O_IPTOS:
191599475Sluigi				print_flags("iptos", cmd, f_iptos);
191699475Sluigi				break;
191799475Sluigi
191899475Sluigi			case O_ICMPTYPE:
191999475Sluigi				print_icmptypes((ipfw_insn_u32 *)cmd);
192099475Sluigi				break;
192199475Sluigi
192298943Sluigi			case O_ESTAB:
192398943Sluigi				printf(" established");
192498943Sluigi				break;
192598943Sluigi
1926136075Sgreen			case O_TCPDATALEN:
1927136075Sgreen				if (F_LEN(cmd) == 1)
1928136075Sgreen				    printf(" tcpdatalen %u", cmd->arg1 );
1929136075Sgreen				else
1930136075Sgreen				    print_newports((ipfw_insn_u16 *)cmd, 0,
1931136075Sgreen					O_TCPDATALEN);
1932136075Sgreen				break;
1933136075Sgreen
193498943Sluigi			case O_TCPFLAGS:
193598943Sluigi				print_flags("tcpflags", cmd, f_tcpflags);
193698943Sluigi				break;
193798943Sluigi
193898943Sluigi			case O_TCPOPTS:
193998943Sluigi				print_flags("tcpoptions", cmd, f_tcpopts);
194098943Sluigi				break;
194198943Sluigi
194298943Sluigi			case O_TCPWIN:
194398943Sluigi				printf(" tcpwin %d", ntohs(cmd->arg1));
194498943Sluigi				break;
194598943Sluigi
194698943Sluigi			case O_TCPACK:
194798943Sluigi				printf(" tcpack %d", ntohl(cmd32->d[0]));
194898943Sluigi				break;
194998943Sluigi
195098943Sluigi			case O_TCPSEQ:
195198943Sluigi				printf(" tcpseq %d", ntohl(cmd32->d[0]));
195298943Sluigi				break;
195398943Sluigi
195498943Sluigi			case O_UID:
195598943Sluigi			    {
195698943Sluigi				struct passwd *pwd = getpwuid(cmd32->d[0]);
195798943Sluigi
195898943Sluigi				if (pwd)
195998943Sluigi					printf(" uid %s", pwd->pw_name);
196098943Sluigi				else
196198943Sluigi					printf(" uid %u", cmd32->d[0]);
196298943Sluigi			    }
196398943Sluigi				break;
196498943Sluigi
196598943Sluigi			case O_GID:
196698943Sluigi			    {
196798943Sluigi				struct group *grp = getgrgid(cmd32->d[0]);
196898943Sluigi
196998943Sluigi				if (grp)
197098943Sluigi					printf(" gid %s", grp->gr_name);
197198943Sluigi				else
197298943Sluigi					printf(" gid %u", cmd32->d[0]);
197398943Sluigi			    }
197498943Sluigi				break;
197598943Sluigi
1976133600Scsjp			case O_JAIL:
1977133600Scsjp				printf(" jail %d", cmd32->d[0]);
1978133600Scsjp				break;
1979133600Scsjp
1980112250Scjc			case O_VERREVPATH:
1981112250Scjc				printf(" verrevpath");
1982112250Scjc				break;
1983116919Sluigi
1984128575Sandre			case O_VERSRCREACH:
1985128575Sandre				printf(" versrcreach");
1986128575Sandre				break;
1987128575Sandre
1988133387Sandre			case O_ANTISPOOF:
1989133387Sandre				printf(" antispoof");
1990133387Sandre				break;
1991133387Sandre
1992117241Sluigi			case O_IPSEC:
1993117241Sluigi				printf(" ipsec");
1994117241Sluigi				break;
1995117241Sluigi
1996117469Sluigi			case O_NOP:
1997117626Sluigi				comment = (char *)(cmd + 1);
1998117469Sluigi				break;
1999117469Sluigi
200098943Sluigi			case O_KEEP_STATE:
200198943Sluigi				printf(" keep-state");
200298943Sluigi				break;
200398943Sluigi
2004159636Soleg			case O_LIMIT: {
200598943Sluigi				struct _s_x *p = limit_masks;
200698943Sluigi				ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
2007117328Sluigi				uint8_t x = c->limit_mask;
2008117469Sluigi				char const *comma = " ";
200998943Sluigi
201098943Sluigi				printf(" limit");
2011117577Sluigi				for (; p->x != 0 ; p++)
201299909Sluigi					if ((x & p->x) == p->x) {
201398943Sluigi						x &= ~p->x;
201498943Sluigi						printf("%s%s", comma, p->s);
201598943Sluigi						comma = ",";
201698943Sluigi					}
2017159636Soleg				PRINT_UINT_ARG(" ", c->conn_limit);
201898943Sluigi				break;
2019159636Soleg			}
202098943Sluigi
2021146894Smlaier			case O_IP6:
2022152923Sume				printf(" ip6");
2023145246Sbrooks				break;
2024145246Sbrooks
2025146894Smlaier			case O_IP4:
2026152923Sume				printf(" ip4");
2027146894Smlaier				break;
2028146894Smlaier
2029145246Sbrooks			case O_ICMP6TYPE:
2030145246Sbrooks				print_icmp6types((ipfw_insn_u32 *)cmd);
2031145246Sbrooks				break;
2032145246Sbrooks
2033145246Sbrooks			case O_EXT_HDR:
2034145246Sbrooks				print_ext6hdr( (ipfw_insn *) cmd );
2035145246Sbrooks				break;
2036145246Sbrooks
2037158879Soleg			case O_TAGGED:
2038158879Soleg				if (F_LEN(cmd) == 1)
2039159636Soleg					PRINT_UINT_ARG(" tagged ", cmd->arg1);
2040158879Soleg				else
2041159636Soleg					print_newports((ipfw_insn_u16 *)cmd, 0,
2042159636Soleg					    O_TAGGED);
2043158879Soleg				break;
2044158879Soleg
204598943Sluigi			default:
204698943Sluigi				printf(" [opcode %d len %d]",
204798943Sluigi				    cmd->opcode, cmd->len);
204898943Sluigi			}
204998943Sluigi		}
205098943Sluigi		if (cmd->len & F_OR) {
205198943Sluigi			printf(" or");
205298943Sluigi			or_block = 1;
205398943Sluigi		} else if (or_block) {
205498943Sluigi			printf(" }");
205598943Sluigi			or_block = 0;
205698943Sluigi		}
205798943Sluigi	}
2058102087Sluigi	show_prerequisites(&flags, HAVE_IP, 0);
2059117626Sluigi	if (comment)
2060117626Sluigi		printf(" // %s", comment);
206198943Sluigi	printf("\n");
206298943Sluigi}
206398943Sluigi
206498943Sluigistatic void
2065112189Smaximshow_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth)
206698943Sluigi{
206798943Sluigi	struct protoent *pe;
206898943Sluigi	struct in_addr a;
2069115793Sticso	uint16_t rulenum;
2070159160Smlaier	char buf[INET6_ADDRSTRLEN];
207198943Sluigi
207298943Sluigi	if (!do_expired) {
207398943Sluigi		if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT))
207498943Sluigi			return;
207598943Sluigi	}
2076115793Sticso	bcopy(&d->rule, &rulenum, sizeof(rulenum));
2077117328Sluigi	printf("%05d", rulenum);
2078117469Sluigi	if (pcwidth>0 || bcwidth>0)
2079117328Sluigi	    printf(" %*llu %*llu (%ds)", pcwidth,
2080117328Sluigi		align_uint64(&d->pcnt), bcwidth,
2081117328Sluigi		align_uint64(&d->bcnt), d->expire);
208298943Sluigi	switch (d->dyn_type) {
208398943Sluigi	case O_LIMIT_PARENT:
208498943Sluigi		printf(" PARENT %d", d->count);
208598943Sluigi		break;
208698943Sluigi	case O_LIMIT:
208798943Sluigi		printf(" LIMIT");
208898943Sluigi		break;
208998943Sluigi	case O_KEEP_STATE: /* bidir, no mask */
2090106505Smaxim		printf(" STATE");
209198943Sluigi		break;
209298943Sluigi	}
209398943Sluigi
209498943Sluigi	if ((pe = getprotobynumber(d->id.proto)) != NULL)
209598943Sluigi		printf(" %s", pe->p_name);
209698943Sluigi	else
209798943Sluigi		printf(" proto %u", d->id.proto);
209898943Sluigi
2099159160Smlaier	if (d->id.addr_type == 4) {
2100159160Smlaier		a.s_addr = htonl(d->id.src_ip);
2101159160Smlaier		printf(" %s %d", inet_ntoa(a), d->id.src_port);
210298943Sluigi
2103159160Smlaier		a.s_addr = htonl(d->id.dst_ip);
2104159160Smlaier		printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port);
2105159160Smlaier	} else if (d->id.addr_type == 6) {
2106159160Smlaier		printf(" %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf,
2107159160Smlaier		    sizeof(buf)), d->id.src_port);
2108159160Smlaier		printf(" <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf,
2109159160Smlaier		    sizeof(buf)), d->id.dst_port);
2110159160Smlaier	} else
2111159160Smlaier		printf(" UNKNOWN <-> UNKNOWN\n");
2112159160Smlaier
211398943Sluigi	printf("\n");
211498943Sluigi}
211598943Sluigi
2116117469Sluigistatic int
211798943Sluigisort_q(const void *pa, const void *pb)
211898943Sluigi{
211998943Sluigi	int rev = (do_sort < 0);
212098943Sluigi	int field = rev ? -do_sort : do_sort;
212198943Sluigi	long long res = 0;
212298943Sluigi	const struct dn_flow_queue *a = pa;
212398943Sluigi	const struct dn_flow_queue *b = pb;
212498943Sluigi
212598943Sluigi	switch (field) {
212698943Sluigi	case 1: /* pkts */
212798943Sluigi		res = a->len - b->len;
212898943Sluigi		break;
212998943Sluigi	case 2: /* bytes */
213098943Sluigi		res = a->len_bytes - b->len_bytes;
213198943Sluigi		break;
213298943Sluigi
213398943Sluigi	case 3: /* tot pkts */
213498943Sluigi		res = a->tot_pkts - b->tot_pkts;
213598943Sluigi		break;
213698943Sluigi
213798943Sluigi	case 4: /* tot bytes */
213898943Sluigi		res = a->tot_bytes - b->tot_bytes;
213998943Sluigi		break;
214098943Sluigi	}
214198943Sluigi	if (res < 0)
214298943Sluigi		res = -1;
214398943Sluigi	if (res > 0)
214498943Sluigi		res = 1;
214598943Sluigi	return (int)(rev ? res : -res);
214698943Sluigi}
214798943Sluigi
214898943Sluigistatic void
214998943Sluigilist_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
215098943Sluigi{
215198943Sluigi	int l;
2152145246Sbrooks	int index_printed, indexes = 0;
2153145246Sbrooks	char buff[255];
2154145246Sbrooks	struct protoent *pe;
215598943Sluigi
215698943Sluigi	if (fs->rq_elements == 0)
215798943Sluigi		return;
215898943Sluigi
215998943Sluigi	if (do_sort != 0)
216098943Sluigi		heapsort(q, fs->rq_elements, sizeof *q, sort_q);
2161145246Sbrooks
2162145246Sbrooks	/* Print IPv4 flows */
2163145246Sbrooks	index_printed = 0;
216498943Sluigi	for (l = 0; l < fs->rq_elements; l++) {
216598943Sluigi		struct in_addr ina;
216698943Sluigi
2167145246Sbrooks		/* XXX: Should check for IPv4 flows */
2168145246Sbrooks		if (IS_IP6_FLOW_ID(&(q[l].id)))
2169145246Sbrooks			continue;
2170145246Sbrooks
2171145246Sbrooks		if (!index_printed) {
2172145246Sbrooks			index_printed = 1;
2173145246Sbrooks			if (indexes > 0)	/* currently a no-op */
2174145246Sbrooks				printf("\n");
2175145246Sbrooks			indexes++;
2176145246Sbrooks			printf("    "
2177145246Sbrooks			    "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
2178145246Sbrooks			    fs->flow_mask.proto,
2179145246Sbrooks			    fs->flow_mask.src_ip, fs->flow_mask.src_port,
2180145246Sbrooks			    fs->flow_mask.dst_ip, fs->flow_mask.dst_port);
2181145246Sbrooks
2182145246Sbrooks			printf("BKT Prot ___Source IP/port____ "
2183145246Sbrooks			    "____Dest. IP/port____ "
2184145246Sbrooks			    "Tot_pkt/bytes Pkt/Byte Drp\n");
2185145246Sbrooks		}
2186145246Sbrooks
218798943Sluigi		printf("%3d ", q[l].hash_slot);
218898943Sluigi		pe = getprotobynumber(q[l].id.proto);
218998943Sluigi		if (pe)
219098943Sluigi			printf("%-4s ", pe->p_name);
219198943Sluigi		else
219298943Sluigi			printf("%4u ", q[l].id.proto);
2193145246Sbrooks		ina.s_addr = htonl(q[l].id.src_ip);
219498943Sluigi		printf("%15s/%-5d ",
219598943Sluigi		    inet_ntoa(ina), q[l].id.src_port);
219698943Sluigi		ina.s_addr = htonl(q[l].id.dst_ip);
219798943Sluigi		printf("%15s/%-5d ",
219898943Sluigi		    inet_ntoa(ina), q[l].id.dst_port);
219998943Sluigi		printf("%4qu %8qu %2u %4u %3u\n",
220098943Sluigi		    q[l].tot_pkts, q[l].tot_bytes,
220198943Sluigi		    q[l].len, q[l].len_bytes, q[l].drops);
220298943Sluigi		if (verbose)
220398943Sluigi			printf("   S %20qd  F %20qd\n",
220498943Sluigi			    q[l].S, q[l].F);
220598943Sluigi	}
2206145246Sbrooks
2207145246Sbrooks	/* Print IPv6 flows */
2208145246Sbrooks	index_printed = 0;
2209145246Sbrooks	for (l = 0; l < fs->rq_elements; l++) {
2210145246Sbrooks		if (!IS_IP6_FLOW_ID(&(q[l].id)))
2211145246Sbrooks			continue;
2212145246Sbrooks
2213145246Sbrooks		if (!index_printed) {
2214145246Sbrooks			index_printed = 1;
2215145246Sbrooks			if (indexes > 0)
2216145246Sbrooks				printf("\n");
2217145246Sbrooks			indexes++;
2218145246Sbrooks			printf("\n        mask: proto: 0x%02x, flow_id: 0x%08x,  ",
2219145246Sbrooks			    fs->flow_mask.proto, fs->flow_mask.flow_id6);
2220145246Sbrooks			inet_ntop(AF_INET6, &(fs->flow_mask.src_ip6),
2221145246Sbrooks			    buff, sizeof(buff));
2222145246Sbrooks			printf("%s/0x%04x -> ", buff, fs->flow_mask.src_port);
2223145246Sbrooks			inet_ntop( AF_INET6, &(fs->flow_mask.dst_ip6),
2224145246Sbrooks			    buff, sizeof(buff) );
2225145246Sbrooks			printf("%s/0x%04x\n", buff, fs->flow_mask.dst_port);
2226145246Sbrooks
2227145246Sbrooks			printf("BKT ___Prot___ _flow-id_ "
2228145246Sbrooks			    "______________Source IPv6/port_______________ "
2229145246Sbrooks			    "_______________Dest. IPv6/port_______________ "
2230145246Sbrooks			    "Tot_pkt/bytes Pkt/Byte Drp\n");
2231145246Sbrooks		}
2232145246Sbrooks		printf("%3d ", q[l].hash_slot);
2233145246Sbrooks		pe = getprotobynumber(q[l].id.proto);
2234145246Sbrooks		if (pe != NULL)
2235145246Sbrooks			printf("%9s ", pe->p_name);
2236145246Sbrooks		else
2237145246Sbrooks			printf("%9u ", q[l].id.proto);
2238145246Sbrooks		printf("%7d  %39s/%-5d ", q[l].id.flow_id6,
2239145246Sbrooks		    inet_ntop(AF_INET6, &(q[l].id.src_ip6), buff, sizeof(buff)),
2240145246Sbrooks		    q[l].id.src_port);
2241145246Sbrooks		printf(" %39s/%-5d ",
2242145246Sbrooks		    inet_ntop(AF_INET6, &(q[l].id.dst_ip6), buff, sizeof(buff)),
2243145246Sbrooks		    q[l].id.dst_port);
2244145246Sbrooks		printf(" %4qu %8qu %2u %4u %3u\n",
2245145246Sbrooks		    q[l].tot_pkts, q[l].tot_bytes,
2246145246Sbrooks		    q[l].len, q[l].len_bytes, q[l].drops);
2247145246Sbrooks		if (verbose)
2248145246Sbrooks			printf("   S %20qd  F %20qd\n", q[l].S, q[l].F);
2249145246Sbrooks	}
225098943Sluigi}
225198943Sluigi
225298943Sluigistatic void
225398943Sluigiprint_flowset_parms(struct dn_flow_set *fs, char *prefix)
225498943Sluigi{
225598943Sluigi	int l;
225698943Sluigi	char qs[30];
225798943Sluigi	char plr[30];
225898943Sluigi	char red[90];	/* Display RED parameters */
225998943Sluigi
226098943Sluigi	l = fs->qsize;
226198943Sluigi	if (fs->flags_fs & DN_QSIZE_IS_BYTES) {
226298943Sluigi		if (l >= 8192)
226398943Sluigi			sprintf(qs, "%d KB", l / 1024);
226498943Sluigi		else
226598943Sluigi			sprintf(qs, "%d B", l);
226698943Sluigi	} else
226798943Sluigi		sprintf(qs, "%3d sl.", l);
226898943Sluigi	if (fs->plr)
226998943Sluigi		sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
227098943Sluigi	else
227198943Sluigi		plr[0] = '\0';
227298943Sluigi	if (fs->flags_fs & DN_IS_RED)	/* RED parameters */
227398943Sluigi		sprintf(red,
227498943Sluigi		    "\n\t  %cRED w_q %f min_th %d max_th %d max_p %f",
227598943Sluigi		    (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ',
227698943Sluigi		    1.0 * fs->w_q / (double)(1 << SCALE_RED),
227798943Sluigi		    SCALE_VAL(fs->min_th),
227898943Sluigi		    SCALE_VAL(fs->max_th),
227998943Sluigi		    1.0 * fs->max_p / (double)(1 << SCALE_RED));
228098943Sluigi	else
228198943Sluigi		sprintf(red, "droptail");
228298943Sluigi
228398943Sluigi	printf("%s %s%s %d queues (%d buckets) %s\n",
228498943Sluigi	    prefix, qs, plr, fs->rq_elements, fs->rq_size, red);
228598943Sluigi}
228698943Sluigi
228798943Sluigistatic void
2288117469Sluigilist_pipes(void *data, uint nbytes, int ac, char *av[])
228998943Sluigi{
2290117469Sluigi	int rulenum;
229198943Sluigi	void *next = data;
229298943Sluigi	struct dn_pipe *p = (struct dn_pipe *) data;
229398943Sluigi	struct dn_flow_set *fs;
229498943Sluigi	struct dn_flow_queue *q;
229598943Sluigi	int l;
229698943Sluigi
229798943Sluigi	if (ac > 0)
229898943Sluigi		rulenum = strtoul(*av++, NULL, 10);
229998943Sluigi	else
230098943Sluigi		rulenum = 0;
230198943Sluigi	for (; nbytes >= sizeof *p; p = (struct dn_pipe *)next) {
230298943Sluigi		double b = p->bandwidth;
230398943Sluigi		char buf[30];
230498943Sluigi		char prefix[80];
230598943Sluigi
2306161001Sstefanf		if (SLIST_NEXT(p, next) != (struct dn_pipe *)DN_IS_PIPE)
230798943Sluigi			break;	/* done with pipes, now queues */
230898943Sluigi
230998943Sluigi		/*
231098943Sluigi		 * compute length, as pipe have variable size
231198943Sluigi		 */
231298943Sluigi		l = sizeof(*p) + p->fs.rq_elements * sizeof(*q);
2313117469Sluigi		next = (char *)p + l;
231498943Sluigi		nbytes -= l;
231598943Sluigi
2316134225Spjd		if ((rulenum != 0 && rulenum != p->pipe_nr) || do_pipe == 2)
231798943Sluigi			continue;
231898943Sluigi
231998943Sluigi		/*
232098943Sluigi		 * Print rate (or clocking interface)
232198943Sluigi		 */
232298943Sluigi		if (p->if_name[0] != '\0')
232398943Sluigi			sprintf(buf, "%s", p->if_name);
232498943Sluigi		else if (b == 0)
232598943Sluigi			sprintf(buf, "unlimited");
232698943Sluigi		else if (b >= 1000000)
232798943Sluigi			sprintf(buf, "%7.3f Mbit/s", b/1000000);
232898943Sluigi		else if (b >= 1000)
232998943Sluigi			sprintf(buf, "%7.3f Kbit/s", b/1000);
233098943Sluigi		else
233198943Sluigi			sprintf(buf, "%7.3f bit/s ", b);
233298943Sluigi
233398943Sluigi		sprintf(prefix, "%05d: %s %4d ms ",
233498943Sluigi		    p->pipe_nr, buf, p->delay);
233598943Sluigi		print_flowset_parms(&(p->fs), prefix);
233698943Sluigi		if (verbose)
233798943Sluigi			printf("   V %20qd\n", p->V >> MY_M);
2338106505Smaxim
233998943Sluigi		q = (struct dn_flow_queue *)(p+1);
234098943Sluigi		list_queues(&(p->fs), q);
234198943Sluigi	}
234298943Sluigi	for (fs = next; nbytes >= sizeof *fs; fs = next) {
234398943Sluigi		char prefix[80];
234498943Sluigi
2345161001Sstefanf		if (SLIST_NEXT(fs, next) != (struct dn_flow_set *)DN_IS_QUEUE)
234698943Sluigi			break;
234798943Sluigi		l = sizeof(*fs) + fs->rq_elements * sizeof(*q);
2348117469Sluigi		next = (char *)fs + l;
234998943Sluigi		nbytes -= l;
2350134225Spjd
2351134225Spjd		if (rulenum != 0 && ((rulenum != fs->fs_nr && do_pipe == 2) ||
2352134225Spjd		    (rulenum != fs->parent_nr && do_pipe == 1))) {
2353134225Spjd			continue;
2354134225Spjd		}
2355134225Spjd
235698943Sluigi		q = (struct dn_flow_queue *)(fs+1);
235798943Sluigi		sprintf(prefix, "q%05d: weight %d pipe %d ",
235898943Sluigi		    fs->fs_nr, fs->weight, fs->parent_nr);
235998943Sluigi		print_flowset_parms(fs, prefix);
236098943Sluigi		list_queues(fs, q);
236198943Sluigi	}
236298943Sluigi}
236398943Sluigi
2364101978Sluigi/*
2365101978Sluigi * This one handles all set-related commands
2366101978Sluigi * 	ipfw set { show | enable | disable }
2367101978Sluigi * 	ipfw set swap X Y
2368101978Sluigi * 	ipfw set move X to Y
2369101978Sluigi * 	ipfw set move rule X to Y
2370101978Sluigi */
237198943Sluigistatic void
2372101978Sluigisets_handler(int ac, char *av[])
2373101978Sluigi{
2374117328Sluigi	uint32_t set_disable, masks[2];
2375101978Sluigi	int i, nbytes;
2376117328Sluigi	uint16_t rulenum;
2377117328Sluigi	uint8_t cmd, new_set;
2378101978Sluigi
2379101978Sluigi	ac--;
2380101978Sluigi	av++;
2381101978Sluigi
2382101978Sluigi	if (!ac)
2383101978Sluigi		errx(EX_USAGE, "set needs command");
2384140271Sbrooks	if (_substrcmp(*av, "show") == 0) {
2385101978Sluigi		void *data;
2386117469Sluigi		char const *msg;
2387101978Sluigi
2388101978Sluigi		nbytes = sizeof(struct ip_fw);
2389117328Sluigi		if ((data = calloc(1, nbytes)) == NULL)
2390117328Sluigi			err(EX_OSERR, "calloc");
2391119740Stmm		if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0)
2392101978Sluigi			err(EX_OSERR, "getsockopt(IP_FW_GET)");
2393115793Sticso		bcopy(&((struct ip_fw *)data)->next_rule,
2394115793Sticso			&set_disable, sizeof(set_disable));
2395101978Sluigi
2396117655Sluigi		for (i = 0, msg = "disable" ; i < RESVD_SET; i++)
2397117577Sluigi			if ((set_disable & (1<<i))) {
2398101978Sluigi				printf("%s %d", msg, i);
2399101978Sluigi				msg = "";
2400101978Sluigi			}
2401101978Sluigi		msg = (set_disable) ? " enable" : "enable";
2402117655Sluigi		for (i = 0; i < RESVD_SET; i++)
2403117577Sluigi			if (!(set_disable & (1<<i))) {
2404101978Sluigi				printf("%s %d", msg, i);
2405101978Sluigi				msg = "";
2406101978Sluigi			}
2407101978Sluigi		printf("\n");
2408140271Sbrooks	} else if (_substrcmp(*av, "swap") == 0) {
2409101978Sluigi		ac--; av++;
2410101978Sluigi		if (ac != 2)
2411101978Sluigi			errx(EX_USAGE, "set swap needs 2 set numbers\n");
2412101978Sluigi		rulenum = atoi(av[0]);
2413101978Sluigi		new_set = atoi(av[1]);
2414117655Sluigi		if (!isdigit(*(av[0])) || rulenum > RESVD_SET)
2415101978Sluigi			errx(EX_DATAERR, "invalid set number %s\n", av[0]);
2416117655Sluigi		if (!isdigit(*(av[1])) || new_set > RESVD_SET)
2417101978Sluigi			errx(EX_DATAERR, "invalid set number %s\n", av[1]);
2418101978Sluigi		masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
2419117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
2420140271Sbrooks	} else if (_substrcmp(*av, "move") == 0) {
2421101978Sluigi		ac--; av++;
2422140271Sbrooks		if (ac && _substrcmp(*av, "rule") == 0) {
2423101978Sluigi			cmd = 2;
2424101978Sluigi			ac--; av++;
2425101978Sluigi		} else
2426101978Sluigi			cmd = 3;
2427140271Sbrooks		if (ac != 3 || _substrcmp(av[1], "to") != 0)
2428101978Sluigi			errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
2429101978Sluigi		rulenum = atoi(av[0]);
2430101978Sluigi		new_set = atoi(av[2]);
2431117655Sluigi		if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) ||
2432101978Sluigi			(cmd == 2 && rulenum == 65535) )
2433101978Sluigi			errx(EX_DATAERR, "invalid source number %s\n", av[0]);
2434117655Sluigi		if (!isdigit(*(av[2])) || new_set > RESVD_SET)
2435101978Sluigi			errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
2436101978Sluigi		masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
2437117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
2438140271Sbrooks	} else if (_substrcmp(*av, "disable") == 0 ||
2439140271Sbrooks		   _substrcmp(*av, "enable") == 0 ) {
2440140271Sbrooks		int which = _substrcmp(*av, "enable") == 0 ? 1 : 0;
2441101978Sluigi
2442101978Sluigi		ac--; av++;
2443101978Sluigi		masks[0] = masks[1] = 0;
2444101978Sluigi
2445101978Sluigi		while (ac) {
2446101978Sluigi			if (isdigit(**av)) {
2447101978Sluigi				i = atoi(*av);
2448117655Sluigi				if (i < 0 || i > RESVD_SET)
2449101978Sluigi					errx(EX_DATAERR,
2450101978Sluigi					    "invalid set number %d\n", i);
2451101978Sluigi				masks[which] |= (1<<i);
2452140271Sbrooks			} else if (_substrcmp(*av, "disable") == 0)
2453101978Sluigi				which = 0;
2454140271Sbrooks			else if (_substrcmp(*av, "enable") == 0)
2455101978Sluigi				which = 1;
2456101978Sluigi			else
2457101978Sluigi				errx(EX_DATAERR,
2458101978Sluigi					"invalid set command %s\n", *av);
2459101978Sluigi			av++; ac--;
2460101978Sluigi		}
2461101978Sluigi		if ( (masks[0] & masks[1]) != 0 )
2462101978Sluigi			errx(EX_DATAERR,
2463101978Sluigi			    "cannot enable and disable the same set\n");
2464101978Sluigi
2465117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(masks));
2466101978Sluigi		if (i)
2467101978Sluigi			warn("set enable/disable: setsockopt(IP_FW_DEL)");
2468101978Sluigi	} else
2469101978Sluigi		errx(EX_USAGE, "invalid set command %s\n", *av);
2470101978Sluigi}
2471101978Sluigi
2472101978Sluigistatic void
2473109126Sdillonsysctl_handler(int ac, char *av[], int which)
2474109126Sdillon{
2475109126Sdillon	ac--;
2476109126Sdillon	av++;
2477109126Sdillon
2478119668Smaxim	if (ac == 0) {
2479109126Sdillon		warnx("missing keyword to enable/disable\n");
2480140271Sbrooks	} else if (_substrcmp(*av, "firewall") == 0) {
2481116770Sluigi		sysctlbyname("net.inet.ip.fw.enable", NULL, 0,
2482116770Sluigi		    &which, sizeof(which));
2483140271Sbrooks	} else if (_substrcmp(*av, "one_pass") == 0) {
2484116770Sluigi		sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0,
2485116770Sluigi		    &which, sizeof(which));
2486140271Sbrooks	} else if (_substrcmp(*av, "debug") == 0) {
2487116770Sluigi		sysctlbyname("net.inet.ip.fw.debug", NULL, 0,
2488116770Sluigi		    &which, sizeof(which));
2489140271Sbrooks	} else if (_substrcmp(*av, "verbose") == 0) {
2490116770Sluigi		sysctlbyname("net.inet.ip.fw.verbose", NULL, 0,
2491116770Sluigi		    &which, sizeof(which));
2492140271Sbrooks	} else if (_substrcmp(*av, "dyn_keepalive") == 0) {
2493116770Sluigi		sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0,
2494116770Sluigi		    &which, sizeof(which));
2495140271Sbrooks	} else if (_substrcmp(*av, "altq") == 0) {
2496136071Sgreen		altq_set_enabled(which);
2497109126Sdillon	} else {
2498109126Sdillon		warnx("unrecognize enable/disable keyword: %s\n", *av);
2499109126Sdillon	}
2500109126Sdillon}
2501109126Sdillon
2502109126Sdillonstatic void
2503117469Sluigilist(int ac, char *av[], int show_counters)
250498943Sluigi{
250598943Sluigi	struct ip_fw *r;
250698943Sluigi	ipfw_dyn_rule *dynrules, *d;
250798943Sluigi
2508117469Sluigi#define NEXT(r)	((struct ip_fw *)((char *)r + RULESIZE(r)))
2509117469Sluigi	char *lim;
2510117469Sluigi	void *data = NULL;
2511112189Smaxim	int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width;
251298943Sluigi	int exitval = EX_OK;
251398943Sluigi	int lac;
251498943Sluigi	char **lav;
2515117469Sluigi	u_long rnum, last;
251698943Sluigi	char *endptr;
251798943Sluigi	int seen = 0;
2518170923Smaxim	uint8_t set;
251998943Sluigi
252098943Sluigi	const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
252198943Sluigi	int nalloc = 1024;	/* start somewhere... */
252298943Sluigi
2523135036Smaxim	last = 0;
2524135036Smaxim
2525117328Sluigi	if (test_only) {
2526117328Sluigi		fprintf(stderr, "Testing only, list disabled\n");
2527117328Sluigi		return;
2528117328Sluigi	}
2529117328Sluigi
253098943Sluigi	ac--;
253198943Sluigi	av++;
253298943Sluigi
253398943Sluigi	/* get rules or pipes from kernel, resizing array as necessary */
253498943Sluigi	nbytes = nalloc;
253598943Sluigi
253698943Sluigi	while (nbytes >= nalloc) {
253798943Sluigi		nalloc = nalloc * 2 + 200;
253898943Sluigi		nbytes = nalloc;
253998943Sluigi		if ((data = realloc(data, nbytes)) == NULL)
254098943Sluigi			err(EX_OSERR, "realloc");
2541119740Stmm		if (do_cmd(ocmd, data, (uintptr_t)&nbytes) < 0)
254298943Sluigi			err(EX_OSERR, "getsockopt(IP_%s_GET)",
254398943Sluigi				do_pipe ? "DUMMYNET" : "FW");
254498943Sluigi	}
254598943Sluigi
254698943Sluigi	if (do_pipe) {
254798943Sluigi		list_pipes(data, nbytes, ac, av);
254898943Sluigi		goto done;
254998943Sluigi	}
255098943Sluigi
255198943Sluigi	/*
255298943Sluigi	 * Count static rules. They have variable size so we
255398943Sluigi	 * need to scan the list to count them.
255498943Sluigi	 */
2555117469Sluigi	for (nstat = 1, r = data, lim = (char *)data + nbytes;
2556117469Sluigi		    r->rulenum < 65535 && (char *)r < lim;
2557117469Sluigi		    ++nstat, r = NEXT(r) )
255898943Sluigi		; /* nothing */
255998943Sluigi
256098943Sluigi	/*
256198943Sluigi	 * Count dynamic rules. This is easier as they have
256298943Sluigi	 * fixed size.
256398943Sluigi	 */
2564117469Sluigi	r = NEXT(r);
256598943Sluigi	dynrules = (ipfw_dyn_rule *)r ;
2566117469Sluigi	n = (char *)r - (char *)data;
256798943Sluigi	ndyn = (nbytes - n) / sizeof *dynrules;
256898943Sluigi
2569112189Smaxim	/* if showing stats, figure out column widths ahead of time */
2570112189Smaxim	bcwidth = pcwidth = 0;
2571117469Sluigi	if (show_counters) {
2572117469Sluigi		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
2573170923Smaxim			/* skip rules from another set */
2574170923Smaxim			if (use_set && r->set != use_set - 1)
2575170923Smaxim				continue;
2576170923Smaxim
2577112189Smaxim			/* packet counter */
2578115793Sticso			width = snprintf(NULL, 0, "%llu",
2579115793Sticso			    align_uint64(&r->pcnt));
2580112189Smaxim			if (width > pcwidth)
2581112189Smaxim				pcwidth = width;
2582112189Smaxim
2583112189Smaxim			/* byte counter */
2584115793Sticso			width = snprintf(NULL, 0, "%llu",
2585115793Sticso			    align_uint64(&r->bcnt));
2586112189Smaxim			if (width > bcwidth)
2587112189Smaxim				bcwidth = width;
2588112189Smaxim		}
2589112189Smaxim	}
2590112189Smaxim	if (do_dynamic && ndyn) {
2591112189Smaxim		for (n = 0, d = dynrules; n < ndyn; n++, d++) {
2592170923Smaxim			if (use_set) {
2593170923Smaxim				/* skip rules from another set */
2594171989Smaxim				bcopy((char *)&d->rule + sizeof(uint16_t),
2595170923Smaxim				      &set, sizeof(uint8_t));
2596170923Smaxim				if (set != use_set - 1)
2597170923Smaxim					continue;
2598170923Smaxim			}
2599115793Sticso			width = snprintf(NULL, 0, "%llu",
2600115793Sticso			    align_uint64(&d->pcnt));
2601112189Smaxim			if (width > pcwidth)
2602112189Smaxim				pcwidth = width;
2603112189Smaxim
2604115793Sticso			width = snprintf(NULL, 0, "%llu",
2605115793Sticso			    align_uint64(&d->bcnt));
2606112189Smaxim			if (width > bcwidth)
2607112189Smaxim				bcwidth = width;
2608112189Smaxim		}
2609112189Smaxim	}
261098943Sluigi	/* if no rule numbers were specified, list all rules */
261198943Sluigi	if (ac == 0) {
2612170923Smaxim		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
2613170923Smaxim			if (use_set && r->set != use_set - 1)
2614170923Smaxim				continue;
2615112189Smaxim			show_ipfw(r, pcwidth, bcwidth);
2616170923Smaxim		}
261798943Sluigi
261898943Sluigi		if (do_dynamic && ndyn) {
261998943Sluigi			printf("## Dynamic rules (%d):\n", ndyn);
2620170923Smaxim			for (n = 0, d = dynrules; n < ndyn; n++, d++) {
2621170923Smaxim				if (use_set) {
2622171989Smaxim					bcopy((char *)&d->rule + sizeof(uint16_t),
2623170923Smaxim					      &set, sizeof(uint8_t));
2624170923Smaxim					if (set != use_set - 1)
2625170923Smaxim						continue;
2626170923Smaxim				}
2627112189Smaxim				show_dyn_ipfw(d, pcwidth, bcwidth);
262898943Sluigi		}
2629170923Smaxim		}
263098943Sluigi		goto done;
263198943Sluigi	}
263298943Sluigi
263398943Sluigi	/* display specific rules requested on command line */
263498943Sluigi
263598943Sluigi	for (lac = ac, lav = av; lac != 0; lac--) {
263698943Sluigi		/* convert command line rule # */
2637117469Sluigi		last = rnum = strtoul(*lav++, &endptr, 10);
2638117469Sluigi		if (*endptr == '-')
2639117469Sluigi			last = strtoul(endptr+1, &endptr, 10);
264098943Sluigi		if (*endptr) {
264198943Sluigi			exitval = EX_USAGE;
264298943Sluigi			warnx("invalid rule number: %s", *(lav - 1));
264398943Sluigi			continue;
264498943Sluigi		}
2645117469Sluigi		for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) {
2646117469Sluigi			if (r->rulenum > last)
264798943Sluigi				break;
2648170923Smaxim			if (use_set && r->set != use_set - 1)
2649170923Smaxim				continue;
2650117469Sluigi			if (r->rulenum >= rnum && r->rulenum <= last) {
2651112189Smaxim				show_ipfw(r, pcwidth, bcwidth);
265298943Sluigi				seen = 1;
265398943Sluigi			}
265498943Sluigi		}
265598943Sluigi		if (!seen) {
265698943Sluigi			/* give precedence to other error(s) */
265798943Sluigi			if (exitval == EX_OK)
265898943Sluigi				exitval = EX_UNAVAILABLE;
265998943Sluigi			warnx("rule %lu does not exist", rnum);
266098943Sluigi		}
266198943Sluigi	}
266298943Sluigi
266398943Sluigi	if (do_dynamic && ndyn) {
266498943Sluigi		printf("## Dynamic rules:\n");
266598943Sluigi		for (lac = ac, lav = av; lac != 0; lac--) {
2666145246Sbrooks			last = rnum = strtoul(*lav++, &endptr, 10);
2667117469Sluigi			if (*endptr == '-')
2668117469Sluigi				last = strtoul(endptr+1, &endptr, 10);
266998943Sluigi			if (*endptr)
267098943Sluigi				/* already warned */
267198943Sluigi				continue;
267298943Sluigi			for (n = 0, d = dynrules; n < ndyn; n++, d++) {
2673115793Sticso				uint16_t rulenum;
2674115793Sticso
2675115793Sticso				bcopy(&d->rule, &rulenum, sizeof(rulenum));
2676115793Sticso				if (rulenum > rnum)
267798943Sluigi					break;
2678170923Smaxim				if (use_set) {
2679171989Smaxim					bcopy((char *)&d->rule + sizeof(uint16_t),
2680170923Smaxim					      &set, sizeof(uint8_t));
2681170923Smaxim					if (set != use_set - 1)
2682170923Smaxim						continue;
2683170923Smaxim				}
2684117469Sluigi				if (r->rulenum >= rnum && r->rulenum <= last)
2685112189Smaxim					show_dyn_ipfw(d, pcwidth, bcwidth);
268698943Sluigi			}
268798943Sluigi		}
268898943Sluigi	}
268998943Sluigi
269098943Sluigi	ac = 0;
269198943Sluigi
269298943Sluigidone:
269398943Sluigi	free(data);
269498943Sluigi
269598943Sluigi	if (exitval != EX_OK)
269698943Sluigi		exit(exitval);
2697117469Sluigi#undef NEXT
269898943Sluigi}
269998943Sluigi
270098943Sluigistatic void
270198943Sluigishow_usage(void)
270298943Sluigi{
270398943Sluigi	fprintf(stderr, "usage: ipfw [options]\n"
270498943Sluigi"do \"ipfw -h\" or see ipfw manpage for details\n"
270598943Sluigi);
270698943Sluigi	exit(EX_USAGE);
270798943Sluigi}
270898943Sluigi
270998943Sluigistatic void
271098943Sluigihelp(void)
271198943Sluigi{
2712117328Sluigi	fprintf(stderr,
2713117328Sluigi"ipfw syntax summary (but please do read the ipfw(8) manpage):\n"
2714123495Sluigi"ipfw [-abcdefhnNqStTv] <command> where <command> is one of:\n"
2715117328Sluigi"add [num] [set N] [prob x] RULE-BODY\n"
2716117328Sluigi"{pipe|queue} N config PIPE-BODY\n"
2717117328Sluigi"[pipe|queue] {zero|delete|show} [N{,N}]\n"
2718165648Spiso"nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|reset|\n"
2719165648Spiso"		reverse|proxy_only|redirect_addr linkspec|\n"
2720165648Spiso"		redirect_port linkspec|redirect_proto linkspec}\n"
2721117328Sluigi"set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n"
2722170923Smaxim"set N {show|list|zero|resetlog|delete} [N{,N}] | flush\n"
2723130281Sru"table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n"
272498943Sluigi"\n"
2725136071Sgreen"RULE-BODY:	check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n"
2726149020Sbz"ACTION:	check-state | allow | count | deny | unreach{,6} CODE |\n"
2727149020Sbz"               skipto N | {divert|tee} PORT | forward ADDR |\n"
2728178888Sjulian"               pipe N | queue N | nat N | setfib FIB\n"
2729136071Sgreen"PARAMS: 	[log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n"
273098943Sluigi"ADDR:		[ MAC dst src ether_type ] \n"
2731145246Sbrooks"		[ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
2732145246Sbrooks"		[ ipv6|ip6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n"
2733130281Sru"IPADDR:	[not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n"
2734145246Sbrooks"IP6ADDR:	[not] { any | me | me6 | ip6/bits | IP6LIST }\n"
2735145246Sbrooks"IP6LIST:	{ ip6 | ip6/bits }[,IP6LIST]\n"
2736117544Sluigi"IPLIST:	{ ip | ip/bits | ip:mask }[,IPLIST]\n"
2737117544Sluigi"OPTION_LIST:	OPTION [OPTION_LIST]\n"
2738136247Sgreen"OPTION:	bridged | diverted | diverted-loopback | diverted-output |\n"
2739145246Sbrooks"	{dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n"
2740145246Sbrooks"	{dst-port|src-port} LIST |\n"
2741117328Sluigi"	estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n"
2742117328Sluigi"	iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n"
2743117328Sluigi"	ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n"
2744178888Sjulian"	icmp6types LIST | ext6hdr LIST | flow-id N[,N] | fib FIB |\n"
2745117328Sluigi"	mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n"
2746117328Sluigi"	setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n"
2747136075Sgreen"	tcpdatalen LIST | verrevpath | versrcreach | antispoof\n"
274898943Sluigi);
274998943Sluigiexit(0);
275098943Sluigi}
275198943Sluigi
275298943Sluigi
275398943Sluigistatic int
275498943Sluigilookup_host (char *host, struct in_addr *ipaddr)
275598943Sluigi{
275698943Sluigi	struct hostent *he;
275798943Sluigi
275898943Sluigi	if (!inet_aton(host, ipaddr)) {
275998943Sluigi		if ((he = gethostbyname(host)) == NULL)
276098943Sluigi			return(-1);
276198943Sluigi		*ipaddr = *(struct in_addr *)he->h_addr_list[0];
276298943Sluigi	}
276398943Sluigi	return(0);
276498943Sluigi}
276598943Sluigi
276698943Sluigi/*
276798943Sluigi * fills the addr and mask fields in the instruction as appropriate from av.
276898943Sluigi * Update length as appropriate.
276998943Sluigi * The following formats are allowed:
277098943Sluigi *	me	returns O_IP_*_ME
277198943Sluigi *	1.2.3.4		single IP address
277298943Sluigi *	1.2.3.4:5.6.7.8	address:mask
277398943Sluigi *	1.2.3.4/24	address/mask
277498943Sluigi *	1.2.3.4/26{1,6,5,4,23}	set of addresses in a subnet
2775117328Sluigi * We can have multiple comma-separated address/mask entries.
277698943Sluigi */
277798943Sluigistatic void
277898943Sluigifill_ip(ipfw_insn_ip *cmd, char *av)
277998943Sluigi{
2780117328Sluigi	int len = 0;
2781117328Sluigi	uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
278298943Sluigi
278398943Sluigi	cmd->o.len &= ~F_LEN_MASK;	/* zero len */
278498943Sluigi
2785140271Sbrooks	if (_substrcmp(av, "any") == 0)
278698943Sluigi		return;
278798943Sluigi
2788140271Sbrooks	if (_substrcmp(av, "me") == 0) {
278998943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn);
279098943Sluigi		return;
279198943Sluigi	}
279298943Sluigi
2793140271Sbrooks	if (strncmp(av, "table(", 6) == 0) {
2794130281Sru		char *p = strchr(av + 6, ',');
2795130281Sru
2796130281Sru		if (p)
2797130281Sru			*p++ = '\0';
2798130281Sru		cmd->o.opcode = O_IP_DST_LOOKUP;
2799130281Sru		cmd->o.arg1 = strtoul(av + 6, NULL, 0);
2800130281Sru		if (p) {
2801130281Sru			cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2802130281Sru			d[0] = strtoul(p, NULL, 0);
2803130281Sru		} else
2804130281Sru			cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2805130281Sru		return;
2806130281Sru	}
2807130281Sru
2808117328Sluigi    while (av) {
2809117328Sluigi	/*
2810117328Sluigi	 * After the address we can have '/' or ':' indicating a mask,
2811117328Sluigi	 * ',' indicating another address follows, '{' indicating a
2812117328Sluigi	 * set of addresses of unspecified size.
2813117328Sluigi	 */
2814165851Smlaier	char *t = NULL, *p = strpbrk(av, "/:,{");
2815117328Sluigi	int masklen;
2816165851Smlaier	char md, nd;
2817117328Sluigi
281898943Sluigi	if (p) {
281998943Sluigi		md = *p;
282098943Sluigi		*p++ = '\0';
2821165851Smlaier		if ((t = strpbrk(p, ",{")) != NULL) {
2822165851Smlaier			nd = *t;
2823165851Smlaier			*t = '\0';
2824165851Smlaier		}
2825117328Sluigi	} else
2826117328Sluigi		md = '\0';
282798943Sluigi
2828117328Sluigi	if (lookup_host(av, (struct in_addr *)&d[0]) != 0)
282998943Sluigi		errx(EX_NOHOST, "hostname ``%s'' unknown", av);
283098943Sluigi	switch (md) {
283198943Sluigi	case ':':
2832117328Sluigi		if (!inet_aton(p, (struct in_addr *)&d[1]))
283398943Sluigi			errx(EX_DATAERR, "bad netmask ``%s''", p);
283498943Sluigi		break;
283598943Sluigi	case '/':
2836117328Sluigi		masklen = atoi(p);
2837117328Sluigi		if (masklen == 0)
2838117328Sluigi			d[1] = htonl(0);	/* mask */
2839117328Sluigi		else if (masklen > 32)
284098943Sluigi			errx(EX_DATAERR, "bad width ``%s''", p);
284198943Sluigi		else
2842117328Sluigi			d[1] = htonl(~0 << (32 - masklen));
284398943Sluigi		break;
2844117328Sluigi	case '{':	/* no mask, assume /24 and put back the '{' */
2845117328Sluigi		d[1] = htonl(~0 << (32 - 24));
2846117328Sluigi		*(--p) = md;
2847117328Sluigi		break;
2848117328Sluigi
2849117328Sluigi	case ',':	/* single address plus continuation */
2850117328Sluigi		*(--p) = md;
2851117328Sluigi		/* FALLTHROUGH */
2852117328Sluigi	case 0:		/* initialization value */
285398943Sluigi	default:
2854117328Sluigi		d[1] = htonl(~0);	/* force /32 */
285598943Sluigi		break;
285698943Sluigi	}
2857117328Sluigi	d[0] &= d[1];		/* mask base address with mask */
2858165851Smlaier	if (t)
2859165851Smlaier		*t = nd;
2860117328Sluigi	/* find next separator */
286198943Sluigi	if (p)
2862117328Sluigi		p = strpbrk(p, ",{");
2863117328Sluigi	if (p && *p == '{') {
2864117328Sluigi		/*
2865117328Sluigi		 * We have a set of addresses. They are stored as follows:
2866117328Sluigi		 *   arg1	is the set size (powers of 2, 2..256)
2867117328Sluigi		 *   addr	is the base address IN HOST FORMAT
2868117328Sluigi		 *   mask..	is an array of arg1 bits (rounded up to
2869117328Sluigi		 *		the next multiple of 32) with bits set
2870117328Sluigi		 *		for each host in the map.
2871117328Sluigi		 */
2872117328Sluigi		uint32_t *map = (uint32_t *)&cmd->mask;
287398943Sluigi		int low, high;
2874117577Sluigi		int i = contigmask((uint8_t *)&(d[1]), 32);
287598943Sluigi
2876117328Sluigi		if (len > 0)
2877117328Sluigi			errx(EX_DATAERR, "address set cannot be in a list");
2878117328Sluigi		if (i < 24 || i > 31)
2879117328Sluigi			errx(EX_DATAERR, "invalid set with mask %d\n", i);
2880117328Sluigi		cmd->o.arg1 = 1<<(32-i);	/* map length		*/
2881117328Sluigi		d[0] = ntohl(d[0]);		/* base addr in host format */
288298943Sluigi		cmd->o.opcode = O_IP_DST_SET;	/* default */
288398943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32;
2884101117Sluigi		for (i = 0; i < (cmd->o.arg1+31)/32 ; i++)
2885117328Sluigi			map[i] = 0;	/* clear map */
288698943Sluigi
2887117328Sluigi		av = p + 1;
2888117328Sluigi		low = d[0] & 0xff;
288998943Sluigi		high = low + cmd->o.arg1 - 1;
2890117328Sluigi		/*
2891117328Sluigi		 * Here, i stores the previous value when we specify a range
2892117328Sluigi		 * of addresses within a mask, e.g. 45-63. i = -1 means we
2893117328Sluigi		 * have no previous value.
2894117328Sluigi		 */
2895116716Sluigi		i = -1;	/* previous value in a range */
289698943Sluigi		while (isdigit(*av)) {
289798943Sluigi			char *s;
2898117328Sluigi			int a = strtol(av, &s, 0);
289998943Sluigi
2900117328Sluigi			if (s == av) { /* no parameter */
2901117328Sluigi			    if (*av != '}')
2902117328Sluigi				errx(EX_DATAERR, "set not closed\n");
2903117328Sluigi			    if (i != -1)
2904117328Sluigi				errx(EX_DATAERR, "incomplete range %d-", i);
2905117328Sluigi			    break;
2906117328Sluigi			}
2907117328Sluigi			if (a < low || a > high)
2908117328Sluigi			    errx(EX_DATAERR, "addr %d out of range [%d-%d]\n",
290998943Sluigi				a, low, high);
291098943Sluigi			a -= low;
2911116716Sluigi			if (i == -1)	/* no previous in range */
2912116716Sluigi			    i = a;
2913116716Sluigi			else {		/* check that range is valid */
2914116716Sluigi			    if (i > a)
2915116716Sluigi				errx(EX_DATAERR, "invalid range %d-%d",
2916116716Sluigi					i+low, a+low);
2917116716Sluigi			    if (*s == '-')
2918116716Sluigi				errx(EX_DATAERR, "double '-' in range");
2919116716Sluigi			}
2920116716Sluigi			for (; i <= a; i++)
2921117328Sluigi			    map[i/32] |= 1<<(i & 31);
2922116716Sluigi			i = -1;
2923116716Sluigi			if (*s == '-')
2924116716Sluigi			    i = a;
2925117328Sluigi			else if (*s == '}')
2926116716Sluigi			    break;
292798943Sluigi			av = s+1;
292898943Sluigi		}
292998943Sluigi		return;
293098943Sluigi	}
2931117328Sluigi	av = p;
2932117328Sluigi	if (av)			/* then *av must be a ',' */
2933117328Sluigi		av++;
293498943Sluigi
2935117328Sluigi	/* Check this entry */
2936117328Sluigi	if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */
2937117328Sluigi		/*
2938117328Sluigi		 * 'any' turns the entire list into a NOP.
2939117328Sluigi		 * 'not any' never matches, so it is removed from the
2940117328Sluigi		 * list unless it is the only item, in which case we
2941117328Sluigi		 * report an error.
2942117328Sluigi		 */
2943117328Sluigi		if (cmd->o.len & F_NOT) {	/* "not any" never matches */
2944117328Sluigi			if (av == NULL && len == 0) /* only this entry */
2945117328Sluigi				errx(EX_DATAERR, "not any never matches");
2946117328Sluigi		}
2947117328Sluigi		/* else do nothing and skip this entry */
2948128067Smaxim		return;
2949117328Sluigi	}
2950117328Sluigi	/* A single IP can be stored in an optimized format */
2951117328Sluigi	if (d[1] == IP_MASK_ALL && av == NULL && len == 0) {
295298943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2953117328Sluigi		return;
2954117328Sluigi	}
2955117328Sluigi	len += 2;	/* two words... */
2956117328Sluigi	d += 2;
2957117328Sluigi    } /* end while */
2958162363Sjhay    if (len + 1 > F_LEN_MASK)
2959162363Sjhay	errx(EX_DATAERR, "address list too long");
2960117328Sluigi    cmd->o.len |= len+1;
296198943Sluigi}
296298943Sluigi
296398943Sluigi
2964145246Sbrooks/* Try to find ipv6 address by hostname */
2965145246Sbrooksstatic int
2966145246Sbrookslookup_host6 (char *host, struct in6_addr *ip6addr)
2967145246Sbrooks{
2968145246Sbrooks	struct hostent *he;
2969145246Sbrooks
2970145246Sbrooks	if (!inet_pton(AF_INET6, host, ip6addr)) {
2971145246Sbrooks		if ((he = gethostbyname2(host, AF_INET6)) == NULL)
2972145246Sbrooks			return(-1);
2973145246Sbrooks		memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr));
2974145246Sbrooks	}
2975145246Sbrooks	return(0);
2976145246Sbrooks}
2977145246Sbrooks
2978145246Sbrooks
2979145246Sbrooks/* n2mask sets n bits of the mask */
2980145246Sbrooksstatic void
2981145246Sbrooksn2mask(struct in6_addr *mask, int n)
2982145246Sbrooks{
2983145246Sbrooks	static int	minimask[9] =
2984145246Sbrooks	    { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
2985145246Sbrooks	u_char		*p;
2986145246Sbrooks
2987145246Sbrooks	memset(mask, 0, sizeof(struct in6_addr));
2988145246Sbrooks	p = (u_char *) mask;
2989145246Sbrooks	for (; n > 0; p++, n -= 8) {
2990145246Sbrooks		if (n >= 8)
2991145246Sbrooks			*p = 0xff;
2992145246Sbrooks		else
2993145246Sbrooks			*p = minimask[n];
2994145246Sbrooks	}
2995145246Sbrooks	return;
2996145246Sbrooks}
2997145246Sbrooks
2998145246Sbrooks
299998943Sluigi/*
3000145246Sbrooks * fill the addr and mask fields in the instruction as appropriate from av.
3001145246Sbrooks * Update length as appropriate.
3002145246Sbrooks * The following formats are allowed:
3003145246Sbrooks *     any     matches any IP6. Actually returns an empty instruction.
3004145246Sbrooks *     me      returns O_IP6_*_ME
3005145246Sbrooks *
3006145246Sbrooks *     03f1::234:123:0342                single IP6 addres
3007145246Sbrooks *     03f1::234:123:0342/24            address/mask
3008145246Sbrooks *     03f1::234:123:0342/24,03f1::234:123:0343/               List of address
3009145246Sbrooks *
3010145246Sbrooks * Set of address (as in ipv6) not supported because ipv6 address
3011145246Sbrooks * are typically random past the initial prefix.
3012145246Sbrooks * Return 1 on success, 0 on failure.
3013145246Sbrooks */
3014145246Sbrooksstatic int
3015145246Sbrooksfill_ip6(ipfw_insn_ip6 *cmd, char *av)
3016145246Sbrooks{
3017145246Sbrooks	int len = 0;
3018145246Sbrooks	struct in6_addr *d = &(cmd->addr6);
3019145246Sbrooks	/*
3020145246Sbrooks	 * Needed for multiple address.
3021145246Sbrooks	 * Note d[1] points to struct in6_add r mask6 of cmd
3022145246Sbrooks	 */
3023145246Sbrooks
3024145246Sbrooks       cmd->o.len &= ~F_LEN_MASK;	/* zero len */
3025145246Sbrooks
3026145246Sbrooks       if (strcmp(av, "any") == 0)
3027145246Sbrooks	       return (1);
3028145246Sbrooks
3029145246Sbrooks
3030145246Sbrooks       if (strcmp(av, "me") == 0) {	/* Set the data for "me" opt*/
3031145246Sbrooks	       cmd->o.len |= F_INSN_SIZE(ipfw_insn);
3032145246Sbrooks	       return (1);
3033145246Sbrooks       }
3034145246Sbrooks
3035145246Sbrooks       if (strcmp(av, "me6") == 0) {	/* Set the data for "me" opt*/
3036145246Sbrooks	       cmd->o.len |= F_INSN_SIZE(ipfw_insn);
3037145246Sbrooks	       return (1);
3038145246Sbrooks       }
3039145246Sbrooks
3040145246Sbrooks       av = strdup(av);
3041145246Sbrooks       while (av) {
3042145246Sbrooks		/*
3043145246Sbrooks		 * After the address we can have '/' indicating a mask,
3044145246Sbrooks		 * or ',' indicating another address follows.
3045145246Sbrooks		 */
3046145246Sbrooks
3047145246Sbrooks		char *p;
3048145246Sbrooks		int masklen;
3049145246Sbrooks		char md = '\0';
3050145246Sbrooks
3051145246Sbrooks		if ((p = strpbrk(av, "/,")) ) {
3052145246Sbrooks			md = *p;	/* save the separator */
3053145246Sbrooks			*p = '\0';	/* terminate address string */
3054145246Sbrooks			p++;		/* and skip past it */
3055145246Sbrooks		}
3056145246Sbrooks		/* now p points to NULL, mask or next entry */
3057145246Sbrooks
3058145246Sbrooks		/* lookup stores address in *d as a side effect */
3059145246Sbrooks		if (lookup_host6(av, d) != 0) {
3060145246Sbrooks			/* XXX: failed. Free memory and go */
3061145246Sbrooks			errx(EX_DATAERR, "bad address \"%s\"", av);
3062145246Sbrooks		}
3063145246Sbrooks		/* next, look at the mask, if any */
3064145246Sbrooks		masklen = (md == '/') ? atoi(p) : 128;
3065145246Sbrooks		if (masklen > 128 || masklen < 0)
3066145246Sbrooks			errx(EX_DATAERR, "bad width \"%s\''", p);
3067145246Sbrooks		else
3068145246Sbrooks			n2mask(&d[1], masklen);
3069145246Sbrooks
3070145246Sbrooks		APPLY_MASK(d, &d[1])   /* mask base address with mask */
3071145246Sbrooks
3072145246Sbrooks		/* find next separator */
3073145246Sbrooks
3074145246Sbrooks		if (md == '/') {	/* find separator past the mask */
3075145246Sbrooks			p = strpbrk(p, ",");
3076145246Sbrooks			if (p != NULL)
3077145246Sbrooks				p++;
3078145246Sbrooks		}
3079145246Sbrooks		av = p;
3080145246Sbrooks
3081145246Sbrooks		/* Check this entry */
3082145246Sbrooks		if (masklen == 0) {
3083145246Sbrooks			/*
3084145246Sbrooks			 * 'any' turns the entire list into a NOP.
3085145246Sbrooks			 * 'not any' never matches, so it is removed from the
3086145246Sbrooks			 * list unless it is the only item, in which case we
3087145246Sbrooks			 * report an error.
3088145246Sbrooks			 */
3089145246Sbrooks			if (cmd->o.len & F_NOT && av == NULL && len == 0)
3090145246Sbrooks				errx(EX_DATAERR, "not any never matches");
3091145246Sbrooks			continue;
3092145246Sbrooks		}
3093145246Sbrooks
3094145246Sbrooks		/*
3095145246Sbrooks		 * A single IP can be stored alone
3096145246Sbrooks		 */
3097145246Sbrooks		if (masklen == 128 && av == NULL && len == 0) {
3098145246Sbrooks			len = F_INSN_SIZE(struct in6_addr);
3099145246Sbrooks			break;
3100145246Sbrooks		}
3101145246Sbrooks
3102145246Sbrooks		/* Update length and pointer to arguments */
3103145246Sbrooks		len += F_INSN_SIZE(struct in6_addr)*2;
3104145246Sbrooks		d += 2;
3105145246Sbrooks	} /* end while */
3106145246Sbrooks
3107145246Sbrooks	/*
3108145246Sbrooks	 * Total length of the command, remember that 1 is the size of
3109145246Sbrooks	 * the base command.
3110145246Sbrooks	 */
3111162363Sjhay	if (len + 1 > F_LEN_MASK)
3112162363Sjhay		errx(EX_DATAERR, "address list too long");
3113145246Sbrooks	cmd->o.len |= len+1;
3114145246Sbrooks	free(av);
3115145246Sbrooks	return (1);
3116145246Sbrooks}
3117145246Sbrooks
3118145246Sbrooks/*
3119145246Sbrooks * fills command for ipv6 flow-id filtering
3120145246Sbrooks * note that the 20 bit flow number is stored in a array of u_int32_t
3121145246Sbrooks * it's supported lists of flow-id, so in the o.arg1 we store how many
3122145246Sbrooks * additional flow-id we want to filter, the basic is 1
3123145246Sbrooks */
3124145246Sbrooksvoid
3125145246Sbrooksfill_flow6( ipfw_insn_u32 *cmd, char *av )
3126145246Sbrooks{
3127145246Sbrooks	u_int32_t type;	 /* Current flow number */
3128145246Sbrooks	u_int16_t nflow = 0;    /* Current flow index */
3129145246Sbrooks	char *s = av;
3130145246Sbrooks	cmd->d[0] = 0;	  /* Initializing the base number*/
3131145246Sbrooks
3132145246Sbrooks	while (s) {
3133145246Sbrooks		av = strsep( &s, ",") ;
3134145246Sbrooks		type = strtoul(av, &av, 0);
3135145246Sbrooks		if (*av != ',' && *av != '\0')
3136145246Sbrooks			errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
3137145246Sbrooks		if (type > 0xfffff)
3138145246Sbrooks			errx(EX_DATAERR, "flow number out of range %s", av);
3139145246Sbrooks		cmd->d[nflow] |= type;
3140145246Sbrooks		nflow++;
3141145246Sbrooks	}
3142145246Sbrooks	if( nflow > 0 ) {
3143145246Sbrooks		cmd->o.opcode = O_FLOW6ID;
3144145246Sbrooks		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow;
3145145246Sbrooks		cmd->o.arg1 = nflow;
3146145246Sbrooks	}
3147145246Sbrooks	else {
3148145246Sbrooks		errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
3149145246Sbrooks	}
3150145246Sbrooks}
3151145246Sbrooks
3152145246Sbrooksstatic ipfw_insn *
3153145246Sbrooksadd_srcip6(ipfw_insn *cmd, char *av)
3154145246Sbrooks{
3155145246Sbrooks
3156145246Sbrooks	fill_ip6((ipfw_insn_ip6 *)cmd, av);
3157145246Sbrooks	if (F_LEN(cmd) == 0)				/* any */
3158145246Sbrooks		;
3159145246Sbrooks	if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) {	/* "me" */
3160145246Sbrooks		cmd->opcode = O_IP6_SRC_ME;
3161145246Sbrooks	} else if (F_LEN(cmd) ==
3162145246Sbrooks	    (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
3163145246Sbrooks		/* single IP, no mask*/
3164145246Sbrooks		cmd->opcode = O_IP6_SRC;
3165145246Sbrooks	} else {					/* addr/mask opt */
3166145246Sbrooks		cmd->opcode = O_IP6_SRC_MASK;
3167145246Sbrooks	}
3168145246Sbrooks	return cmd;
3169145246Sbrooks}
3170145246Sbrooks
3171145246Sbrooksstatic ipfw_insn *
3172145246Sbrooksadd_dstip6(ipfw_insn *cmd, char *av)
3173145246Sbrooks{
3174145246Sbrooks
3175145246Sbrooks	fill_ip6((ipfw_insn_ip6 *)cmd, av);
3176145246Sbrooks	if (F_LEN(cmd) == 0)				/* any */
3177145246Sbrooks		;
3178145246Sbrooks	if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) {	/* "me" */
3179145246Sbrooks		cmd->opcode = O_IP6_DST_ME;
3180145246Sbrooks	} else if (F_LEN(cmd) ==
3181145246Sbrooks	    (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
3182145246Sbrooks		/* single IP, no mask*/
3183145246Sbrooks		cmd->opcode = O_IP6_DST;
3184145246Sbrooks	} else {					/* addr/mask opt */
3185145246Sbrooks		cmd->opcode = O_IP6_DST_MASK;
3186145246Sbrooks	}
3187145246Sbrooks	return cmd;
3188145246Sbrooks}
3189145246Sbrooks
3190145246Sbrooks
3191145246Sbrooks/*
319298943Sluigi * helper function to process a set of flags and set bits in the
319398943Sluigi * appropriate masks.
319498943Sluigi */
319598943Sluigistatic void
319698943Sluigifill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
319798943Sluigi	struct _s_x *flags, char *p)
319898943Sluigi{
3199117328Sluigi	uint8_t set=0, clear=0;
320098943Sluigi
320198943Sluigi	while (p && *p) {
320298943Sluigi		char *q;	/* points to the separator */
320398943Sluigi		int val;
3204117328Sluigi		uint8_t *which;	/* mask we are working on */
320598943Sluigi
320698943Sluigi		if (*p == '!') {
320798943Sluigi			p++;
320898943Sluigi			which = &clear;
320998943Sluigi		} else
321098943Sluigi			which = &set;
321198943Sluigi		q = strchr(p, ',');
321298943Sluigi		if (q)
321398943Sluigi			*q++ = '\0';
321498943Sluigi		val = match_token(flags, p);
321598943Sluigi		if (val <= 0)
321698943Sluigi			errx(EX_DATAERR, "invalid flag %s", p);
3217117328Sluigi		*which |= (uint8_t)val;
321898943Sluigi		p = q;
321998943Sluigi	}
322098943Sluigi        cmd->opcode = opcode;
322198943Sluigi        cmd->len =  (cmd->len & (F_NOT | F_OR)) | 1;
322298943Sluigi        cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
322398943Sluigi}
322498943Sluigi
322598943Sluigi
322698943Sluigistatic void
322798943Sluigidelete(int ac, char *av[])
322898943Sluigi{
3229117328Sluigi	uint32_t rulenum;
3230117469Sluigi	struct dn_pipe p;
323198943Sluigi	int i;
323298943Sluigi	int exitval = EX_OK;
3233101628Sluigi	int do_set = 0;
323498943Sluigi
3235117469Sluigi	memset(&p, 0, sizeof p);
323698943Sluigi
323798943Sluigi	av++; ac--;
3238130013Scsjp	NEED1("missing rule specification");
3239140271Sbrooks	if (ac > 0 && _substrcmp(*av, "set") == 0) {
3240170923Smaxim		/* Do not allow using the following syntax:
3241170923Smaxim		 *	ipfw set N delete set M
3242170923Smaxim		 */
3243170923Smaxim		if (use_set)
3244170923Smaxim			errx(EX_DATAERR, "invalid syntax");
3245101978Sluigi		do_set = 1;	/* delete set */
3246101628Sluigi		ac--; av++;
3247101978Sluigi	}
324898943Sluigi
324998943Sluigi	/* Rule number */
325098943Sluigi	while (ac && isdigit(**av)) {
325198943Sluigi		i = atoi(*av); av++; ac--;
3252165648Spiso		if (do_nat) {
3253165648Spiso			exitval = do_cmd(IP_FW_NAT_DEL, &i, sizeof i);
3254165648Spiso			if (exitval) {
3255165648Spiso				exitval = EX_UNAVAILABLE;
3256165648Spiso				warn("rule %u not available", i);
3257165648Spiso			}
3258165648Spiso 		} else if (do_pipe) {
325998943Sluigi			if (do_pipe == 1)
3260117469Sluigi				p.pipe_nr = i;
326198943Sluigi			else
3262117469Sluigi				p.fs.fs_nr = i;
3263117469Sluigi			i = do_cmd(IP_DUMMYNET_DEL, &p, sizeof p);
326498943Sluigi			if (i) {
326598943Sluigi				exitval = 1;
326698943Sluigi				warn("rule %u: setsockopt(IP_DUMMYNET_DEL)",
3267117469Sluigi				    do_pipe == 1 ? p.pipe_nr : p.fs.fs_nr);
326898943Sluigi			}
326998943Sluigi		} else {
3270170923Smaxim			if (use_set)
3271170923Smaxim				rulenum = (i & 0xffff) | (5 << 24) |
3272170923Smaxim				    ((use_set - 1) << 16);
3273170923Smaxim			else
3274101978Sluigi			rulenum =  (i & 0xffff) | (do_set << 24);
3275117328Sluigi			i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum);
327698943Sluigi			if (i) {
327798943Sluigi				exitval = EX_UNAVAILABLE;
327898943Sluigi				warn("rule %u: setsockopt(IP_FW_DEL)",
327998943Sluigi				    rulenum);
328098943Sluigi			}
328198943Sluigi		}
328298943Sluigi	}
328398943Sluigi	if (exitval != EX_OK)
328498943Sluigi		exit(exitval);
328598943Sluigi}
328698943Sluigi
328798943Sluigi
328898943Sluigi/*
328998943Sluigi * fill the interface structure. We do not check the name as we can
329098943Sluigi * create interfaces dynamically, so checking them at insert time
329198943Sluigi * makes relatively little sense.
3292121816Sbrooks * Interface names containing '*', '?', or '[' are assumed to be shell
3293121816Sbrooks * patterns which match interfaces.
329498943Sluigi */
329598943Sluigistatic void
329698943Sluigifill_iface(ipfw_insn_if *cmd, char *arg)
329798943Sluigi{
329898943Sluigi	cmd->name[0] = '\0';
329998943Sluigi	cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
330098943Sluigi
330198943Sluigi	/* Parse the interface or address */
3302140271Sbrooks	if (strcmp(arg, "any") == 0)
330398943Sluigi		cmd->o.len = 0;		/* effectively ignore this command */
330498943Sluigi	else if (!isdigit(*arg)) {
3305121816Sbrooks		strlcpy(cmd->name, arg, sizeof(cmd->name));
3306121816Sbrooks		cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
330798943Sluigi	} else if (!inet_aton(arg, &cmd->p.ip))
330898943Sluigi		errx(EX_DATAERR, "bad ip address ``%s''", arg);
330998943Sluigi}
331098943Sluigi
3311165648Spiso/*
3312165648Spiso * Search for interface with name "ifn", and fill n accordingly:
3313165648Spiso *
3314165648Spiso * n->ip        ip address of interface "ifn"
3315165648Spiso * n->if_name   copy of interface name "ifn"
3316165648Spiso */
331798943Sluigistatic void
3318165648Spisoset_addr_dynamic(const char *ifn, struct cfg_nat *n)
3319165648Spiso{
3320165648Spiso	size_t needed;
3321165648Spiso	int mib[6];
3322165648Spiso	char *buf, *lim, *next;
3323165648Spiso	struct if_msghdr *ifm;
3324165648Spiso	struct ifa_msghdr *ifam;
3325165648Spiso	struct sockaddr_dl *sdl;
3326165648Spiso	struct sockaddr_in *sin;
3327165648Spiso	int ifIndex, ifMTU;
3328165648Spiso
3329165648Spiso	mib[0] = CTL_NET;
3330165648Spiso	mib[1] = PF_ROUTE;
3331165648Spiso	mib[2] = 0;
3332165648Spiso	mib[3] = AF_INET;
3333165648Spiso	mib[4] = NET_RT_IFLIST;
3334165648Spiso	mib[5] = 0;
3335165648Spiso/*
3336165648Spiso * Get interface data.
3337165648Spiso */
3338165648Spiso	if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
3339165648Spiso		err(1, "iflist-sysctl-estimate");
3340165648Spiso	if ((buf = malloc(needed)) == NULL)
3341165648Spiso		errx(1, "malloc failed");
3342165648Spiso	if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
3343165648Spiso		err(1, "iflist-sysctl-get");
3344165648Spiso	lim = buf + needed;
3345165648Spiso/*
3346165648Spiso * Loop through interfaces until one with
3347165648Spiso * given name is found. This is done to
3348165648Spiso * find correct interface index for routing
3349165648Spiso * message processing.
3350165648Spiso */
3351165648Spiso	ifIndex	= 0;
3352165648Spiso	next = buf;
3353165648Spiso	while (next < lim) {
3354165648Spiso		ifm = (struct if_msghdr *)next;
3355165648Spiso		next += ifm->ifm_msglen;
3356165648Spiso		if (ifm->ifm_version != RTM_VERSION) {
3357165648Spiso			if (verbose)
3358165648Spiso				warnx("routing message version %d "
3359165648Spiso				    "not understood", ifm->ifm_version);
3360165648Spiso			continue;
3361165648Spiso		}
3362165648Spiso		if (ifm->ifm_type == RTM_IFINFO) {
3363165648Spiso			sdl = (struct sockaddr_dl *)(ifm + 1);
3364165648Spiso			if (strlen(ifn) == sdl->sdl_nlen &&
3365165648Spiso			    strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
3366165648Spiso				ifIndex = ifm->ifm_index;
3367165648Spiso				ifMTU = ifm->ifm_data.ifi_mtu;
3368165648Spiso				break;
3369165648Spiso			}
3370165648Spiso		}
3371165648Spiso	}
3372165648Spiso	if (!ifIndex)
3373165648Spiso		errx(1, "unknown interface name %s", ifn);
3374165648Spiso/*
3375165648Spiso * Get interface address.
3376165648Spiso */
3377165648Spiso	sin = NULL;
3378165648Spiso	while (next < lim) {
3379165648Spiso		ifam = (struct ifa_msghdr *)next;
3380165648Spiso		next += ifam->ifam_msglen;
3381165648Spiso		if (ifam->ifam_version != RTM_VERSION) {
3382165648Spiso			if (verbose)
3383165648Spiso				warnx("routing message version %d "
3384165648Spiso				    "not understood", ifam->ifam_version);
3385165648Spiso			continue;
3386165648Spiso		}
3387165648Spiso		if (ifam->ifam_type != RTM_NEWADDR)
3388165648Spiso			break;
3389165648Spiso		if (ifam->ifam_addrs & RTA_IFA) {
3390165648Spiso			int i;
3391165648Spiso			char *cp = (char *)(ifam + 1);
3392165648Spiso
3393165648Spiso			for (i = 1; i < RTA_IFA; i <<= 1) {
3394165648Spiso				if (ifam->ifam_addrs & i)
3395165648Spiso					cp += SA_SIZE((struct sockaddr *)cp);
3396165648Spiso			}
3397165648Spiso			if (((struct sockaddr *)cp)->sa_family == AF_INET) {
3398165648Spiso				sin = (struct sockaddr_in *)cp;
3399165648Spiso				break;
3400165648Spiso			}
3401165648Spiso		}
3402165648Spiso	}
3403165648Spiso	if (sin == NULL)
3404165648Spiso		errx(1, "%s: cannot get interface address", ifn);
3405165648Spiso
3406165648Spiso	n->ip = sin->sin_addr;
3407165648Spiso	strncpy(n->if_name, ifn, IF_NAMESIZE);
3408165648Spiso
3409165648Spiso	free(buf);
3410165648Spiso}
3411165648Spiso
3412165648Spiso/*
3413165648Spiso * XXX - The following functions, macros and definitions come from natd.c:
3414165648Spiso * it would be better to move them outside natd.c, in a file
3415165648Spiso * (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live
3416165648Spiso * with it.
3417165648Spiso */
3418165648Spiso
3419165648Spiso/*
3420165648Spiso * Definition of a port range, and macros to deal with values.
3421165648Spiso * FORMAT:  HI 16-bits == first port in range, 0 == all ports.
3422165648Spiso *          LO 16-bits == number of ports in range
3423165648Spiso * NOTES:   - Port values are not stored in network byte order.
3424165648Spiso */
3425165648Spiso
3426165648Spiso#define port_range u_long
3427165648Spiso
3428165648Spiso#define GETLOPORT(x)     ((x) >> 0x10)
3429165648Spiso#define GETNUMPORTS(x)   ((x) & 0x0000ffff)
3430165648Spiso#define GETHIPORT(x)     (GETLOPORT((x)) + GETNUMPORTS((x)))
3431165648Spiso
3432165648Spiso/* Set y to be the low-port value in port_range variable x. */
3433165648Spiso#define SETLOPORT(x,y)   ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
3434165648Spiso
3435165648Spiso/* Set y to be the number of ports in port_range variable x. */
3436165648Spiso#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
3437165648Spiso
3438165648Spisostatic void
3439165648SpisoStrToAddr (const char* str, struct in_addr* addr)
3440165648Spiso{
3441165648Spiso	struct hostent* hp;
3442165648Spiso
3443165648Spiso	if (inet_aton (str, addr))
3444165648Spiso		return;
3445165648Spiso
3446165648Spiso	hp = gethostbyname (str);
3447165648Spiso	if (!hp)
3448165648Spiso		errx (1, "unknown host %s", str);
3449165648Spiso
3450165648Spiso	memcpy (addr, hp->h_addr, sizeof (struct in_addr));
3451165648Spiso}
3452165648Spiso
3453165648Spisostatic int
3454165648SpisoStrToPortRange (const char* str, const char* proto, port_range *portRange)
3455165648Spiso{
3456165648Spiso	char*           sep;
3457165648Spiso	struct servent*	sp;
3458165648Spiso	char*		end;
3459165648Spiso	u_short         loPort;
3460165648Spiso	u_short         hiPort;
3461165648Spiso
3462165648Spiso	/* First see if this is a service, return corresponding port if so. */
3463165648Spiso	sp = getservbyname (str,proto);
3464165648Spiso	if (sp) {
3465165648Spiso	        SETLOPORT(*portRange, ntohs(sp->s_port));
3466165648Spiso		SETNUMPORTS(*portRange, 1);
3467165648Spiso		return 0;
3468165648Spiso	}
3469165648Spiso
3470165648Spiso	/* Not a service, see if it's a single port or port range. */
3471165648Spiso	sep = strchr (str, '-');
3472165648Spiso	if (sep == NULL) {
3473165648Spiso	        SETLOPORT(*portRange, strtol(str, &end, 10));
3474165648Spiso		if (end != str) {
3475165648Spiso		        /* Single port. */
3476165648Spiso		        SETNUMPORTS(*portRange, 1);
3477165648Spiso			return 0;
3478165648Spiso		}
3479165648Spiso
3480165648Spiso		/* Error in port range field. */
3481165648Spiso		errx (EX_DATAERR, "%s/%s: unknown service", str, proto);
3482165648Spiso	}
3483165648Spiso
3484165648Spiso	/* Port range, get the values and sanity check. */
3485165648Spiso	sscanf (str, "%hu-%hu", &loPort, &hiPort);
3486165648Spiso	SETLOPORT(*portRange, loPort);
3487165648Spiso	SETNUMPORTS(*portRange, 0);	/* Error by default */
3488165648Spiso	if (loPort <= hiPort)
3489165648Spiso	        SETNUMPORTS(*portRange, hiPort - loPort + 1);
3490165648Spiso
3491165648Spiso	if (GETNUMPORTS(*portRange) == 0)
3492165648Spiso	        errx (EX_DATAERR, "invalid port range %s", str);
3493165648Spiso
3494165648Spiso	return 0;
3495165648Spiso}
3496165648Spiso
3497165648Spisostatic int
3498165648SpisoStrToProto (const char* str)
3499165648Spiso{
3500165648Spiso	if (!strcmp (str, "tcp"))
3501165648Spiso		return IPPROTO_TCP;
3502165648Spiso
3503165648Spiso	if (!strcmp (str, "udp"))
3504165648Spiso		return IPPROTO_UDP;
3505165648Spiso
3506165648Spiso	errx (EX_DATAERR, "unknown protocol %s. Expected tcp or udp", str);
3507165648Spiso}
3508165648Spiso
3509165648Spisostatic int
3510165648SpisoStrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
3511165648Spiso		       port_range *portRange)
3512165648Spiso{
3513165648Spiso	char*	ptr;
3514165648Spiso
3515165648Spiso	ptr = strchr (str, ':');
3516165648Spiso	if (!ptr)
3517165648Spiso		errx (EX_DATAERR, "%s is missing port number", str);
3518165648Spiso
3519165648Spiso	*ptr = '\0';
3520165648Spiso	++ptr;
3521165648Spiso
3522165648Spiso	StrToAddr (str, addr);
3523165648Spiso	return StrToPortRange (ptr, proto, portRange);
3524165648Spiso}
3525165648Spiso
3526165648Spiso/* End of stuff taken from natd.c. */
3527165648Spiso
3528165648Spiso#define INC_ARGCV() do {        \
3529165648Spiso	(*_av)++;               \
3530165648Spiso	(*_ac)--;               \
3531165648Spiso	av = *_av;              \
3532165648Spiso	ac = *_ac;              \
3533165648Spiso} while(0)
3534165648Spiso
3535165648Spiso/*
3536165648Spiso * The next 3 functions add support for the addr, port and proto redirect and
3537165648Spiso * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect()
3538165648Spiso * and SetupProtoRedirect() from natd.c.
3539165648Spiso *
3540165648Spiso * Every setup_* function fills at least one redirect entry
3541165648Spiso * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool)
3542165648Spiso * in buf.
3543165648Spiso *
3544165648Spiso * The format of data in buf is:
3545165648Spiso *
3546165648Spiso *
3547165648Spiso *     cfg_nat    cfg_redir    cfg_spool    ......  cfg_spool
3548165648Spiso *
3549165648Spiso *    -------------------------------------        ------------
3550165648Spiso *   |          | .....X ... |          |         |           |  .....
3551165648Spiso *    ------------------------------------- ...... ------------
3552165648Spiso *                     ^
3553165648Spiso *                spool_cnt       n=0       ......   n=(X-1)
3554165648Spiso *
3555165648Spiso * len points to the amount of available space in buf
3556165648Spiso * space counts the memory consumed by every function
3557165648Spiso *
3558165648Spiso * XXX - Every function get all the argv params so it
3559165648Spiso * has to check, in optional parameters, that the next
3560165648Spiso * args is a valid option for the redir entry and not
3561165648Spiso * another token. Only redir_port and redir_proto are
3562165648Spiso * affected by this.
3563165648Spiso */
3564165648Spiso
3565165648Spisostatic int
3566165648Spisosetup_redir_addr(char *spool_buf, int len,
3567165648Spiso		 int *_ac, char ***_av)
3568165648Spiso{
3569165648Spiso	char **av, *sep; /* Token separator. */
3570165648Spiso	/* Temporary buffer used to hold server pool ip's. */
3571165648Spiso	char tmp_spool_buf[NAT_BUF_LEN];
3572165648Spiso	int ac, i, space, lsnat;
3573165648Spiso	struct cfg_redir *r;
3574165648Spiso	struct cfg_spool *tmp;
3575165648Spiso
3576165648Spiso	av = *_av;
3577165648Spiso	ac = *_ac;
3578165648Spiso	space = 0;
3579165648Spiso	lsnat = 0;
3580165648Spiso	if (len >= SOF_REDIR) {
3581165648Spiso		r = (struct cfg_redir *)spool_buf;
3582165648Spiso		/* Skip cfg_redir at beginning of buf. */
3583165648Spiso		spool_buf = &spool_buf[SOF_REDIR];
3584165648Spiso		space = SOF_REDIR;
3585165648Spiso		len -= SOF_REDIR;
3586165648Spiso	} else
3587165648Spiso		goto nospace;
3588165648Spiso	r->mode = REDIR_ADDR;
3589165648Spiso	/* Extract local address. */
3590165648Spiso	if (ac == 0)
3591165648Spiso		errx(EX_DATAERR, "redirect_addr: missing local address");
3592165648Spiso	sep = strchr(*av, ',');
3593165648Spiso	if (sep) {		/* LSNAT redirection syntax. */
3594165648Spiso		r->laddr.s_addr = INADDR_NONE;
3595165648Spiso		/* Preserve av, copy spool servers to tmp_spool_buf. */
3596165648Spiso		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
3597165648Spiso		lsnat = 1;
3598165648Spiso	} else
3599165648Spiso		StrToAddr(*av, &r->laddr);
3600165648Spiso	INC_ARGCV();
3601165648Spiso
3602165648Spiso	/* Extract public address. */
3603165648Spiso	if (ac == 0)
3604165648Spiso		errx(EX_DATAERR, "redirect_addr: missing public address");
3605165648Spiso	StrToAddr(*av, &r->paddr);
3606165648Spiso	INC_ARGCV();
3607165648Spiso
3608165648Spiso	/* Setup LSNAT server pool. */
3609165648Spiso	if (sep) {
3610165648Spiso		sep = strtok(tmp_spool_buf, ",");
3611165648Spiso		while (sep != NULL) {
3612165648Spiso			tmp = (struct cfg_spool *)spool_buf;
3613165648Spiso			if (len < SOF_SPOOL)
3614165648Spiso				goto nospace;
3615165648Spiso			len -= SOF_SPOOL;
3616165648Spiso			space += SOF_SPOOL;
3617165648Spiso			StrToAddr(sep, &tmp->addr);
3618165648Spiso			tmp->port = ~0;
3619165648Spiso			r->spool_cnt++;
3620165648Spiso			/* Point to the next possible cfg_spool. */
3621165648Spiso			spool_buf = &spool_buf[SOF_SPOOL];
3622165648Spiso			sep = strtok(NULL, ",");
3623165648Spiso		}
3624165648Spiso	}
3625165648Spiso	return(space);
3626165648Spisonospace:
3627165648Spiso	errx(EX_DATAERR, "redirect_addr: buf is too small\n");
3628165648Spiso}
3629165648Spiso
3630165648Spisostatic int
3631165648Spisosetup_redir_port(char *spool_buf, int len,
3632165648Spiso		 int *_ac, char ***_av)
3633165648Spiso{
3634165648Spiso	char **av, *sep, *protoName;
3635165648Spiso	char tmp_spool_buf[NAT_BUF_LEN];
3636165648Spiso	int ac, space, lsnat;
3637165648Spiso	struct cfg_redir *r;
3638165648Spiso	struct cfg_spool *tmp;
3639165648Spiso	u_short numLocalPorts;
3640165648Spiso	port_range portRange;
3641165648Spiso
3642165648Spiso	av = *_av;
3643165648Spiso	ac = *_ac;
3644165648Spiso	space = 0;
3645165648Spiso	lsnat = 0;
3646165648Spiso	numLocalPorts = 0;
3647165648Spiso
3648165648Spiso	if (len >= SOF_REDIR) {
3649165648Spiso		r = (struct cfg_redir *)spool_buf;
3650165648Spiso		/* Skip cfg_redir at beginning of buf. */
3651165648Spiso		spool_buf = &spool_buf[SOF_REDIR];
3652165648Spiso		space = SOF_REDIR;
3653165648Spiso		len -= SOF_REDIR;
3654165648Spiso	} else
3655165648Spiso		goto nospace;
3656165648Spiso	r->mode = REDIR_PORT;
3657165648Spiso	/*
3658165648Spiso	 * Extract protocol.
3659165648Spiso	 */
3660165648Spiso	if (ac == 0)
3661165648Spiso		errx (EX_DATAERR, "redirect_port: missing protocol");
3662165648Spiso	r->proto = StrToProto(*av);
3663165648Spiso	protoName = *av;
3664165648Spiso	INC_ARGCV();
3665165648Spiso
3666165648Spiso	/*
3667165648Spiso	 * Extract local address.
3668165648Spiso	 */
3669165648Spiso	if (ac == 0)
3670165648Spiso		errx (EX_DATAERR, "redirect_port: missing local address");
3671165648Spiso
3672165648Spiso	sep = strchr(*av, ',');
3673165648Spiso	/* LSNAT redirection syntax. */
3674165648Spiso	if (sep) {
3675165648Spiso		r->laddr.s_addr = INADDR_NONE;
3676165648Spiso		r->lport = ~0;
3677165648Spiso		numLocalPorts = 1;
3678165648Spiso		/* Preserve av, copy spool servers to tmp_spool_buf. */
3679165648Spiso		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
3680165648Spiso		lsnat = 1;
3681165648Spiso	} else {
3682165648Spiso		if (StrToAddrAndPortRange (*av, &r->laddr, protoName,
3683165648Spiso		    &portRange) != 0)
3684165648Spiso			errx(EX_DATAERR, "redirect_port:"
3685165648Spiso			    "invalid local port range");
3686165648Spiso
3687165648Spiso		r->lport = GETLOPORT(portRange);
3688165648Spiso		numLocalPorts = GETNUMPORTS(portRange);
3689165648Spiso	}
3690165648Spiso	INC_ARGCV();
3691165648Spiso
3692165648Spiso	/*
3693165648Spiso	 * Extract public port and optionally address.
3694165648Spiso	 */
3695165648Spiso	if (ac == 0)
3696165648Spiso		errx (EX_DATAERR, "redirect_port: missing public port");
3697165648Spiso
3698165648Spiso	sep = strchr (*av, ':');
3699165648Spiso	if (sep) {
3700165648Spiso	        if (StrToAddrAndPortRange (*av, &r->paddr, protoName,
3701165648Spiso		    &portRange) != 0)
3702165648Spiso		        errx(EX_DATAERR, "redirect_port:"
3703165648Spiso			    "invalid public port range");
3704165648Spiso	} else {
3705165648Spiso		r->paddr.s_addr = INADDR_ANY;
3706165648Spiso		if (StrToPortRange (*av, protoName, &portRange) != 0)
3707165648Spiso		        errx(EX_DATAERR, "redirect_port:"
3708165648Spiso			    "invalid public port range");
3709165648Spiso	}
3710165648Spiso
3711165648Spiso	r->pport = GETLOPORT(portRange);
3712165648Spiso	r->pport_cnt = GETNUMPORTS(portRange);
3713165648Spiso	INC_ARGCV();
3714165648Spiso
3715165648Spiso	/*
3716165648Spiso	 * Extract remote address and optionally port.
3717165648Spiso	 */
3718165648Spiso	/*
3719165648Spiso	 * NB: isalpha(**av) => we've to check that next parameter is really an
3720165648Spiso	 * option for this redirect entry, else stop here processing arg[cv].
3721165648Spiso	 */
3722165648Spiso	if (ac != 0 && !isalpha(**av)) {
3723165648Spiso		sep = strchr (*av, ':');
3724165648Spiso		if (sep) {
3725165648Spiso		        if (StrToAddrAndPortRange (*av, &r->raddr, protoName,
3726165648Spiso			    &portRange) != 0)
3727165648Spiso				errx(EX_DATAERR, "redirect_port:"
3728165648Spiso				    "invalid remote port range");
3729165648Spiso		} else {
3730165648Spiso		        SETLOPORT(portRange, 0);
3731165648Spiso			SETNUMPORTS(portRange, 1);
3732165648Spiso			StrToAddr (*av, &r->raddr);
3733165648Spiso		}
3734165648Spiso		INC_ARGCV();
3735165648Spiso	} else {
3736165648Spiso		SETLOPORT(portRange, 0);
3737165648Spiso		SETNUMPORTS(portRange, 1);
3738165648Spiso		r->raddr.s_addr = INADDR_ANY;
3739165648Spiso	}
3740165648Spiso	r->rport = GETLOPORT(portRange);
3741165648Spiso	r->rport_cnt = GETNUMPORTS(portRange);
3742165648Spiso
3743165648Spiso	/*
3744165648Spiso	 * Make sure port ranges match up, then add the redirect ports.
3745165648Spiso	 */
3746165648Spiso	if (numLocalPorts != r->pport_cnt)
3747165648Spiso	        errx(EX_DATAERR, "redirect_port:"
3748165648Spiso		    "port ranges must be equal in size");
3749165648Spiso
3750165648Spiso	/* Remote port range is allowed to be '0' which means all ports. */
3751165648Spiso	if (r->rport_cnt != numLocalPorts &&
3752165648Spiso	    (r->rport_cnt != 1 || r->rport != 0))
3753165648Spiso	        errx(EX_DATAERR, "redirect_port: remote port must"
3754165648Spiso		    "be 0 or equal to local port range in size");
3755165648Spiso
3756165648Spiso	/*
3757165648Spiso	 * Setup LSNAT server pool.
3758165648Spiso	 */
3759165648Spiso	if (lsnat) {
3760165648Spiso		sep = strtok(tmp_spool_buf, ",");
3761165648Spiso		while (sep != NULL) {
3762165648Spiso			tmp = (struct cfg_spool *)spool_buf;
3763165648Spiso			if (len < SOF_SPOOL)
3764165648Spiso				goto nospace;
3765165648Spiso			len -= SOF_SPOOL;
3766165648Spiso			space += SOF_SPOOL;
3767165648Spiso			if (StrToAddrAndPortRange(sep, &tmp->addr, protoName,
3768165648Spiso			    &portRange) != 0)
3769165648Spiso				errx(EX_DATAERR, "redirect_port:"
3770165648Spiso				    "invalid local port range");
3771165648Spiso			if (GETNUMPORTS(portRange) != 1)
3772165648Spiso				errx(EX_DATAERR, "redirect_port: local port"
3773165648Spiso				    "must be single in this context");
3774165648Spiso			tmp->port = GETLOPORT(portRange);
3775165648Spiso			r->spool_cnt++;
3776165648Spiso			/* Point to the next possible cfg_spool. */
3777165648Spiso			spool_buf = &spool_buf[SOF_SPOOL];
3778165648Spiso			sep = strtok(NULL, ",");
3779165648Spiso		}
3780165648Spiso	}
3781165648Spiso	return (space);
3782165648Spisonospace:
3783165648Spiso	errx(EX_DATAERR, "redirect_port: buf is too small\n");
3784165648Spiso}
3785165648Spiso
3786165648Spisostatic int
3787165648Spisosetup_redir_proto(char *spool_buf, int len,
3788165648Spiso		 int *_ac, char ***_av)
3789165648Spiso{
3790165648Spiso	char **av;
3791165648Spiso	int ac, i, space;
3792165648Spiso	struct protoent *protoent;
3793165648Spiso	struct cfg_redir *r;
3794165648Spiso
3795165648Spiso	av = *_av;
3796165648Spiso	ac = *_ac;
3797165648Spiso	if (len >= SOF_REDIR) {
3798165648Spiso		r = (struct cfg_redir *)spool_buf;
3799165648Spiso		/* Skip cfg_redir at beginning of buf. */
3800165648Spiso		spool_buf = &spool_buf[SOF_REDIR];
3801165648Spiso		space = SOF_REDIR;
3802165648Spiso		len -= SOF_REDIR;
3803165648Spiso	} else
3804165648Spiso		goto nospace;
3805165648Spiso	r->mode = REDIR_PROTO;
3806165648Spiso	/*
3807165648Spiso	 * Extract protocol.
3808165648Spiso	 */
3809165648Spiso	if (ac == 0)
3810165648Spiso		errx(EX_DATAERR, "redirect_proto: missing protocol");
3811165648Spiso
3812165648Spiso	protoent = getprotobyname(*av);
3813165648Spiso	if (protoent == NULL)
3814165648Spiso		errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av);
3815165648Spiso	else
3816165648Spiso		r->proto = protoent->p_proto;
3817165648Spiso
3818165648Spiso	INC_ARGCV();
3819165648Spiso
3820165648Spiso	/*
3821165648Spiso	 * Extract local address.
3822165648Spiso	 */
3823165648Spiso	if (ac == 0)
3824165648Spiso		errx(EX_DATAERR, "redirect_proto: missing local address");
3825165648Spiso	else
3826165648Spiso		StrToAddr(*av, &r->laddr);
3827165648Spiso
3828165648Spiso	INC_ARGCV();
3829165648Spiso
3830165648Spiso	/*
3831165648Spiso	 * Extract optional public address.
3832165648Spiso	 */
3833165648Spiso	if (ac == 0) {
3834165648Spiso		r->paddr.s_addr = INADDR_ANY;
3835165648Spiso		r->raddr.s_addr = INADDR_ANY;
3836165648Spiso	} else {
3837165648Spiso		/* see above in setup_redir_port() */
3838165648Spiso		if (!isalpha(**av)) {
3839165648Spiso			StrToAddr(*av, &r->paddr);
3840165648Spiso			INC_ARGCV();
3841165648Spiso
3842165648Spiso			/*
3843165648Spiso			 * Extract optional remote address.
3844165648Spiso			 */
3845165648Spiso			/* see above in setup_redir_port() */
3846165648Spiso			if (ac!=0 && !isalpha(**av)) {
3847165648Spiso				StrToAddr(*av, &r->raddr);
3848165648Spiso				INC_ARGCV();
3849165648Spiso			}
3850165648Spiso		}
3851165648Spiso	}
3852165648Spiso	return (space);
3853165648Spisonospace:
3854165648Spiso	errx(EX_DATAERR, "redirect_proto: buf is too small\n");
3855165648Spiso}
3856165648Spiso
3857165648Spisostatic void
3858165648Spisoshow_nat(int ac, char **av);
3859165648Spiso
3860165648Spisostatic void
3861165648Spisoprint_nat_config(char *buf) {
3862165648Spiso	struct cfg_nat *n;
3863165648Spiso	int i, cnt, flag, off;
3864165648Spiso	struct cfg_redir *t;
3865165648Spiso	struct cfg_spool *s;
3866165648Spiso	struct protoent *p;
3867165648Spiso
3868165648Spiso	n = (struct cfg_nat *)buf;
3869165648Spiso	flag = 1;
3870165648Spiso	off  = sizeof(*n);
3871165648Spiso	printf("ipfw nat %u config", n->id);
3872165648Spiso	if (strlen(n->if_name) != 0)
3873165648Spiso		printf(" if %s", n->if_name);
3874165648Spiso	else if (n->ip.s_addr != 0)
3875165648Spiso		printf(" ip %s", inet_ntoa(n->ip));
3876165648Spiso	while (n->mode != 0) {
3877165648Spiso		if (n->mode & PKT_ALIAS_LOG) {
3878165648Spiso			printf(" log");
3879165648Spiso			n->mode &= ~PKT_ALIAS_LOG;
3880165648Spiso		} else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
3881165648Spiso			printf(" deny_in");
3882165648Spiso			n->mode &= ~PKT_ALIAS_DENY_INCOMING;
3883165648Spiso		} else if (n->mode & PKT_ALIAS_SAME_PORTS) {
3884165648Spiso			printf(" same_ports");
3885165648Spiso			n->mode &= ~PKT_ALIAS_SAME_PORTS;
3886165648Spiso		} else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
3887165648Spiso			printf(" unreg_only");
3888165648Spiso			n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
3889165648Spiso		} else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
3890165648Spiso			printf(" reset");
3891165648Spiso			n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
3892165648Spiso		} else if (n->mode & PKT_ALIAS_REVERSE) {
3893165648Spiso			printf(" reverse");
3894165648Spiso			n->mode &= ~PKT_ALIAS_REVERSE;
3895165648Spiso		} else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
3896165648Spiso			printf(" proxy_only");
3897165648Spiso			n->mode &= ~PKT_ALIAS_PROXY_ONLY;
3898165648Spiso		}
3899165648Spiso	}
3900165648Spiso	/* Print all the redirect's data configuration. */
3901165648Spiso	for (cnt = 0; cnt < n->redir_cnt; cnt++) {
3902165648Spiso		t = (struct cfg_redir *)&buf[off];
3903165648Spiso		off += SOF_REDIR;
3904165648Spiso		switch (t->mode) {
3905165648Spiso		case REDIR_ADDR:
3906165648Spiso			printf(" redirect_addr");
3907165648Spiso			if (t->spool_cnt == 0)
3908165648Spiso				printf(" %s", inet_ntoa(t->laddr));
3909165648Spiso			else
3910165648Spiso				for (i = 0; i < t->spool_cnt; i++) {
3911165648Spiso					s = (struct cfg_spool *)&buf[off];
3912165648Spiso					if (i)
3913165648Spiso						printf(",");
3914165648Spiso					else
3915165648Spiso						printf(" ");
3916165648Spiso					printf("%s", inet_ntoa(s->addr));
3917165648Spiso					off += SOF_SPOOL;
3918165648Spiso				}
3919165648Spiso			printf(" %s", inet_ntoa(t->paddr));
3920165648Spiso			break;
3921165648Spiso		case REDIR_PORT:
3922165648Spiso			p = getprotobynumber(t->proto);
3923165648Spiso			printf(" redirect_port %s ", p->p_name);
3924165648Spiso			if (!t->spool_cnt) {
3925165648Spiso				printf("%s:%u", inet_ntoa(t->laddr), t->lport);
3926165648Spiso				if (t->pport_cnt > 1)
3927165648Spiso					printf("-%u", t->lport +
3928165648Spiso					    t->pport_cnt - 1);
3929165648Spiso			} else
3930165648Spiso				for (i=0; i < t->spool_cnt; i++) {
3931165648Spiso					s = (struct cfg_spool *)&buf[off];
3932165648Spiso					if (i)
3933165648Spiso						printf(",");
3934165648Spiso					printf("%s:%u", inet_ntoa(s->addr),
3935165648Spiso					    s->port);
3936165648Spiso					off += SOF_SPOOL;
3937165648Spiso				}
3938165648Spiso
3939165648Spiso			printf(" ");
3940165648Spiso			if (t->paddr.s_addr)
3941165648Spiso				printf("%s:", inet_ntoa(t->paddr));
3942165648Spiso			printf("%u", t->pport);
3943165648Spiso			if (!t->spool_cnt && t->pport_cnt > 1)
3944165648Spiso				printf("-%u", t->pport + t->pport_cnt - 1);
3945165648Spiso
3946165648Spiso			if (t->raddr.s_addr) {
3947165648Spiso				printf(" %s", inet_ntoa(t->raddr));
3948165648Spiso				if (t->rport) {
3949165648Spiso					printf(":%u", t->rport);
3950165648Spiso					if (!t->spool_cnt && t->rport_cnt > 1)
3951165648Spiso						printf("-%u", t->rport +
3952165648Spiso						    t->rport_cnt - 1);
3953165648Spiso				}
3954165648Spiso			}
3955165648Spiso			break;
3956165648Spiso		case REDIR_PROTO:
3957165648Spiso			p = getprotobynumber(t->proto);
3958165648Spiso			printf(" redirect_proto %s %s", p->p_name,
3959165648Spiso			    inet_ntoa(t->laddr));
3960165648Spiso			if (t->paddr.s_addr != 0) {
3961165648Spiso				printf(" %s", inet_ntoa(t->paddr));
3962165648Spiso				if (t->raddr.s_addr)
3963165648Spiso					printf(" %s", inet_ntoa(t->raddr));
3964165648Spiso			}
3965165648Spiso			break;
3966165648Spiso		default:
3967165648Spiso			errx(EX_DATAERR, "unknown redir mode");
3968165648Spiso			break;
3969165648Spiso		}
3970165648Spiso	}
3971165648Spiso	printf("\n");
3972165648Spiso}
3973165648Spiso
3974165648Spisostatic void
3975165648Spisoconfig_nat(int ac, char **av)
3976165648Spiso{
3977165648Spiso	struct cfg_nat *n;              /* Nat instance configuration. */
3978165648Spiso	struct in_addr ip;
3979165648Spiso	int i, len, off, tok;
3980165648Spiso	char *id, buf[NAT_BUF_LEN]; 	/* Buffer for serialized data. */
3981165648Spiso
3982165648Spiso	len = NAT_BUF_LEN;
3983165648Spiso	/* Offset in buf: save space for n at the beginning. */
3984165648Spiso	off = sizeof(*n);
3985165648Spiso	memset(buf, 0, sizeof(buf));
3986165648Spiso	n = (struct cfg_nat *)buf;
3987165648Spiso
3988165648Spiso	av++; ac--;
3989165648Spiso	/* Nat id. */
3990165648Spiso	if (ac && isdigit(**av)) {
3991165648Spiso		id = *av;
3992165648Spiso		i = atoi(*av);
3993165648Spiso		ac--; av++;
3994165648Spiso		n->id = i;
3995165648Spiso	} else
3996165648Spiso		errx(EX_DATAERR, "missing nat id");
3997165648Spiso	if (ac == 0)
3998165648Spiso		errx(EX_DATAERR, "missing option");
3999165648Spiso
4000165648Spiso	while (ac > 0) {
4001165648Spiso		tok = match_token(nat_params, *av);
4002165648Spiso		ac--; av++;
4003165648Spiso		switch (tok) {
4004165648Spiso		case TOK_IP:
4005165648Spiso			if (ac == 0)
4006165648Spiso				errx(EX_DATAERR, "missing option");
4007165648Spiso			if (!inet_aton(av[0], &(n->ip)))
4008165648Spiso				errx(EX_DATAERR, "bad ip address ``%s''",
4009165648Spiso				    av[0]);
4010165648Spiso			ac--; av++;
4011165648Spiso			break;
4012165648Spiso		case TOK_IF:
4013175511Smaxim			if (ac == 0)
4014175511Smaxim				errx(EX_DATAERR, "missing option");
4015165648Spiso			set_addr_dynamic(av[0], n);
4016165648Spiso			ac--; av++;
4017165648Spiso			break;
4018165648Spiso		case TOK_ALOG:
4019165648Spiso			n->mode |= PKT_ALIAS_LOG;
4020165648Spiso			break;
4021165648Spiso		case TOK_DENY_INC:
4022165648Spiso			n->mode |= PKT_ALIAS_DENY_INCOMING;
4023165648Spiso			break;
4024165648Spiso		case TOK_SAME_PORTS:
4025165648Spiso			n->mode |= PKT_ALIAS_SAME_PORTS;
4026165648Spiso			break;
4027165648Spiso		case TOK_UNREG_ONLY:
4028165648Spiso			n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
4029165648Spiso			break;
4030165648Spiso		case TOK_RESET_ADDR:
4031165648Spiso			n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
4032165648Spiso			break;
4033165648Spiso		case TOK_ALIAS_REV:
4034165648Spiso			n->mode |= PKT_ALIAS_REVERSE;
4035165648Spiso			break;
4036165648Spiso		case TOK_PROXY_ONLY:
4037165648Spiso			n->mode |= PKT_ALIAS_PROXY_ONLY;
4038165648Spiso			break;
4039165648Spiso			/*
4040165648Spiso			 * All the setup_redir_* functions work directly in the final
4041165648Spiso			 * buffer, see above for details.
4042165648Spiso			 */
4043165648Spiso		case TOK_REDIR_ADDR:
4044165648Spiso		case TOK_REDIR_PORT:
4045165648Spiso		case TOK_REDIR_PROTO:
4046165648Spiso			switch (tok) {
4047165648Spiso			case TOK_REDIR_ADDR:
4048165648Spiso				i = setup_redir_addr(&buf[off], len, &ac, &av);
4049165648Spiso				break;
4050165648Spiso			case TOK_REDIR_PORT:
4051165648Spiso				i = setup_redir_port(&buf[off], len, &ac, &av);
4052165648Spiso				break;
4053165648Spiso			case TOK_REDIR_PROTO:
4054165648Spiso				i = setup_redir_proto(&buf[off], len, &ac, &av);
4055165648Spiso				break;
4056165648Spiso			}
4057165648Spiso			n->redir_cnt++;
4058165648Spiso			off += i;
4059165648Spiso			len -= i;
4060165648Spiso			break;
4061165648Spiso		default:
4062165648Spiso			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
4063165648Spiso		}
4064165648Spiso	}
4065165648Spiso
4066165648Spiso	i = do_cmd(IP_FW_NAT_CFG, buf, off);
4067165648Spiso	if (i)
4068165648Spiso		err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
4069165648Spiso
4070165648Spiso	/* After every modification, we show the resultant rule. */
4071165648Spiso	int _ac = 3;
4072165648Spiso	char *_av[] = {"show", "config", id};
4073165648Spiso	show_nat(_ac, _av);
4074165648Spiso}
4075165648Spiso
4076165648Spisostatic void
407798943Sluigiconfig_pipe(int ac, char **av)
407898943Sluigi{
4079117469Sluigi	struct dn_pipe p;
408098943Sluigi	int i;
408198943Sluigi	char *end;
408298943Sluigi	void *par = NULL;
408398943Sluigi
4084117469Sluigi	memset(&p, 0, sizeof p);
408598943Sluigi
408698943Sluigi	av++; ac--;
408798943Sluigi	/* Pipe number */
408898943Sluigi	if (ac && isdigit(**av)) {
408998943Sluigi		i = atoi(*av); av++; ac--;
409098943Sluigi		if (do_pipe == 1)
4091117469Sluigi			p.pipe_nr = i;
409298943Sluigi		else
4093117469Sluigi			p.fs.fs_nr = i;
409498943Sluigi	}
409599475Sluigi	while (ac > 0) {
409698943Sluigi		double d;
409798943Sluigi		int tok = match_token(dummynet_params, *av);
409898943Sluigi		ac--; av++;
409998943Sluigi
410098943Sluigi		switch(tok) {
4101101978Sluigi		case TOK_NOERROR:
4102117469Sluigi			p.fs.flags_fs |= DN_NOERROR;
4103101978Sluigi			break;
4104101978Sluigi
410598943Sluigi		case TOK_PLR:
410698943Sluigi			NEED1("plr needs argument 0..1\n");
410798943Sluigi			d = strtod(av[0], NULL);
410898943Sluigi			if (d > 1)
410998943Sluigi				d = 1;
411098943Sluigi			else if (d < 0)
411198943Sluigi				d = 0;
4112117469Sluigi			p.fs.plr = (int)(d*0x7fffffff);
411398943Sluigi			ac--; av++;
411498943Sluigi			break;
411598943Sluigi
411698943Sluigi		case TOK_QUEUE:
411798943Sluigi			NEED1("queue needs queue size\n");
411898943Sluigi			end = NULL;
4119117469Sluigi			p.fs.qsize = strtoul(av[0], &end, 0);
412098943Sluigi			if (*end == 'K' || *end == 'k') {
4121117469Sluigi				p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
4122117469Sluigi				p.fs.qsize *= 1024;
4123140271Sbrooks			} else if (*end == 'B' ||
4124140271Sbrooks			    _substrcmp2(end, "by", "bytes") == 0) {
4125117469Sluigi				p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
412698943Sluigi			}
412798943Sluigi			ac--; av++;
412898943Sluigi			break;
412998943Sluigi
413098943Sluigi		case TOK_BUCKETS:
413198943Sluigi			NEED1("buckets needs argument\n");
4132117469Sluigi			p.fs.rq_size = strtoul(av[0], NULL, 0);
413398943Sluigi			ac--; av++;
413498943Sluigi			break;
413598943Sluigi
413698943Sluigi		case TOK_MASK:
413798943Sluigi			NEED1("mask needs mask specifier\n");
413898943Sluigi			/*
413998943Sluigi			 * per-flow queue, mask is dst_ip, dst_port,
414098943Sluigi			 * src_ip, src_port, proto measured in bits
414198943Sluigi			 */
414298943Sluigi			par = NULL;
414398943Sluigi
4144145246Sbrooks			bzero(&p.fs.flow_mask, sizeof(p.fs.flow_mask));
414598943Sluigi			end = NULL;
414698943Sluigi
414798943Sluigi			while (ac >= 1) {
4148117328Sluigi			    uint32_t *p32 = NULL;
4149117328Sluigi			    uint16_t *p16 = NULL;
4150145246Sbrooks			    uint32_t *p20 = NULL;
4151145246Sbrooks			    struct in6_addr *pa6 = NULL;
4152145246Sbrooks			    uint32_t a;
415398943Sluigi
415498943Sluigi			    tok = match_token(dummynet_params, *av);
415598943Sluigi			    ac--; av++;
415698943Sluigi			    switch(tok) {
415798943Sluigi			    case TOK_ALL:
415898943Sluigi				    /*
415998943Sluigi				     * special case, all bits significant
416098943Sluigi				     */
4161117469Sluigi				    p.fs.flow_mask.dst_ip = ~0;
4162117469Sluigi				    p.fs.flow_mask.src_ip = ~0;
4163117469Sluigi				    p.fs.flow_mask.dst_port = ~0;
4164117469Sluigi				    p.fs.flow_mask.src_port = ~0;
4165117469Sluigi				    p.fs.flow_mask.proto = ~0;
4166145246Sbrooks				    n2mask(&(p.fs.flow_mask.dst_ip6), 128);
4167145246Sbrooks				    n2mask(&(p.fs.flow_mask.src_ip6), 128);
4168145246Sbrooks				    p.fs.flow_mask.flow_id6 = ~0;
4169117469Sluigi				    p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
417098943Sluigi				    goto end_mask;
417198943Sluigi
417298943Sluigi			    case TOK_DSTIP:
4173117469Sluigi				    p32 = &p.fs.flow_mask.dst_ip;
417498943Sluigi				    break;
417598943Sluigi
417698943Sluigi			    case TOK_SRCIP:
4177117469Sluigi				    p32 = &p.fs.flow_mask.src_ip;
417898943Sluigi				    break;
417998943Sluigi
4180145246Sbrooks			    case TOK_DSTIP6:
4181145246Sbrooks				    pa6 = &(p.fs.flow_mask.dst_ip6);
4182145246Sbrooks				    break;
4183145246Sbrooks
4184145246Sbrooks			    case TOK_SRCIP6:
4185145246Sbrooks				    pa6 = &(p.fs.flow_mask.src_ip6);
4186145246Sbrooks				    break;
4187145246Sbrooks
4188145246Sbrooks			    case TOK_FLOWID:
4189145246Sbrooks				    p20 = &p.fs.flow_mask.flow_id6;
4190145246Sbrooks				    break;
4191145246Sbrooks
419298943Sluigi			    case TOK_DSTPORT:
4193117469Sluigi				    p16 = &p.fs.flow_mask.dst_port;
419498943Sluigi				    break;
419598943Sluigi
419698943Sluigi			    case TOK_SRCPORT:
4197117469Sluigi				    p16 = &p.fs.flow_mask.src_port;
419898943Sluigi				    break;
419998943Sluigi
420098943Sluigi			    case TOK_PROTO:
420198943Sluigi				    break;
420298943Sluigi
420398943Sluigi			    default:
420498943Sluigi				    ac++; av--; /* backtrack */
420598943Sluigi				    goto end_mask;
420698943Sluigi			    }
420798943Sluigi			    if (ac < 1)
420898943Sluigi				    errx(EX_USAGE, "mask: value missing");
420998943Sluigi			    if (*av[0] == '/') {
421098943Sluigi				    a = strtoul(av[0]+1, &end, 0);
4211145246Sbrooks				    if (pa6 == NULL)
4212145246Sbrooks					    a = (a == 32) ? ~0 : (1 << a) - 1;
4213106505Smaxim			    } else
421499909Sluigi				    a = strtoul(av[0], &end, 0);
421598943Sluigi			    if (p32 != NULL)
421698943Sluigi				    *p32 = a;
421798943Sluigi			    else if (p16 != NULL) {
4218139821Sbrooks				    if (a > 0xFFFF)
421998943Sluigi					    errx(EX_DATAERR,
4220144687Sbrooks						"port mask must be 16 bit");
4221117328Sluigi				    *p16 = (uint16_t)a;
4222145246Sbrooks			    } else if (p20 != NULL) {
4223145246Sbrooks				    if (a > 0xfffff)
4224145246Sbrooks					errx(EX_DATAERR,
4225145246Sbrooks					    "flow_id mask must be 20 bit");
4226145246Sbrooks				    *p20 = (uint32_t)a;
4227145246Sbrooks			    } else if (pa6 != NULL) {
4228145246Sbrooks				    if (a < 0 || a > 128)
4229145246Sbrooks					errx(EX_DATAERR,
4230145246Sbrooks					    "in6addr invalid mask len");
4231145246Sbrooks				    else
4232145246Sbrooks					n2mask(pa6, a);
423398943Sluigi			    } else {
4234139821Sbrooks				    if (a > 0xFF)
423598943Sluigi					    errx(EX_DATAERR,
4236144687Sbrooks						"proto mask must be 8 bit");
4237117469Sluigi				    p.fs.flow_mask.proto = (uint8_t)a;
423898943Sluigi			    }
423998943Sluigi			    if (a != 0)
4240117469Sluigi				    p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
424198943Sluigi			    ac--; av++;
424298943Sluigi			} /* end while, config masks */
424398943Sluigiend_mask:
424498943Sluigi			break;
424598943Sluigi
424698943Sluigi		case TOK_RED:
424798943Sluigi		case TOK_GRED:
424898943Sluigi			NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
4249117469Sluigi			p.fs.flags_fs |= DN_IS_RED;
425098943Sluigi			if (tok == TOK_GRED)
4251117469Sluigi				p.fs.flags_fs |= DN_IS_GENTLE_RED;
425298943Sluigi			/*
425398943Sluigi			 * the format for parameters is w_q/min_th/max_th/max_p
425498943Sluigi			 */
425598943Sluigi			if ((end = strsep(&av[0], "/"))) {
425698943Sluigi			    double w_q = strtod(end, NULL);
425798943Sluigi			    if (w_q > 1 || w_q <= 0)
425898943Sluigi				errx(EX_DATAERR, "0 < w_q <= 1");
4259117469Sluigi			    p.fs.w_q = (int) (w_q * (1 << SCALE_RED));
426098943Sluigi			}
426198943Sluigi			if ((end = strsep(&av[0], "/"))) {
4262117469Sluigi			    p.fs.min_th = strtoul(end, &end, 0);
426398943Sluigi			    if (*end == 'K' || *end == 'k')
4264117469Sluigi				p.fs.min_th *= 1024;
426598943Sluigi			}
426698943Sluigi			if ((end = strsep(&av[0], "/"))) {
4267117469Sluigi			    p.fs.max_th = strtoul(end, &end, 0);
426898943Sluigi			    if (*end == 'K' || *end == 'k')
4269117469Sluigi				p.fs.max_th *= 1024;
427098943Sluigi			}
427198943Sluigi			if ((end = strsep(&av[0], "/"))) {
427298943Sluigi			    double max_p = strtod(end, NULL);
427398943Sluigi			    if (max_p > 1 || max_p <= 0)
427498943Sluigi				errx(EX_DATAERR, "0 < max_p <= 1");
4275117469Sluigi			    p.fs.max_p = (int)(max_p * (1 << SCALE_RED));
427698943Sluigi			}
427798943Sluigi			ac--; av++;
427898943Sluigi			break;
427998943Sluigi
428098943Sluigi		case TOK_DROPTAIL:
4281117469Sluigi			p.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
428298943Sluigi			break;
4283106505Smaxim
428498943Sluigi		case TOK_BW:
428598943Sluigi			NEED1("bw needs bandwidth or interface\n");
428698943Sluigi			if (do_pipe != 1)
428798943Sluigi			    errx(EX_DATAERR, "bandwidth only valid for pipes");
428898943Sluigi			/*
428998943Sluigi			 * set clocking interface or bandwidth value
429098943Sluigi			 */
429198943Sluigi			if (av[0][0] >= 'a' && av[0][0] <= 'z') {
4292117469Sluigi			    int l = sizeof(p.if_name)-1;
429398943Sluigi			    /* interface name */
4294117469Sluigi			    strncpy(p.if_name, av[0], l);
4295117469Sluigi			    p.if_name[l] = '\0';
4296117469Sluigi			    p.bandwidth = 0;
429798943Sluigi			} else {
4298117469Sluigi			    p.if_name[0] = '\0';
4299117469Sluigi			    p.bandwidth = strtoul(av[0], &end, 0);
430098943Sluigi			    if (*end == 'K' || *end == 'k') {
430198943Sluigi				end++;
4302117469Sluigi				p.bandwidth *= 1000;
430398943Sluigi			    } else if (*end == 'M') {
430498943Sluigi				end++;
4305117469Sluigi				p.bandwidth *= 1000000;
430698943Sluigi			    }
4307161550Sdwmalone			    if ((*end == 'B' &&
4308161550Sdwmalone				  _substrcmp2(end, "Bi", "Bit/s") != 0) ||
4309140271Sbrooks			        _substrcmp2(end, "by", "bytes") == 0)
4310117469Sluigi				p.bandwidth *= 8;
4311117469Sluigi			    if (p.bandwidth < 0)
431298943Sluigi				errx(EX_DATAERR, "bandwidth too large");
431398943Sluigi			}
431498943Sluigi			ac--; av++;
431598943Sluigi			break;
431698943Sluigi
431798943Sluigi		case TOK_DELAY:
431898943Sluigi			if (do_pipe != 1)
431998943Sluigi				errx(EX_DATAERR, "delay only valid for pipes");
432098943Sluigi			NEED1("delay needs argument 0..10000ms\n");
4321117469Sluigi			p.delay = strtoul(av[0], NULL, 0);
432298943Sluigi			ac--; av++;
432398943Sluigi			break;
432498943Sluigi
432598943Sluigi		case TOK_WEIGHT:
432698943Sluigi			if (do_pipe == 1)
432798943Sluigi				errx(EX_DATAERR,"weight only valid for queues");
432898943Sluigi			NEED1("weight needs argument 0..100\n");
4329117469Sluigi			p.fs.weight = strtoul(av[0], &end, 0);
433098943Sluigi			ac--; av++;
433198943Sluigi			break;
433298943Sluigi
433398943Sluigi		case TOK_PIPE:
433498943Sluigi			if (do_pipe == 1)
433598943Sluigi				errx(EX_DATAERR,"pipe only valid for queues");
433698943Sluigi			NEED1("pipe needs pipe_number\n");
4337117469Sluigi			p.fs.parent_nr = strtoul(av[0], &end, 0);
433898943Sluigi			ac--; av++;
433998943Sluigi			break;
434098943Sluigi
434198943Sluigi		default:
4342124924Smaxim			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
434398943Sluigi		}
434498943Sluigi	}
434598943Sluigi	if (do_pipe == 1) {
4346117469Sluigi		if (p.pipe_nr == 0)
434798943Sluigi			errx(EX_DATAERR, "pipe_nr must be > 0");
4348117469Sluigi		if (p.delay > 10000)
434998943Sluigi			errx(EX_DATAERR, "delay must be < 10000");
435098943Sluigi	} else { /* do_pipe == 2, queue */
4351117469Sluigi		if (p.fs.parent_nr == 0)
435298943Sluigi			errx(EX_DATAERR, "pipe must be > 0");
4353117469Sluigi		if (p.fs.weight >100)
435498943Sluigi			errx(EX_DATAERR, "weight must be <= 100");
435598943Sluigi	}
4356117469Sluigi	if (p.fs.flags_fs & DN_QSIZE_IS_BYTES) {
4357176626Sdwmalone		size_t len;
4358176626Sdwmalone		long limit;
4359176626Sdwmalone
4360176626Sdwmalone		len = sizeof(limit);
4361176626Sdwmalone		if (sysctlbyname("net.inet.ip.dummynet.pipe_byte_limit",
4362176626Sdwmalone			&limit, &len, NULL, 0) == -1)
4363176626Sdwmalone			limit = 1024*1024;
4364176626Sdwmalone		if (p.fs.qsize > limit)
4365176626Sdwmalone			errx(EX_DATAERR, "queue size must be < %ldB", limit);
436698943Sluigi	} else {
4367176626Sdwmalone		size_t len;
4368176626Sdwmalone		long limit;
4369176626Sdwmalone
4370176626Sdwmalone		len = sizeof(limit);
4371176626Sdwmalone		if (sysctlbyname("net.inet.ip.dummynet.pipe_slot_limit",
4372176626Sdwmalone			&limit, &len, NULL, 0) == -1)
4373176626Sdwmalone			limit = 100;
4374176626Sdwmalone		if (p.fs.qsize > limit)
4375176626Sdwmalone			errx(EX_DATAERR, "2 <= queue size <= %ld", limit);
437698943Sluigi	}
4377117469Sluigi	if (p.fs.flags_fs & DN_IS_RED) {
437898943Sluigi		size_t len;
437998943Sluigi		int lookup_depth, avg_pkt_size;
438098943Sluigi		double s, idle, weight, w_q;
4381117469Sluigi		struct clockinfo ck;
438298943Sluigi		int t;
438398943Sluigi
4384117469Sluigi		if (p.fs.min_th >= p.fs.max_th)
438598943Sluigi		    errx(EX_DATAERR, "min_th %d must be < than max_th %d",
4386117469Sluigi			p.fs.min_th, p.fs.max_th);
4387117469Sluigi		if (p.fs.max_th == 0)
438898943Sluigi		    errx(EX_DATAERR, "max_th must be > 0");
438998943Sluigi
439098943Sluigi		len = sizeof(int);
439198943Sluigi		if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
439298943Sluigi			&lookup_depth, &len, NULL, 0) == -1)
439398943Sluigi		    errx(1, "sysctlbyname(\"%s\")",
439498943Sluigi			"net.inet.ip.dummynet.red_lookup_depth");
439598943Sluigi		if (lookup_depth == 0)
439698943Sluigi		    errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth"
439798943Sluigi			" must be greater than zero");
439898943Sluigi
439998943Sluigi		len = sizeof(int);
440098943Sluigi		if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
440198943Sluigi			&avg_pkt_size, &len, NULL, 0) == -1)
440298943Sluigi
440398943Sluigi		    errx(1, "sysctlbyname(\"%s\")",
440498943Sluigi			"net.inet.ip.dummynet.red_avg_pkt_size");
440598943Sluigi		if (avg_pkt_size == 0)
440698943Sluigi			errx(EX_DATAERR,
440798943Sluigi			    "net.inet.ip.dummynet.red_avg_pkt_size must"
440898943Sluigi			    " be greater than zero");
440998943Sluigi
441098943Sluigi		len = sizeof(struct clockinfo);
4411117469Sluigi		if (sysctlbyname("kern.clockrate", &ck, &len, NULL, 0) == -1)
441298943Sluigi			errx(1, "sysctlbyname(\"%s\")", "kern.clockrate");
441398943Sluigi
441498943Sluigi		/*
441598943Sluigi		 * Ticks needed for sending a medium-sized packet.
441698943Sluigi		 * Unfortunately, when we are configuring a WF2Q+ queue, we
441798943Sluigi		 * do not have bandwidth information, because that is stored
441898943Sluigi		 * in the parent pipe, and also we have multiple queues
441998943Sluigi		 * competing for it. So we set s=0, which is not very
442098943Sluigi		 * correct. But on the other hand, why do we want RED with
442198943Sluigi		 * WF2Q+ ?
442298943Sluigi		 */
4423117469Sluigi		if (p.bandwidth==0) /* this is a WF2Q+ queue */
442498943Sluigi			s = 0;
442598943Sluigi		else
4426174713Soleg			s = (double)ck.hz * avg_pkt_size * 8 / p.bandwidth;
442798943Sluigi
442898943Sluigi		/*
442998943Sluigi		 * max idle time (in ticks) before avg queue size becomes 0.
443098943Sluigi		 * NOTA:  (3/w_q) is approx the value x so that
443198943Sluigi		 * (1-w_q)^x < 10^-3.
443298943Sluigi		 */
4433117469Sluigi		w_q = ((double)p.fs.w_q) / (1 << SCALE_RED);
443498943Sluigi		idle = s * 3. / w_q;
4435117469Sluigi		p.fs.lookup_step = (int)idle / lookup_depth;
4436117469Sluigi		if (!p.fs.lookup_step)
4437117469Sluigi			p.fs.lookup_step = 1;
443898943Sluigi		weight = 1 - w_q;
4439174713Soleg		for (t = p.fs.lookup_step; t > 1; --t)
4440174713Soleg			weight *= 1 - w_q;
4441117469Sluigi		p.fs.lookup_weight = (int)(weight * (1 << SCALE_RED));
444298943Sluigi	}
4443117469Sluigi	i = do_cmd(IP_DUMMYNET_CONFIGURE, &p, sizeof p);
444498943Sluigi	if (i)
444598943Sluigi		err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
444698943Sluigi}
444798943Sluigi
444898943Sluigistatic void
4449169424Smaximget_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask)
445098943Sluigi{
445198943Sluigi	int i, l;
4452169424Smaxim	char *ap, *ptr, *optr;
4453169424Smaxim	struct ether_addr *mac;
4454169424Smaxim	const char *macset = "0123456789abcdefABCDEF:";
445598943Sluigi
4456169424Smaxim	if (strcmp(p, "any") == 0) {
4457169424Smaxim		for (i = 0; i < ETHER_ADDR_LEN; i++)
4458169424Smaxim			addr[i] = mask[i] = 0;
445998943Sluigi		return;
4460169424Smaxim	}
446198943Sluigi
4462169424Smaxim	optr = ptr = strdup(p);
4463169424Smaxim	if ((ap = strsep(&ptr, "&/")) != NULL && *ap != 0) {
4464169424Smaxim		l = strlen(ap);
4465169424Smaxim		if (strspn(ap, macset) != l || (mac = ether_aton(ap)) == NULL)
4466169424Smaxim			errx(EX_DATAERR, "Incorrect MAC address");
4467169424Smaxim		bcopy(mac, addr, ETHER_ADDR_LEN);
4468169424Smaxim	} else
4469169424Smaxim		errx(EX_DATAERR, "Incorrect MAC address");
4470169424Smaxim
4471169424Smaxim	if (ptr != NULL) { /* we have mask? */
4472169424Smaxim		if (p[ptr - optr - 1] == '/') { /* mask len */
4473169424Smaxim			l = strtol(ptr, &ap, 10);
4474169424Smaxim			if (*ap != 0 || l > ETHER_ADDR_LEN * 8 || l < 0)
4475169424Smaxim				errx(EX_DATAERR, "Incorrect mask length");
4476169424Smaxim			for (i = 0; l > 0 && i < ETHER_ADDR_LEN; l -= 8, i++)
4477169424Smaxim				mask[i] = (l >= 8) ? 0xff: (~0) << (8 - l);
4478169424Smaxim		} else { /* mask */
4479169424Smaxim			l = strlen(ptr);
4480169424Smaxim			if (strspn(ptr, macset) != l ||
4481169424Smaxim			    (mac = ether_aton(ptr)) == NULL)
4482169424Smaxim				errx(EX_DATAERR, "Incorrect mask");
4483169424Smaxim			bcopy(mac, mask, ETHER_ADDR_LEN);
448498943Sluigi		}
4485169424Smaxim	} else { /* default mask: ff:ff:ff:ff:ff:ff */
4486169424Smaxim		for (i = 0; i < ETHER_ADDR_LEN; i++)
448798943Sluigi			mask[i] = 0xff;
448898943Sluigi	}
4489169424Smaxim	for (i = 0; i < ETHER_ADDR_LEN; i++)
449098943Sluigi		addr[i] &= mask[i];
4491169424Smaxim
4492169424Smaxim	free(optr);
449398943Sluigi}
449498943Sluigi
449598943Sluigi/*
449698943Sluigi * helper function, updates the pointer to cmd with the length
449798943Sluigi * of the current command, and also cleans up the first word of
449898943Sluigi * the new command in case it has been clobbered before.
449998943Sluigi */
450098943Sluigistatic ipfw_insn *
450198943Sluiginext_cmd(ipfw_insn *cmd)
450298943Sluigi{
450398943Sluigi	cmd += F_LEN(cmd);
450498943Sluigi	bzero(cmd, sizeof(*cmd));
450598943Sluigi	return cmd;
450698943Sluigi}
450798943Sluigi
450898943Sluigi/*
4509117469Sluigi * Takes arguments and copies them into a comment
4510117469Sluigi */
4511117469Sluigistatic void
4512117469Sluigifill_comment(ipfw_insn *cmd, int ac, char **av)
4513117469Sluigi{
4514117469Sluigi	int i, l;
4515117469Sluigi	char *p = (char *)(cmd + 1);
4516117577Sluigi
4517117469Sluigi	cmd->opcode = O_NOP;
4518117469Sluigi	cmd->len =  (cmd->len & (F_NOT | F_OR));
4519117469Sluigi
4520117469Sluigi	/* Compute length of comment string. */
4521117469Sluigi	for (i = 0, l = 0; i < ac; i++)
4522117469Sluigi		l += strlen(av[i]) + 1;
4523117469Sluigi	if (l == 0)
4524117469Sluigi		return;
4525117469Sluigi	if (l > 84)
4526117469Sluigi		errx(EX_DATAERR,
4527117469Sluigi		    "comment too long (max 80 chars)");
4528117469Sluigi	l = 1 + (l+3)/4;
4529117469Sluigi	cmd->len =  (cmd->len & (F_NOT | F_OR)) | l;
4530117469Sluigi	for (i = 0; i < ac; i++) {
4531117469Sluigi		strcpy(p, av[i]);
4532117469Sluigi		p += strlen(av[i]);
4533117469Sluigi		*p++ = ' ';
4534117469Sluigi	}
4535117469Sluigi	*(--p) = '\0';
4536117469Sluigi}
4537117577Sluigi
4538117469Sluigi/*
453998943Sluigi * A function to fill simple commands of size 1.
454098943Sluigi * Existing flags are preserved.
454198943Sluigi */
454298943Sluigistatic void
4543117328Sluigifill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg)
454498943Sluigi{
454598943Sluigi	cmd->opcode = opcode;
454698943Sluigi	cmd->len =  ((cmd->len | flags) & (F_NOT | F_OR)) | 1;
454798943Sluigi	cmd->arg1 = arg;
454898943Sluigi}
454998943Sluigi
455098943Sluigi/*
455198943Sluigi * Fetch and add the MAC address and type, with masks. This generates one or
455298943Sluigi * two microinstructions, and returns the pointer to the last one.
455398943Sluigi */
455498943Sluigistatic ipfw_insn *
455598943Sluigiadd_mac(ipfw_insn *cmd, int ac, char *av[])
455698943Sluigi{
4557102087Sluigi	ipfw_insn_mac *mac;
455898943Sluigi
4559102087Sluigi	if (ac < 2)
4560102098Sluigi		errx(EX_DATAERR, "MAC dst src");
456198943Sluigi
456298943Sluigi	cmd->opcode = O_MACADDR2;
456398943Sluigi	cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
456498943Sluigi
456598943Sluigi	mac = (ipfw_insn_mac *)cmd;
4566101978Sluigi	get_mac_addr_mask(av[0], mac->addr, mac->mask);	/* dst */
4567169424Smaxim	get_mac_addr_mask(av[1], &(mac->addr[ETHER_ADDR_LEN]),
4568169424Smaxim	    &(mac->mask[ETHER_ADDR_LEN])); /* src */
4569102087Sluigi	return cmd;
4570102087Sluigi}
457198943Sluigi
4572102087Sluigistatic ipfw_insn *
4573102087Sluigiadd_mactype(ipfw_insn *cmd, int ac, char *av)
4574102087Sluigi{
4575102087Sluigi	if (ac < 1)
4576102087Sluigi		errx(EX_DATAERR, "missing MAC type");
4577102087Sluigi	if (strcmp(av, "any") != 0) { /* we have a non-null type */
4578102087Sluigi		fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE);
457998943Sluigi		cmd->opcode = O_MAC_TYPE;
4580102087Sluigi		return cmd;
4581102087Sluigi	} else
4582102087Sluigi		return NULL;
4583102087Sluigi}
458498943Sluigi
4585102087Sluigistatic ipfw_insn *
4586152923Sumeadd_proto0(ipfw_insn *cmd, char *av, u_char *protop)
4587102087Sluigi{
4588102087Sluigi	struct protoent *pe;
4589152923Sume	char *ep;
4590152923Sume	int proto;
4591102087Sluigi
4592156315Sume	proto = strtol(av, &ep, 10);
4593156315Sume	if (*ep != '\0' || proto <= 0) {
4594152923Sume		if ((pe = getprotobyname(av)) == NULL)
4595152923Sume			return NULL;
4596152923Sume		proto = pe->p_proto;
4597152923Sume	}
4598145246Sbrooks
4599152923Sume	fill_cmd(cmd, O_PROTO, 0, proto);
4600152923Sume	*protop = proto;
4601152923Sume	return cmd;
4602152923Sume}
4603152923Sume
4604152923Sumestatic ipfw_insn *
4605152923Sumeadd_proto(ipfw_insn *cmd, char *av, u_char *protop)
4606152923Sume{
4607152923Sume	u_char proto = IPPROTO_IP;
4608152923Sume
4609156315Sume	if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
4610146894Smlaier		; /* do not set O_IP4 nor O_IP6 */
4611152923Sume	else if (strcmp(av, "ip4") == 0)
4612152923Sume		/* explicit "just IPv4" rule */
4613152923Sume		fill_cmd(cmd, O_IP4, 0, 0);
4614152923Sume	else if (strcmp(av, "ip6") == 0) {
4615152923Sume		/* explicit "just IPv6" rule */
4616152923Sume		proto = IPPROTO_IPV6;
4617152923Sume		fill_cmd(cmd, O_IP6, 0, 0);
4618152923Sume	} else
4619152923Sume		return add_proto0(cmd, av, protop);
4620152923Sume
4621152923Sume	*protop = proto;
4622152923Sume	return cmd;
4623152923Sume}
4624152923Sume
4625152923Sumestatic ipfw_insn *
4626152923Sumeadd_proto_compat(ipfw_insn *cmd, char *av, u_char *protop)
4627152923Sume{
4628152923Sume	u_char proto = IPPROTO_IP;
4629152923Sume
4630152923Sume	if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
4631152923Sume		; /* do not set O_IP4 nor O_IP6 */
4632146894Smlaier	else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0)
4633146894Smlaier		/* explicit "just IPv4" rule */
4634146894Smlaier		fill_cmd(cmd, O_IP4, 0, 0);
4635146894Smlaier	else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) {
4636146894Smlaier		/* explicit "just IPv6" rule */
4637152923Sume		proto = IPPROTO_IPV6;
4638146894Smlaier		fill_cmd(cmd, O_IP6, 0, 0);
4639152923Sume	} else
4640152923Sume		return add_proto0(cmd, av, protop);
4641145246Sbrooks
4642152923Sume	*protop = proto;
464398943Sluigi	return cmd;
464498943Sluigi}
464598943Sluigi
4646102087Sluigistatic ipfw_insn *
4647102087Sluigiadd_srcip(ipfw_insn *cmd, char *av)
4648102087Sluigi{
4649102087Sluigi	fill_ip((ipfw_insn_ip *)cmd, av);
4650102087Sluigi	if (cmd->opcode == O_IP_DST_SET)			/* set */
4651102087Sluigi		cmd->opcode = O_IP_SRC_SET;
4652130281Sru	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
4653130281Sru		cmd->opcode = O_IP_SRC_LOOKUP;
4654102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
4655102087Sluigi		cmd->opcode = O_IP_SRC_ME;
4656102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
4657102087Sluigi		cmd->opcode = O_IP_SRC;
4658117328Sluigi	else							/* addr/mask */
4659102087Sluigi		cmd->opcode = O_IP_SRC_MASK;
4660102087Sluigi	return cmd;
4661102087Sluigi}
4662102087Sluigi
4663102087Sluigistatic ipfw_insn *
4664102087Sluigiadd_dstip(ipfw_insn *cmd, char *av)
4665102087Sluigi{
4666102087Sluigi	fill_ip((ipfw_insn_ip *)cmd, av);
4667102087Sluigi	if (cmd->opcode == O_IP_DST_SET)			/* set */
4668102087Sluigi		;
4669130281Sru	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
4670130281Sru		;
4671102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
4672102087Sluigi		cmd->opcode = O_IP_DST_ME;
4673102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
4674102087Sluigi		cmd->opcode = O_IP_DST;
4675117328Sluigi	else							/* addr/mask */
4676102087Sluigi		cmd->opcode = O_IP_DST_MASK;
4677102087Sluigi	return cmd;
4678102087Sluigi}
4679102087Sluigi
4680102087Sluigistatic ipfw_insn *
4681102087Sluigiadd_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
4682102087Sluigi{
4683140271Sbrooks	if (_substrcmp(av, "any") == 0) {
4684102087Sluigi		return NULL;
4685102087Sluigi	} else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) {
4686102087Sluigi		/* XXX todo: check that we have a protocol with ports */
4687102087Sluigi		cmd->opcode = opcode;
4688102087Sluigi		return cmd;
4689102087Sluigi	}
4690102087Sluigi	return NULL;
4691102087Sluigi}
4692102087Sluigi
4693145246Sbrooksstatic ipfw_insn *
4694145246Sbrooksadd_src(ipfw_insn *cmd, char *av, u_char proto)
4695145246Sbrooks{
4696145246Sbrooks	struct in6_addr a;
4697158553Smlaier	char *host, *ch;
4698158553Smlaier	ipfw_insn *ret = NULL;
4699145246Sbrooks
4700158553Smlaier	if ((host = strdup(av)) == NULL)
4701158553Smlaier		return NULL;
4702158553Smlaier	if ((ch = strrchr(host, '/')) != NULL)
4703158553Smlaier		*ch = '\0';
4704158553Smlaier
4705145246Sbrooks	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
4706158553Smlaier	    inet_pton(AF_INET6, host, &a))
4707158553Smlaier		ret = add_srcip6(cmd, av);
4708145246Sbrooks	/* XXX: should check for IPv4, not !IPv6 */
4709161483Sdwmalone	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
4710161483Sdwmalone	    !inet_pton(AF_INET6, host, &a)))
4711158553Smlaier		ret = add_srcip(cmd, av);
4712161483Sdwmalone	if (ret == NULL && strcmp(av, "any") != 0)
4713158553Smlaier		ret = cmd;
4714145246Sbrooks
4715158553Smlaier	free(host);
4716158553Smlaier	return ret;
4717145246Sbrooks}
4718145246Sbrooks
4719145246Sbrooksstatic ipfw_insn *
4720145246Sbrooksadd_dst(ipfw_insn *cmd, char *av, u_char proto)
4721145246Sbrooks{
4722145246Sbrooks	struct in6_addr a;
4723158553Smlaier	char *host, *ch;
4724158553Smlaier	ipfw_insn *ret = NULL;
4725145246Sbrooks
4726158553Smlaier	if ((host = strdup(av)) == NULL)
4727158553Smlaier		return NULL;
4728158553Smlaier	if ((ch = strrchr(host, '/')) != NULL)
4729158553Smlaier		*ch = '\0';
4730158553Smlaier
4731145246Sbrooks	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
4732158553Smlaier	    inet_pton(AF_INET6, host, &a))
4733158553Smlaier		ret = add_dstip6(cmd, av);
4734145246Sbrooks	/* XXX: should check for IPv4, not !IPv6 */
4735161483Sdwmalone	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
4736161483Sdwmalone	    !inet_pton(AF_INET6, host, &a)))
4737158553Smlaier		ret = add_dstip(cmd, av);
4738161483Sdwmalone	if (ret == NULL && strcmp(av, "any") != 0)
4739158553Smlaier		ret = cmd;
4740145246Sbrooks
4741158553Smlaier	free(host);
4742158553Smlaier	return ret;
4743145246Sbrooks}
4744145246Sbrooks
474598943Sluigi/*
474698943Sluigi * Parse arguments and assemble the microinstructions which make up a rule.
474798943Sluigi * Rules are added into the 'rulebuf' and then copied in the correct order
474898943Sluigi * into the actual rule.
474998943Sluigi *
4750136071Sgreen * The syntax for a rule starts with the action, followed by
4751136071Sgreen * optional action parameters, and the various match patterns.
4752108533Sschweikh * In the assembled microcode, the first opcode must be an O_PROBE_STATE
475398943Sluigi * (generated if the rule includes a keep-state option), then the
4754136071Sgreen * various match patterns, log/altq actions, and the actual action.
4755106505Smaxim *
475698943Sluigi */
475798943Sluigistatic void
475898943Sluigiadd(int ac, char *av[])
475998943Sluigi{
476098943Sluigi	/*
476198943Sluigi	 * rules are added into the 'rulebuf' and then copied in
476298943Sluigi	 * the correct order into the actual rule.
476398943Sluigi	 * Some things that need to go out of order (prob, action etc.)
476498943Sluigi	 * go into actbuf[].
476598943Sluigi	 */
4766117328Sluigi	static uint32_t rulebuf[255], actbuf[255], cmdbuf[255];
476798943Sluigi
4768117469Sluigi	ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
4769102087Sluigi	ipfw_insn *first_cmd;	/* first match pattern */
477098943Sluigi
477198943Sluigi	struct ip_fw *rule;
477298943Sluigi
477398943Sluigi	/*
477498943Sluigi	 * various flags used to record that we entered some fields.
477598943Sluigi	 */
4776101116Sluigi	ipfw_insn *have_state = NULL;	/* check-state or keep-state */
4777158879Soleg	ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL;
4778134475Smaxim	size_t len;
477998943Sluigi
478098943Sluigi	int i;
478198943Sluigi
478298943Sluigi	int open_par = 0;	/* open parenthesis ( */
478398943Sluigi
478498943Sluigi	/* proto is here because it is used to fetch ports */
478598943Sluigi	u_char proto = IPPROTO_IP;	/* default protocol */
478698943Sluigi
4787107289Sluigi	double match_prob = 1; /* match probability, default is always match */
4788107289Sluigi
478998943Sluigi	bzero(actbuf, sizeof(actbuf));		/* actions go here */
479098943Sluigi	bzero(cmdbuf, sizeof(cmdbuf));
479198943Sluigi	bzero(rulebuf, sizeof(rulebuf));
479298943Sluigi
479398943Sluigi	rule = (struct ip_fw *)rulebuf;
479498943Sluigi	cmd = (ipfw_insn *)cmdbuf;
479598943Sluigi	action = (ipfw_insn *)actbuf;
479698943Sluigi
479798943Sluigi	av++; ac--;
479898943Sluigi
479998943Sluigi	/* [rule N]	-- Rule number optional */
480098943Sluigi	if (ac && isdigit(**av)) {
480198943Sluigi		rule->rulenum = atoi(*av);
480298943Sluigi		av++;
480398943Sluigi		ac--;
480498943Sluigi	}
480598943Sluigi
4806117655Sluigi	/* [set N]	-- set number (0..RESVD_SET), optional */
4807140271Sbrooks	if (ac > 1 && _substrcmp(*av, "set") == 0) {
4808101628Sluigi		int set = strtoul(av[1], NULL, 10);
4809117655Sluigi		if (set < 0 || set > RESVD_SET)
4810101628Sluigi			errx(EX_DATAERR, "illegal set %s", av[1]);
4811101628Sluigi		rule->set = set;
4812101628Sluigi		av += 2; ac -= 2;
4813101628Sluigi	}
4814101628Sluigi
481598943Sluigi	/* [prob D]	-- match probability, optional */
4816140271Sbrooks	if (ac > 1 && _substrcmp(*av, "prob") == 0) {
4817107289Sluigi		match_prob = strtod(av[1], NULL);
481898943Sluigi
4819107289Sluigi		if (match_prob <= 0 || match_prob > 1)
482098943Sluigi			errx(EX_DATAERR, "illegal match prob. %s", av[1]);
482198943Sluigi		av += 2; ac -= 2;
482298943Sluigi	}
482398943Sluigi
482498943Sluigi	/* action	-- mandatory */
482598943Sluigi	NEED1("missing action");
482698943Sluigi	i = match_token(rule_actions, *av);
482798943Sluigi	ac--; av++;
482898943Sluigi	action->len = 1;	/* default */
482998943Sluigi	switch(i) {
483098943Sluigi	case TOK_CHECKSTATE:
4831101116Sluigi		have_state = action;
483298943Sluigi		action->opcode = O_CHECK_STATE;
483398943Sluigi		break;
483498943Sluigi
483598943Sluigi	case TOK_ACCEPT:
483698943Sluigi		action->opcode = O_ACCEPT;
483798943Sluigi		break;
483898943Sluigi
483998943Sluigi	case TOK_DENY:
484098943Sluigi		action->opcode = O_DENY;
484199475Sluigi		action->arg1 = 0;
484298943Sluigi		break;
484398943Sluigi
484499475Sluigi	case TOK_REJECT:
484599475Sluigi		action->opcode = O_REJECT;
484699475Sluigi		action->arg1 = ICMP_UNREACH_HOST;
484799475Sluigi		break;
484899475Sluigi
484999475Sluigi	case TOK_RESET:
485099475Sluigi		action->opcode = O_REJECT;
485199475Sluigi		action->arg1 = ICMP_REJECT_RST;
485299475Sluigi		break;
485399475Sluigi
4854149020Sbz	case TOK_RESET6:
4855149020Sbz		action->opcode = O_UNREACH6;
4856149020Sbz		action->arg1 = ICMP6_UNREACH_RST;
4857149020Sbz		break;
4858149020Sbz
485999475Sluigi	case TOK_UNREACH:
486099475Sluigi		action->opcode = O_REJECT;
486199475Sluigi		NEED1("missing reject code");
486299475Sluigi		fill_reject_code(&action->arg1, *av);
486399475Sluigi		ac--; av++;
486499475Sluigi		break;
486599475Sluigi
4866149020Sbz	case TOK_UNREACH6:
4867149020Sbz		action->opcode = O_UNREACH6;
4868149020Sbz		NEED1("missing unreach code");
4869149020Sbz		fill_unreach6_code(&action->arg1, *av);
4870149020Sbz		ac--; av++;
4871149020Sbz		break;
4872149020Sbz
487398943Sluigi	case TOK_COUNT:
487498943Sluigi		action->opcode = O_COUNT;
487598943Sluigi		break;
487698943Sluigi
4877176517Spiso	case TOK_NAT:
4878176517Spiso 		action->opcode = O_NAT;
4879176517Spiso 		action->len = F_INSN_SIZE(ipfw_insn_nat);
4880176517Spiso		goto chkarg;
4881178888Sjulian
488298943Sluigi	case TOK_QUEUE:
4883153374Sglebius		action->opcode = O_QUEUE;
4884153374Sglebius		goto chkarg;
488598943Sluigi	case TOK_PIPE:
4886153374Sglebius		action->opcode = O_PIPE;
4887153374Sglebius		goto chkarg;
488898943Sluigi	case TOK_SKIPTO:
4889153374Sglebius		action->opcode = O_SKIPTO;
4890153374Sglebius		goto chkarg;
4891153374Sglebius	case TOK_NETGRAPH:
4892153374Sglebius		action->opcode = O_NETGRAPH;
4893153374Sglebius		goto chkarg;
4894153374Sglebius	case TOK_NGTEE:
4895153374Sglebius		action->opcode = O_NGTEE;
4896153374Sglebius		goto chkarg;
489798943Sluigi	case TOK_DIVERT:
4898153374Sglebius		action->opcode = O_DIVERT;
4899153374Sglebius		goto chkarg;
490098943Sluigi	case TOK_TEE:
4901153374Sglebius		action->opcode = O_TEE;
4902153374Sglebiuschkarg:
4903153374Sglebius		if (!ac)
4904153374Sglebius			errx(EX_USAGE, "missing argument for %s", *(av - 1));
4905153374Sglebius		if (isdigit(**av)) {
4906153374Sglebius			action->arg1 = strtoul(*av, NULL, 10);
4907153374Sglebius			if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG)
4908153374Sglebius				errx(EX_DATAERR, "illegal argument for %s",
4909153374Sglebius				    *(av - 1));
4910153374Sglebius		} else if (_substrcmp(*av, TABLEARG) == 0) {
4911153374Sglebius			action->arg1 = IP_FW_TABLEARG;
4912153374Sglebius		} else if (i == TOK_DIVERT || i == TOK_TEE) {
491398943Sluigi			struct servent *s;
491498943Sluigi			setservent(1);
491598943Sluigi			s = getservbyname(av[0], "divert");
491698943Sluigi			if (s != NULL)
491798943Sluigi				action->arg1 = ntohs(s->s_port);
491898943Sluigi			else
491998943Sluigi				errx(EX_DATAERR, "illegal divert/tee port");
4920153374Sglebius		} else
4921153374Sglebius			errx(EX_DATAERR, "illegal argument for %s", *(av - 1));
492298943Sluigi		ac--; av++;
492398943Sluigi		break;
492498943Sluigi
492598943Sluigi	case TOK_FORWARD: {
492698943Sluigi		ipfw_insn_sa *p = (ipfw_insn_sa *)action;
492798943Sluigi		char *s, *end;
492898943Sluigi
492998943Sluigi		NEED1("missing forward address[:port]");
493098943Sluigi
493198943Sluigi		action->opcode = O_FORWARD_IP;
493298943Sluigi		action->len = F_INSN_SIZE(ipfw_insn_sa);
493398943Sluigi
493498943Sluigi		p->sa.sin_len = sizeof(struct sockaddr_in);
493598943Sluigi		p->sa.sin_family = AF_INET;
493698943Sluigi		p->sa.sin_port = 0;
493798943Sluigi		/*
493898943Sluigi		 * locate the address-port separator (':' or ',')
493998943Sluigi		 */
494098943Sluigi		s = strchr(*av, ':');
494198943Sluigi		if (s == NULL)
494298943Sluigi			s = strchr(*av, ',');
494398943Sluigi		if (s != NULL) {
494498943Sluigi			*(s++) = '\0';
494598943Sluigi			i = strtoport(s, &end, 0 /* base */, 0 /* proto */);
494698943Sluigi			if (s == end)
494798943Sluigi				errx(EX_DATAERR,
494898943Sluigi				    "illegal forwarding port ``%s''", s);
4949103241Sluigi			p->sa.sin_port = (u_short)i;
495098943Sluigi		}
4951161456Sjulian		if (_substrcmp(*av, "tablearg") == 0)
4952161456Sjulian			p->sa.sin_addr.s_addr = INADDR_ANY;
4953161456Sjulian		else
4954161424Sjulian			lookup_host(*av, &(p->sa.sin_addr));
495598943Sluigi		ac--; av++;
495698943Sluigi		break;
4957161424Sjulian	    }
4958117469Sluigi	case TOK_COMMENT:
4959117469Sluigi		/* pretend it is a 'count' rule followed by the comment */
4960117469Sluigi		action->opcode = O_COUNT;
4961117469Sluigi		ac++; av--;	/* go back... */
4962117469Sluigi		break;
4963178888Sjulian
4964178888Sjulian	case TOK_SETFIB:
4965178888Sjulian	    {
4966178888Sjulian		int numfibs;
4967178888Sjulian
4968178888Sjulian		action->opcode = O_SETFIB;
4969178888Sjulian 		NEED1("missing fib number");
4970178888Sjulian 	        action->arg1 = strtoul(*av, NULL, 10);
4971178888Sjulian		if (sysctlbyname("net.fibs", &numfibs, &i, NULL, 0) == -1)
4972178888Sjulian			errx(EX_DATAERR, "fibs not suported.\n");
4973178888Sjulian		if (action->arg1 >= numfibs)  /* Temporary */
4974178888Sjulian			errx(EX_DATAERR, "fib too large.\n");
4975178888Sjulian 		ac--; av++;
4976178888Sjulian 		break;
4977178888Sjulian	    }
4978165648Spiso
497998943Sluigi	default:
4980102087Sluigi		errx(EX_DATAERR, "invalid action %s\n", av[-1]);
498198943Sluigi	}
498298943Sluigi	action = next_cmd(action);
498398943Sluigi
498498943Sluigi	/*
4985136071Sgreen	 * [altq queuename] -- altq tag, optional
498698943Sluigi	 * [log [logamount N]]	-- log, optional
498798943Sluigi	 *
4988136071Sgreen	 * If they exist, it go first in the cmdbuf, but then it is
498998943Sluigi	 * skipped in the copy section to the end of the buffer.
499098943Sluigi	 */
4991136071Sgreen	while (ac != 0 && (i = match_token(rule_action_params, *av)) != -1) {
4992136071Sgreen		ac--; av++;
4993136071Sgreen		switch (i) {
4994136071Sgreen		case TOK_LOG:
4995136071Sgreen		    {
4996136071Sgreen			ipfw_insn_log *c = (ipfw_insn_log *)cmd;
4997136071Sgreen			int l;
499898943Sluigi
4999136071Sgreen			if (have_log)
5000136071Sgreen				errx(EX_DATAERR,
5001136071Sgreen				    "log cannot be specified more than once");
5002136071Sgreen			have_log = (ipfw_insn *)c;
5003136071Sgreen			cmd->len = F_INSN_SIZE(ipfw_insn_log);
5004136071Sgreen			cmd->opcode = O_LOG;
5005140271Sbrooks			if (ac && _substrcmp(*av, "logamount") == 0) {
5006136071Sgreen				ac--; av++;
5007136071Sgreen				NEED1("logamount requires argument");
5008136071Sgreen				l = atoi(*av);
5009136071Sgreen				if (l < 0)
5010136071Sgreen					errx(EX_DATAERR,
5011136071Sgreen					    "logamount must be positive");
5012136071Sgreen				c->max_log = l;
5013136071Sgreen				ac--; av++;
5014136071Sgreen			} else {
5015136071Sgreen				len = sizeof(c->max_log);
5016136071Sgreen				if (sysctlbyname("net.inet.ip.fw.verbose_limit",
5017136071Sgreen				    &c->max_log, &len, NULL, 0) == -1)
5018136071Sgreen					errx(1, "sysctlbyname(\"%s\")",
5019136071Sgreen					    "net.inet.ip.fw.verbose_limit");
5020136071Sgreen			}
5021136071Sgreen		    }
5022136071Sgreen			break;
5023136071Sgreen
5024136071Sgreen		case TOK_ALTQ:
5025136071Sgreen		    {
5026136071Sgreen			ipfw_insn_altq *a = (ipfw_insn_altq *)cmd;
5027136071Sgreen
5028136071Sgreen			NEED1("missing altq queue name");
5029136071Sgreen			if (have_altq)
5030136071Sgreen				errx(EX_DATAERR,
5031136071Sgreen				    "altq cannot be specified more than once");
5032136071Sgreen			have_altq = (ipfw_insn *)a;
5033136071Sgreen			cmd->len = F_INSN_SIZE(ipfw_insn_altq);
5034136071Sgreen			cmd->opcode = O_ALTQ;
5035136071Sgreen			fill_altq_qid(&a->qid, *av);
503698943Sluigi			ac--; av++;
5037136071Sgreen		    }
5038136071Sgreen			break;
5039136071Sgreen
5040158879Soleg		case TOK_TAG:
5041159636Soleg		case TOK_UNTAG: {
5042159636Soleg			uint16_t tag;
5043159636Soleg
5044158879Soleg			if (have_tag)
5045159636Soleg				errx(EX_USAGE, "tag and untag cannot be "
5046159636Soleg				    "specified more than once");
5047159636Soleg			GET_UINT_ARG(tag, 1, 65534, i, rule_action_params);
5048158879Soleg			have_tag = cmd;
5049159636Soleg			fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag);
5050158879Soleg			ac--; av++;
5051158879Soleg			break;
5052159636Soleg		}
5053158879Soleg
5054136071Sgreen		default:
5055136071Sgreen			abort();
505698943Sluigi		}
505798943Sluigi		cmd = next_cmd(cmd);
505898943Sluigi	}
505998943Sluigi
5060101116Sluigi	if (have_state)	/* must be a check-state, we are done */
506198943Sluigi		goto done;
506298943Sluigi
506398943Sluigi#define OR_START(target)					\
506498943Sluigi	if (ac && (*av[0] == '(' || *av[0] == '{')) {		\
506598943Sluigi		if (open_par)					\
506698943Sluigi			errx(EX_USAGE, "nested \"(\" not allowed\n"); \
5067101641Sluigi		prev = NULL;					\
506898943Sluigi		open_par = 1;					\
506998943Sluigi		if ( (av[0])[1] == '\0') {			\
507098943Sluigi			ac--; av++;				\
507198943Sluigi		} else						\
507298943Sluigi			(*av)++;				\
507398943Sluigi	}							\
507498943Sluigi	target:							\
507598943Sluigi
507698943Sluigi
507798943Sluigi#define	CLOSE_PAR						\
507898943Sluigi	if (open_par) {						\
507998943Sluigi		if (ac && (					\
5080140271Sbrooks		    strcmp(*av, ")") == 0 ||			\
5081140271Sbrooks		    strcmp(*av, "}") == 0)) {			\
5082101641Sluigi			prev = NULL;				\
508398943Sluigi			open_par = 0;				\
508498943Sluigi			ac--; av++;				\
508598943Sluigi		} else						\
508698943Sluigi			errx(EX_USAGE, "missing \")\"\n");	\
508798943Sluigi	}
5088106505Smaxim
508998943Sluigi#define NOT_BLOCK						\
5090140271Sbrooks	if (ac && _substrcmp(*av, "not") == 0) {		\
509198943Sluigi		if (cmd->len & F_NOT)				\
509298943Sluigi			errx(EX_USAGE, "double \"not\" not allowed\n"); \
509398943Sluigi		cmd->len |= F_NOT;				\
509498943Sluigi		ac--; av++;					\
509598943Sluigi	}
509698943Sluigi
509798943Sluigi#define OR_BLOCK(target)					\
5098140271Sbrooks	if (ac && _substrcmp(*av, "or") == 0) {		\
509998943Sluigi		if (prev == NULL || open_par == 0)		\
510098943Sluigi			errx(EX_DATAERR, "invalid OR block");	\
510198943Sluigi		prev->len |= F_OR;				\
510298943Sluigi		ac--; av++;					\
510398943Sluigi		goto target;					\
510498943Sluigi	}							\
510598943Sluigi	CLOSE_PAR;
510698943Sluigi
5107102087Sluigi	first_cmd = cmd;
5108102098Sluigi
5109102098Sluigi#if 0
511098943Sluigi	/*
5111102087Sluigi	 * MAC addresses, optional.
5112102087Sluigi	 * If we have this, we skip the part "proto from src to dst"
5113102087Sluigi	 * and jump straight to the option parsing.
5114102087Sluigi	 */
5115102087Sluigi	NOT_BLOCK;
5116102087Sluigi	NEED1("missing protocol");
5117140271Sbrooks	if (_substrcmp(*av, "MAC") == 0 ||
5118140271Sbrooks	    _substrcmp(*av, "mac") == 0) {
5119102087Sluigi		ac--; av++;	/* the "MAC" keyword */
5120102087Sluigi		add_mac(cmd, ac, av); /* exits in case of errors */
5121102087Sluigi		cmd = next_cmd(cmd);
5122102087Sluigi		ac -= 2; av += 2;	/* dst-mac and src-mac */
5123102087Sluigi		NOT_BLOCK;
5124102087Sluigi		NEED1("missing mac type");
5125102087Sluigi		if (add_mactype(cmd, ac, av[0]))
5126102087Sluigi			cmd = next_cmd(cmd);
5127102087Sluigi		ac--; av++;	/* any or mac-type */
5128102087Sluigi		goto read_options;
5129102087Sluigi	}
5130102098Sluigi#endif
5131102087Sluigi
5132102087Sluigi	/*
513398943Sluigi	 * protocol, mandatory
513498943Sluigi	 */
513598943Sluigi    OR_START(get_proto);
513698943Sluigi	NOT_BLOCK;
513798943Sluigi	NEED1("missing protocol");
5138152923Sume	if (add_proto_compat(cmd, *av, &proto)) {
5139102087Sluigi		av++; ac--;
5140147105Smlaier		if (F_LEN(cmd) != 0) {
5141102087Sluigi			prev = cmd;
5142102087Sluigi			cmd = next_cmd(cmd);
5143102087Sluigi		}
5144102098Sluigi	} else if (first_cmd != cmd) {
5145116438Smaxim		errx(EX_DATAERR, "invalid protocol ``%s''", *av);
5146102098Sluigi	} else
5147102098Sluigi		goto read_options;
514898943Sluigi    OR_BLOCK(get_proto);
514998943Sluigi
515098943Sluigi	/*
5151102087Sluigi	 * "from", mandatory
515298943Sluigi	 */
5153140271Sbrooks	if (!ac || _substrcmp(*av, "from") != 0)
515498943Sluigi		errx(EX_USAGE, "missing ``from''");
515598943Sluigi	ac--; av++;
515698943Sluigi
515798943Sluigi	/*
515898943Sluigi	 * source IP, mandatory
515998943Sluigi	 */
516098943Sluigi    OR_START(source_ip);
516198943Sluigi	NOT_BLOCK;	/* optional "not" */
516298943Sluigi	NEED1("missing source address");
5163145246Sbrooks	if (add_src(cmd, *av, proto)) {
5164102087Sluigi		ac--; av++;
5165102087Sluigi		if (F_LEN(cmd) != 0) {	/* ! any */
5166102087Sluigi			prev = cmd;
5167102087Sluigi			cmd = next_cmd(cmd);
5168102087Sluigi		}
5169145246Sbrooks	} else
5170145246Sbrooks		errx(EX_USAGE, "bad source address %s", *av);
517198943Sluigi    OR_BLOCK(source_ip);
517298943Sluigi
517398943Sluigi	/*
517498943Sluigi	 * source ports, optional
517598943Sluigi	 */
517698943Sluigi	NOT_BLOCK;	/* optional "not" */
5177101641Sluigi	if (ac) {
5178140271Sbrooks		if (_substrcmp(*av, "any") == 0 ||
5179102087Sluigi		    add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
5180102087Sluigi			ac--; av++;
5181102087Sluigi			if (F_LEN(cmd) != 0)
5182102087Sluigi				cmd = next_cmd(cmd);
5183101641Sluigi		}
518498943Sluigi	}
518598943Sluigi
518698943Sluigi	/*
5187102087Sluigi	 * "to", mandatory
518898943Sluigi	 */
5189140271Sbrooks	if (!ac || _substrcmp(*av, "to") != 0)
519098943Sluigi		errx(EX_USAGE, "missing ``to''");
519198943Sluigi	av++; ac--;
519298943Sluigi
519398943Sluigi	/*
519498943Sluigi	 * destination, mandatory
519598943Sluigi	 */
519698943Sluigi    OR_START(dest_ip);
519798943Sluigi	NOT_BLOCK;	/* optional "not" */
519898943Sluigi	NEED1("missing dst address");
5199145246Sbrooks	if (add_dst(cmd, *av, proto)) {
5200102087Sluigi		ac--; av++;
5201102087Sluigi		if (F_LEN(cmd) != 0) {	/* ! any */
5202102087Sluigi			prev = cmd;
5203102087Sluigi			cmd = next_cmd(cmd);
5204102087Sluigi		}
5205145246Sbrooks	} else
5206145246Sbrooks		errx( EX_USAGE, "bad destination address %s", *av);
520798943Sluigi    OR_BLOCK(dest_ip);
520898943Sluigi
520998943Sluigi	/*
521098943Sluigi	 * dest. ports, optional
521198943Sluigi	 */
521298943Sluigi	NOT_BLOCK;	/* optional "not" */
5213101641Sluigi	if (ac) {
5214140271Sbrooks		if (_substrcmp(*av, "any") == 0 ||
5215102087Sluigi		    add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
5216102087Sluigi			ac--; av++;
5217102087Sluigi			if (F_LEN(cmd) != 0)
5218102087Sluigi				cmd = next_cmd(cmd);
5219101641Sluigi		}
522098943Sluigi	}
522198943Sluigi
522298943Sluigiread_options:
5223102087Sluigi	if (ac && first_cmd == cmd) {
5224102087Sluigi		/*
5225102087Sluigi		 * nothing specified so far, store in the rule to ease
5226102087Sluigi		 * printout later.
5227102087Sluigi		 */
5228102087Sluigi		 rule->_pad = 1;
5229102087Sluigi	}
523098943Sluigi	prev = NULL;
523198943Sluigi	while (ac) {
5232101641Sluigi		char *s;
5233101641Sluigi		ipfw_insn_u32 *cmd32;	/* alias for cmd */
523498943Sluigi
5235101641Sluigi		s = *av;
5236101641Sluigi		cmd32 = (ipfw_insn_u32 *)cmd;
5237101641Sluigi
523898943Sluigi		if (*s == '!') {	/* alternate syntax for NOT */
523998943Sluigi			if (cmd->len & F_NOT)
524098943Sluigi				errx(EX_USAGE, "double \"not\" not allowed\n");
524198943Sluigi			cmd->len = F_NOT;
524298943Sluigi			s++;
524398943Sluigi		}
524498943Sluigi		i = match_token(rule_options, s);
524598943Sluigi		ac--; av++;
524698943Sluigi		switch(i) {
524798943Sluigi		case TOK_NOT:
524898943Sluigi			if (cmd->len & F_NOT)
524998943Sluigi				errx(EX_USAGE, "double \"not\" not allowed\n");
525098943Sluigi			cmd->len = F_NOT;
525198943Sluigi			break;
525298943Sluigi
525398943Sluigi		case TOK_OR:
5254101641Sluigi			if (open_par == 0 || prev == NULL)
525598943Sluigi				errx(EX_USAGE, "invalid \"or\" block\n");
525698943Sluigi			prev->len |= F_OR;
525798943Sluigi			break;
5258101641Sluigi
5259101641Sluigi		case TOK_STARTBRACE:
5260101641Sluigi			if (open_par)
5261101641Sluigi				errx(EX_USAGE, "+nested \"(\" not allowed\n");
5262101641Sluigi			open_par = 1;
5263101641Sluigi			break;
5264101641Sluigi
5265101641Sluigi		case TOK_ENDBRACE:
5266101641Sluigi			if (!open_par)
5267101641Sluigi				errx(EX_USAGE, "+missing \")\"\n");
5268101641Sluigi			open_par = 0;
5269102087Sluigi			prev = NULL;
5270101641Sluigi        		break;
5271101641Sluigi
527298943Sluigi		case TOK_IN:
527398943Sluigi			fill_cmd(cmd, O_IN, 0, 0);
527498943Sluigi			break;
527598943Sluigi
527698943Sluigi		case TOK_OUT:
527798943Sluigi			cmd->len ^= F_NOT; /* toggle F_NOT */
527898943Sluigi			fill_cmd(cmd, O_IN, 0, 0);
527998943Sluigi			break;
528098943Sluigi
5281136073Sgreen		case TOK_DIVERTED:
5282136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 3);
5283136073Sgreen			break;
5284136073Sgreen
5285136073Sgreen		case TOK_DIVERTEDLOOPBACK:
5286136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 1);
5287136073Sgreen			break;
5288136073Sgreen
5289136073Sgreen		case TOK_DIVERTEDOUTPUT:
5290136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 2);
5291136073Sgreen			break;
5292136073Sgreen
529398943Sluigi		case TOK_FRAG:
529498943Sluigi			fill_cmd(cmd, O_FRAG, 0, 0);
529598943Sluigi			break;
529698943Sluigi
529798943Sluigi		case TOK_LAYER2:
529898943Sluigi			fill_cmd(cmd, O_LAYER2, 0, 0);
529998943Sluigi			break;
530098943Sluigi
530198943Sluigi		case TOK_XMIT:
530298943Sluigi		case TOK_RECV:
530398943Sluigi		case TOK_VIA:
530498943Sluigi			NEED1("recv, xmit, via require interface name"
530598943Sluigi				" or address");
530698943Sluigi			fill_iface((ipfw_insn_if *)cmd, av[0]);
530798943Sluigi			ac--; av++;
530898943Sluigi			if (F_LEN(cmd) == 0)	/* not a valid address */
530998943Sluigi				break;
531098943Sluigi			if (i == TOK_XMIT)
531198943Sluigi				cmd->opcode = O_XMIT;
531298943Sluigi			else if (i == TOK_RECV)
531398943Sluigi				cmd->opcode = O_RECV;
531498943Sluigi			else if (i == TOK_VIA)
531598943Sluigi				cmd->opcode = O_VIA;
531698943Sluigi			break;
531798943Sluigi
531899475Sluigi		case TOK_ICMPTYPES:
531999475Sluigi			NEED1("icmptypes requires list of types");
532099475Sluigi			fill_icmptypes((ipfw_insn_u32 *)cmd, *av);
532199475Sluigi			av++; ac--;
532299475Sluigi			break;
5323145246Sbrooks
5324145246Sbrooks		case TOK_ICMP6TYPES:
5325145246Sbrooks			NEED1("icmptypes requires list of types");
5326145246Sbrooks			fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av);
5327145246Sbrooks			av++; ac--;
5328145246Sbrooks			break;
532999475Sluigi
533098943Sluigi		case TOK_IPTTL:
533198943Sluigi			NEED1("ipttl requires TTL");
5332116690Sluigi			if (strpbrk(*av, "-,")) {
5333116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPTTL))
5334116690Sluigi				errx(EX_DATAERR, "invalid ipttl %s", *av);
5335116690Sluigi			} else
5336116690Sluigi			    fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
533798943Sluigi			ac--; av++;
533898943Sluigi			break;
533998943Sluigi
534098943Sluigi		case TOK_IPID:
5341116690Sluigi			NEED1("ipid requires id");
5342116690Sluigi			if (strpbrk(*av, "-,")) {
5343116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPID))
5344116690Sluigi				errx(EX_DATAERR, "invalid ipid %s", *av);
5345116690Sluigi			} else
5346116690Sluigi			    fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0));
534798943Sluigi			ac--; av++;
534898943Sluigi			break;
534998943Sluigi
535098943Sluigi		case TOK_IPLEN:
535198943Sluigi			NEED1("iplen requires length");
5352116690Sluigi			if (strpbrk(*av, "-,")) {
5353116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPLEN))
5354116690Sluigi				errx(EX_DATAERR, "invalid ip len %s", *av);
5355116690Sluigi			} else
5356116690Sluigi			    fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0));
535798943Sluigi			ac--; av++;
535898943Sluigi			break;
535998943Sluigi
536098943Sluigi		case TOK_IPVER:
536198943Sluigi			NEED1("ipver requires version");
536298943Sluigi			fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0));
536398943Sluigi			ac--; av++;
536498943Sluigi			break;
536598943Sluigi
536699475Sluigi		case TOK_IPPRECEDENCE:
536799475Sluigi			NEED1("ipprecedence requires value");
536899475Sluigi			fill_cmd(cmd, O_IPPRECEDENCE, 0,
536999475Sluigi			    (strtoul(*av, NULL, 0) & 7) << 5);
537099475Sluigi			ac--; av++;
537199475Sluigi			break;
537299475Sluigi
537398943Sluigi		case TOK_IPOPTS:
537498943Sluigi			NEED1("missing argument for ipoptions");
5375101116Sluigi			fill_flags(cmd, O_IPOPT, f_ipopts, *av);
537698943Sluigi			ac--; av++;
537798943Sluigi			break;
537898943Sluigi
537999475Sluigi		case TOK_IPTOS:
538099475Sluigi			NEED1("missing argument for iptos");
5381101116Sluigi			fill_flags(cmd, O_IPTOS, f_iptos, *av);
538299475Sluigi			ac--; av++;
538399475Sluigi			break;
538499475Sluigi
538598943Sluigi		case TOK_UID:
538698943Sluigi			NEED1("uid requires argument");
538798943Sluigi		    {
538898943Sluigi			char *end;
538998943Sluigi			uid_t uid;
539098943Sluigi			struct passwd *pwd;
539198943Sluigi
539298943Sluigi			cmd->opcode = O_UID;
539398943Sluigi			uid = strtoul(*av, &end, 0);
539498943Sluigi			pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av);
539598943Sluigi			if (pwd == NULL)
539698943Sluigi				errx(EX_DATAERR, "uid \"%s\" nonexistent", *av);
5397106504Smaxim			cmd32->d[0] = pwd->pw_uid;
5398135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
539998943Sluigi			ac--; av++;
540098943Sluigi		    }
540198943Sluigi			break;
540298943Sluigi
540398943Sluigi		case TOK_GID:
540498943Sluigi			NEED1("gid requires argument");
540598943Sluigi		    {
540698943Sluigi			char *end;
540798943Sluigi			gid_t gid;
540898943Sluigi			struct group *grp;
540998943Sluigi
541098943Sluigi			cmd->opcode = O_GID;
541198943Sluigi			gid = strtoul(*av, &end, 0);
541298943Sluigi			grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av);
541398943Sluigi			if (grp == NULL)
541498943Sluigi				errx(EX_DATAERR, "gid \"%s\" nonexistent", *av);
5415106504Smaxim			cmd32->d[0] = grp->gr_gid;
5416135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
541798943Sluigi			ac--; av++;
541898943Sluigi		    }
541998943Sluigi			break;
542098943Sluigi
5421133600Scsjp		case TOK_JAIL:
5422133600Scsjp			NEED1("jail requires argument");
5423133600Scsjp		    {
5424133600Scsjp			char *end;
5425133600Scsjp			int jid;
5426133600Scsjp
5427133600Scsjp			cmd->opcode = O_JAIL;
5428133600Scsjp			jid = (int)strtol(*av, &end, 0);
5429133600Scsjp			if (jid < 0 || *end != '\0')
5430133600Scsjp				errx(EX_DATAERR, "jail requires prison ID");
5431135554Scsjp			cmd32->d[0] = (uint32_t)jid;
5432135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
5433133600Scsjp			ac--; av++;
5434133600Scsjp		    }
5435133600Scsjp			break;
5436133600Scsjp
543798943Sluigi		case TOK_ESTAB:
543898943Sluigi			fill_cmd(cmd, O_ESTAB, 0, 0);
543998943Sluigi			break;
544098943Sluigi
544198943Sluigi		case TOK_SETUP:
544298943Sluigi			fill_cmd(cmd, O_TCPFLAGS, 0,
544398943Sluigi				(TH_SYN) | ( (TH_ACK) & 0xff) <<8 );
544498943Sluigi			break;
544598943Sluigi
5446136075Sgreen		case TOK_TCPDATALEN:
5447136075Sgreen			NEED1("tcpdatalen requires length");
5448136075Sgreen			if (strpbrk(*av, "-,")) {
5449136075Sgreen			    if (!add_ports(cmd, *av, 0, O_TCPDATALEN))
5450136075Sgreen				errx(EX_DATAERR, "invalid tcpdata len %s", *av);
5451136075Sgreen			} else
5452136075Sgreen			    fill_cmd(cmd, O_TCPDATALEN, 0,
5453136075Sgreen				    strtoul(*av, NULL, 0));
5454136075Sgreen			ac--; av++;
5455136075Sgreen			break;
5456136075Sgreen
545798943Sluigi		case TOK_TCPOPTS:
545898943Sluigi			NEED1("missing argument for tcpoptions");
545998943Sluigi			fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av);
546098943Sluigi			ac--; av++;
546198943Sluigi			break;
546298943Sluigi
546398943Sluigi		case TOK_TCPSEQ:
546498943Sluigi		case TOK_TCPACK:
546598943Sluigi			NEED1("tcpseq/tcpack requires argument");
546698943Sluigi			cmd->len = F_INSN_SIZE(ipfw_insn_u32);
546798943Sluigi			cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK;
546898943Sluigi			cmd32->d[0] = htonl(strtoul(*av, NULL, 0));
546998943Sluigi			ac--; av++;
547098943Sluigi			break;
547198943Sluigi
547298943Sluigi		case TOK_TCPWIN:
547398943Sluigi			NEED1("tcpwin requires length");
547498943Sluigi			fill_cmd(cmd, O_TCPWIN, 0,
547598943Sluigi			    htons(strtoul(*av, NULL, 0)));
547698943Sluigi			ac--; av++;
547798943Sluigi			break;
547898943Sluigi
547998943Sluigi		case TOK_TCPFLAGS:
548098943Sluigi			NEED1("missing argument for tcpflags");
548198943Sluigi			cmd->opcode = O_TCPFLAGS;
548298943Sluigi			fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av);
548398943Sluigi			ac--; av++;
548498943Sluigi			break;
548598943Sluigi
548698943Sluigi		case TOK_KEEPSTATE:
5487101641Sluigi			if (open_par)
5488101641Sluigi				errx(EX_USAGE, "keep-state cannot be part "
5489101641Sluigi				    "of an or block");
549099909Sluigi			if (have_state)
5491101116Sluigi				errx(EX_USAGE, "only one of keep-state "
549299909Sluigi					"and limit is allowed");
5493101116Sluigi			have_state = cmd;
549498943Sluigi			fill_cmd(cmd, O_KEEP_STATE, 0, 0);
549598943Sluigi			break;
549698943Sluigi
5497159636Soleg		case TOK_LIMIT: {
5498159636Soleg			ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
5499159636Soleg			int val;
5500159636Soleg
5501101641Sluigi			if (open_par)
5502159636Soleg				errx(EX_USAGE,
5503159636Soleg				    "limit cannot be part of an or block");
550499909Sluigi			if (have_state)
5505168819Smaxim				errx(EX_USAGE, "only one of keep-state and "
5506159636Soleg				    "limit is allowed");
5507101116Sluigi			have_state = cmd;
550898943Sluigi
550998943Sluigi			cmd->len = F_INSN_SIZE(ipfw_insn_limit);
551098943Sluigi			cmd->opcode = O_LIMIT;
5511159636Soleg			c->limit_mask = c->conn_limit = 0;
551298943Sluigi
5513159636Soleg			while (ac > 0) {
5514159636Soleg				if ((val = match_token(limit_masks, *av)) <= 0)
551598943Sluigi					break;
551698943Sluigi				c->limit_mask |= val;
551798943Sluigi				ac--; av++;
551898943Sluigi			}
5519159636Soleg
552098943Sluigi			if (c->limit_mask == 0)
5521159636Soleg				errx(EX_USAGE, "limit: missing limit mask");
5522159636Soleg
5523159636Soleg			GET_UINT_ARG(c->conn_limit, 1, 65534, TOK_LIMIT,
5524159636Soleg			    rule_options);
5525159636Soleg
552698943Sluigi			ac--; av++;
552798943Sluigi			break;
5528159636Soleg		}
552998943Sluigi
5530102087Sluigi		case TOK_PROTO:
5531102087Sluigi			NEED1("missing protocol");
5532145246Sbrooks			if (add_proto(cmd, *av, &proto)) {
5533102087Sluigi				ac--; av++;
5534102098Sluigi			} else
5535116438Smaxim				errx(EX_DATAERR, "invalid protocol ``%s''",
5536116438Smaxim				    *av);
5537102087Sluigi			break;
5538106505Smaxim
5539102087Sluigi		case TOK_SRCIP:
5540102087Sluigi			NEED1("missing source IP");
5541102087Sluigi			if (add_srcip(cmd, *av)) {
5542102087Sluigi				ac--; av++;
5543102087Sluigi			}
5544102087Sluigi			break;
5545102087Sluigi
5546102087Sluigi		case TOK_DSTIP:
5547102087Sluigi			NEED1("missing destination IP");
5548102087Sluigi			if (add_dstip(cmd, *av)) {
5549102087Sluigi				ac--; av++;
5550102087Sluigi			}
5551102087Sluigi			break;
5552102087Sluigi
5553145246Sbrooks		case TOK_SRCIP6:
5554145246Sbrooks			NEED1("missing source IP6");
5555145246Sbrooks			if (add_srcip6(cmd, *av)) {
5556145246Sbrooks				ac--; av++;
5557145246Sbrooks			}
5558145246Sbrooks			break;
5559145246Sbrooks
5560145246Sbrooks		case TOK_DSTIP6:
5561145246Sbrooks			NEED1("missing destination IP6");
5562145246Sbrooks			if (add_dstip6(cmd, *av)) {
5563145246Sbrooks				ac--; av++;
5564145246Sbrooks			}
5565145246Sbrooks			break;
5566145246Sbrooks
5567102087Sluigi		case TOK_SRCPORT:
5568102087Sluigi			NEED1("missing source port");
5569140271Sbrooks			if (_substrcmp(*av, "any") == 0 ||
5570102087Sluigi			    add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
5571102087Sluigi				ac--; av++;
5572102087Sluigi			} else
5573102087Sluigi				errx(EX_DATAERR, "invalid source port %s", *av);
5574102087Sluigi			break;
5575102087Sluigi
5576102087Sluigi		case TOK_DSTPORT:
5577102087Sluigi			NEED1("missing destination port");
5578140271Sbrooks			if (_substrcmp(*av, "any") == 0 ||
5579102087Sluigi			    add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
5580102087Sluigi				ac--; av++;
5581102087Sluigi			} else
5582102087Sluigi				errx(EX_DATAERR, "invalid destination port %s",
5583102087Sluigi				    *av);
5584102087Sluigi			break;
5585102087Sluigi
5586102087Sluigi		case TOK_MAC:
5587102087Sluigi			if (add_mac(cmd, ac, av)) {
5588102087Sluigi				ac -= 2; av += 2;
5589102087Sluigi			}
5590102087Sluigi			break;
5591102087Sluigi
5592102087Sluigi		case TOK_MACTYPE:
5593102087Sluigi			NEED1("missing mac type");
5594102087Sluigi			if (!add_mactype(cmd, ac, *av))
5595116438Smaxim				errx(EX_DATAERR, "invalid mac type %s", *av);
5596102087Sluigi			ac--; av++;
5597102087Sluigi			break;
5598102087Sluigi
5599112250Scjc		case TOK_VERREVPATH:
5600112250Scjc			fill_cmd(cmd, O_VERREVPATH, 0, 0);
5601112250Scjc			break;
5602116919Sluigi
5603128575Sandre		case TOK_VERSRCREACH:
5604128575Sandre			fill_cmd(cmd, O_VERSRCREACH, 0, 0);
5605128575Sandre			break;
5606128575Sandre
5607133387Sandre		case TOK_ANTISPOOF:
5608133387Sandre			fill_cmd(cmd, O_ANTISPOOF, 0, 0);
5609133387Sandre			break;
5610133387Sandre
5611117241Sluigi		case TOK_IPSEC:
5612117241Sluigi			fill_cmd(cmd, O_IPSEC, 0, 0);
5613117241Sluigi			break;
5614117241Sluigi
5615145246Sbrooks		case TOK_IPV6:
5616145246Sbrooks			fill_cmd(cmd, O_IP6, 0, 0);
5617145246Sbrooks			break;
5618145246Sbrooks
5619146894Smlaier		case TOK_IPV4:
5620146894Smlaier			fill_cmd(cmd, O_IP4, 0, 0);
5621146894Smlaier			break;
5622146894Smlaier
5623145246Sbrooks		case TOK_EXT6HDR:
5624145246Sbrooks			fill_ext6hdr( cmd, *av );
5625145246Sbrooks			ac--; av++;
5626145246Sbrooks			break;
5627145246Sbrooks
5628145246Sbrooks		case TOK_FLOWID:
5629145246Sbrooks			if (proto != IPPROTO_IPV6 )
5630145246Sbrooks				errx( EX_USAGE, "flow-id filter is active "
5631145246Sbrooks				    "only for ipv6 protocol\n");
5632145246Sbrooks			fill_flow6( (ipfw_insn_u32 *) cmd, *av );
5633145246Sbrooks			ac--; av++;
5634145246Sbrooks			break;
5635145246Sbrooks
5636117469Sluigi		case TOK_COMMENT:
5637117469Sluigi			fill_comment(cmd, ac, av);
5638117469Sluigi			av += ac;
5639117469Sluigi			ac = 0;
5640117469Sluigi			break;
5641117469Sluigi
5642158879Soleg		case TOK_TAGGED:
5643159636Soleg			if (ac > 0 && strpbrk(*av, "-,")) {
5644158879Soleg				if (!add_ports(cmd, *av, 0, O_TAGGED))
5645159636Soleg					errx(EX_DATAERR, "tagged: invalid tag"
5646159636Soleg					    " list: %s", *av);
5647158879Soleg			}
5648159636Soleg			else {
5649159636Soleg				uint16_t tag;
5650159636Soleg
5651159636Soleg				GET_UINT_ARG(tag, 1, 65534, TOK_TAGGED,
5652159636Soleg				    rule_options);
5653159636Soleg				fill_cmd(cmd, O_TAGGED, 0, tag);
5654159636Soleg			}
5655158879Soleg			ac--; av++;
5656158879Soleg			break;
5657158879Soleg
5658178888Sjulian		case TOK_FIB:
5659178888Sjulian			NEED1("fib requires fib number");
5660178888Sjulian			fill_cmd(cmd, O_FIB, 0, strtoul(*av, NULL, 0));
5661178888Sjulian			ac--; av++;
5662178888Sjulian			break;
5663178888Sjulian
566498943Sluigi		default:
566598943Sluigi			errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
566698943Sluigi		}
566798943Sluigi		if (F_LEN(cmd) > 0) {	/* prepare to advance */
566898943Sluigi			prev = cmd;
566998943Sluigi			cmd = next_cmd(cmd);
567098943Sluigi		}
567198943Sluigi	}
567298943Sluigi
567398943Sluigidone:
567498943Sluigi	/*
567598943Sluigi	 * Now copy stuff into the rule.
567698943Sluigi	 * If we have a keep-state option, the first instruction
567798943Sluigi	 * must be a PROBE_STATE (which is generated here).
567898943Sluigi	 * If we have a LOG option, it was stored as the first command,
567998943Sluigi	 * and now must be moved to the top of the action part.
568098943Sluigi	 */
568198943Sluigi	dst = (ipfw_insn *)rule->cmd;
568298943Sluigi
568398943Sluigi	/*
5684107289Sluigi	 * First thing to write into the command stream is the match probability.
5685107289Sluigi	 */
5686107289Sluigi	if (match_prob != 1) { /* 1 means always match */
5687107289Sluigi		dst->opcode = O_PROB;
5688107289Sluigi		dst->len = 2;
5689107289Sluigi		*((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff);
5690107289Sluigi		dst += dst->len;
5691107289Sluigi	}
5692107289Sluigi
5693107289Sluigi	/*
569498943Sluigi	 * generate O_PROBE_STATE if necessary
569598943Sluigi	 */
5696101116Sluigi	if (have_state && have_state->opcode != O_CHECK_STATE) {
569798943Sluigi		fill_cmd(dst, O_PROBE_STATE, 0, 0);
569898943Sluigi		dst = next_cmd(dst);
569998943Sluigi	}
5700158879Soleg
5701158879Soleg	/* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */
570298943Sluigi	for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
570398943Sluigi		i = F_LEN(src);
570498943Sluigi
5705101116Sluigi		switch (src->opcode) {
5706101116Sluigi		case O_LOG:
5707101116Sluigi		case O_KEEP_STATE:
5708101116Sluigi		case O_LIMIT:
5709136071Sgreen		case O_ALTQ:
5710158879Soleg		case O_TAG:
5711101116Sluigi			break;
5712101116Sluigi		default:
5713117328Sluigi			bcopy(src, dst, i * sizeof(uint32_t));
571498943Sluigi			dst += i;
571598943Sluigi		}
571698943Sluigi	}
571798943Sluigi
571898943Sluigi	/*
5719101116Sluigi	 * put back the have_state command as last opcode
5720101116Sluigi	 */
5721101295Sluigi	if (have_state && have_state->opcode != O_CHECK_STATE) {
5722101116Sluigi		i = F_LEN(have_state);
5723117328Sluigi		bcopy(have_state, dst, i * sizeof(uint32_t));
5724101116Sluigi		dst += i;
5725101116Sluigi	}
5726101116Sluigi	/*
572798943Sluigi	 * start action section
572898943Sluigi	 */
572998943Sluigi	rule->act_ofs = dst - rule->cmd;
573098943Sluigi
5731158879Soleg	/* put back O_LOG, O_ALTQ, O_TAG if necessary */
5732136071Sgreen	if (have_log) {
5733136071Sgreen		i = F_LEN(have_log);
5734136071Sgreen		bcopy(have_log, dst, i * sizeof(uint32_t));
573598943Sluigi		dst += i;
573698943Sluigi	}
5737136071Sgreen	if (have_altq) {
5738136071Sgreen		i = F_LEN(have_altq);
5739136071Sgreen		bcopy(have_altq, dst, i * sizeof(uint32_t));
5740136071Sgreen		dst += i;
5741136071Sgreen	}
5742158879Soleg	if (have_tag) {
5743158879Soleg		i = F_LEN(have_tag);
5744158879Soleg		bcopy(have_tag, dst, i * sizeof(uint32_t));
5745158879Soleg		dst += i;
5746158879Soleg	}
574798943Sluigi	/*
574898943Sluigi	 * copy all other actions
574998943Sluigi	 */
575098943Sluigi	for (src = (ipfw_insn *)actbuf; src != action; src += i) {
575198943Sluigi		i = F_LEN(src);
5752117328Sluigi		bcopy(src, dst, i * sizeof(uint32_t));
575398943Sluigi		dst += i;
575498943Sluigi	}
575598943Sluigi
5756117328Sluigi	rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd);
5757117469Sluigi	i = (char *)dst - (char *)rule;
5758119740Stmm	if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1)
575998943Sluigi		err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
576098943Sluigi	if (!do_quiet)
5761117469Sluigi		show_ipfw(rule, 0, 0);
576298943Sluigi}
576398943Sluigi
576498943Sluigistatic void
5765117328Sluigizero(int ac, char *av[], int optname /* IP_FW_ZERO or IP_FW_RESETLOG */)
576698943Sluigi{
5767170923Smaxim	uint32_t arg, saved_arg;
576898943Sluigi	int failed = EX_OK;
5769117469Sluigi	char const *name = optname == IP_FW_ZERO ?  "ZERO" : "RESETLOG";
5770170923Smaxim	char const *errstr;
577198943Sluigi
577298943Sluigi	av++; ac--;
577398943Sluigi
577498943Sluigi	if (!ac) {
577598943Sluigi		/* clear all entries */
5776117328Sluigi		if (do_cmd(optname, NULL, 0) < 0)
5777117328Sluigi			err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name);
577898943Sluigi		if (!do_quiet)
5779117328Sluigi			printf("%s.\n", optname == IP_FW_ZERO ?
5780117328Sluigi			    "Accounting cleared":"Logging counts reset");
578198943Sluigi
578298943Sluigi		return;
578398943Sluigi	}
578498943Sluigi
578598943Sluigi	while (ac) {
578698943Sluigi		/* Rule number */
578798943Sluigi		if (isdigit(**av)) {
5788170923Smaxim			arg = strtonum(*av, 0, 0xffff, &errstr);
5789170923Smaxim			if (errstr)
5790170923Smaxim				errx(EX_DATAERR,
5791170923Smaxim				    "invalid rule number %s\n", *av);
5792170923Smaxim			saved_arg = arg;
5793170923Smaxim			if (use_set)
5794170923Smaxim				arg |= (1 << 24) | ((use_set - 1) << 16);
579598943Sluigi			av++;
579698943Sluigi			ac--;
5797170923Smaxim			if (do_cmd(optname, &arg, sizeof(arg))) {
5798117328Sluigi				warn("rule %u: setsockopt(IP_FW_%s)",
5799170923Smaxim				    saved_arg, name);
580098943Sluigi				failed = EX_UNAVAILABLE;
580198943Sluigi			} else if (!do_quiet)
5802170923Smaxim				printf("Entry %d %s.\n", saved_arg,
5803117328Sluigi				    optname == IP_FW_ZERO ?
5804117328Sluigi					"cleared" : "logging count reset");
580598943Sluigi		} else {
580698943Sluigi			errx(EX_USAGE, "invalid rule number ``%s''", *av);
580798943Sluigi		}
580898943Sluigi	}
580998943Sluigi	if (failed != EX_OK)
581098943Sluigi		exit(failed);
581198943Sluigi}
581298943Sluigi
581398943Sluigistatic void
5814117544Sluigiflush(int force)
581598943Sluigi{
581698943Sluigi	int cmd = do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH;
581798943Sluigi
5818117544Sluigi	if (!force && !do_quiet) { /* need to ask user */
581998943Sluigi		int c;
582098943Sluigi
582198943Sluigi		printf("Are you sure? [yn] ");
582298943Sluigi		fflush(stdout);
582398943Sluigi		do {
582498943Sluigi			c = toupper(getc(stdin));
582598943Sluigi			while (c != '\n' && getc(stdin) != '\n')
582698943Sluigi				if (feof(stdin))
582798943Sluigi					return; /* and do not flush */
582898943Sluigi		} while (c != 'Y' && c != 'N');
582998943Sluigi		printf("\n");
583098943Sluigi		if (c == 'N')	/* user said no */
583198943Sluigi			return;
583298943Sluigi	}
5833170923Smaxim	/* `ipfw set N flush` - is the same that `ipfw delete set N` */
5834170923Smaxim	if (use_set) {
5835170923Smaxim		uint32_t arg = ((use_set - 1) & 0xffff) | (1 << 24);
5836170923Smaxim		if (do_cmd(IP_FW_DEL, &arg, sizeof(arg)) < 0)
5837170923Smaxim			err(EX_UNAVAILABLE, "setsockopt(IP_FW_DEL)");
5838170923Smaxim	} else if (do_cmd(cmd, NULL, 0) < 0)
583998943Sluigi		err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
584098943Sluigi		    do_pipe ? "DUMMYNET" : "FW");
584198943Sluigi	if (!do_quiet)
584298943Sluigi		printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules");
584398943Sluigi}
584498943Sluigi
5845117469Sluigi/*
5846117544Sluigi * Free a the (locally allocated) copy of command line arguments.
5847117469Sluigi */
5848117544Sluigistatic void
5849117544Sluigifree_args(int ac, char **av)
5850117544Sluigi{
5851117544Sluigi	int i;
5852117544Sluigi
5853117544Sluigi	for (i=0; i < ac; i++)
5854117544Sluigi		free(av[i]);
5855117544Sluigi	free(av);
5856117544Sluigi}
5857117544Sluigi
5858117544Sluigi/*
5859130281Sru * This one handles all table-related commands
5860130281Sru * 	ipfw table N add addr[/masklen] [value]
5861130281Sru * 	ipfw table N delete addr[/masklen]
5862130281Sru * 	ipfw table N flush
5863130281Sru * 	ipfw table N list
5864130281Sru */
5865130281Srustatic void
5866130281Srutable_handler(int ac, char *av[])
5867130281Sru{
5868130281Sru	ipfw_table_entry ent;
5869130281Sru	ipfw_table *tbl;
5870130281Sru	int do_add;
5871130281Sru	char *p;
5872130281Sru	socklen_t l;
5873130281Sru	uint32_t a;
5874130281Sru
5875130281Sru	ac--; av++;
5876130281Sru	if (ac && isdigit(**av)) {
5877130281Sru		ent.tbl = atoi(*av);
5878130281Sru		ac--; av++;
5879130281Sru	} else
5880130281Sru		errx(EX_USAGE, "table number required");
5881130281Sru	NEED1("table needs command");
5882140271Sbrooks	if (_substrcmp(*av, "add") == 0 ||
5883140271Sbrooks	    _substrcmp(*av, "delete") == 0) {
5884130281Sru		do_add = **av == 'a';
5885130281Sru		ac--; av++;
5886130281Sru		if (!ac)
5887130281Sru			errx(EX_USAGE, "IP address required");
5888130281Sru		p = strchr(*av, '/');
5889130281Sru		if (p) {
5890130281Sru			*p++ = '\0';
5891130281Sru			ent.masklen = atoi(p);
5892130281Sru			if (ent.masklen > 32)
5893130281Sru				errx(EX_DATAERR, "bad width ``%s''", p);
5894130281Sru		} else
5895130281Sru			ent.masklen = 32;
5896130281Sru		if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0)
5897130298Sru			errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
5898130281Sru		ac--; av++;
5899161424Sjulian		if (do_add && ac) {
5900161424Sjulian			unsigned int tval;
5901161424Sjulian			/* isdigit is a bit of a hack here.. */
5902161424Sjulian			if (strchr(*av, (int)'.') == NULL && isdigit(**av))  {
5903161424Sjulian				ent.value = strtoul(*av, NULL, 0);
5904161424Sjulian			} else {
5905161424Sjulian		        	if (lookup_host(*av, (struct in_addr *)&tval) == 0) {
5906161424Sjulian					/* The value must be stored in host order	 *
5907161424Sjulian					 * so that the values < 65k can be distinguished */
5908161424Sjulian		       			ent.value = ntohl(tval);
5909161424Sjulian				} else {
5910161424Sjulian					errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
5911161424Sjulian				}
5912161424Sjulian			}
5913161424Sjulian		} else
5914130281Sru			ent.value = 0;
5915130281Sru		if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL,
5916157335Sjulian		    &ent, sizeof(ent)) < 0) {
5917155639Sjulian			/* If running silent, don't bomb out on these errors. */
5918155639Sjulian			if (!(do_quiet && (errno == (do_add ? EEXIST : ESRCH))))
5919155639Sjulian				err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
5920155639Sjulian				    do_add ? "ADD" : "DEL");
5921155639Sjulian			/* In silent mode, react to a failed add by deleting */
5922157332Sjulian			if (do_add) {
5923155639Sjulian				do_cmd(IP_FW_TABLE_DEL, &ent, sizeof(ent));
5924155639Sjulian				if (do_cmd(IP_FW_TABLE_ADD,
5925155639Sjulian				    &ent, sizeof(ent)) < 0)
5926155639Sjulian					err(EX_OSERR,
5927155639Sjulian				            "setsockopt(IP_FW_TABLE_ADD)");
5928157332Sjulian			}
5929157335Sjulian		}
5930140271Sbrooks	} else if (_substrcmp(*av, "flush") == 0) {
5931130281Sru		if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl, sizeof(ent.tbl)) < 0)
5932130281Sru			err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
5933140271Sbrooks	} else if (_substrcmp(*av, "list") == 0) {
5934130281Sru		a = ent.tbl;
5935130281Sru		l = sizeof(a);
5936130281Sru		if (do_cmd(IP_FW_TABLE_GETSIZE, &a, (uintptr_t)&l) < 0)
5937130281Sru			err(EX_OSERR, "getsockopt(IP_FW_TABLE_GETSIZE)");
5938130281Sru		l = sizeof(*tbl) + a * sizeof(ipfw_table_entry);
5939130281Sru		tbl = malloc(l);
5940130281Sru		if (tbl == NULL)
5941130281Sru			err(EX_OSERR, "malloc");
5942130281Sru		tbl->tbl = ent.tbl;
5943130281Sru		if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0)
5944130281Sru			err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)");
5945130281Sru		for (a = 0; a < tbl->cnt; a++) {
5946161424Sjulian			unsigned int tval;
5947161424Sjulian			tval = tbl->ent[a].value;
5948176391Sjulian			if (do_value_as_ip) {
5949161424Sjulian			    char tbuf[128];
5950161456Sjulian			    strncpy(tbuf, inet_ntoa(*(struct in_addr *)
5951161456Sjulian				&tbl->ent[a].addr), 127);
5952176391Sjulian			    /* inet_ntoa expects network order */
5953161424Sjulian			    tval = htonl(tval);
5954161456Sjulian			    printf("%s/%u %s\n", tbuf, tbl->ent[a].masklen,
5955161456Sjulian			        inet_ntoa(*(struct in_addr *)&tval));
5956161424Sjulian			} else {
5957161424Sjulian			    printf("%s/%u %u\n",
5958161456Sjulian			        inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
5959176391Sjulian			        tbl->ent[a].masklen, tval);
5960161424Sjulian			}
5961130281Sru		}
5962130281Sru	} else
5963130281Sru		errx(EX_USAGE, "invalid table command %s", *av);
5964130281Sru}
5965130281Sru
5966165648Spisostatic void
5967165648Spisoshow_nat(int ac, char **av) {
5968165648Spiso	struct cfg_nat *n;
5969165648Spiso	struct cfg_redir *e;
5970165648Spiso	int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
5971176393Spiso	int nat_cnt, redir_cnt, r;
5972165648Spiso	uint8_t *data, *p;
5973165648Spiso	char **lav, *endptr;
5974165648Spiso
5975165648Spiso	do_rule = 0;
5976165648Spiso	nalloc = 1024;
5977165648Spiso	size = 0;
5978165648Spiso	data = NULL;
5979176445Spiso	frule = 0;
5980176445Spiso	lrule = 65535; /* max ipfw rule number */
5981165648Spiso	ac--; av++;
5982165648Spiso
5983165648Spiso	/* Parse parameters. */
5984165648Spiso	for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
5985165648Spiso		if (!strncmp(av[0], "config", strlen(av[0]))) {
5986165648Spiso			cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
5987165648Spiso			continue;
5988165648Spiso		}
5989165648Spiso		/* Convert command line rule #. */
5990165648Spiso		frule = lrule = strtoul(av[0], &endptr, 10);
5991165648Spiso		if (*endptr == '-')
5992165648Spiso			lrule = strtoul(endptr+1, &endptr, 10);
5993165648Spiso		if (lrule == 0)
5994165648Spiso			err(EX_USAGE, "invalid rule number: %s", av[0]);
5995165648Spiso		do_rule = 1;
5996165648Spiso	}
5997165648Spiso
5998165648Spiso	nbytes = nalloc;
5999165648Spiso	while (nbytes >= nalloc) {
6000165648Spiso		nalloc = nalloc * 2;
6001165648Spiso		nbytes = nalloc;
6002165648Spiso		if ((data = realloc(data, nbytes)) == NULL)
6003165648Spiso			err(EX_OSERR, "realloc");
6004165648Spiso		if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
6005165648Spiso			err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
6006165648Spiso			    (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
6007165648Spiso	}
6008165648Spiso	if (nbytes == 0)
6009176445Spiso		exit(0);
6010165648Spiso	if (do_cfg) {
6011165648Spiso		nat_cnt = *((int *)data);
6012165648Spiso		for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
6013165648Spiso			n = (struct cfg_nat *)&data[i];
6014176445Spiso			if (frule <= n->id && lrule >= n->id)
6015176445Spiso				print_nat_config(&data[i]);
6016165648Spiso			i += sizeof(struct cfg_nat);
6017176393Spiso			for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
6018176393Spiso				e = (struct cfg_redir *)&data[i];
6019165648Spiso				i += sizeof(struct cfg_redir) + e->spool_cnt *
6020165648Spiso				    sizeof(struct cfg_spool);
6021176393Spiso			}
6022165648Spiso		}
6023165648Spiso	} else {
6024165648Spiso		for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
6025165648Spiso			p = &data[i];
6026165648Spiso			if (p == data + nbytes)
6027165648Spiso				break;
6028165648Spiso			bcopy(p, &r, sizeof(int));
6029165648Spiso			if (do_rule) {
6030165648Spiso				if (!(frule <= r && lrule >= r))
6031165648Spiso					continue;
6032165648Spiso			}
6033165648Spiso			printf("nat %u: %s\n", r, p+sizeof(int));
6034165648Spiso		}
6035165648Spiso	}
6036165648Spiso}
6037165648Spiso
6038130281Sru/*
6039117544Sluigi * Called with the arguments (excluding program name).
6040117544Sluigi * Returns 0 if successful, 1 if empty command, errx() in case of errors.
6041117544Sluigi */
604298943Sluigistatic int
6043117328Sluigiipfw_main(int oldac, char **oldav)
604498943Sluigi{
6045117469Sluigi	int ch, ac, save_ac;
6046170923Smaxim	const char *errstr;
6047117469Sluigi	char **av, **save_av;
6048117469Sluigi	int do_acct = 0;		/* Show packet/byte count */
6049117469Sluigi
6050117469Sluigi#define WHITESP		" \t\f\v\n\r"
6051117544Sluigi	if (oldac == 0)
6052117544Sluigi		return 1;
6053117544Sluigi	else if (oldac == 1) {
6054117328Sluigi		/*
6055117328Sluigi		 * If we are called with a single string, try to split it into
6056117328Sluigi		 * arguments for subsequent parsing.
6057117328Sluigi		 * But first, remove spaces after a ',', by copying the string
6058117328Sluigi		 * in-place.
6059117328Sluigi		 */
6060117469Sluigi		char *arg = oldav[0];	/* The string... */
6061117328Sluigi		int l = strlen(arg);
6062117328Sluigi		int copy = 0;		/* 1 if we need to copy, 0 otherwise */
6063117328Sluigi		int i, j;
6064117469Sluigi		for (i = j = 0; i < l; i++) {
6065117469Sluigi			if (arg[i] == '#')	/* comment marker */
6066117469Sluigi				break;
6067117328Sluigi			if (copy) {
6068117328Sluigi				arg[j++] = arg[i];
6069117469Sluigi				copy = !index("," WHITESP, arg[i]);
6070117328Sluigi			} else {
6071117469Sluigi				copy = !index(WHITESP, arg[i]);
6072117328Sluigi				if (copy)
6073117328Sluigi					arg[j++] = arg[i];
6074117328Sluigi			}
6075117469Sluigi		}
6076117328Sluigi		if (!copy && j > 0)	/* last char was a 'blank', remove it */
6077117328Sluigi			j--;
6078117328Sluigi		l = j;			/* the new argument length */
6079117328Sluigi		arg[j++] = '\0';
6080117469Sluigi		if (l == 0)		/* empty string! */
6081117544Sluigi			return 1;
6082117328Sluigi
6083117328Sluigi		/*
6084117328Sluigi		 * First, count number of arguments. Because of the previous
6085117469Sluigi		 * processing, this is just the number of blanks plus 1.
6086117328Sluigi		 */
6087117469Sluigi		for (i = 0, ac = 1; i < l; i++)
6088117469Sluigi			if (index(WHITESP, arg[i]) != NULL)
6089117328Sluigi				ac++;
6090117328Sluigi
6091117328Sluigi		av = calloc(ac, sizeof(char *));
6092117328Sluigi
6093117328Sluigi		/*
6094117328Sluigi		 * Second, copy arguments from cmd[] to av[]. For each one,
6095117328Sluigi		 * j is the initial character, i is the one past the end.
6096117328Sluigi		 */
6097117469Sluigi		for (ac = 0, i = j = 0; i < l; i++)
6098117469Sluigi			if (index(WHITESP, arg[i]) != NULL || i == l-1) {
6099117328Sluigi				if (i == l-1)
6100117328Sluigi					i++;
6101117328Sluigi				av[ac] = calloc(i-j+1, 1);
6102117328Sluigi				bcopy(arg+j, av[ac], i-j);
6103117328Sluigi				ac++;
6104117328Sluigi				j = i + 1;
6105117328Sluigi			}
6106117328Sluigi	} else {
6107117328Sluigi		/*
6108117328Sluigi		 * If an argument ends with ',' join with the next one.
6109117328Sluigi		 */
6110117328Sluigi		int first, i, l;
6111117328Sluigi
6112117328Sluigi		av = calloc(oldac, sizeof(char *));
6113117328Sluigi		for (first = i = ac = 0, l = 0; i < oldac; i++) {
6114117328Sluigi			char *arg = oldav[i];
6115117328Sluigi			int k = strlen(arg);
6116117328Sluigi
6117117328Sluigi			l += k;
6118117328Sluigi			if (arg[k-1] != ',' || i == oldac-1) {
6119117328Sluigi				/* Time to copy. */
6120117328Sluigi				av[ac] = calloc(l+1, 1);
6121117328Sluigi				for (l=0; first <= i; first++) {
6122117328Sluigi					strcat(av[ac]+l, oldav[first]);
6123117328Sluigi					l += strlen(oldav[first]);
6124117328Sluigi				}
6125117328Sluigi				ac++;
6126117328Sluigi				l = 0;
6127117328Sluigi				first = i+1;
6128117328Sluigi			}
6129117328Sluigi		}
6130117328Sluigi	}
6131117328Sluigi
613298943Sluigi	/* Set the force flag for non-interactive processes */
6133123804Smaxim	if (!do_force)
6134123804Smaxim		do_force = !isatty(STDIN_FILENO);
613598943Sluigi
6136117469Sluigi	/* Save arguments for final freeing of memory. */
6137117469Sluigi	save_ac = ac;
6138117469Sluigi	save_av = av;
6139117469Sluigi
6140117469Sluigi	optind = optreset = 0;
6141176391Sjulian	while ((ch = getopt(ac, av, "abcdefhinNqs:STtv")) != -1)
614298943Sluigi		switch (ch) {
614398943Sluigi		case 'a':
614498943Sluigi			do_acct = 1;
614598943Sluigi			break;
6146117328Sluigi
6147123495Sluigi		case 'b':
6148123495Sluigi			comment_only = 1;
6149123495Sluigi			do_compact = 1;
6150123495Sluigi			break;
6151123495Sluigi
6152102098Sluigi		case 'c':
6153102098Sluigi			do_compact = 1;
6154102098Sluigi			break;
6155117328Sluigi
615698943Sluigi		case 'd':
615798943Sluigi			do_dynamic = 1;
615898943Sluigi			break;
6159117328Sluigi
616098943Sluigi		case 'e':
616198943Sluigi			do_expired = 1;
616298943Sluigi			break;
6163117328Sluigi
616498943Sluigi		case 'f':
616598943Sluigi			do_force = 1;
616698943Sluigi			break;
6167117328Sluigi
6168117328Sluigi		case 'h': /* help */
6169117544Sluigi			free_args(save_ac, save_av);
6170117328Sluigi			help();
6171117328Sluigi			break;	/* NOTREACHED */
6172117328Sluigi
6173176391Sjulian		case 'i':
6174176391Sjulian			do_value_as_ip = 1;
6175176391Sjulian			break;
6176176391Sjulian
6177117328Sluigi		case 'n':
6178117328Sluigi			test_only = 1;
6179117328Sluigi			break;
6180117328Sluigi
618198943Sluigi		case 'N':
618298943Sluigi			do_resolv = 1;
618398943Sluigi			break;
6184117328Sluigi
618598943Sluigi		case 'q':
618698943Sluigi			do_quiet = 1;
618798943Sluigi			break;
6188117328Sluigi
6189117328Sluigi		case 's': /* sort */
6190117328Sluigi			do_sort = atoi(optarg);
6191117328Sluigi			break;
6192117328Sluigi
6193101628Sluigi		case 'S':
6194101628Sluigi			show_sets = 1;
6195101628Sluigi			break;
6196117328Sluigi
619798943Sluigi		case 't':
619898943Sluigi			do_time = 1;
619998943Sluigi			break;
6200117328Sluigi
6201117472Sluigi		case 'T':
6202117472Sluigi			do_time = 2;	/* numeric timestamp */
6203117472Sluigi			break;
6204117472Sluigi
620598943Sluigi		case 'v': /* verbose */
6206117328Sluigi			verbose = 1;
620798943Sluigi			break;
6208117328Sluigi
620998943Sluigi		default:
6210117544Sluigi			free_args(save_ac, save_av);
6211117544Sluigi			return 1;
621298943Sluigi		}
621398943Sluigi
621498943Sluigi	ac -= optind;
621598943Sluigi	av += optind;
621698943Sluigi	NEED1("bad arguments, for usage summary ``ipfw''");
621798943Sluigi
621898943Sluigi	/*
6219117544Sluigi	 * An undocumented behaviour of ipfw1 was to allow rule numbers first,
6220117544Sluigi	 * e.g. "100 add allow ..." instead of "add 100 allow ...".
6221117544Sluigi	 * In case, swap first and second argument to get the normal form.
6222117544Sluigi	 */
6223117544Sluigi	if (ac > 1 && isdigit(*av[0])) {
6224117544Sluigi		char *p = av[0];
6225117544Sluigi
6226117544Sluigi		av[0] = av[1];
6227117544Sluigi		av[1] = p;
6228117544Sluigi	}
6229117544Sluigi
6230117544Sluigi	/*
6231165648Spiso	 * Optional: pipe, queue or nat.
623298943Sluigi	 */
6233165648Spiso	do_nat = 0;
6234117821Smaxim	do_pipe = 0;
6235165648Spiso	if (!strncmp(*av, "nat", strlen(*av)))
6236165648Spiso 	        do_nat = 1;
6237165648Spiso 	else if (!strncmp(*av, "pipe", strlen(*av)))
623898943Sluigi		do_pipe = 1;
6239140271Sbrooks	else if (_substrcmp(*av, "queue") == 0)
624098943Sluigi		do_pipe = 2;
6241170923Smaxim	else if (!strncmp(*av, "set", strlen(*av))) {
6242170923Smaxim		if (ac > 1 && isdigit(av[1][0])) {
6243170923Smaxim			use_set = strtonum(av[1], 0, RESVD_SET, &errstr);
6244170923Smaxim			if (errstr)
6245170923Smaxim				errx(EX_DATAERR,
6246170923Smaxim				    "invalid set number %s\n", av[1]);
6247170923Smaxim			ac -= 2; av += 2; use_set++;
6248170923Smaxim		}
6249170923Smaxim	}
6250170923Smaxim
6251165648Spiso	if (do_pipe || do_nat) {
625298943Sluigi		ac--;
625398943Sluigi		av++;
625498943Sluigi	}
625598943Sluigi	NEED1("missing command");
625698943Sluigi
625798943Sluigi	/*
6258165648Spiso	 * For pipes, queues and nats we normally say 'nat|pipe NN config'
6259165648Spiso	 * but the code is easier to parse as 'nat|pipe config NN'
626098943Sluigi	 * so we swap the two arguments.
626198943Sluigi	 */
6262165648Spiso	if ((do_pipe || do_nat) && ac > 1 && isdigit(*av[0])) {
626398943Sluigi		char *p = av[0];
6264117544Sluigi
626598943Sluigi		av[0] = av[1];
626698943Sluigi		av[1] = p;
626798943Sluigi	}
6268117328Sluigi
6269170923Smaxim	int try_next = 0;
6270170923Smaxim	if (use_set == 0) {
6271170923Smaxim		if (_substrcmp(*av, "add") == 0)
6272170923Smaxim			add(ac, av);
6273170923Smaxim		else if (do_nat && _substrcmp(*av, "show") == 0)
6274170923Smaxim 			show_nat(ac, av);
6275170923Smaxim		else if (do_pipe && _substrcmp(*av, "config") == 0)
6276170923Smaxim			config_pipe(ac, av);
6277170923Smaxim		else if (do_nat && _substrcmp(*av, "config") == 0)
6278170923Smaxim 			config_nat(ac, av);
6279173080Smaxim		else if (_substrcmp(*av, "set") == 0)
6280173080Smaxim			sets_handler(ac, av);
6281173080Smaxim		else if (_substrcmp(*av, "table") == 0)
6282173080Smaxim			table_handler(ac, av);
6283173080Smaxim		else if (_substrcmp(*av, "enable") == 0)
6284173080Smaxim			sysctl_handler(ac, av, 1);
6285173080Smaxim		else if (_substrcmp(*av, "disable") == 0)
6286173080Smaxim			sysctl_handler(ac, av, 0);
6287173080Smaxim		else
6288173080Smaxim			try_next = 1;
6289170923Smaxim	}
6290117469Sluigi
6291170923Smaxim	if (use_set || try_next) {
6292170923Smaxim		if (_substrcmp(*av, "delete") == 0)
6293170923Smaxim			delete(ac, av);
6294170923Smaxim		else if (_substrcmp(*av, "flush") == 0)
6295170923Smaxim			flush(do_force);
6296170923Smaxim		else if (_substrcmp(*av, "zero") == 0)
6297170923Smaxim			zero(ac, av, IP_FW_ZERO);
6298170923Smaxim		else if (_substrcmp(*av, "resetlog") == 0)
6299170923Smaxim			zero(ac, av, IP_FW_RESETLOG);
6300170923Smaxim		else if (_substrcmp(*av, "print") == 0 ||
6301170923Smaxim		         _substrcmp(*av, "list") == 0)
6302170923Smaxim			list(ac, av, do_acct);
6303170923Smaxim		else if (_substrcmp(*av, "show") == 0)
6304170923Smaxim			list(ac, av, 1 /* show counters */);
6305170923Smaxim		else
6306170923Smaxim			errx(EX_USAGE, "bad command `%s'", *av);
6307170923Smaxim	}
6308170923Smaxim
6309117469Sluigi	/* Free memory allocated in the argument parsing. */
6310117544Sluigi	free_args(save_ac, save_av);
631198943Sluigi	return 0;
631298943Sluigi}
631398943Sluigi
631498943Sluigi
631598943Sluigistatic void
6316106505Smaximipfw_readfile(int ac, char *av[])
631798943Sluigi{
631898943Sluigi#define MAX_ARGS	32
631998943Sluigi	char	buf[BUFSIZ];
6320117469Sluigi	char	*cmd = NULL, *filename = av[ac-1];
6321117469Sluigi	int	c, lineno=0;
632298943Sluigi	FILE	*f = NULL;
632398943Sluigi	pid_t	preproc = 0;
632498943Sluigi
6325117469Sluigi	filename = av[ac-1];
6326117469Sluigi
6327123804Smaxim	while ((c = getopt(ac, av, "cfNnp:qS")) != -1) {
632898943Sluigi		switch(c) {
6329117469Sluigi		case 'c':
6330117469Sluigi			do_compact = 1;
6331117469Sluigi			break;
6332117469Sluigi
6333123804Smaxim		case 'f':
6334123804Smaxim			do_force = 1;
6335123804Smaxim			break;
6336123804Smaxim
6337117469Sluigi		case 'N':
6338117469Sluigi			do_resolv = 1;
6339117469Sluigi			break;
6340117469Sluigi
6341117328Sluigi		case 'n':
6342117328Sluigi			test_only = 1;
6343117328Sluigi			break;
6344117328Sluigi
634598943Sluigi		case 'p':
634698943Sluigi			cmd = optarg;
6347117469Sluigi			/*
6348117469Sluigi			 * Skip previous args and delete last one, so we
6349117469Sluigi			 * pass all but the last argument to the preprocessor
6350117469Sluigi			 * via av[optind-1]
6351117469Sluigi			 */
6352117469Sluigi			av += optind - 1;
6353117469Sluigi			ac -= optind - 1;
6354162773Smaxim			if (ac < 2)
6355162773Smaxim				errx(EX_USAGE, "no filename argument");
6356117469Sluigi			av[ac-1] = NULL;
6357117469Sluigi			fprintf(stderr, "command is %s\n", av[0]);
635898943Sluigi			break;
635998943Sluigi
636098943Sluigi		case 'q':
6361117469Sluigi			do_quiet = 1;
636298943Sluigi			break;
636398943Sluigi
6364117469Sluigi		case 'S':
6365117469Sluigi			show_sets = 1;
6366117469Sluigi			break;
6367117469Sluigi
636898943Sluigi		default:
636998943Sluigi			errx(EX_USAGE, "bad arguments, for usage"
637098943Sluigi			     " summary ``ipfw''");
637198943Sluigi		}
637298943Sluigi
6373117469Sluigi		if (cmd != NULL)
6374108231Skbyanc			break;
6375108231Skbyanc	}
6376108231Skbyanc
6377117469Sluigi	if (cmd == NULL && ac != optind + 1) {
6378117469Sluigi		fprintf(stderr, "ac %d, optind %d\n", ac, optind);
6379117469Sluigi		errx(EX_USAGE, "extraneous filename arguments");
6380108231Skbyanc	}
6381108231Skbyanc
6382117469Sluigi	if ((f = fopen(filename, "r")) == NULL)
6383117469Sluigi		err(EX_UNAVAILABLE, "fopen: %s", filename);
638498943Sluigi
6385117469Sluigi	if (cmd != NULL) {			/* pipe through preprocessor */
638698943Sluigi		int pipedes[2];
638798943Sluigi
638898943Sluigi		if (pipe(pipedes) == -1)
638998943Sluigi			err(EX_OSERR, "cannot create pipe");
639098943Sluigi
6391117469Sluigi		preproc = fork();
6392117469Sluigi		if (preproc == -1)
639398943Sluigi			err(EX_OSERR, "cannot fork");
639498943Sluigi
6395117577Sluigi		if (preproc == 0) {
6396117469Sluigi			/*
6397117469Sluigi			 * Child, will run the preprocessor with the
6398117469Sluigi			 * file on stdin and the pipe on stdout.
6399117469Sluigi			 */
640098943Sluigi			if (dup2(fileno(f), 0) == -1
640198943Sluigi			    || dup2(pipedes[1], 1) == -1)
640298943Sluigi				err(EX_OSERR, "dup2()");
640398943Sluigi			fclose(f);
640498943Sluigi			close(pipedes[1]);
640598943Sluigi			close(pipedes[0]);
6406117469Sluigi			execvp(cmd, av);
640798943Sluigi			err(EX_OSERR, "execvp(%s) failed", cmd);
6408117469Sluigi		} else { /* parent, will reopen f as the pipe */
640998943Sluigi			fclose(f);
641098943Sluigi			close(pipedes[1]);
641198943Sluigi			if ((f = fdopen(pipedes[0], "r")) == NULL) {
641298943Sluigi				int savederrno = errno;
641398943Sluigi
641498943Sluigi				(void)kill(preproc, SIGTERM);
641598943Sluigi				errno = savederrno;
641698943Sluigi				err(EX_OSERR, "fdopen()");
641798943Sluigi			}
641898943Sluigi		}
641998943Sluigi	}
642098943Sluigi
6421117469Sluigi	while (fgets(buf, BUFSIZ, f)) {		/* read commands */
6422117469Sluigi		char linename[10];
6423117469Sluigi		char *args[1];
6424117469Sluigi
642598943Sluigi		lineno++;
642698943Sluigi		sprintf(linename, "Line %d", lineno);
6427117328Sluigi		setprogname(linename); /* XXX */
6428117469Sluigi		args[0] = buf;
6429117469Sluigi		ipfw_main(1, args);
643098943Sluigi	}
643198943Sluigi	fclose(f);
6432117469Sluigi	if (cmd != NULL) {
6433117469Sluigi		int status;
6434117469Sluigi
643598943Sluigi		if (waitpid(preproc, &status, 0) == -1)
643698943Sluigi			errx(EX_OSERR, "waitpid()");
643798943Sluigi		if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
643898943Sluigi			errx(EX_UNAVAILABLE,
643998943Sluigi			    "preprocessor exited with status %d",
644098943Sluigi			    WEXITSTATUS(status));
644198943Sluigi		else if (WIFSIGNALED(status))
644298943Sluigi			errx(EX_UNAVAILABLE,
644398943Sluigi			    "preprocessor exited with signal %d",
644498943Sluigi			    WTERMSIG(status));
644598943Sluigi	}
644698943Sluigi}
644798943Sluigi
644898943Sluigiint
644998943Sluigimain(int ac, char *av[])
645098943Sluigi{
645198943Sluigi	/*
645298943Sluigi	 * If the last argument is an absolute pathname, interpret it
645398943Sluigi	 * as a file to be preprocessed.
645498943Sluigi	 */
645598943Sluigi
645698943Sluigi	if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0)
645798943Sluigi		ipfw_readfile(ac, av);
6458117544Sluigi	else {
6459117544Sluigi		if (ipfw_main(ac-1, av+1))
6460117544Sluigi			show_usage();
6461117544Sluigi	}
646298943Sluigi	return EX_OK;
646398943Sluigi}
6464