ipfw2.c revision 169424
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 169424 2007-05-09 18:31:49Z maxim $
2198943Sluigi */
2298943Sluigi
2398943Sluigi#include <sys/param.h>
2498943Sluigi#include <sys/mbuf.h>
2598943Sluigi#include <sys/socket.h>
2698943Sluigi#include <sys/sockio.h>
2798943Sluigi#include <sys/sysctl.h>
2898943Sluigi#include <sys/time.h>
2998943Sluigi#include <sys/wait.h>
30136071Sgreen#include <sys/queue.h>
3198943Sluigi
3298943Sluigi#include <ctype.h>
3398943Sluigi#include <err.h>
3498943Sluigi#include <errno.h>
3598943Sluigi#include <grp.h>
3698943Sluigi#include <limits.h>
3798943Sluigi#include <netdb.h>
3898943Sluigi#include <pwd.h>
3998943Sluigi#include <signal.h>
4098943Sluigi#include <stdio.h>
4198943Sluigi#include <stdlib.h>
4298943Sluigi#include <stdarg.h>
4398943Sluigi#include <string.h>
44117469Sluigi#include <timeconv.h>	/* XXX do we need this ? */
4598943Sluigi#include <unistd.h>
4698943Sluigi#include <sysexits.h>
47136071Sgreen#include <unistd.h>
48136071Sgreen#include <fcntl.h>
4998943Sluigi
50169424Smaxim#include <net/ethernet.h>
5198943Sluigi#include <net/if.h>
52165648Spiso#include <net/if_dl.h>
53136071Sgreen#include <net/pfvar.h>
54145246Sbrooks#include <net/route.h> /* def. of struct route */
5598943Sluigi#include <netinet/in.h>
5698943Sluigi#include <netinet/in_systm.h>
5798943Sluigi#include <netinet/ip.h>
5898943Sluigi#include <netinet/ip_icmp.h>
59145246Sbrooks#include <netinet/icmp6.h>
6098943Sluigi#include <netinet/ip_fw.h>
6198943Sluigi#include <netinet/ip_dummynet.h>
6298943Sluigi#include <netinet/tcp.h>
6398943Sluigi#include <arpa/inet.h>
64165648Spiso#include <alias.h>
6598943Sluigi
66117328Sluigiint
6798943Sluigi		do_resolv,		/* Would try to resolve all */
6898943Sluigi		do_time,		/* Show time stamps */
6998943Sluigi		do_quiet,		/* Be quiet in add and flush */
7098943Sluigi		do_pipe,		/* this cmd refers to a pipe */
71165648Spiso	        do_nat, 		/* Nat configuration. */
7298943Sluigi		do_sort,		/* field to sort results (0 = no) */
7398943Sluigi		do_dynamic,		/* display dynamic rules */
7498943Sluigi		do_expired,		/* display expired dynamic rules */
75102098Sluigi		do_compact,		/* show rules in compact mode */
76123804Smaxim		do_force,		/* do not ask for confirmation */
77101628Sluigi		show_sets,		/* display rule sets */
78117328Sluigi		test_only,		/* only check syntax */
79123495Sluigi		comment_only,		/* only print action and comment */
8098943Sluigi		verbose;
8198943Sluigi
8298943Sluigi#define	IP_MASK_ALL	0xffffffff
83130013Scsjp/*
84130013Scsjp * the following macro returns an error message if we run out of
85130013Scsjp * arguments.
86130013Scsjp */
87130013Scsjp#define NEED1(msg)      {if (!ac) errx(EX_USAGE, msg);}
8898943Sluigi
89159636Soleg#define GET_UINT_ARG(arg, min, max, tok, s_x) do {			\
90159636Soleg	if (!ac)							\
91159636Soleg		errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \
92159636Soleg	if (_substrcmp(*av, "tablearg") == 0) {				\
93159636Soleg		arg = IP_FW_TABLEARG;					\
94159636Soleg		break;							\
95159636Soleg	}								\
96159636Soleg									\
97159636Soleg	{								\
98159636Soleg	long val;							\
99159636Soleg	char *end;							\
100159636Soleg									\
101159636Soleg	val = strtol(*av, &end, 10);					\
102159636Soleg									\
103159636Soleg	if (!isdigit(**av) || *end != '\0' || (val == 0 && errno == EINVAL)) \
104159636Soleg		errx(EX_DATAERR, "%s: invalid argument: %s",		\
105159636Soleg		    match_value(s_x, tok), *av);			\
106159636Soleg									\
107159636Soleg	if (errno == ERANGE || val < min || val > max)			\
108159636Soleg		errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \
109159636Soleg		    match_value(s_x, tok), min, max, *av);		\
110159636Soleg									\
111159636Soleg	if (val == IP_FW_TABLEARG)					\
112159636Soleg		errx(EX_DATAERR, "%s: illegal argument value: %s",	\
113159636Soleg		    match_value(s_x, tok), *av);			\
114159636Soleg	arg = val;							\
115159636Soleg	}								\
116158879Soleg} while (0)
117158879Soleg
118159636Soleg#define PRINT_UINT_ARG(str, arg) do {					\
119159636Soleg	if (str != NULL)						\
120159636Soleg		printf("%s",str);					\
121159636Soleg	if (arg == IP_FW_TABLEARG)					\
122159636Soleg		printf("tablearg");					\
123159636Soleg	else								\
124159636Soleg		printf("%u", (uint32_t)arg);				\
125159636Soleg} while (0)
126159636Soleg
12798943Sluigi/*
128117328Sluigi * _s_x is a structure that stores a string <-> token pairs, used in
129117328Sluigi * various places in the parser. Entries are stored in arrays,
130117328Sluigi * with an entry with s=NULL as terminator.
131117328Sluigi * The search routines are match_token() and match_value().
132117328Sluigi * Often, an element with x=0 contains an error string.
13398943Sluigi *
13498943Sluigi */
13598943Sluigistruct _s_x {
136117469Sluigi	char const *s;
13798943Sluigi	int x;
13898943Sluigi};
13998943Sluigi
14098943Sluigistatic struct _s_x f_tcpflags[] = {
14198943Sluigi	{ "syn", TH_SYN },
14298943Sluigi	{ "fin", TH_FIN },
14398943Sluigi	{ "ack", TH_ACK },
14498943Sluigi	{ "psh", TH_PUSH },
14598943Sluigi	{ "rst", TH_RST },
14698943Sluigi	{ "urg", TH_URG },
14798943Sluigi	{ "tcp flag", 0 },
14898943Sluigi	{ NULL,	0 }
14998943Sluigi};
15098943Sluigi
15198943Sluigistatic struct _s_x f_tcpopts[] = {
15298943Sluigi	{ "mss",	IP_FW_TCPOPT_MSS },
15398943Sluigi	{ "maxseg",	IP_FW_TCPOPT_MSS },
15498943Sluigi	{ "window",	IP_FW_TCPOPT_WINDOW },
15598943Sluigi	{ "sack",	IP_FW_TCPOPT_SACK },
15698943Sluigi	{ "ts",		IP_FW_TCPOPT_TS },
15798943Sluigi	{ "timestamp",	IP_FW_TCPOPT_TS },
15898943Sluigi	{ "cc",		IP_FW_TCPOPT_CC },
15998943Sluigi	{ "tcp option",	0 },
16098943Sluigi	{ NULL,	0 }
16198943Sluigi};
16298943Sluigi
16398943Sluigi/*
16498943Sluigi * IP options span the range 0 to 255 so we need to remap them
16598943Sluigi * (though in fact only the low 5 bits are significant).
16698943Sluigi */
16798943Sluigistatic struct _s_x f_ipopts[] = {
16898943Sluigi	{ "ssrr",	IP_FW_IPOPT_SSRR},
16998943Sluigi	{ "lsrr",	IP_FW_IPOPT_LSRR},
17098943Sluigi	{ "rr",		IP_FW_IPOPT_RR},
17198943Sluigi	{ "ts",		IP_FW_IPOPT_TS},
17298943Sluigi	{ "ip option",	0 },
17398943Sluigi	{ NULL,	0 }
17498943Sluigi};
17598943Sluigi
17698943Sluigistatic struct _s_x f_iptos[] = {
17798943Sluigi	{ "lowdelay",	IPTOS_LOWDELAY},
17898943Sluigi	{ "throughput",	IPTOS_THROUGHPUT},
17998943Sluigi	{ "reliability", IPTOS_RELIABILITY},
18098943Sluigi	{ "mincost",	IPTOS_MINCOST},
18198943Sluigi	{ "congestion",	IPTOS_CE},
18298943Sluigi	{ "ecntransport", IPTOS_ECT},
18398943Sluigi	{ "ip tos option", 0},
18498943Sluigi	{ NULL,	0 }
18598943Sluigi};
18698943Sluigi
18798943Sluigistatic struct _s_x limit_masks[] = {
18898943Sluigi	{"all",		DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
18998943Sluigi	{"src-addr",	DYN_SRC_ADDR},
19098943Sluigi	{"src-port",	DYN_SRC_PORT},
19198943Sluigi	{"dst-addr",	DYN_DST_ADDR},
19298943Sluigi	{"dst-port",	DYN_DST_PORT},
19398943Sluigi	{NULL,		0}
19498943Sluigi};
19598943Sluigi
19698943Sluigi/*
19798943Sluigi * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines
19898943Sluigi * This is only used in this code.
19998943Sluigi */
20098943Sluigi#define IPPROTO_ETHERTYPE	0x1000
20198943Sluigistatic struct _s_x ether_types[] = {
20298943Sluigi    /*
20398943Sluigi     * Note, we cannot use "-:&/" in the names because they are field
20498943Sluigi     * separators in the type specifications. Also, we use s = NULL as
20598943Sluigi     * end-delimiter, because a type of 0 can be legal.
20698943Sluigi     */
20798943Sluigi	{ "ip",		0x0800 },
20898943Sluigi	{ "ipv4",	0x0800 },
20998943Sluigi	{ "ipv6",	0x86dd },
21098943Sluigi	{ "arp",	0x0806 },
21198943Sluigi	{ "rarp",	0x8035 },
21298943Sluigi	{ "vlan",	0x8100 },
21398943Sluigi	{ "loop",	0x9000 },
21498943Sluigi	{ "trail",	0x1000 },
21598943Sluigi	{ "at",		0x809b },
21698943Sluigi	{ "atalk",	0x809b },
21798943Sluigi	{ "aarp",	0x80f3 },
21898943Sluigi	{ "pppoe_disc",	0x8863 },
21998943Sluigi	{ "pppoe_sess",	0x8864 },
22098943Sluigi	{ "ipx_8022",	0x00E0 },
22198943Sluigi	{ "ipx_8023",	0x0000 },
22298943Sluigi	{ "ipx_ii",	0x8137 },
22398943Sluigi	{ "ipx_snap",	0x8137 },
22498943Sluigi	{ "ipx",	0x8137 },
22598943Sluigi	{ "ns",		0x0600 },
22698943Sluigi	{ NULL,		0 }
22798943Sluigi};
22898943Sluigi
22998943Sluigistatic void show_usage(void);
23098943Sluigi
23198943Sluigienum tokens {
23298943Sluigi	TOK_NULL=0,
23398943Sluigi
23498943Sluigi	TOK_OR,
23598943Sluigi	TOK_NOT,
236101641Sluigi	TOK_STARTBRACE,
237101641Sluigi	TOK_ENDBRACE,
23898943Sluigi
23998943Sluigi	TOK_ACCEPT,
24098943Sluigi	TOK_COUNT,
24198943Sluigi	TOK_PIPE,
24298943Sluigi	TOK_QUEUE,
24398943Sluigi	TOK_DIVERT,
24498943Sluigi	TOK_TEE,
245141351Sglebius	TOK_NETGRAPH,
246141351Sglebius	TOK_NGTEE,
24798943Sluigi	TOK_FORWARD,
24898943Sluigi	TOK_SKIPTO,
24998943Sluigi	TOK_DENY,
25098943Sluigi	TOK_REJECT,
25198943Sluigi	TOK_RESET,
25298943Sluigi	TOK_UNREACH,
25398943Sluigi	TOK_CHECKSTATE,
254165648Spiso	TOK_NAT,
25598943Sluigi
256136071Sgreen	TOK_ALTQ,
257136071Sgreen	TOK_LOG,
258158879Soleg	TOK_TAG,
259158879Soleg	TOK_UNTAG,
260136071Sgreen
261158879Soleg	TOK_TAGGED,
26298943Sluigi	TOK_UID,
26398943Sluigi	TOK_GID,
264133600Scsjp	TOK_JAIL,
26598943Sluigi	TOK_IN,
26698943Sluigi	TOK_LIMIT,
26798943Sluigi	TOK_KEEPSTATE,
26898943Sluigi	TOK_LAYER2,
26998943Sluigi	TOK_OUT,
270136073Sgreen	TOK_DIVERTED,
271136073Sgreen	TOK_DIVERTEDLOOPBACK,
272136073Sgreen	TOK_DIVERTEDOUTPUT,
27398943Sluigi	TOK_XMIT,
27498943Sluigi	TOK_RECV,
27598943Sluigi	TOK_VIA,
27698943Sluigi	TOK_FRAG,
27798943Sluigi	TOK_IPOPTS,
27898943Sluigi	TOK_IPLEN,
27998943Sluigi	TOK_IPID,
28098943Sluigi	TOK_IPPRECEDENCE,
28198943Sluigi	TOK_IPTOS,
28298943Sluigi	TOK_IPTTL,
28398943Sluigi	TOK_IPVER,
28498943Sluigi	TOK_ESTAB,
28598943Sluigi	TOK_SETUP,
286136075Sgreen	TOK_TCPDATALEN,
28798943Sluigi	TOK_TCPFLAGS,
28898943Sluigi	TOK_TCPOPTS,
28998943Sluigi	TOK_TCPSEQ,
29098943Sluigi	TOK_TCPACK,
29198943Sluigi	TOK_TCPWIN,
29298943Sluigi	TOK_ICMPTYPES,
293102087Sluigi	TOK_MAC,
294102087Sluigi	TOK_MACTYPE,
295112250Scjc	TOK_VERREVPATH,
296128575Sandre	TOK_VERSRCREACH,
297133387Sandre	TOK_ANTISPOOF,
298117241Sluigi	TOK_IPSEC,
299117469Sluigi	TOK_COMMENT,
30098943Sluigi
30198943Sluigi	TOK_PLR,
302101978Sluigi	TOK_NOERROR,
30398943Sluigi	TOK_BUCKETS,
30498943Sluigi	TOK_DSTIP,
30598943Sluigi	TOK_SRCIP,
30698943Sluigi	TOK_DSTPORT,
30798943Sluigi	TOK_SRCPORT,
30898943Sluigi	TOK_ALL,
30998943Sluigi	TOK_MASK,
31098943Sluigi	TOK_BW,
31198943Sluigi	TOK_DELAY,
31298943Sluigi	TOK_RED,
31398943Sluigi	TOK_GRED,
31498943Sluigi	TOK_DROPTAIL,
31598943Sluigi	TOK_PROTO,
31698943Sluigi	TOK_WEIGHT,
317165648Spiso	TOK_IP,
318165648Spiso	TOK_IF,
319165648Spiso 	TOK_ALOG,
320165648Spiso 	TOK_DENY_INC,
321165648Spiso 	TOK_SAME_PORTS,
322165648Spiso 	TOK_UNREG_ONLY,
323165648Spiso 	TOK_RESET_ADDR,
324165648Spiso 	TOK_ALIAS_REV,
325165648Spiso 	TOK_PROXY_ONLY,
326165648Spiso	TOK_REDIR_ADDR,
327165648Spiso	TOK_REDIR_PORT,
328165648Spiso	TOK_REDIR_PROTO,
329145246Sbrooks
330145246Sbrooks	TOK_IPV6,
331145246Sbrooks	TOK_FLOWID,
332145246Sbrooks	TOK_ICMP6TYPES,
333145246Sbrooks	TOK_EXT6HDR,
334145246Sbrooks	TOK_DSTIP6,
335145246Sbrooks	TOK_SRCIP6,
336146894Smlaier
337146894Smlaier	TOK_IPV4,
338149020Sbz	TOK_UNREACH6,
339149020Sbz	TOK_RESET6,
34098943Sluigi};
34198943Sluigi
34298943Sluigistruct _s_x dummynet_params[] = {
34398943Sluigi	{ "plr",		TOK_PLR },
344101978Sluigi	{ "noerror",		TOK_NOERROR },
34598943Sluigi	{ "buckets",		TOK_BUCKETS },
34698943Sluigi	{ "dst-ip",		TOK_DSTIP },
34798943Sluigi	{ "src-ip",		TOK_SRCIP },
34898943Sluigi	{ "dst-port",		TOK_DSTPORT },
34998943Sluigi	{ "src-port",		TOK_SRCPORT },
35098943Sluigi	{ "proto",		TOK_PROTO },
35198943Sluigi	{ "weight",		TOK_WEIGHT },
35298943Sluigi	{ "all",		TOK_ALL },
35398943Sluigi	{ "mask",		TOK_MASK },
35498943Sluigi	{ "droptail",		TOK_DROPTAIL },
35598943Sluigi	{ "red",		TOK_RED },
35698943Sluigi	{ "gred",		TOK_GRED },
35798943Sluigi	{ "bw",			TOK_BW },
35898943Sluigi	{ "bandwidth",		TOK_BW },
35998943Sluigi	{ "delay",		TOK_DELAY },
36099475Sluigi	{ "pipe",		TOK_PIPE },
36198943Sluigi	{ "queue",		TOK_QUEUE },
362145246Sbrooks	{ "flow-id",		TOK_FLOWID},
363145246Sbrooks	{ "dst-ipv6",		TOK_DSTIP6},
364145246Sbrooks	{ "dst-ip6",		TOK_DSTIP6},
365145246Sbrooks	{ "src-ipv6",		TOK_SRCIP6},
366145246Sbrooks	{ "src-ip6",		TOK_SRCIP6},
36798943Sluigi	{ "dummynet-params",	TOK_NULL },
368117328Sluigi	{ NULL, 0 }	/* terminator */
36998943Sluigi};
37098943Sluigi
371165648Spisostruct _s_x nat_params[] = {
372165648Spiso	{ "ip",	                TOK_IP },
373165648Spiso	{ "if",	                TOK_IF },
374165648Spiso 	{ "log",                TOK_ALOG },
375165648Spiso 	{ "deny_in",	        TOK_DENY_INC },
376165648Spiso 	{ "same_ports",	        TOK_SAME_PORTS },
377165648Spiso 	{ "unreg_only",	        TOK_UNREG_ONLY },
378165648Spiso 	{ "reset",	        TOK_RESET_ADDR },
379165648Spiso 	{ "reverse",	        TOK_ALIAS_REV },
380165648Spiso 	{ "proxy_only",	        TOK_PROXY_ONLY },
381165648Spiso	{ "redirect_addr",	TOK_REDIR_ADDR },
382165648Spiso	{ "redirect_port",	TOK_REDIR_PORT },
383165648Spiso	{ "redirect_proto",	TOK_REDIR_PROTO },
384165648Spiso 	{ NULL, 0 }	/* terminator */
385165648Spiso};
386165648Spiso
38798943Sluigistruct _s_x rule_actions[] = {
38898943Sluigi	{ "accept",		TOK_ACCEPT },
38998943Sluigi	{ "pass",		TOK_ACCEPT },
39098943Sluigi	{ "allow",		TOK_ACCEPT },
39198943Sluigi	{ "permit",		TOK_ACCEPT },
39298943Sluigi	{ "count",		TOK_COUNT },
39398943Sluigi	{ "pipe",		TOK_PIPE },
39498943Sluigi	{ "queue",		TOK_QUEUE },
39598943Sluigi	{ "divert",		TOK_DIVERT },
39698943Sluigi	{ "tee",		TOK_TEE },
397141351Sglebius	{ "netgraph",		TOK_NETGRAPH },
398141351Sglebius	{ "ngtee",		TOK_NGTEE },
39998943Sluigi	{ "fwd",		TOK_FORWARD },
40098943Sluigi	{ "forward",		TOK_FORWARD },
40198943Sluigi	{ "skipto",		TOK_SKIPTO },
40298943Sluigi	{ "deny",		TOK_DENY },
40398943Sluigi	{ "drop",		TOK_DENY },
40498943Sluigi	{ "reject",		TOK_REJECT },
405149020Sbz	{ "reset6",		TOK_RESET6 },
40698943Sluigi	{ "reset",		TOK_RESET },
407149020Sbz	{ "unreach6",		TOK_UNREACH6 },
40899475Sluigi	{ "unreach",		TOK_UNREACH },
40998943Sluigi	{ "check-state",	TOK_CHECKSTATE },
410117469Sluigi	{ "//",			TOK_COMMENT },
411165648Spiso	{ "nat",                TOK_NAT },
412117328Sluigi	{ NULL, 0 }	/* terminator */
41398943Sluigi};
41498943Sluigi
415136071Sgreenstruct _s_x rule_action_params[] = {
416136071Sgreen	{ "altq",		TOK_ALTQ },
417136071Sgreen	{ "log",		TOK_LOG },
418158879Soleg	{ "tag",		TOK_TAG },
419158879Soleg	{ "untag",		TOK_UNTAG },
420136071Sgreen	{ NULL, 0 }	/* terminator */
421136071Sgreen};
422136071Sgreen
42398943Sluigistruct _s_x rule_options[] = {
424158879Soleg	{ "tagged",		TOK_TAGGED },
42598943Sluigi	{ "uid",		TOK_UID },
42698943Sluigi	{ "gid",		TOK_GID },
427133600Scsjp	{ "jail",		TOK_JAIL },
42898943Sluigi	{ "in",			TOK_IN },
42998943Sluigi	{ "limit",		TOK_LIMIT },
43098943Sluigi	{ "keep-state",		TOK_KEEPSTATE },
43198943Sluigi	{ "bridged",		TOK_LAYER2 },
43298943Sluigi	{ "layer2",		TOK_LAYER2 },
43398943Sluigi	{ "out",		TOK_OUT },
434136073Sgreen	{ "diverted",		TOK_DIVERTED },
435136073Sgreen	{ "diverted-loopback",	TOK_DIVERTEDLOOPBACK },
436136073Sgreen	{ "diverted-output",	TOK_DIVERTEDOUTPUT },
43798943Sluigi	{ "xmit",		TOK_XMIT },
43898943Sluigi	{ "recv",		TOK_RECV },
43998943Sluigi	{ "via",		TOK_VIA },
44098943Sluigi	{ "fragment",		TOK_FRAG },
44198943Sluigi	{ "frag",		TOK_FRAG },
44298943Sluigi	{ "ipoptions",		TOK_IPOPTS },
44398943Sluigi	{ "ipopts",		TOK_IPOPTS },
44498943Sluigi	{ "iplen",		TOK_IPLEN },
44598943Sluigi	{ "ipid",		TOK_IPID },
44698943Sluigi	{ "ipprecedence",	TOK_IPPRECEDENCE },
44798943Sluigi	{ "iptos",		TOK_IPTOS },
44898943Sluigi	{ "ipttl",		TOK_IPTTL },
44998943Sluigi	{ "ipversion",		TOK_IPVER },
45098943Sluigi	{ "ipver",		TOK_IPVER },
45198943Sluigi	{ "estab",		TOK_ESTAB },
45298943Sluigi	{ "established",	TOK_ESTAB },
45398943Sluigi	{ "setup",		TOK_SETUP },
454136075Sgreen	{ "tcpdatalen",		TOK_TCPDATALEN },
45598943Sluigi	{ "tcpflags",		TOK_TCPFLAGS },
45698943Sluigi	{ "tcpflgs",		TOK_TCPFLAGS },
45798943Sluigi	{ "tcpoptions",		TOK_TCPOPTS },
45898943Sluigi	{ "tcpopts",		TOK_TCPOPTS },
45998943Sluigi	{ "tcpseq",		TOK_TCPSEQ },
46098943Sluigi	{ "tcpack",		TOK_TCPACK },
46198943Sluigi	{ "tcpwin",		TOK_TCPWIN },
46299909Sluigi	{ "icmptype",		TOK_ICMPTYPES },
46398943Sluigi	{ "icmptypes",		TOK_ICMPTYPES },
464102087Sluigi	{ "dst-ip",		TOK_DSTIP },
465102087Sluigi	{ "src-ip",		TOK_SRCIP },
466102087Sluigi	{ "dst-port",		TOK_DSTPORT },
467102087Sluigi	{ "src-port",		TOK_SRCPORT },
468102087Sluigi	{ "proto",		TOK_PROTO },
469102087Sluigi	{ "MAC",		TOK_MAC },
470102087Sluigi	{ "mac",		TOK_MAC },
471102087Sluigi	{ "mac-type",		TOK_MACTYPE },
472112250Scjc	{ "verrevpath",		TOK_VERREVPATH },
473128575Sandre	{ "versrcreach",	TOK_VERSRCREACH },
474133387Sandre	{ "antispoof",		TOK_ANTISPOOF },
475117241Sluigi	{ "ipsec",		TOK_IPSEC },
476145246Sbrooks	{ "icmp6type",		TOK_ICMP6TYPES },
477145246Sbrooks	{ "icmp6types",		TOK_ICMP6TYPES },
478145246Sbrooks	{ "ext6hdr",		TOK_EXT6HDR},
479145246Sbrooks	{ "flow-id",		TOK_FLOWID},
480145246Sbrooks	{ "ipv6",		TOK_IPV6},
481145246Sbrooks	{ "ip6",		TOK_IPV6},
482146894Smlaier	{ "ipv4",		TOK_IPV4},
483146894Smlaier	{ "ip4",		TOK_IPV4},
484145246Sbrooks	{ "dst-ipv6",		TOK_DSTIP6},
485145246Sbrooks	{ "dst-ip6",		TOK_DSTIP6},
486145246Sbrooks	{ "src-ipv6",		TOK_SRCIP6},
487145246Sbrooks	{ "src-ip6",		TOK_SRCIP6},
488117469Sluigi	{ "//",			TOK_COMMENT },
48998943Sluigi
49098943Sluigi	{ "not",		TOK_NOT },		/* pseudo option */
49198943Sluigi	{ "!", /* escape ? */	TOK_NOT },		/* pseudo option */
49298943Sluigi	{ "or",			TOK_OR },		/* pseudo option */
49398943Sluigi	{ "|", /* escape */	TOK_OR },		/* pseudo option */
494101641Sluigi	{ "{",			TOK_STARTBRACE },	/* pseudo option */
495101641Sluigi	{ "(",			TOK_STARTBRACE },	/* pseudo option */
496101641Sluigi	{ "}",			TOK_ENDBRACE },		/* pseudo option */
497101641Sluigi	{ ")",			TOK_ENDBRACE },		/* pseudo option */
498117328Sluigi	{ NULL, 0 }	/* terminator */
49998943Sluigi};
50098943Sluigi
501153374Sglebius#define	TABLEARG	"tablearg"
502153374Sglebius
503117328Sluigistatic __inline uint64_t
504117328Sluigialign_uint64(uint64_t *pll) {
505117328Sluigi	uint64_t ret;
506115793Sticso
507115793Sticso	bcopy (pll, &ret, sizeof(ret));
508115793Sticso	return ret;
509129389Sstefanf}
510115793Sticso
511117328Sluigi/*
512117328Sluigi * conditionally runs the command.
513117328Sluigi */
514117469Sluigistatic int
515119740Stmmdo_cmd(int optname, void *optval, uintptr_t optlen)
516117328Sluigi{
517117328Sluigi	static int s = -1;	/* the socket */
518117328Sluigi	int i;
519117577Sluigi
520117328Sluigi	if (test_only)
521117328Sluigi		return 0;
522117328Sluigi
523117328Sluigi	if (s == -1)
524117328Sluigi		s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
525117328Sluigi	if (s < 0)
526117328Sluigi		err(EX_UNAVAILABLE, "socket");
527117328Sluigi
528117328Sluigi	if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
529130281Sru	    optname == IP_FW_ADD || optname == IP_FW_TABLE_LIST ||
530165648Spiso	    optname == IP_FW_TABLE_GETSIZE ||
531165648Spiso	    optname == IP_FW_NAT_GET_CONFIG ||
532165648Spiso	    optname == IP_FW_NAT_GET_LOG)
533117328Sluigi		i = getsockopt(s, IPPROTO_IP, optname, optval,
534117328Sluigi			(socklen_t *)optlen);
535117328Sluigi	else
536117328Sluigi		i = setsockopt(s, IPPROTO_IP, optname, optval, optlen);
537117328Sluigi	return i;
538117328Sluigi}
539117328Sluigi
54098943Sluigi/**
54198943Sluigi * match_token takes a table and a string, returns the value associated
542117328Sluigi * with the string (-1 in case of failure).
54398943Sluigi */
54498943Sluigistatic int
54598943Sluigimatch_token(struct _s_x *table, char *string)
54698943Sluigi{
54798943Sluigi	struct _s_x *pt;
548117469Sluigi	uint i = strlen(string);
54998943Sluigi
55098943Sluigi	for (pt = table ; i && pt->s != NULL ; pt++)
55198943Sluigi		if (strlen(pt->s) == i && !bcmp(string, pt->s, i))
55298943Sluigi			return pt->x;
55398943Sluigi	return -1;
554129389Sstefanf}
55598943Sluigi
556117328Sluigi/**
557117328Sluigi * match_value takes a table and a value, returns the string associated
558117328Sluigi * with the value (NULL in case of failure).
559117328Sluigi */
560117469Sluigistatic char const *
561117469Sluigimatch_value(struct _s_x *p, int value)
56298943Sluigi{
56398943Sluigi	for (; p->s != NULL; p++)
56498943Sluigi		if (p->x == value)
56598943Sluigi			return p->s;
56698943Sluigi	return NULL;
56798943Sluigi}
56898943Sluigi
56998943Sluigi/*
570140271Sbrooks * _substrcmp takes two strings and returns 1 if they do not match,
571140271Sbrooks * and 0 if they match exactly or the first string is a sub-string
572140271Sbrooks * of the second.  A warning is printed to stderr in the case that the
573140271Sbrooks * first string is a sub-string of the second.
574140271Sbrooks *
575140271Sbrooks * This function will be removed in the future through the usual
576140271Sbrooks * deprecation process.
577140271Sbrooks */
578140271Sbrooksstatic int
579140271Sbrooks_substrcmp(const char *str1, const char* str2)
580140271Sbrooks{
581140271Sbrooks
582140271Sbrooks	if (strncmp(str1, str2, strlen(str1)) != 0)
583140271Sbrooks		return 1;
584140271Sbrooks
585140271Sbrooks	if (strlen(str1) != strlen(str2))
586140271Sbrooks		warnx("DEPRECATED: '%s' matched '%s' as a sub-string",
587140271Sbrooks		    str1, str2);
588140271Sbrooks	return 0;
589140271Sbrooks}
590140271Sbrooks
591140271Sbrooks/*
592140271Sbrooks * _substrcmp2 takes three strings and returns 1 if the first two do not match,
593140271Sbrooks * and 0 if they match exactly or the second string is a sub-string
594140271Sbrooks * of the first.  A warning is printed to stderr in the case that the
595140271Sbrooks * first string does not match the third.
596140271Sbrooks *
597140271Sbrooks * This function exists to warn about the bizzare construction
598140271Sbrooks * strncmp(str, "by", 2) which is used to allow people to use a shotcut
599140271Sbrooks * for "bytes".  The problem is that in addition to accepting "by",
600140271Sbrooks * "byt", "byte", and "bytes", it also excepts "by_rabid_dogs" and any
601140271Sbrooks * other string beginning with "by".
602140271Sbrooks *
603140271Sbrooks * This function will be removed in the future through the usual
604140271Sbrooks * deprecation process.
605140271Sbrooks */
606140271Sbrooksstatic int
607140271Sbrooks_substrcmp2(const char *str1, const char* str2, const char* str3)
608140271Sbrooks{
609140271Sbrooks
610140271Sbrooks	if (strncmp(str1, str2, strlen(str2)) != 0)
611140271Sbrooks		return 1;
612140271Sbrooks
613140271Sbrooks	if (strcmp(str1, str3) != 0)
614140271Sbrooks		warnx("DEPRECATED: '%s' matched '%s'",
615140271Sbrooks		    str1, str3);
616140271Sbrooks	return 0;
617140271Sbrooks}
618140271Sbrooks
619140271Sbrooks/*
62098943Sluigi * prints one port, symbolic or numeric
62198943Sluigi */
62298943Sluigistatic void
623117328Sluigiprint_port(int proto, uint16_t port)
62498943Sluigi{
62598943Sluigi
62698943Sluigi	if (proto == IPPROTO_ETHERTYPE) {
627117469Sluigi		char const *s;
62898943Sluigi
62998943Sluigi		if (do_resolv && (s = match_value(ether_types, port)) )
63098943Sluigi			printf("%s", s);
63198943Sluigi		else
63298943Sluigi			printf("0x%04x", port);
63398943Sluigi	} else {
63498943Sluigi		struct servent *se = NULL;
63598943Sluigi		if (do_resolv) {
63698943Sluigi			struct protoent *pe = getprotobynumber(proto);
63798943Sluigi
63898943Sluigi			se = getservbyport(htons(port), pe ? pe->p_name : NULL);
63998943Sluigi		}
64098943Sluigi		if (se)
64198943Sluigi			printf("%s", se->s_name);
64298943Sluigi		else
64398943Sluigi			printf("%d", port);
64498943Sluigi	}
64598943Sluigi}
64698943Sluigi
647117328Sluigistruct _s_x _port_name[] = {
648117328Sluigi	{"dst-port",	O_IP_DSTPORT},
649117328Sluigi	{"src-port",	O_IP_SRCPORT},
650117328Sluigi	{"ipid",	O_IPID},
651117328Sluigi	{"iplen",	O_IPLEN},
652117328Sluigi	{"ipttl",	O_IPTTL},
653117328Sluigi	{"mac-type",	O_MAC_TYPE},
654136075Sgreen	{"tcpdatalen",	O_TCPDATALEN},
655158879Soleg	{"tagged",	O_TAGGED},
656117328Sluigi	{NULL,		0}
657117328Sluigi};
658117328Sluigi
65998943Sluigi/*
660117328Sluigi * Print the values in a list 16-bit items of the types above.
66198943Sluigi * XXX todo: add support for mask.
66298943Sluigi */
66398943Sluigistatic void
664102087Sluigiprint_newports(ipfw_insn_u16 *cmd, int proto, int opcode)
66598943Sluigi{
666117328Sluigi	uint16_t *p = cmd->ports;
66798943Sluigi	int i;
668117469Sluigi	char const *sep;
66998943Sluigi
67098943Sluigi	if (cmd->o.len & F_NOT)
67198943Sluigi		printf(" not");
672116690Sluigi	if (opcode != 0) {
673117328Sluigi		sep = match_value(_port_name, opcode);
674117328Sluigi		if (sep == NULL)
675116690Sluigi			sep = "???";
676116690Sluigi		printf (" %s", sep);
677116690Sluigi	}
678116690Sluigi	sep = " ";
67998943Sluigi	for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
68098943Sluigi		printf(sep);
68198943Sluigi		print_port(proto, p[0]);
68298943Sluigi		if (p[0] != p[1]) {
68398943Sluigi			printf("-");
68498943Sluigi			print_port(proto, p[1]);
68598943Sluigi		}
68698943Sluigi		sep = ",";
68798943Sluigi	}
68898943Sluigi}
68998943Sluigi
69098943Sluigi/*
69198943Sluigi * Like strtol, but also translates service names into port numbers
69298943Sluigi * for some protocols.
69398943Sluigi * In particular:
69498943Sluigi *	proto == -1 disables the protocol check;
69598943Sluigi *	proto == IPPROTO_ETHERTYPE looks up an internal table
69698943Sluigi *	proto == <some value in /etc/protocols> matches the values there.
697101628Sluigi * Returns *end == s in case the parameter is not found.
69898943Sluigi */
69998943Sluigistatic int
70098943Sluigistrtoport(char *s, char **end, int base, int proto)
70198943Sluigi{
702101628Sluigi	char *p, *buf;
703101628Sluigi	char *s1;
70498943Sluigi	int i;
70598943Sluigi
706101628Sluigi	*end = s;		/* default - not found */
707117577Sluigi	if (*s == '\0')
708101628Sluigi		return 0;	/* not found */
709106505Smaxim
71098943Sluigi	if (isdigit(*s))
71198943Sluigi		return strtol(s, end, base);
71298943Sluigi
71398943Sluigi	/*
714101628Sluigi	 * find separator. '\\' escapes the next char.
71598943Sluigi	 */
716101628Sluigi	for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++)
717101628Sluigi		if (*s1 == '\\' && s1[1] != '\0')
718101628Sluigi			s1++;
71998943Sluigi
720101628Sluigi	buf = malloc(s1 - s + 1);
721101628Sluigi	if (buf == NULL)
722101628Sluigi		return 0;
723101628Sluigi
724101628Sluigi	/*
725101628Sluigi	 * copy into a buffer skipping backslashes
726101628Sluigi	 */
727101628Sluigi	for (p = s, i = 0; p != s1 ; p++)
728117577Sluigi		if (*p != '\\')
729101628Sluigi			buf[i++] = *p;
730101628Sluigi	buf[i++] = '\0';
731101628Sluigi
73298943Sluigi	if (proto == IPPROTO_ETHERTYPE) {
733101628Sluigi		i = match_token(ether_types, buf);
734101628Sluigi		free(buf);
735101628Sluigi		if (i != -1) {	/* found */
73698943Sluigi			*end = s1;
73798943Sluigi			return i;
73898943Sluigi		}
73998943Sluigi	} else {
74098943Sluigi		struct protoent *pe = NULL;
74198943Sluigi		struct servent *se;
74298943Sluigi
74398943Sluigi		if (proto != 0)
74498943Sluigi			pe = getprotobynumber(proto);
74598943Sluigi		setservent(1);
746101628Sluigi		se = getservbyname(buf, pe ? pe->p_name : NULL);
747101628Sluigi		free(buf);
74898943Sluigi		if (se != NULL) {
74998943Sluigi			*end = s1;
75098943Sluigi			return ntohs(se->s_port);
75198943Sluigi		}
75298943Sluigi	}
753101628Sluigi	return 0;	/* not found */
75498943Sluigi}
75598943Sluigi
75698943Sluigi/*
757136071Sgreen * Map between current altq queue id numbers and names.
758136071Sgreen */
759136071Sgreenstatic int altq_fetched = 0;
760136071Sgreenstatic TAILQ_HEAD(, pf_altq) altq_entries =
761136071Sgreen	TAILQ_HEAD_INITIALIZER(altq_entries);
762136071Sgreen
763136071Sgreenstatic void
764136071Sgreenaltq_set_enabled(int enabled)
765136071Sgreen{
766136071Sgreen	int pffd;
767136071Sgreen
768136071Sgreen	pffd = open("/dev/pf", O_RDWR);
769136071Sgreen	if (pffd == -1)
770136071Sgreen		err(EX_UNAVAILABLE,
771136071Sgreen		    "altq support opening pf(4) control device");
772136071Sgreen	if (enabled) {
773136071Sgreen		if (ioctl(pffd, DIOCSTARTALTQ) != 0 && errno != EEXIST)
774136071Sgreen			err(EX_UNAVAILABLE, "enabling altq");
775136071Sgreen	} else {
776136071Sgreen		if (ioctl(pffd, DIOCSTOPALTQ) != 0 && errno != ENOENT)
777136071Sgreen			err(EX_UNAVAILABLE, "disabling altq");
778136071Sgreen	}
779136071Sgreen	close(pffd);
780136071Sgreen}
781136071Sgreen
782136071Sgreenstatic void
783136071Sgreenaltq_fetch()
784136071Sgreen{
785136071Sgreen	struct pfioc_altq pfioc;
786136071Sgreen	struct pf_altq *altq;
787136071Sgreen	int pffd, mnr;
788136071Sgreen
789136071Sgreen	if (altq_fetched)
790136071Sgreen		return;
791136071Sgreen	altq_fetched = 1;
792136071Sgreen	pffd = open("/dev/pf", O_RDONLY);
793136071Sgreen	if (pffd == -1) {
794136071Sgreen		warn("altq support opening pf(4) control device");
795136071Sgreen		return;
796136071Sgreen	}
797136071Sgreen	bzero(&pfioc, sizeof(pfioc));
798136071Sgreen	if (ioctl(pffd, DIOCGETALTQS, &pfioc) != 0) {
799136071Sgreen		warn("altq support getting queue list");
800136071Sgreen		close(pffd);
801136071Sgreen		return;
802136071Sgreen	}
803136071Sgreen	mnr = pfioc.nr;
804136071Sgreen	for (pfioc.nr = 0; pfioc.nr < mnr; pfioc.nr++) {
805136071Sgreen		if (ioctl(pffd, DIOCGETALTQ, &pfioc) != 0) {
806136071Sgreen			if (errno == EBUSY)
807136071Sgreen				break;
808136071Sgreen			warn("altq support getting queue list");
809136071Sgreen			close(pffd);
810136071Sgreen			return;
811136071Sgreen		}
812136071Sgreen		if (pfioc.altq.qid == 0)
813136071Sgreen			continue;
814136071Sgreen		altq = malloc(sizeof(*altq));
815136071Sgreen		if (altq == NULL)
816136071Sgreen			err(EX_OSERR, "malloc");
817136071Sgreen		*altq = pfioc.altq;
818136071Sgreen		TAILQ_INSERT_TAIL(&altq_entries, altq, entries);
819136071Sgreen	}
820136071Sgreen	close(pffd);
821136071Sgreen}
822136071Sgreen
823136071Sgreenstatic u_int32_t
824136071Sgreenaltq_name_to_qid(const char *name)
825136071Sgreen{
826136071Sgreen	struct pf_altq *altq;
827136071Sgreen
828136071Sgreen	altq_fetch();
829136071Sgreen	TAILQ_FOREACH(altq, &altq_entries, entries)
830136071Sgreen		if (strcmp(name, altq->qname) == 0)
831136071Sgreen			break;
832136071Sgreen	if (altq == NULL)
833136071Sgreen		errx(EX_DATAERR, "altq has no queue named `%s'", name);
834136071Sgreen	return altq->qid;
835136071Sgreen}
836136071Sgreen
837136071Sgreenstatic const char *
838136071Sgreenaltq_qid_to_name(u_int32_t qid)
839136071Sgreen{
840136071Sgreen	struct pf_altq *altq;
841136071Sgreen
842136071Sgreen	altq_fetch();
843136071Sgreen	TAILQ_FOREACH(altq, &altq_entries, entries)
844136071Sgreen		if (qid == altq->qid)
845136071Sgreen			break;
846136071Sgreen	if (altq == NULL)
847136071Sgreen		return NULL;
848136071Sgreen	return altq->qname;
849136071Sgreen}
850136071Sgreen
851136071Sgreenstatic void
852136071Sgreenfill_altq_qid(u_int32_t *qid, const char *av)
853136071Sgreen{
854136071Sgreen	*qid = altq_name_to_qid(av);
855136071Sgreen}
856136071Sgreen
857136071Sgreen/*
858117328Sluigi * Fill the body of the command with the list of port ranges.
85998943Sluigi */
86098943Sluigistatic int
86198943Sluigifill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
86298943Sluigi{
863117328Sluigi	uint16_t a, b, *p = cmd->ports;
86498943Sluigi	int i = 0;
865102087Sluigi	char *s = av;
86698943Sluigi
867102087Sluigi	while (*s) {
86898943Sluigi		a = strtoport(av, &s, 0, proto);
869159636Soleg		if (s == av) 			/* empty or invalid argument */
870159636Soleg			return (0);
871159636Soleg
872159636Soleg		switch (*s) {
873159636Soleg		case '-':			/* a range */
874159636Soleg			av = s + 1;
87598943Sluigi			b = strtoport(av, &s, 0, proto);
876159636Soleg			/* Reject expressions like '1-abc' or '1-2-3'. */
877159636Soleg			if (s == av || (*s != ',' && *s != '\0'))
878159636Soleg				return (0);
87998943Sluigi			p[0] = a;
88098943Sluigi			p[1] = b;
881159636Soleg			break;
882159636Soleg		case ',':			/* comma separated list */
883159636Soleg		case '\0':
88498943Sluigi			p[0] = p[1] = a;
885159636Soleg			break;
886159636Soleg		default:
887159636Soleg			warnx("port list: invalid separator <%c> in <%s>",
888101978Sluigi				*s, av);
889159636Soleg			return (0);
890159636Soleg		}
891159636Soleg
892102087Sluigi		i++;
893102087Sluigi		p += 2;
894159636Soleg		av = s + 1;
89598943Sluigi	}
89698943Sluigi	if (i > 0) {
897159636Soleg		if (i + 1 > F_LEN_MASK)
898102087Sluigi			errx(EX_DATAERR, "too many ports/ranges\n");
899159636Soleg		cmd->o.len |= i + 1;	/* leave F_NOT and F_OR untouched */
90098943Sluigi	}
901159636Soleg	return (i);
90298943Sluigi}
90398943Sluigi
90498943Sluigistatic struct _s_x icmpcodes[] = {
90598943Sluigi      { "net",			ICMP_UNREACH_NET },
90698943Sluigi      { "host",			ICMP_UNREACH_HOST },
90798943Sluigi      { "protocol",		ICMP_UNREACH_PROTOCOL },
90898943Sluigi      { "port",			ICMP_UNREACH_PORT },
90998943Sluigi      { "needfrag",		ICMP_UNREACH_NEEDFRAG },
91098943Sluigi      { "srcfail",		ICMP_UNREACH_SRCFAIL },
91198943Sluigi      { "net-unknown",		ICMP_UNREACH_NET_UNKNOWN },
91298943Sluigi      { "host-unknown",		ICMP_UNREACH_HOST_UNKNOWN },
91398943Sluigi      { "isolated",		ICMP_UNREACH_ISOLATED },
91498943Sluigi      { "net-prohib",		ICMP_UNREACH_NET_PROHIB },
91598943Sluigi      { "host-prohib",		ICMP_UNREACH_HOST_PROHIB },
91698943Sluigi      { "tosnet",		ICMP_UNREACH_TOSNET },
91798943Sluigi      { "toshost",		ICMP_UNREACH_TOSHOST },
91898943Sluigi      { "filter-prohib",	ICMP_UNREACH_FILTER_PROHIB },
91998943Sluigi      { "host-precedence",	ICMP_UNREACH_HOST_PRECEDENCE },
92098943Sluigi      { "precedence-cutoff",	ICMP_UNREACH_PRECEDENCE_CUTOFF },
92198943Sluigi      { NULL, 0 }
92298943Sluigi};
92398943Sluigi
92498943Sluigistatic void
92598943Sluigifill_reject_code(u_short *codep, char *str)
92698943Sluigi{
92798943Sluigi	int val;
92898943Sluigi	char *s;
92998943Sluigi
93098943Sluigi	val = strtoul(str, &s, 0);
93198943Sluigi	if (s == str || *s != '\0' || val >= 0x100)
93298943Sluigi		val = match_token(icmpcodes, str);
933102087Sluigi	if (val < 0)
93498943Sluigi		errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
93598943Sluigi	*codep = val;
93698943Sluigi	return;
93798943Sluigi}
93898943Sluigi
93998943Sluigistatic void
940117328Sluigiprint_reject_code(uint16_t code)
94198943Sluigi{
942117469Sluigi	char const *s = match_value(icmpcodes, code);
94398943Sluigi
94498943Sluigi	if (s != NULL)
94599475Sluigi		printf("unreach %s", s);
94698943Sluigi	else
94799475Sluigi		printf("unreach %u", code);
94898943Sluigi}
94998943Sluigi
950149020Sbzstatic struct _s_x icmp6codes[] = {
951149020Sbz      { "no-route",		ICMP6_DST_UNREACH_NOROUTE },
952149020Sbz      { "admin-prohib",		ICMP6_DST_UNREACH_ADMIN },
953149020Sbz      { "address",		ICMP6_DST_UNREACH_ADDR },
954149020Sbz      { "port",			ICMP6_DST_UNREACH_NOPORT },
955149020Sbz      { NULL, 0 }
956149020Sbz};
957149020Sbz
958149020Sbzstatic void
959149020Sbzfill_unreach6_code(u_short *codep, char *str)
960149020Sbz{
961149020Sbz	int val;
962149020Sbz	char *s;
963149020Sbz
964149020Sbz	val = strtoul(str, &s, 0);
965149020Sbz	if (s == str || *s != '\0' || val >= 0x100)
966149020Sbz		val = match_token(icmp6codes, str);
967149020Sbz	if (val < 0)
968149020Sbz		errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str);
969149020Sbz	*codep = val;
970149020Sbz	return;
971149020Sbz}
972149020Sbz
973149020Sbzstatic void
974149020Sbzprint_unreach6_code(uint16_t code)
975149020Sbz{
976149020Sbz	char const *s = match_value(icmp6codes, code);
977149020Sbz
978149020Sbz	if (s != NULL)
979149020Sbz		printf("unreach6 %s", s);
980149020Sbz	else
981149020Sbz		printf("unreach6 %u", code);
982149020Sbz}
983149020Sbz
98498943Sluigi/*
98598943Sluigi * Returns the number of bits set (from left) in a contiguous bitmask,
98698943Sluigi * or -1 if the mask is not contiguous.
98798943Sluigi * XXX this needs a proper fix.
98898943Sluigi * This effectively works on masks in big-endian (network) format.
98998943Sluigi * when compiled on little endian architectures.
99098943Sluigi *
99198943Sluigi * First bit is bit 7 of the first byte -- note, for MAC addresses,
99298943Sluigi * the first bit on the wire is bit 0 of the first byte.
99398943Sluigi * len is the max length in bits.
99498943Sluigi */
99598943Sluigistatic int
996117577Sluigicontigmask(uint8_t *p, int len)
99798943Sluigi{
99898943Sluigi	int i, n;
999117577Sluigi
100098943Sluigi	for (i=0; i<len ; i++)
100198943Sluigi		if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
100298943Sluigi			break;
100398943Sluigi	for (n=i+1; n < len; n++)
100498943Sluigi		if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
100598943Sluigi			return -1; /* mask not contiguous */
100698943Sluigi	return i;
100798943Sluigi}
100898943Sluigi
100998943Sluigi/*
101098943Sluigi * print flags set/clear in the two bitmasks passed as parameters.
101198943Sluigi * There is a specialized check for f_tcpflags.
101298943Sluigi */
101398943Sluigistatic void
1014117469Sluigiprint_flags(char const *name, ipfw_insn *cmd, struct _s_x *list)
101598943Sluigi{
1016117469Sluigi	char const *comma = "";
101798943Sluigi	int i;
1018117577Sluigi	uint8_t set = cmd->arg1 & 0xff;
1019117577Sluigi	uint8_t clear = (cmd->arg1 >> 8) & 0xff;
102098943Sluigi
102198943Sluigi	if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) {
102298943Sluigi		printf(" setup");
102398943Sluigi		return;
102498943Sluigi	}
102598943Sluigi
102698943Sluigi	printf(" %s ", name);
102798943Sluigi	for (i=0; list[i].x != 0; i++) {
102898943Sluigi		if (set & list[i].x) {
102998943Sluigi			set &= ~list[i].x;
103098943Sluigi			printf("%s%s", comma, list[i].s);
103198943Sluigi			comma = ",";
103298943Sluigi		}
103398943Sluigi		if (clear & list[i].x) {
103498943Sluigi			clear &= ~list[i].x;
103598943Sluigi			printf("%s!%s", comma, list[i].s);
103698943Sluigi			comma = ",";
103798943Sluigi		}
103898943Sluigi	}
103998943Sluigi}
104098943Sluigi
104198943Sluigi/*
104298943Sluigi * Print the ip address contained in a command.
104398943Sluigi */
104498943Sluigistatic void
1045117469Sluigiprint_ip(ipfw_insn_ip *cmd, char const *s)
104698943Sluigi{
104798943Sluigi	struct hostent *he = NULL;
1048117328Sluigi	int len = F_LEN((ipfw_insn *)cmd);
1049117328Sluigi	uint32_t *a = ((ipfw_insn_u32 *)cmd)->d;
105098943Sluigi
1051102087Sluigi	printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
105298943Sluigi
105398943Sluigi	if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) {
105498943Sluigi		printf("me");
105598943Sluigi		return;
105698943Sluigi	}
1057130281Sru	if (cmd->o.opcode == O_IP_SRC_LOOKUP ||
1058130281Sru	    cmd->o.opcode == O_IP_DST_LOOKUP) {
1059130281Sru		printf("table(%u", ((ipfw_insn *)cmd)->arg1);
1060130281Sru		if (len == F_INSN_SIZE(ipfw_insn_u32))
1061130281Sru			printf(",%u", *a);
1062130281Sru		printf(")");
1063130281Sru		return;
1064130281Sru	}
106598943Sluigi	if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) {
1066117328Sluigi		uint32_t x, *map = (uint32_t *)&(cmd->mask);
1067116716Sluigi		int i, j;
106898943Sluigi		char comma = '{';
106998943Sluigi
107098943Sluigi		x = cmd->o.arg1 - 1;
107198943Sluigi		x = htonl( ~x );
107298943Sluigi		cmd->addr.s_addr = htonl(cmd->addr.s_addr);
107398943Sluigi		printf("%s/%d", inet_ntoa(cmd->addr),
1074117577Sluigi			contigmask((uint8_t *)&x, 32));
107598943Sluigi		x = cmd->addr.s_addr = htonl(cmd->addr.s_addr);
107698943Sluigi		x &= 0xff; /* base */
1077116716Sluigi		/*
1078116716Sluigi		 * Print bits and ranges.
1079116716Sluigi		 * Locate first bit set (i), then locate first bit unset (j).
1080116716Sluigi		 * If we have 3+ consecutive bits set, then print them as a
1081116716Sluigi		 * range, otherwise only print the initial bit and rescan.
1082116716Sluigi		 */
108398943Sluigi		for (i=0; i < cmd->o.arg1; i++)
1084117328Sluigi			if (map[i/32] & (1<<(i & 31))) {
1085116716Sluigi				for (j=i+1; j < cmd->o.arg1; j++)
1086117328Sluigi					if (!(map[ j/32] & (1<<(j & 31))))
1087116716Sluigi						break;
108898943Sluigi				printf("%c%d", comma, i+x);
1089116716Sluigi				if (j>i+2) { /* range has at least 3 elements */
1090116716Sluigi					printf("-%d", j-1+x);
1091116716Sluigi					i = j-1;
1092116716Sluigi				}
109398943Sluigi				comma = ',';
109498943Sluigi			}
109598943Sluigi		printf("}");
109698943Sluigi		return;
109798943Sluigi	}
1098117328Sluigi	/*
1099117328Sluigi	 * len == 2 indicates a single IP, whereas lists of 1 or more
1100117328Sluigi	 * addr/mask pairs have len = (2n+1). We convert len to n so we
1101117328Sluigi	 * use that to count the number of entries.
1102117328Sluigi	 */
1103117328Sluigi    for (len = len / 2; len > 0; len--, a += 2) {
1104117328Sluigi	int mb =	/* mask length */
1105117328Sluigi	    (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ?
1106117577Sluigi		32 : contigmask((uint8_t *)&(a[1]), 32);
110798943Sluigi	if (mb == 32 && do_resolv)
1108117328Sluigi		he = gethostbyaddr((char *)&(a[0]), sizeof(u_long), AF_INET);
110998943Sluigi	if (he != NULL)		/* resolved to name */
111098943Sluigi		printf("%s", he->h_name);
111198943Sluigi	else if (mb == 0)	/* any */
111298943Sluigi		printf("any");
111398943Sluigi	else {		/* numeric IP followed by some kind of mask */
1114117328Sluigi		printf("%s", inet_ntoa( *((struct in_addr *)&a[0]) ) );
111598943Sluigi		if (mb < 0)
1116117328Sluigi			printf(":%s", inet_ntoa( *((struct in_addr *)&a[1]) ) );
111798943Sluigi		else if (mb < 32)
111898943Sluigi			printf("/%d", mb);
111998943Sluigi	}
1120117328Sluigi	if (len > 1)
1121117328Sluigi		printf(",");
1122117328Sluigi    }
112398943Sluigi}
112498943Sluigi
112598943Sluigi/*
112698943Sluigi * prints a MAC address/mask pair
112798943Sluigi */
112898943Sluigistatic void
1129117577Sluigiprint_mac(uint8_t *addr, uint8_t *mask)
113098943Sluigi{
113198943Sluigi	int l = contigmask(mask, 48);
113298943Sluigi
113398943Sluigi	if (l == 0)
113498943Sluigi		printf(" any");
113598943Sluigi	else {
113698943Sluigi		printf(" %02x:%02x:%02x:%02x:%02x:%02x",
113798943Sluigi		    addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
113898943Sluigi		if (l == -1)
113998943Sluigi			printf("&%02x:%02x:%02x:%02x:%02x:%02x",
114098943Sluigi			    mask[0], mask[1], mask[2],
114198943Sluigi			    mask[3], mask[4], mask[5]);
114298943Sluigi		else if (l < 48)
114398943Sluigi			printf("/%d", l);
114498943Sluigi	}
114598943Sluigi}
114698943Sluigi
114799475Sluigistatic void
114899475Sluigifill_icmptypes(ipfw_insn_u32 *cmd, char *av)
114999475Sluigi{
1150117328Sluigi	uint8_t type;
115198943Sluigi
115299475Sluigi	cmd->d[0] = 0;
115399475Sluigi	while (*av) {
115499475Sluigi		if (*av == ',')
115599475Sluigi			av++;
115699475Sluigi
115799475Sluigi		type = strtoul(av, &av, 0);
115899475Sluigi
115999475Sluigi		if (*av != ',' && *av != '\0')
116099475Sluigi			errx(EX_DATAERR, "invalid ICMP type");
116199475Sluigi
116299475Sluigi		if (type > 31)
116399475Sluigi			errx(EX_DATAERR, "ICMP type out of range");
116499475Sluigi
116599475Sluigi		cmd->d[0] |= 1 << type;
116699475Sluigi	}
116799475Sluigi	cmd->o.opcode = O_ICMPTYPE;
116899475Sluigi	cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
116999475Sluigi}
117099475Sluigi
117199475Sluigistatic void
117299475Sluigiprint_icmptypes(ipfw_insn_u32 *cmd)
117399475Sluigi{
117499475Sluigi	int i;
117599475Sluigi	char sep= ' ';
117699475Sluigi
117799475Sluigi	printf(" icmptypes");
117899475Sluigi	for (i = 0; i < 32; i++) {
117999475Sluigi		if ( (cmd->d[0] & (1 << (i))) == 0)
118099475Sluigi			continue;
118199475Sluigi		printf("%c%d", sep, i);
118299475Sluigi		sep = ',';
118399475Sluigi	}
118499475Sluigi}
118599475Sluigi
1186145246Sbrooks/*
1187145246Sbrooks * Print the ip address contained in a command.
1188145246Sbrooks */
1189145246Sbrooksstatic void
1190145246Sbrooksprint_ip6(ipfw_insn_ip6 *cmd, char const *s)
1191145246Sbrooks{
1192145246Sbrooks       struct hostent *he = NULL;
1193145246Sbrooks       int len = F_LEN((ipfw_insn *) cmd) - 1;
1194145246Sbrooks       struct in6_addr *a = &(cmd->addr6);
1195145246Sbrooks       char trad[255];
1196145246Sbrooks
1197145246Sbrooks       printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
1198145246Sbrooks
1199145246Sbrooks       if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) {
1200145246Sbrooks               printf("me6");
1201145246Sbrooks               return;
1202145246Sbrooks       }
1203145246Sbrooks       if (cmd->o.opcode == O_IP6) {
1204152923Sume               printf(" ip6");
1205145246Sbrooks               return;
1206145246Sbrooks       }
1207145246Sbrooks
1208145246Sbrooks       /*
1209145246Sbrooks        * len == 4 indicates a single IP, whereas lists of 1 or more
1210145246Sbrooks        * addr/mask pairs have len = (2n+1). We convert len to n so we
1211145246Sbrooks        * use that to count the number of entries.
1212145246Sbrooks        */
1213145246Sbrooks
1214145246Sbrooks       for (len = len / 4; len > 0; len -= 2, a += 2) {
1215145246Sbrooks           int mb =        /* mask length */
1216145246Sbrooks               (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ?
1217145246Sbrooks               128 : contigmask((uint8_t *)&(a[1]), 128);
1218145246Sbrooks
1219145246Sbrooks           if (mb == 128 && do_resolv)
1220145246Sbrooks               he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6);
1221145246Sbrooks           if (he != NULL)             /* resolved to name */
1222145246Sbrooks               printf("%s", he->h_name);
1223145246Sbrooks           else if (mb == 0)           /* any */
1224145246Sbrooks               printf("any");
1225145246Sbrooks           else {          /* numeric IP followed by some kind of mask */
1226145246Sbrooks               if (inet_ntop(AF_INET6,  a, trad, sizeof( trad ) ) == NULL)
1227145246Sbrooks                   printf("Error ntop in print_ip6\n");
1228145246Sbrooks               printf("%s",  trad );
1229145246Sbrooks               if (mb < 0)     /* XXX not really legal... */
1230145246Sbrooks                   printf(":%s",
1231145246Sbrooks                       inet_ntop(AF_INET6, &a[1], trad, sizeof(trad)));
1232145246Sbrooks               else if (mb < 128)
1233145246Sbrooks                   printf("/%d", mb);
1234145246Sbrooks           }
1235145246Sbrooks           if (len > 2)
1236145246Sbrooks               printf(",");
1237145246Sbrooks       }
1238145246Sbrooks}
1239145246Sbrooks
1240145246Sbrooksstatic void
1241145246Sbrooksfill_icmp6types(ipfw_insn_icmp6 *cmd, char *av)
1242145246Sbrooks{
1243145246Sbrooks       uint8_t type;
1244145246Sbrooks
1245162344Sjhay       bzero(cmd, sizeof(*cmd));
1246145246Sbrooks       while (*av) {
1247145246Sbrooks           if (*av == ',')
1248145246Sbrooks               av++;
1249145246Sbrooks           type = strtoul(av, &av, 0);
1250145246Sbrooks           if (*av != ',' && *av != '\0')
1251145246Sbrooks               errx(EX_DATAERR, "invalid ICMP6 type");
1252145246Sbrooks	   /*
1253145246Sbrooks	    * XXX: shouldn't this be 0xFF?  I can't see any reason why
1254145246Sbrooks	    * we shouldn't be able to filter all possiable values
1255145246Sbrooks	    * regardless of the ability of the rest of the kernel to do
1256145246Sbrooks	    * anything useful with them.
1257145246Sbrooks	    */
1258145246Sbrooks           if (type > ICMP6_MAXTYPE)
1259145246Sbrooks               errx(EX_DATAERR, "ICMP6 type out of range");
1260145246Sbrooks           cmd->d[type / 32] |= ( 1 << (type % 32));
1261145246Sbrooks       }
1262145246Sbrooks       cmd->o.opcode = O_ICMP6TYPE;
1263145246Sbrooks       cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6);
1264145246Sbrooks}
1265145246Sbrooks
1266145246Sbrooks
1267145246Sbrooksstatic void
1268145246Sbrooksprint_icmp6types(ipfw_insn_u32 *cmd)
1269145246Sbrooks{
1270145246Sbrooks       int i, j;
1271145246Sbrooks       char sep= ' ';
1272145246Sbrooks
1273152923Sume       printf(" ip6 icmp6types");
1274145246Sbrooks       for (i = 0; i < 7; i++)
1275145246Sbrooks               for (j=0; j < 32; ++j) {
1276145246Sbrooks                       if ( (cmd->d[i] & (1 << (j))) == 0)
1277145246Sbrooks                               continue;
1278145246Sbrooks                       printf("%c%d", sep, (i*32 + j));
1279145246Sbrooks                       sep = ',';
1280145246Sbrooks               }
1281145246Sbrooks}
1282145246Sbrooks
1283145246Sbrooksstatic void
1284145246Sbrooksprint_flow6id( ipfw_insn_u32 *cmd)
1285145246Sbrooks{
1286145246Sbrooks       uint16_t i, limit = cmd->o.arg1;
1287145246Sbrooks       char sep = ',';
1288145246Sbrooks
1289145246Sbrooks       printf(" flow-id ");
1290145246Sbrooks       for( i=0; i < limit; ++i) {
1291145246Sbrooks               if (i == limit - 1)
1292145246Sbrooks                       sep = ' ';
1293145246Sbrooks               printf("%d%c", cmd->d[i], sep);
1294145246Sbrooks       }
1295145246Sbrooks}
1296145246Sbrooks
1297145246Sbrooks/* structure and define for the extension header in ipv6 */
1298145246Sbrooksstatic struct _s_x ext6hdrcodes[] = {
1299145246Sbrooks       { "frag",       EXT_FRAGMENT },
1300145246Sbrooks       { "hopopt",     EXT_HOPOPTS },
1301145246Sbrooks       { "route",      EXT_ROUTING },
1302149020Sbz       { "dstopt",     EXT_DSTOPTS },
1303145246Sbrooks       { "ah",         EXT_AH },
1304145246Sbrooks       { "esp",        EXT_ESP },
1305169245Sbz       { "rthdr0",     EXT_RTHDR0 },
1306169245Sbz       { "rthdr2",     EXT_RTHDR2 },
1307145246Sbrooks       { NULL,         0 }
1308145246Sbrooks};
1309145246Sbrooks
1310145246Sbrooks/* fills command for the extension header filtering */
1311145246Sbrooksint
1312145246Sbrooksfill_ext6hdr( ipfw_insn *cmd, char *av)
1313145246Sbrooks{
1314145246Sbrooks       int tok;
1315145246Sbrooks       char *s = av;
1316145246Sbrooks
1317145246Sbrooks       cmd->arg1 = 0;
1318145246Sbrooks
1319145246Sbrooks       while(s) {
1320145246Sbrooks           av = strsep( &s, ",") ;
1321145246Sbrooks           tok = match_token(ext6hdrcodes, av);
1322145246Sbrooks           switch (tok) {
1323145246Sbrooks           case EXT_FRAGMENT:
1324145246Sbrooks               cmd->arg1 |= EXT_FRAGMENT;
1325145246Sbrooks               break;
1326145246Sbrooks
1327145246Sbrooks           case EXT_HOPOPTS:
1328145246Sbrooks               cmd->arg1 |= EXT_HOPOPTS;
1329145246Sbrooks               break;
1330145246Sbrooks
1331145246Sbrooks           case EXT_ROUTING:
1332145246Sbrooks               cmd->arg1 |= EXT_ROUTING;
1333145246Sbrooks               break;
1334145246Sbrooks
1335149020Sbz           case EXT_DSTOPTS:
1336149020Sbz               cmd->arg1 |= EXT_DSTOPTS;
1337149020Sbz               break;
1338149020Sbz
1339145246Sbrooks           case EXT_AH:
1340145246Sbrooks               cmd->arg1 |= EXT_AH;
1341145246Sbrooks               break;
1342145246Sbrooks
1343145246Sbrooks           case EXT_ESP:
1344145246Sbrooks               cmd->arg1 |= EXT_ESP;
1345145246Sbrooks               break;
1346145246Sbrooks
1347169245Sbz           case EXT_RTHDR0:
1348169245Sbz               cmd->arg1 |= EXT_RTHDR0;
1349169245Sbz               break;
1350169245Sbz
1351169245Sbz           case EXT_RTHDR2:
1352169245Sbz               cmd->arg1 |= EXT_RTHDR2;
1353169245Sbz               break;
1354169245Sbz
1355145246Sbrooks           default:
1356145246Sbrooks               errx( EX_DATAERR, "invalid option for ipv6 exten header" );
1357145246Sbrooks               break;
1358145246Sbrooks           }
1359145246Sbrooks       }
1360145246Sbrooks       if (cmd->arg1 == 0 )
1361145246Sbrooks           return 0;
1362145246Sbrooks       cmd->opcode = O_EXT_HDR;
1363145246Sbrooks       cmd->len |= F_INSN_SIZE( ipfw_insn );
1364145246Sbrooks       return 1;
1365145246Sbrooks}
1366145246Sbrooks
1367145246Sbrooksvoid
1368145246Sbrooksprint_ext6hdr( ipfw_insn *cmd )
1369145246Sbrooks{
1370145246Sbrooks       char sep = ' ';
1371145246Sbrooks
1372145246Sbrooks       printf(" extension header:");
1373145246Sbrooks       if (cmd->arg1 & EXT_FRAGMENT ) {
1374145246Sbrooks           printf("%cfragmentation", sep);
1375145246Sbrooks           sep = ',';
1376145246Sbrooks       }
1377145246Sbrooks       if (cmd->arg1 & EXT_HOPOPTS ) {
1378145246Sbrooks           printf("%chop options", sep);
1379145246Sbrooks           sep = ',';
1380145246Sbrooks       }
1381145246Sbrooks       if (cmd->arg1 & EXT_ROUTING ) {
1382145246Sbrooks           printf("%crouting options", sep);
1383145246Sbrooks           sep = ',';
1384145246Sbrooks       }
1385169245Sbz       if (cmd->arg1 & EXT_RTHDR0 ) {
1386169245Sbz           printf("%crthdr0", sep);
1387169245Sbz           sep = ',';
1388169245Sbz       }
1389169245Sbz       if (cmd->arg1 & EXT_RTHDR2 ) {
1390169245Sbz           printf("%crthdr2", sep);
1391169245Sbz           sep = ',';
1392169245Sbz       }
1393149020Sbz       if (cmd->arg1 & EXT_DSTOPTS ) {
1394149020Sbz           printf("%cdestination options", sep);
1395149020Sbz           sep = ',';
1396149020Sbz       }
1397145246Sbrooks       if (cmd->arg1 & EXT_AH ) {
1398145246Sbrooks           printf("%cauthentication header", sep);
1399145246Sbrooks           sep = ',';
1400145246Sbrooks       }
1401145246Sbrooks       if (cmd->arg1 & EXT_ESP ) {
1402145246Sbrooks           printf("%cencapsulated security payload", sep);
1403145246Sbrooks       }
1404145246Sbrooks}
1405145246Sbrooks
140698943Sluigi/*
140798943Sluigi * show_ipfw() prints the body of an ipfw rule.
140898943Sluigi * Because the standard rule has at least proto src_ip dst_ip, we use
140998943Sluigi * a helper function to produce these entries if not provided explicitly.
1410102087Sluigi * The first argument is the list of fields we have, the second is
1411102087Sluigi * the list of fields we want to be printed.
1412101978Sluigi *
1413102087Sluigi * Special cases if we have provided a MAC header:
1414102087Sluigi *   + if the rule does not contain IP addresses/ports, do not print them;
1415102087Sluigi *   + if the rule does not contain an IP proto, print "all" instead of "ip";
1416102087Sluigi *
1417102087Sluigi * Once we have 'have_options', IP header fields are printed as options.
141898943Sluigi */
1419101978Sluigi#define	HAVE_PROTO	0x0001
1420101978Sluigi#define	HAVE_SRCIP	0x0002
1421101978Sluigi#define	HAVE_DSTIP	0x0004
1422169139Smaxim#define	HAVE_PROTO4	0x0008
1423169139Smaxim#define	HAVE_PROTO6	0x0010
1424102087Sluigi#define	HAVE_OPTIONS	0x8000
142598943Sluigi
1426101978Sluigi#define	HAVE_IP		(HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP)
142798943Sluigistatic void
1428102087Sluigishow_prerequisites(int *flags, int want, int cmd)
142998943Sluigi{
1430123495Sluigi	if (comment_only)
1431123495Sluigi		return;
1432102087Sluigi	if ( (*flags & HAVE_IP) == HAVE_IP)
1433102087Sluigi		*flags |= HAVE_OPTIONS;
1434102087Sluigi
1435102087Sluigi	if ( !(*flags & HAVE_OPTIONS)) {
1436102087Sluigi		if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO))
1437146894Smlaier			if ( (*flags & HAVE_PROTO4))
1438146894Smlaier				printf(" ip4");
1439146894Smlaier			else if ( (*flags & HAVE_PROTO6))
1440146894Smlaier				printf(" ip6");
1441146894Smlaier			else
1442146894Smlaier				printf(" ip");
1443146894Smlaier
1444102087Sluigi		if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
1445102087Sluigi			printf(" from any");
1446102087Sluigi		if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP))
1447102087Sluigi			printf(" to any");
1448102087Sluigi	}
144998943Sluigi	*flags |= want;
145098943Sluigi}
145198943Sluigi
145298943Sluigistatic void
1453112189Smaximshow_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
145498943Sluigi{
1455107291Skeramida	static int twidth = 0;
145698943Sluigi	int l;
1457158879Soleg	ipfw_insn *cmd, *tagptr = NULL;
1458117626Sluigi	char *comment = NULL;	/* ptr to comment if we have one */
145998943Sluigi	int proto = 0;		/* default */
146098943Sluigi	int flags = 0;	/* prerequisites */
146198943Sluigi	ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */
1462136071Sgreen	ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */
146398943Sluigi	int or_block = 0;	/* we are in an or block */
1464117328Sluigi	uint32_t set_disable;
146598943Sluigi
1466115793Sticso	bcopy(&rule->next_rule, &set_disable, sizeof(set_disable));
1467101628Sluigi
1468101628Sluigi	if (set_disable & (1 << rule->set)) { /* disabled */
1469101628Sluigi		if (!show_sets)
1470101628Sluigi			return;
1471101628Sluigi		else
1472101628Sluigi			printf("# DISABLED ");
1473101628Sluigi	}
147498943Sluigi	printf("%05u ", rule->rulenum);
147598943Sluigi
1476117469Sluigi	if (pcwidth>0 || bcwidth>0)
1477115793Sticso		printf("%*llu %*llu ", pcwidth, align_uint64(&rule->pcnt),
1478115793Sticso		    bcwidth, align_uint64(&rule->bcnt));
147998943Sluigi
1480117472Sluigi	if (do_time == 2)
1481117472Sluigi		printf("%10u ", rule->timestamp);
1482117472Sluigi	else if (do_time == 1) {
1483107291Skeramida		char timestr[30];
1484107291Skeramida		time_t t = (time_t)0;
1485107291Skeramida
1486107291Skeramida		if (twidth == 0) {
1487107291Skeramida			strcpy(timestr, ctime(&t));
1488107291Skeramida			*strchr(timestr, '\n') = '\0';
1489107291Skeramida			twidth = strlen(timestr);
1490107291Skeramida		}
149198943Sluigi		if (rule->timestamp) {
1492107291Skeramida			t = _long_to_time(rule->timestamp);
149398943Sluigi
149498943Sluigi			strcpy(timestr, ctime(&t));
149598943Sluigi			*strchr(timestr, '\n') = '\0';
149698943Sluigi			printf("%s ", timestr);
149798943Sluigi		} else {
1498107291Skeramida			printf("%*s", twidth, " ");
149998943Sluigi		}
150098943Sluigi	}
150198943Sluigi
1502101628Sluigi	if (show_sets)
1503101628Sluigi		printf("set %d ", rule->set);
1504101628Sluigi
150598943Sluigi	/*
1506107289Sluigi	 * print the optional "match probability"
1507107289Sluigi	 */
1508107289Sluigi	if (rule->cmd_len > 0) {
1509107289Sluigi		cmd = rule->cmd ;
1510107289Sluigi		if (cmd->opcode == O_PROB) {
1511107289Sluigi			ipfw_insn_u32 *p = (ipfw_insn_u32 *)cmd;
1512107289Sluigi			double d = 1.0 * p->d[0];
1513107289Sluigi
1514107289Sluigi			d = (d / 0x7fffffff);
1515107289Sluigi			printf("prob %f ", d);
1516107289Sluigi		}
1517107289Sluigi	}
1518107289Sluigi
1519107289Sluigi	/*
152098943Sluigi	 * first print actions
152198943Sluigi	 */
152298943Sluigi        for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule);
152398943Sluigi			l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
152498943Sluigi		switch(cmd->opcode) {
152598943Sluigi		case O_CHECK_STATE:
152698943Sluigi			printf("check-state");
1527102087Sluigi			flags = HAVE_IP; /* avoid printing anything else */
152898943Sluigi			break;
152998943Sluigi
153098943Sluigi		case O_ACCEPT:
153198943Sluigi			printf("allow");
153298943Sluigi			break;
153398943Sluigi
153498943Sluigi		case O_COUNT:
153598943Sluigi			printf("count");
153698943Sluigi			break;
153798943Sluigi
153898943Sluigi		case O_DENY:
153998943Sluigi			printf("deny");
154098943Sluigi			break;
154198943Sluigi
154299475Sluigi		case O_REJECT:
154399475Sluigi			if (cmd->arg1 == ICMP_REJECT_RST)
154499475Sluigi				printf("reset");
154599475Sluigi			else if (cmd->arg1 == ICMP_UNREACH_HOST)
154699475Sluigi				printf("reject");
154799475Sluigi			else
154899475Sluigi				print_reject_code(cmd->arg1);
154999475Sluigi			break;
155099475Sluigi
1551149020Sbz		case O_UNREACH6:
1552149020Sbz			if (cmd->arg1 == ICMP6_UNREACH_RST)
1553149020Sbz				printf("reset6");
1554149020Sbz			else
1555149020Sbz				print_unreach6_code(cmd->arg1);
1556149020Sbz			break;
1557149020Sbz
1558159636Soleg		case O_SKIPTO:
1559159636Soleg			PRINT_UINT_ARG("skipto ", cmd->arg1);
156098943Sluigi			break;
156198943Sluigi
156298943Sluigi		case O_PIPE:
1563159636Soleg			PRINT_UINT_ARG("pipe ", cmd->arg1);
1564159636Soleg			break;
1565159636Soleg
156698943Sluigi		case O_QUEUE:
1567159636Soleg			PRINT_UINT_ARG("queue ", cmd->arg1);
1568159636Soleg			break;
1569159636Soleg
157098943Sluigi		case O_DIVERT:
1571159636Soleg			PRINT_UINT_ARG("divert ", cmd->arg1);
1572159636Soleg			break;
1573159636Soleg
157498943Sluigi		case O_TEE:
1575159636Soleg			PRINT_UINT_ARG("tee ", cmd->arg1);
1576159636Soleg			break;
1577159636Soleg
1578141351Sglebius		case O_NETGRAPH:
1579159636Soleg			PRINT_UINT_ARG("netgraph ", cmd->arg1);
1580159636Soleg			break;
1581159636Soleg
1582141351Sglebius		case O_NGTEE:
1583159636Soleg			PRINT_UINT_ARG("ngtee ", cmd->arg1);
1584159636Soleg			break;
1585141351Sglebius
158698943Sluigi		case O_FORWARD_IP:
158798943Sluigi		    {
158898943Sluigi			ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
158998943Sluigi
1590161424Sjulian			if (s->sa.sin_addr.s_addr == INADDR_ANY) {
1591161424Sjulian				printf("fwd tablearg");
1592161424Sjulian			} else {
1593161424Sjulian				printf("fwd %s", inet_ntoa(s->sa.sin_addr));
1594161424Sjulian			}
159598943Sluigi			if (s->sa.sin_port)
1596103241Sluigi				printf(",%d", s->sa.sin_port);
159798943Sluigi		    }
159898943Sluigi			break;
159998943Sluigi
160098943Sluigi		case O_LOG: /* O_LOG is printed last */
160198943Sluigi			logptr = (ipfw_insn_log *)cmd;
160298943Sluigi			break;
160398943Sluigi
1604136071Sgreen		case O_ALTQ: /* O_ALTQ is printed after O_LOG */
1605136071Sgreen			altqptr = (ipfw_insn_altq *)cmd;
1606136071Sgreen			break;
1607136071Sgreen
1608158879Soleg		case O_TAG:
1609158879Soleg			tagptr = cmd;
1610158879Soleg			break;
1611158879Soleg
1612165648Spiso		case O_NAT:
1613165648Spiso 			printf("nat %u", cmd->arg1);
1614165648Spiso 			break;
1615165648Spiso
161698943Sluigi		default:
1617136071Sgreen			printf("** unrecognized action %d len %d ",
161898943Sluigi				cmd->opcode, cmd->len);
161998943Sluigi		}
162098943Sluigi	}
162198943Sluigi	if (logptr) {
162298943Sluigi		if (logptr->max_log > 0)
162399909Sluigi			printf(" log logamount %d", logptr->max_log);
162498943Sluigi		else
162599909Sluigi			printf(" log");
162698943Sluigi	}
1627136071Sgreen	if (altqptr) {
1628136071Sgreen		const char *qname;
1629102087Sluigi
1630136071Sgreen		qname = altq_qid_to_name(altqptr->qid);
1631136071Sgreen		if (qname == NULL)
1632136071Sgreen			printf(" altq ?<%u>", altqptr->qid);
1633136071Sgreen		else
1634136071Sgreen			printf(" altq %s", qname);
1635136071Sgreen	}
1636158879Soleg	if (tagptr) {
1637158879Soleg		if (tagptr->len & F_NOT)
1638159636Soleg			PRINT_UINT_ARG(" untag ", tagptr->arg1);
1639158879Soleg		else
1640159636Soleg			PRINT_UINT_ARG(" tag ", tagptr->arg1);
1641158879Soleg	}
1642136071Sgreen
164398943Sluigi	/*
1644102087Sluigi	 * then print the body.
164598943Sluigi	 */
1646146894Smlaier        for (l = rule->act_ofs, cmd = rule->cmd ;
1647146894Smlaier			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
1648146894Smlaier		if ((cmd->len & F_OR) || (cmd->len & F_NOT))
1649146894Smlaier			continue;
1650146894Smlaier		if (cmd->opcode == O_IP4) {
1651146894Smlaier			flags |= HAVE_PROTO4;
1652146894Smlaier			break;
1653146894Smlaier		} else if (cmd->opcode == O_IP6) {
1654146894Smlaier			flags |= HAVE_PROTO6;
1655146894Smlaier			break;
1656146894Smlaier		}
1657146894Smlaier	}
1658102087Sluigi	if (rule->_pad & 1) {	/* empty rules before options */
1659146894Smlaier		if (!do_compact) {
1660146894Smlaier			show_prerequisites(&flags, HAVE_PROTO, 0);
1661146894Smlaier			printf(" from any to any");
1662146894Smlaier		}
1663102087Sluigi		flags |= HAVE_IP | HAVE_OPTIONS;
1664102087Sluigi	}
1665102087Sluigi
1666123495Sluigi	if (comment_only)
1667123495Sluigi		comment = "...";
1668123495Sluigi
166998943Sluigi        for (l = rule->act_ofs, cmd = rule->cmd ;
167098943Sluigi			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
167199475Sluigi		/* useful alias */
167299475Sluigi		ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
167398943Sluigi
1674123495Sluigi		if (comment_only) {
1675123495Sluigi			if (cmd->opcode != O_NOP)
1676123495Sluigi				continue;
1677123495Sluigi			printf(" // %s\n", (char *)(cmd + 1));
1678123495Sluigi			return;
1679123495Sluigi		}
1680123495Sluigi
1681102087Sluigi		show_prerequisites(&flags, 0, cmd->opcode);
1682102087Sluigi
168398943Sluigi		switch(cmd->opcode) {
1684117577Sluigi		case O_PROB:
1685107289Sluigi			break;	/* done already */
1686107289Sluigi
168798943Sluigi		case O_PROBE_STATE:
168898943Sluigi			break; /* no need to print anything here */
168998943Sluigi
169098943Sluigi		case O_IP_SRC:
1691130281Sru		case O_IP_SRC_LOOKUP:
169298943Sluigi		case O_IP_SRC_MASK:
169398943Sluigi		case O_IP_SRC_ME:
169498943Sluigi		case O_IP_SRC_SET:
1695102087Sluigi			show_prerequisites(&flags, HAVE_PROTO, 0);
169698943Sluigi			if (!(flags & HAVE_SRCIP))
169798943Sluigi				printf(" from");
169898943Sluigi			if ((cmd->len & F_OR) && !or_block)
169998943Sluigi				printf(" {");
1700102087Sluigi			print_ip((ipfw_insn_ip *)cmd,
1701102087Sluigi				(flags & HAVE_OPTIONS) ? " src-ip" : "");
170298943Sluigi			flags |= HAVE_SRCIP;
170398943Sluigi			break;
170498943Sluigi
170598943Sluigi		case O_IP_DST:
1706130281Sru		case O_IP_DST_LOOKUP:
170798943Sluigi		case O_IP_DST_MASK:
170898943Sluigi		case O_IP_DST_ME:
170998943Sluigi		case O_IP_DST_SET:
1710102087Sluigi			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
171198943Sluigi			if (!(flags & HAVE_DSTIP))
171298943Sluigi				printf(" to");
171398943Sluigi			if ((cmd->len & F_OR) && !or_block)
171498943Sluigi				printf(" {");
1715102087Sluigi			print_ip((ipfw_insn_ip *)cmd,
1716102087Sluigi				(flags & HAVE_OPTIONS) ? " dst-ip" : "");
171798943Sluigi			flags |= HAVE_DSTIP;
171898943Sluigi			break;
171998943Sluigi
1720145246Sbrooks		case O_IP6_SRC:
1721145246Sbrooks		case O_IP6_SRC_MASK:
1722145246Sbrooks		case O_IP6_SRC_ME:
1723147105Smlaier			show_prerequisites(&flags, HAVE_PROTO, 0);
1724145246Sbrooks			if (!(flags & HAVE_SRCIP))
1725145246Sbrooks				printf(" from");
1726145246Sbrooks			if ((cmd->len & F_OR) && !or_block)
1727145246Sbrooks				printf(" {");
1728145246Sbrooks			print_ip6((ipfw_insn_ip6 *)cmd,
1729145246Sbrooks			    (flags & HAVE_OPTIONS) ? " src-ip6" : "");
1730145246Sbrooks			flags |= HAVE_SRCIP | HAVE_PROTO;
1731145246Sbrooks			break;
1732145246Sbrooks
1733145246Sbrooks		case O_IP6_DST:
1734145246Sbrooks		case O_IP6_DST_MASK:
1735145246Sbrooks		case O_IP6_DST_ME:
1736145246Sbrooks			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1737145246Sbrooks			if (!(flags & HAVE_DSTIP))
1738145246Sbrooks				printf(" to");
1739145246Sbrooks			if ((cmd->len & F_OR) && !or_block)
1740145246Sbrooks				printf(" {");
1741145246Sbrooks			print_ip6((ipfw_insn_ip6 *)cmd,
1742145246Sbrooks			    (flags & HAVE_OPTIONS) ? " dst-ip6" : "");
1743145246Sbrooks			flags |= HAVE_DSTIP;
1744145246Sbrooks			break;
1745145246Sbrooks
1746145246Sbrooks		case O_FLOW6ID:
1747145246Sbrooks		print_flow6id( (ipfw_insn_u32 *) cmd );
1748145246Sbrooks		flags |= HAVE_OPTIONS;
1749145246Sbrooks		break;
1750145246Sbrooks
175198943Sluigi		case O_IP_DSTPORT:
1752102087Sluigi			show_prerequisites(&flags, HAVE_IP, 0);
175398943Sluigi		case O_IP_SRCPORT:
1754102087Sluigi			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1755101641Sluigi			if ((cmd->len & F_OR) && !or_block)
1756101641Sluigi				printf(" {");
1757102087Sluigi			print_newports((ipfw_insn_u16 *)cmd, proto,
1758102087Sluigi				(flags & HAVE_OPTIONS) ? cmd->opcode : 0);
175998943Sluigi			break;
176098943Sluigi
176198943Sluigi		case O_PROTO: {
1762145246Sbrooks			struct protoent *pe = NULL;
176398943Sluigi
176498943Sluigi			if ((cmd->len & F_OR) && !or_block)
176598943Sluigi				printf(" {");
176698943Sluigi			if (cmd->len & F_NOT)
176798943Sluigi				printf(" not");
176898943Sluigi			proto = cmd->arg1;
1769145567Sbrooks			pe = getprotobynumber(cmd->arg1);
1770146894Smlaier			if ((flags & (HAVE_PROTO4 | HAVE_PROTO6)) &&
1771146894Smlaier			    !(flags & HAVE_PROTO))
1772146894Smlaier				show_prerequisites(&flags,
1773146894Smlaier				    HAVE_IP | HAVE_OPTIONS, 0);
1774102087Sluigi			if (flags & HAVE_OPTIONS)
1775102087Sluigi				printf(" proto");
177698943Sluigi			if (pe)
177798943Sluigi				printf(" %s", pe->p_name);
177898943Sluigi			else
177998943Sluigi				printf(" %u", cmd->arg1);
178098943Sluigi			}
178198943Sluigi			flags |= HAVE_PROTO;
178298943Sluigi			break;
1783106505Smaxim
178498943Sluigi		default: /*options ... */
1785146894Smlaier			if (!(cmd->len & (F_OR|F_NOT)))
1786146894Smlaier				if (((cmd->opcode == O_IP6) &&
1787146894Smlaier				    (flags & HAVE_PROTO6)) ||
1788146894Smlaier				    ((cmd->opcode == O_IP4) &&
1789146894Smlaier				    (flags & HAVE_PROTO4)))
1790146894Smlaier					break;
1791102087Sluigi			show_prerequisites(&flags, HAVE_IP | HAVE_OPTIONS, 0);
179298943Sluigi			if ((cmd->len & F_OR) && !or_block)
179398943Sluigi				printf(" {");
179498943Sluigi			if (cmd->len & F_NOT && cmd->opcode != O_IN)
179598943Sluigi				printf(" not");
179698943Sluigi			switch(cmd->opcode) {
1797169139Smaxim			case O_MACADDR2: {
1798169139Smaxim				ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
1799169139Smaxim
1800169139Smaxim				printf(" MAC");
1801169139Smaxim				print_mac(m->addr, m->mask);
1802169139Smaxim				print_mac(m->addr + 6, m->mask + 6);
1803169139Smaxim				}
1804169139Smaxim				break;
1805169139Smaxim
1806169139Smaxim			case O_MAC_TYPE:
1807169139Smaxim				print_newports((ipfw_insn_u16 *)cmd,
1808169139Smaxim						IPPROTO_ETHERTYPE, cmd->opcode);
1809169139Smaxim				break;
1810169139Smaxim
1811169139Smaxim
181298943Sluigi			case O_FRAG:
181398943Sluigi				printf(" frag");
181498943Sluigi				break;
181598943Sluigi
181698943Sluigi			case O_IN:
181798943Sluigi				printf(cmd->len & F_NOT ? " out" : " in");
181898943Sluigi				break;
181998943Sluigi
1820136073Sgreen			case O_DIVERTED:
1821136073Sgreen				switch (cmd->arg1) {
1822136073Sgreen				case 3:
1823136073Sgreen					printf(" diverted");
1824136073Sgreen					break;
1825136073Sgreen				case 1:
1826136073Sgreen					printf(" diverted-loopback");
1827136073Sgreen					break;
1828136073Sgreen				case 2:
1829136073Sgreen					printf(" diverted-output");
1830136073Sgreen					break;
1831136073Sgreen				default:
1832136073Sgreen					printf(" diverted-?<%u>", cmd->arg1);
1833136073Sgreen					break;
1834136073Sgreen				}
1835136073Sgreen				break;
1836136073Sgreen
183798943Sluigi			case O_LAYER2:
183898943Sluigi				printf(" layer2");
183998943Sluigi				break;
184098943Sluigi			case O_XMIT:
184198943Sluigi			case O_RECV:
1842140423Sglebius			case O_VIA:
1843140423Sglebius			    {
1844117469Sluigi				char const *s;
184598943Sluigi				ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd;
184698943Sluigi
184798943Sluigi				if (cmd->opcode == O_XMIT)
184898943Sluigi					s = "xmit";
184998943Sluigi				else if (cmd->opcode == O_RECV)
185098943Sluigi					s = "recv";
1851117469Sluigi				else /* if (cmd->opcode == O_VIA) */
185298943Sluigi					s = "via";
185398943Sluigi				if (cmdif->name[0] == '\0')
185499475Sluigi					printf(" %s %s", s,
185599475Sluigi					    inet_ntoa(cmdif->p.ip));
1856140423Sglebius				else
1857140423Sglebius					printf(" %s %s", s, cmdif->name);
1858140423Sglebius
185998943Sluigi				break;
1860140423Sglebius			    }
186198943Sluigi			case O_IPID:
1862116690Sluigi				if (F_LEN(cmd) == 1)
1863116690Sluigi				    printf(" ipid %u", cmd->arg1 );
1864116690Sluigi				else
1865116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1866116690Sluigi					O_IPID);
186798943Sluigi				break;
186898943Sluigi
186998943Sluigi			case O_IPTTL:
1870116690Sluigi				if (F_LEN(cmd) == 1)
1871116690Sluigi				    printf(" ipttl %u", cmd->arg1 );
1872116690Sluigi				else
1873116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1874116690Sluigi					O_IPTTL);
187598943Sluigi				break;
187698943Sluigi
187798943Sluigi			case O_IPVER:
187898943Sluigi				printf(" ipver %u", cmd->arg1 );
187998943Sluigi				break;
188098943Sluigi
188199475Sluigi			case O_IPPRECEDENCE:
188299475Sluigi				printf(" ipprecedence %u", (cmd->arg1) >> 5 );
188399475Sluigi				break;
188499475Sluigi
188598943Sluigi			case O_IPLEN:
1886116690Sluigi				if (F_LEN(cmd) == 1)
1887116690Sluigi				    printf(" iplen %u", cmd->arg1 );
1888116690Sluigi				else
1889116690Sluigi				    print_newports((ipfw_insn_u16 *)cmd, 0,
1890116690Sluigi					O_IPLEN);
189198943Sluigi				break;
189298943Sluigi
1893101116Sluigi			case O_IPOPT:
189498943Sluigi				print_flags("ipoptions", cmd, f_ipopts);
189598943Sluigi				break;
189698943Sluigi
189799475Sluigi			case O_IPTOS:
189899475Sluigi				print_flags("iptos", cmd, f_iptos);
189999475Sluigi				break;
190099475Sluigi
190199475Sluigi			case O_ICMPTYPE:
190299475Sluigi				print_icmptypes((ipfw_insn_u32 *)cmd);
190399475Sluigi				break;
190499475Sluigi
190598943Sluigi			case O_ESTAB:
190698943Sluigi				printf(" established");
190798943Sluigi				break;
190898943Sluigi
1909136075Sgreen			case O_TCPDATALEN:
1910136075Sgreen				if (F_LEN(cmd) == 1)
1911136075Sgreen				    printf(" tcpdatalen %u", cmd->arg1 );
1912136075Sgreen				else
1913136075Sgreen				    print_newports((ipfw_insn_u16 *)cmd, 0,
1914136075Sgreen					O_TCPDATALEN);
1915136075Sgreen				break;
1916136075Sgreen
191798943Sluigi			case O_TCPFLAGS:
191898943Sluigi				print_flags("tcpflags", cmd, f_tcpflags);
191998943Sluigi				break;
192098943Sluigi
192198943Sluigi			case O_TCPOPTS:
192298943Sluigi				print_flags("tcpoptions", cmd, f_tcpopts);
192398943Sluigi				break;
192498943Sluigi
192598943Sluigi			case O_TCPWIN:
192698943Sluigi				printf(" tcpwin %d", ntohs(cmd->arg1));
192798943Sluigi				break;
192898943Sluigi
192998943Sluigi			case O_TCPACK:
193098943Sluigi				printf(" tcpack %d", ntohl(cmd32->d[0]));
193198943Sluigi				break;
193298943Sluigi
193398943Sluigi			case O_TCPSEQ:
193498943Sluigi				printf(" tcpseq %d", ntohl(cmd32->d[0]));
193598943Sluigi				break;
193698943Sluigi
193798943Sluigi			case O_UID:
193898943Sluigi			    {
193998943Sluigi				struct passwd *pwd = getpwuid(cmd32->d[0]);
194098943Sluigi
194198943Sluigi				if (pwd)
194298943Sluigi					printf(" uid %s", pwd->pw_name);
194398943Sluigi				else
194498943Sluigi					printf(" uid %u", cmd32->d[0]);
194598943Sluigi			    }
194698943Sluigi				break;
194798943Sluigi
194898943Sluigi			case O_GID:
194998943Sluigi			    {
195098943Sluigi				struct group *grp = getgrgid(cmd32->d[0]);
195198943Sluigi
195298943Sluigi				if (grp)
195398943Sluigi					printf(" gid %s", grp->gr_name);
195498943Sluigi				else
195598943Sluigi					printf(" gid %u", cmd32->d[0]);
195698943Sluigi			    }
195798943Sluigi				break;
195898943Sluigi
1959133600Scsjp			case O_JAIL:
1960133600Scsjp				printf(" jail %d", cmd32->d[0]);
1961133600Scsjp				break;
1962133600Scsjp
1963112250Scjc			case O_VERREVPATH:
1964112250Scjc				printf(" verrevpath");
1965112250Scjc				break;
1966116919Sluigi
1967128575Sandre			case O_VERSRCREACH:
1968128575Sandre				printf(" versrcreach");
1969128575Sandre				break;
1970128575Sandre
1971133387Sandre			case O_ANTISPOOF:
1972133387Sandre				printf(" antispoof");
1973133387Sandre				break;
1974133387Sandre
1975117241Sluigi			case O_IPSEC:
1976117241Sluigi				printf(" ipsec");
1977117241Sluigi				break;
1978117241Sluigi
1979117469Sluigi			case O_NOP:
1980117626Sluigi				comment = (char *)(cmd + 1);
1981117469Sluigi				break;
1982117469Sluigi
198398943Sluigi			case O_KEEP_STATE:
198498943Sluigi				printf(" keep-state");
198598943Sluigi				break;
198698943Sluigi
1987159636Soleg			case O_LIMIT: {
198898943Sluigi				struct _s_x *p = limit_masks;
198998943Sluigi				ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
1990117328Sluigi				uint8_t x = c->limit_mask;
1991117469Sluigi				char const *comma = " ";
199298943Sluigi
199398943Sluigi				printf(" limit");
1994117577Sluigi				for (; p->x != 0 ; p++)
199599909Sluigi					if ((x & p->x) == p->x) {
199698943Sluigi						x &= ~p->x;
199798943Sluigi						printf("%s%s", comma, p->s);
199898943Sluigi						comma = ",";
199998943Sluigi					}
2000159636Soleg				PRINT_UINT_ARG(" ", c->conn_limit);
200198943Sluigi				break;
2002159636Soleg			}
200398943Sluigi
2004146894Smlaier			case O_IP6:
2005152923Sume				printf(" ip6");
2006145246Sbrooks				break;
2007145246Sbrooks
2008146894Smlaier			case O_IP4:
2009152923Sume				printf(" ip4");
2010146894Smlaier				break;
2011146894Smlaier
2012145246Sbrooks			case O_ICMP6TYPE:
2013145246Sbrooks				print_icmp6types((ipfw_insn_u32 *)cmd);
2014145246Sbrooks				break;
2015145246Sbrooks
2016145246Sbrooks			case O_EXT_HDR:
2017145246Sbrooks				print_ext6hdr( (ipfw_insn *) cmd );
2018145246Sbrooks				break;
2019145246Sbrooks
2020158879Soleg			case O_TAGGED:
2021158879Soleg				if (F_LEN(cmd) == 1)
2022159636Soleg					PRINT_UINT_ARG(" tagged ", cmd->arg1);
2023158879Soleg				else
2024159636Soleg					print_newports((ipfw_insn_u16 *)cmd, 0,
2025159636Soleg					    O_TAGGED);
2026158879Soleg				break;
2027158879Soleg
202898943Sluigi			default:
202998943Sluigi				printf(" [opcode %d len %d]",
203098943Sluigi				    cmd->opcode, cmd->len);
203198943Sluigi			}
203298943Sluigi		}
203398943Sluigi		if (cmd->len & F_OR) {
203498943Sluigi			printf(" or");
203598943Sluigi			or_block = 1;
203698943Sluigi		} else if (or_block) {
203798943Sluigi			printf(" }");
203898943Sluigi			or_block = 0;
203998943Sluigi		}
204098943Sluigi	}
2041102087Sluigi	show_prerequisites(&flags, HAVE_IP, 0);
2042117626Sluigi	if (comment)
2043117626Sluigi		printf(" // %s", comment);
204498943Sluigi	printf("\n");
204598943Sluigi}
204698943Sluigi
204798943Sluigistatic void
2048112189Smaximshow_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth)
204998943Sluigi{
205098943Sluigi	struct protoent *pe;
205198943Sluigi	struct in_addr a;
2052115793Sticso	uint16_t rulenum;
2053159160Smlaier	char buf[INET6_ADDRSTRLEN];
205498943Sluigi
205598943Sluigi	if (!do_expired) {
205698943Sluigi		if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT))
205798943Sluigi			return;
205898943Sluigi	}
2059115793Sticso	bcopy(&d->rule, &rulenum, sizeof(rulenum));
2060117328Sluigi	printf("%05d", rulenum);
2061117469Sluigi	if (pcwidth>0 || bcwidth>0)
2062117328Sluigi	    printf(" %*llu %*llu (%ds)", pcwidth,
2063117328Sluigi		align_uint64(&d->pcnt), bcwidth,
2064117328Sluigi		align_uint64(&d->bcnt), d->expire);
206598943Sluigi	switch (d->dyn_type) {
206698943Sluigi	case O_LIMIT_PARENT:
206798943Sluigi		printf(" PARENT %d", d->count);
206898943Sluigi		break;
206998943Sluigi	case O_LIMIT:
207098943Sluigi		printf(" LIMIT");
207198943Sluigi		break;
207298943Sluigi	case O_KEEP_STATE: /* bidir, no mask */
2073106505Smaxim		printf(" STATE");
207498943Sluigi		break;
207598943Sluigi	}
207698943Sluigi
207798943Sluigi	if ((pe = getprotobynumber(d->id.proto)) != NULL)
207898943Sluigi		printf(" %s", pe->p_name);
207998943Sluigi	else
208098943Sluigi		printf(" proto %u", d->id.proto);
208198943Sluigi
2082159160Smlaier	if (d->id.addr_type == 4) {
2083159160Smlaier		a.s_addr = htonl(d->id.src_ip);
2084159160Smlaier		printf(" %s %d", inet_ntoa(a), d->id.src_port);
208598943Sluigi
2086159160Smlaier		a.s_addr = htonl(d->id.dst_ip);
2087159160Smlaier		printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port);
2088159160Smlaier	} else if (d->id.addr_type == 6) {
2089159160Smlaier		printf(" %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf,
2090159160Smlaier		    sizeof(buf)), d->id.src_port);
2091159160Smlaier		printf(" <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf,
2092159160Smlaier		    sizeof(buf)), d->id.dst_port);
2093159160Smlaier	} else
2094159160Smlaier		printf(" UNKNOWN <-> UNKNOWN\n");
2095159160Smlaier
209698943Sluigi	printf("\n");
209798943Sluigi}
209898943Sluigi
2099117469Sluigistatic int
210098943Sluigisort_q(const void *pa, const void *pb)
210198943Sluigi{
210298943Sluigi	int rev = (do_sort < 0);
210398943Sluigi	int field = rev ? -do_sort : do_sort;
210498943Sluigi	long long res = 0;
210598943Sluigi	const struct dn_flow_queue *a = pa;
210698943Sluigi	const struct dn_flow_queue *b = pb;
210798943Sluigi
210898943Sluigi	switch (field) {
210998943Sluigi	case 1: /* pkts */
211098943Sluigi		res = a->len - b->len;
211198943Sluigi		break;
211298943Sluigi	case 2: /* bytes */
211398943Sluigi		res = a->len_bytes - b->len_bytes;
211498943Sluigi		break;
211598943Sluigi
211698943Sluigi	case 3: /* tot pkts */
211798943Sluigi		res = a->tot_pkts - b->tot_pkts;
211898943Sluigi		break;
211998943Sluigi
212098943Sluigi	case 4: /* tot bytes */
212198943Sluigi		res = a->tot_bytes - b->tot_bytes;
212298943Sluigi		break;
212398943Sluigi	}
212498943Sluigi	if (res < 0)
212598943Sluigi		res = -1;
212698943Sluigi	if (res > 0)
212798943Sluigi		res = 1;
212898943Sluigi	return (int)(rev ? res : -res);
212998943Sluigi}
213098943Sluigi
213198943Sluigistatic void
213298943Sluigilist_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
213398943Sluigi{
213498943Sluigi	int l;
2135145246Sbrooks	int index_printed, indexes = 0;
2136145246Sbrooks	char buff[255];
2137145246Sbrooks	struct protoent *pe;
213898943Sluigi
213998943Sluigi	if (fs->rq_elements == 0)
214098943Sluigi		return;
214198943Sluigi
214298943Sluigi	if (do_sort != 0)
214398943Sluigi		heapsort(q, fs->rq_elements, sizeof *q, sort_q);
2144145246Sbrooks
2145145246Sbrooks	/* Print IPv4 flows */
2146145246Sbrooks	index_printed = 0;
214798943Sluigi	for (l = 0; l < fs->rq_elements; l++) {
214898943Sluigi		struct in_addr ina;
214998943Sluigi
2150145246Sbrooks		/* XXX: Should check for IPv4 flows */
2151145246Sbrooks		if (IS_IP6_FLOW_ID(&(q[l].id)))
2152145246Sbrooks			continue;
2153145246Sbrooks
2154145246Sbrooks		if (!index_printed) {
2155145246Sbrooks			index_printed = 1;
2156145246Sbrooks			if (indexes > 0)	/* currently a no-op */
2157145246Sbrooks				printf("\n");
2158145246Sbrooks			indexes++;
2159145246Sbrooks			printf("    "
2160145246Sbrooks			    "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
2161145246Sbrooks			    fs->flow_mask.proto,
2162145246Sbrooks			    fs->flow_mask.src_ip, fs->flow_mask.src_port,
2163145246Sbrooks			    fs->flow_mask.dst_ip, fs->flow_mask.dst_port);
2164145246Sbrooks
2165145246Sbrooks			printf("BKT Prot ___Source IP/port____ "
2166145246Sbrooks			    "____Dest. IP/port____ "
2167145246Sbrooks			    "Tot_pkt/bytes Pkt/Byte Drp\n");
2168145246Sbrooks		}
2169145246Sbrooks
217098943Sluigi		printf("%3d ", q[l].hash_slot);
217198943Sluigi		pe = getprotobynumber(q[l].id.proto);
217298943Sluigi		if (pe)
217398943Sluigi			printf("%-4s ", pe->p_name);
217498943Sluigi		else
217598943Sluigi			printf("%4u ", q[l].id.proto);
2176145246Sbrooks		ina.s_addr = htonl(q[l].id.src_ip);
217798943Sluigi		printf("%15s/%-5d ",
217898943Sluigi		    inet_ntoa(ina), q[l].id.src_port);
217998943Sluigi		ina.s_addr = htonl(q[l].id.dst_ip);
218098943Sluigi		printf("%15s/%-5d ",
218198943Sluigi		    inet_ntoa(ina), q[l].id.dst_port);
218298943Sluigi		printf("%4qu %8qu %2u %4u %3u\n",
218398943Sluigi		    q[l].tot_pkts, q[l].tot_bytes,
218498943Sluigi		    q[l].len, q[l].len_bytes, q[l].drops);
218598943Sluigi		if (verbose)
218698943Sluigi			printf("   S %20qd  F %20qd\n",
218798943Sluigi			    q[l].S, q[l].F);
218898943Sluigi	}
2189145246Sbrooks
2190145246Sbrooks	/* Print IPv6 flows */
2191145246Sbrooks	index_printed = 0;
2192145246Sbrooks	for (l = 0; l < fs->rq_elements; l++) {
2193145246Sbrooks		if (!IS_IP6_FLOW_ID(&(q[l].id)))
2194145246Sbrooks			continue;
2195145246Sbrooks
2196145246Sbrooks		if (!index_printed) {
2197145246Sbrooks			index_printed = 1;
2198145246Sbrooks			if (indexes > 0)
2199145246Sbrooks				printf("\n");
2200145246Sbrooks			indexes++;
2201145246Sbrooks			printf("\n        mask: proto: 0x%02x, flow_id: 0x%08x,  ",
2202145246Sbrooks			    fs->flow_mask.proto, fs->flow_mask.flow_id6);
2203145246Sbrooks			inet_ntop(AF_INET6, &(fs->flow_mask.src_ip6),
2204145246Sbrooks			    buff, sizeof(buff));
2205145246Sbrooks			printf("%s/0x%04x -> ", buff, fs->flow_mask.src_port);
2206145246Sbrooks			inet_ntop( AF_INET6, &(fs->flow_mask.dst_ip6),
2207145246Sbrooks			    buff, sizeof(buff) );
2208145246Sbrooks			printf("%s/0x%04x\n", buff, fs->flow_mask.dst_port);
2209145246Sbrooks
2210145246Sbrooks			printf("BKT ___Prot___ _flow-id_ "
2211145246Sbrooks			    "______________Source IPv6/port_______________ "
2212145246Sbrooks			    "_______________Dest. IPv6/port_______________ "
2213145246Sbrooks			    "Tot_pkt/bytes Pkt/Byte Drp\n");
2214145246Sbrooks		}
2215145246Sbrooks		printf("%3d ", q[l].hash_slot);
2216145246Sbrooks		pe = getprotobynumber(q[l].id.proto);
2217145246Sbrooks		if (pe != NULL)
2218145246Sbrooks			printf("%9s ", pe->p_name);
2219145246Sbrooks		else
2220145246Sbrooks			printf("%9u ", q[l].id.proto);
2221145246Sbrooks		printf("%7d  %39s/%-5d ", q[l].id.flow_id6,
2222145246Sbrooks		    inet_ntop(AF_INET6, &(q[l].id.src_ip6), buff, sizeof(buff)),
2223145246Sbrooks		    q[l].id.src_port);
2224145246Sbrooks		printf(" %39s/%-5d ",
2225145246Sbrooks		    inet_ntop(AF_INET6, &(q[l].id.dst_ip6), buff, sizeof(buff)),
2226145246Sbrooks		    q[l].id.dst_port);
2227145246Sbrooks		printf(" %4qu %8qu %2u %4u %3u\n",
2228145246Sbrooks		    q[l].tot_pkts, q[l].tot_bytes,
2229145246Sbrooks		    q[l].len, q[l].len_bytes, q[l].drops);
2230145246Sbrooks		if (verbose)
2231145246Sbrooks			printf("   S %20qd  F %20qd\n", q[l].S, q[l].F);
2232145246Sbrooks	}
223398943Sluigi}
223498943Sluigi
223598943Sluigistatic void
223698943Sluigiprint_flowset_parms(struct dn_flow_set *fs, char *prefix)
223798943Sluigi{
223898943Sluigi	int l;
223998943Sluigi	char qs[30];
224098943Sluigi	char plr[30];
224198943Sluigi	char red[90];	/* Display RED parameters */
224298943Sluigi
224398943Sluigi	l = fs->qsize;
224498943Sluigi	if (fs->flags_fs & DN_QSIZE_IS_BYTES) {
224598943Sluigi		if (l >= 8192)
224698943Sluigi			sprintf(qs, "%d KB", l / 1024);
224798943Sluigi		else
224898943Sluigi			sprintf(qs, "%d B", l);
224998943Sluigi	} else
225098943Sluigi		sprintf(qs, "%3d sl.", l);
225198943Sluigi	if (fs->plr)
225298943Sluigi		sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
225398943Sluigi	else
225498943Sluigi		plr[0] = '\0';
225598943Sluigi	if (fs->flags_fs & DN_IS_RED)	/* RED parameters */
225698943Sluigi		sprintf(red,
225798943Sluigi		    "\n\t  %cRED w_q %f min_th %d max_th %d max_p %f",
225898943Sluigi		    (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ',
225998943Sluigi		    1.0 * fs->w_q / (double)(1 << SCALE_RED),
226098943Sluigi		    SCALE_VAL(fs->min_th),
226198943Sluigi		    SCALE_VAL(fs->max_th),
226298943Sluigi		    1.0 * fs->max_p / (double)(1 << SCALE_RED));
226398943Sluigi	else
226498943Sluigi		sprintf(red, "droptail");
226598943Sluigi
226698943Sluigi	printf("%s %s%s %d queues (%d buckets) %s\n",
226798943Sluigi	    prefix, qs, plr, fs->rq_elements, fs->rq_size, red);
226898943Sluigi}
226998943Sluigi
227098943Sluigistatic void
2271117469Sluigilist_pipes(void *data, uint nbytes, int ac, char *av[])
227298943Sluigi{
2273117469Sluigi	int rulenum;
227498943Sluigi	void *next = data;
227598943Sluigi	struct dn_pipe *p = (struct dn_pipe *) data;
227698943Sluigi	struct dn_flow_set *fs;
227798943Sluigi	struct dn_flow_queue *q;
227898943Sluigi	int l;
227998943Sluigi
228098943Sluigi	if (ac > 0)
228198943Sluigi		rulenum = strtoul(*av++, NULL, 10);
228298943Sluigi	else
228398943Sluigi		rulenum = 0;
228498943Sluigi	for (; nbytes >= sizeof *p; p = (struct dn_pipe *)next) {
228598943Sluigi		double b = p->bandwidth;
228698943Sluigi		char buf[30];
228798943Sluigi		char prefix[80];
228898943Sluigi
2289161001Sstefanf		if (SLIST_NEXT(p, next) != (struct dn_pipe *)DN_IS_PIPE)
229098943Sluigi			break;	/* done with pipes, now queues */
229198943Sluigi
229298943Sluigi		/*
229398943Sluigi		 * compute length, as pipe have variable size
229498943Sluigi		 */
229598943Sluigi		l = sizeof(*p) + p->fs.rq_elements * sizeof(*q);
2296117469Sluigi		next = (char *)p + l;
229798943Sluigi		nbytes -= l;
229898943Sluigi
2299134225Spjd		if ((rulenum != 0 && rulenum != p->pipe_nr) || do_pipe == 2)
230098943Sluigi			continue;
230198943Sluigi
230298943Sluigi		/*
230398943Sluigi		 * Print rate (or clocking interface)
230498943Sluigi		 */
230598943Sluigi		if (p->if_name[0] != '\0')
230698943Sluigi			sprintf(buf, "%s", p->if_name);
230798943Sluigi		else if (b == 0)
230898943Sluigi			sprintf(buf, "unlimited");
230998943Sluigi		else if (b >= 1000000)
231098943Sluigi			sprintf(buf, "%7.3f Mbit/s", b/1000000);
231198943Sluigi		else if (b >= 1000)
231298943Sluigi			sprintf(buf, "%7.3f Kbit/s", b/1000);
231398943Sluigi		else
231498943Sluigi			sprintf(buf, "%7.3f bit/s ", b);
231598943Sluigi
231698943Sluigi		sprintf(prefix, "%05d: %s %4d ms ",
231798943Sluigi		    p->pipe_nr, buf, p->delay);
231898943Sluigi		print_flowset_parms(&(p->fs), prefix);
231998943Sluigi		if (verbose)
232098943Sluigi			printf("   V %20qd\n", p->V >> MY_M);
2321106505Smaxim
232298943Sluigi		q = (struct dn_flow_queue *)(p+1);
232398943Sluigi		list_queues(&(p->fs), q);
232498943Sluigi	}
232598943Sluigi	for (fs = next; nbytes >= sizeof *fs; fs = next) {
232698943Sluigi		char prefix[80];
232798943Sluigi
2328161001Sstefanf		if (SLIST_NEXT(fs, next) != (struct dn_flow_set *)DN_IS_QUEUE)
232998943Sluigi			break;
233098943Sluigi		l = sizeof(*fs) + fs->rq_elements * sizeof(*q);
2331117469Sluigi		next = (char *)fs + l;
233298943Sluigi		nbytes -= l;
2333134225Spjd
2334134225Spjd		if (rulenum != 0 && ((rulenum != fs->fs_nr && do_pipe == 2) ||
2335134225Spjd		    (rulenum != fs->parent_nr && do_pipe == 1))) {
2336134225Spjd			continue;
2337134225Spjd		}
2338134225Spjd
233998943Sluigi		q = (struct dn_flow_queue *)(fs+1);
234098943Sluigi		sprintf(prefix, "q%05d: weight %d pipe %d ",
234198943Sluigi		    fs->fs_nr, fs->weight, fs->parent_nr);
234298943Sluigi		print_flowset_parms(fs, prefix);
234398943Sluigi		list_queues(fs, q);
234498943Sluigi	}
234598943Sluigi}
234698943Sluigi
2347101978Sluigi/*
2348101978Sluigi * This one handles all set-related commands
2349101978Sluigi * 	ipfw set { show | enable | disable }
2350101978Sluigi * 	ipfw set swap X Y
2351101978Sluigi * 	ipfw set move X to Y
2352101978Sluigi * 	ipfw set move rule X to Y
2353101978Sluigi */
235498943Sluigistatic void
2355101978Sluigisets_handler(int ac, char *av[])
2356101978Sluigi{
2357117328Sluigi	uint32_t set_disable, masks[2];
2358101978Sluigi	int i, nbytes;
2359117328Sluigi	uint16_t rulenum;
2360117328Sluigi	uint8_t cmd, new_set;
2361101978Sluigi
2362101978Sluigi	ac--;
2363101978Sluigi	av++;
2364101978Sluigi
2365101978Sluigi	if (!ac)
2366101978Sluigi		errx(EX_USAGE, "set needs command");
2367140271Sbrooks	if (_substrcmp(*av, "show") == 0) {
2368101978Sluigi		void *data;
2369117469Sluigi		char const *msg;
2370101978Sluigi
2371101978Sluigi		nbytes = sizeof(struct ip_fw);
2372117328Sluigi		if ((data = calloc(1, nbytes)) == NULL)
2373117328Sluigi			err(EX_OSERR, "calloc");
2374119740Stmm		if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0)
2375101978Sluigi			err(EX_OSERR, "getsockopt(IP_FW_GET)");
2376115793Sticso		bcopy(&((struct ip_fw *)data)->next_rule,
2377115793Sticso			&set_disable, sizeof(set_disable));
2378101978Sluigi
2379117655Sluigi		for (i = 0, msg = "disable" ; i < RESVD_SET; i++)
2380117577Sluigi			if ((set_disable & (1<<i))) {
2381101978Sluigi				printf("%s %d", msg, i);
2382101978Sluigi				msg = "";
2383101978Sluigi			}
2384101978Sluigi		msg = (set_disable) ? " enable" : "enable";
2385117655Sluigi		for (i = 0; i < RESVD_SET; i++)
2386117577Sluigi			if (!(set_disable & (1<<i))) {
2387101978Sluigi				printf("%s %d", msg, i);
2388101978Sluigi				msg = "";
2389101978Sluigi			}
2390101978Sluigi		printf("\n");
2391140271Sbrooks	} else if (_substrcmp(*av, "swap") == 0) {
2392101978Sluigi		ac--; av++;
2393101978Sluigi		if (ac != 2)
2394101978Sluigi			errx(EX_USAGE, "set swap needs 2 set numbers\n");
2395101978Sluigi		rulenum = atoi(av[0]);
2396101978Sluigi		new_set = atoi(av[1]);
2397117655Sluigi		if (!isdigit(*(av[0])) || rulenum > RESVD_SET)
2398101978Sluigi			errx(EX_DATAERR, "invalid set number %s\n", av[0]);
2399117655Sluigi		if (!isdigit(*(av[1])) || new_set > RESVD_SET)
2400101978Sluigi			errx(EX_DATAERR, "invalid set number %s\n", av[1]);
2401101978Sluigi		masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
2402117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
2403140271Sbrooks	} else if (_substrcmp(*av, "move") == 0) {
2404101978Sluigi		ac--; av++;
2405140271Sbrooks		if (ac && _substrcmp(*av, "rule") == 0) {
2406101978Sluigi			cmd = 2;
2407101978Sluigi			ac--; av++;
2408101978Sluigi		} else
2409101978Sluigi			cmd = 3;
2410140271Sbrooks		if (ac != 3 || _substrcmp(av[1], "to") != 0)
2411101978Sluigi			errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
2412101978Sluigi		rulenum = atoi(av[0]);
2413101978Sluigi		new_set = atoi(av[2]);
2414117655Sluigi		if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) ||
2415101978Sluigi			(cmd == 2 && rulenum == 65535) )
2416101978Sluigi			errx(EX_DATAERR, "invalid source number %s\n", av[0]);
2417117655Sluigi		if (!isdigit(*(av[2])) || new_set > RESVD_SET)
2418101978Sluigi			errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
2419101978Sluigi		masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
2420117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
2421140271Sbrooks	} else if (_substrcmp(*av, "disable") == 0 ||
2422140271Sbrooks		   _substrcmp(*av, "enable") == 0 ) {
2423140271Sbrooks		int which = _substrcmp(*av, "enable") == 0 ? 1 : 0;
2424101978Sluigi
2425101978Sluigi		ac--; av++;
2426101978Sluigi		masks[0] = masks[1] = 0;
2427101978Sluigi
2428101978Sluigi		while (ac) {
2429101978Sluigi			if (isdigit(**av)) {
2430101978Sluigi				i = atoi(*av);
2431117655Sluigi				if (i < 0 || i > RESVD_SET)
2432101978Sluigi					errx(EX_DATAERR,
2433101978Sluigi					    "invalid set number %d\n", i);
2434101978Sluigi				masks[which] |= (1<<i);
2435140271Sbrooks			} else if (_substrcmp(*av, "disable") == 0)
2436101978Sluigi				which = 0;
2437140271Sbrooks			else if (_substrcmp(*av, "enable") == 0)
2438101978Sluigi				which = 1;
2439101978Sluigi			else
2440101978Sluigi				errx(EX_DATAERR,
2441101978Sluigi					"invalid set command %s\n", *av);
2442101978Sluigi			av++; ac--;
2443101978Sluigi		}
2444101978Sluigi		if ( (masks[0] & masks[1]) != 0 )
2445101978Sluigi			errx(EX_DATAERR,
2446101978Sluigi			    "cannot enable and disable the same set\n");
2447101978Sluigi
2448117328Sluigi		i = do_cmd(IP_FW_DEL, masks, sizeof(masks));
2449101978Sluigi		if (i)
2450101978Sluigi			warn("set enable/disable: setsockopt(IP_FW_DEL)");
2451101978Sluigi	} else
2452101978Sluigi		errx(EX_USAGE, "invalid set command %s\n", *av);
2453101978Sluigi}
2454101978Sluigi
2455101978Sluigistatic void
2456109126Sdillonsysctl_handler(int ac, char *av[], int which)
2457109126Sdillon{
2458109126Sdillon	ac--;
2459109126Sdillon	av++;
2460109126Sdillon
2461119668Smaxim	if (ac == 0) {
2462109126Sdillon		warnx("missing keyword to enable/disable\n");
2463140271Sbrooks	} else if (_substrcmp(*av, "firewall") == 0) {
2464116770Sluigi		sysctlbyname("net.inet.ip.fw.enable", NULL, 0,
2465116770Sluigi		    &which, sizeof(which));
2466140271Sbrooks	} else if (_substrcmp(*av, "one_pass") == 0) {
2467116770Sluigi		sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0,
2468116770Sluigi		    &which, sizeof(which));
2469140271Sbrooks	} else if (_substrcmp(*av, "debug") == 0) {
2470116770Sluigi		sysctlbyname("net.inet.ip.fw.debug", NULL, 0,
2471116770Sluigi		    &which, sizeof(which));
2472140271Sbrooks	} else if (_substrcmp(*av, "verbose") == 0) {
2473116770Sluigi		sysctlbyname("net.inet.ip.fw.verbose", NULL, 0,
2474116770Sluigi		    &which, sizeof(which));
2475140271Sbrooks	} else if (_substrcmp(*av, "dyn_keepalive") == 0) {
2476116770Sluigi		sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0,
2477116770Sluigi		    &which, sizeof(which));
2478140271Sbrooks	} else if (_substrcmp(*av, "altq") == 0) {
2479136071Sgreen		altq_set_enabled(which);
2480109126Sdillon	} else {
2481109126Sdillon		warnx("unrecognize enable/disable keyword: %s\n", *av);
2482109126Sdillon	}
2483109126Sdillon}
2484109126Sdillon
2485109126Sdillonstatic void
2486117469Sluigilist(int ac, char *av[], int show_counters)
248798943Sluigi{
248898943Sluigi	struct ip_fw *r;
248998943Sluigi	ipfw_dyn_rule *dynrules, *d;
249098943Sluigi
2491117469Sluigi#define NEXT(r)	((struct ip_fw *)((char *)r + RULESIZE(r)))
2492117469Sluigi	char *lim;
2493117469Sluigi	void *data = NULL;
2494112189Smaxim	int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width;
249598943Sluigi	int exitval = EX_OK;
249698943Sluigi	int lac;
249798943Sluigi	char **lav;
2498117469Sluigi	u_long rnum, last;
249998943Sluigi	char *endptr;
250098943Sluigi	int seen = 0;
250198943Sluigi
250298943Sluigi	const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
250398943Sluigi	int nalloc = 1024;	/* start somewhere... */
250498943Sluigi
2505135036Smaxim	last = 0;
2506135036Smaxim
2507117328Sluigi	if (test_only) {
2508117328Sluigi		fprintf(stderr, "Testing only, list disabled\n");
2509117328Sluigi		return;
2510117328Sluigi	}
2511117328Sluigi
251298943Sluigi	ac--;
251398943Sluigi	av++;
251498943Sluigi
251598943Sluigi	/* get rules or pipes from kernel, resizing array as necessary */
251698943Sluigi	nbytes = nalloc;
251798943Sluigi
251898943Sluigi	while (nbytes >= nalloc) {
251998943Sluigi		nalloc = nalloc * 2 + 200;
252098943Sluigi		nbytes = nalloc;
252198943Sluigi		if ((data = realloc(data, nbytes)) == NULL)
252298943Sluigi			err(EX_OSERR, "realloc");
2523119740Stmm		if (do_cmd(ocmd, data, (uintptr_t)&nbytes) < 0)
252498943Sluigi			err(EX_OSERR, "getsockopt(IP_%s_GET)",
252598943Sluigi				do_pipe ? "DUMMYNET" : "FW");
252698943Sluigi	}
252798943Sluigi
252898943Sluigi	if (do_pipe) {
252998943Sluigi		list_pipes(data, nbytes, ac, av);
253098943Sluigi		goto done;
253198943Sluigi	}
253298943Sluigi
253398943Sluigi	/*
253498943Sluigi	 * Count static rules. They have variable size so we
253598943Sluigi	 * need to scan the list to count them.
253698943Sluigi	 */
2537117469Sluigi	for (nstat = 1, r = data, lim = (char *)data + nbytes;
2538117469Sluigi		    r->rulenum < 65535 && (char *)r < lim;
2539117469Sluigi		    ++nstat, r = NEXT(r) )
254098943Sluigi		; /* nothing */
254198943Sluigi
254298943Sluigi	/*
254398943Sluigi	 * Count dynamic rules. This is easier as they have
254498943Sluigi	 * fixed size.
254598943Sluigi	 */
2546117469Sluigi	r = NEXT(r);
254798943Sluigi	dynrules = (ipfw_dyn_rule *)r ;
2548117469Sluigi	n = (char *)r - (char *)data;
254998943Sluigi	ndyn = (nbytes - n) / sizeof *dynrules;
255098943Sluigi
2551112189Smaxim	/* if showing stats, figure out column widths ahead of time */
2552112189Smaxim	bcwidth = pcwidth = 0;
2553117469Sluigi	if (show_counters) {
2554117469Sluigi		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
2555112189Smaxim			/* packet counter */
2556115793Sticso			width = snprintf(NULL, 0, "%llu",
2557115793Sticso			    align_uint64(&r->pcnt));
2558112189Smaxim			if (width > pcwidth)
2559112189Smaxim				pcwidth = width;
2560112189Smaxim
2561112189Smaxim			/* byte counter */
2562115793Sticso			width = snprintf(NULL, 0, "%llu",
2563115793Sticso			    align_uint64(&r->bcnt));
2564112189Smaxim			if (width > bcwidth)
2565112189Smaxim				bcwidth = width;
2566112189Smaxim		}
2567112189Smaxim	}
2568112189Smaxim	if (do_dynamic && ndyn) {
2569112189Smaxim		for (n = 0, d = dynrules; n < ndyn; n++, d++) {
2570115793Sticso			width = snprintf(NULL, 0, "%llu",
2571115793Sticso			    align_uint64(&d->pcnt));
2572112189Smaxim			if (width > pcwidth)
2573112189Smaxim				pcwidth = width;
2574112189Smaxim
2575115793Sticso			width = snprintf(NULL, 0, "%llu",
2576115793Sticso			    align_uint64(&d->bcnt));
2577112189Smaxim			if (width > bcwidth)
2578112189Smaxim				bcwidth = width;
2579112189Smaxim		}
2580112189Smaxim	}
258198943Sluigi	/* if no rule numbers were specified, list all rules */
258298943Sluigi	if (ac == 0) {
2583117469Sluigi		for (n = 0, r = data; n < nstat; n++, r = NEXT(r) )
2584112189Smaxim			show_ipfw(r, pcwidth, bcwidth);
258598943Sluigi
258698943Sluigi		if (do_dynamic && ndyn) {
258798943Sluigi			printf("## Dynamic rules (%d):\n", ndyn);
258898943Sluigi			for (n = 0, d = dynrules; n < ndyn; n++, d++)
2589112189Smaxim				show_dyn_ipfw(d, pcwidth, bcwidth);
259098943Sluigi		}
259198943Sluigi		goto done;
259298943Sluigi	}
259398943Sluigi
259498943Sluigi	/* display specific rules requested on command line */
259598943Sluigi
259698943Sluigi	for (lac = ac, lav = av; lac != 0; lac--) {
259798943Sluigi		/* convert command line rule # */
2598117469Sluigi		last = rnum = strtoul(*lav++, &endptr, 10);
2599117469Sluigi		if (*endptr == '-')
2600117469Sluigi			last = strtoul(endptr+1, &endptr, 10);
260198943Sluigi		if (*endptr) {
260298943Sluigi			exitval = EX_USAGE;
260398943Sluigi			warnx("invalid rule number: %s", *(lav - 1));
260498943Sluigi			continue;
260598943Sluigi		}
2606117469Sluigi		for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) {
2607117469Sluigi			if (r->rulenum > last)
260898943Sluigi				break;
2609117469Sluigi			if (r->rulenum >= rnum && r->rulenum <= last) {
2610112189Smaxim				show_ipfw(r, pcwidth, bcwidth);
261198943Sluigi				seen = 1;
261298943Sluigi			}
261398943Sluigi		}
261498943Sluigi		if (!seen) {
261598943Sluigi			/* give precedence to other error(s) */
261698943Sluigi			if (exitval == EX_OK)
261798943Sluigi				exitval = EX_UNAVAILABLE;
261898943Sluigi			warnx("rule %lu does not exist", rnum);
261998943Sluigi		}
262098943Sluigi	}
262198943Sluigi
262298943Sluigi	if (do_dynamic && ndyn) {
262398943Sluigi		printf("## Dynamic rules:\n");
262498943Sluigi		for (lac = ac, lav = av; lac != 0; lac--) {
2625145246Sbrooks			last = rnum = strtoul(*lav++, &endptr, 10);
2626117469Sluigi			if (*endptr == '-')
2627117469Sluigi				last = strtoul(endptr+1, &endptr, 10);
262898943Sluigi			if (*endptr)
262998943Sluigi				/* already warned */
263098943Sluigi				continue;
263198943Sluigi			for (n = 0, d = dynrules; n < ndyn; n++, d++) {
2632115793Sticso				uint16_t rulenum;
2633115793Sticso
2634115793Sticso				bcopy(&d->rule, &rulenum, sizeof(rulenum));
2635115793Sticso				if (rulenum > rnum)
263698943Sluigi					break;
2637117469Sluigi				if (r->rulenum >= rnum && r->rulenum <= last)
2638112189Smaxim					show_dyn_ipfw(d, pcwidth, bcwidth);
263998943Sluigi			}
264098943Sluigi		}
264198943Sluigi	}
264298943Sluigi
264398943Sluigi	ac = 0;
264498943Sluigi
264598943Sluigidone:
264698943Sluigi	free(data);
264798943Sluigi
264898943Sluigi	if (exitval != EX_OK)
264998943Sluigi		exit(exitval);
2650117469Sluigi#undef NEXT
265198943Sluigi}
265298943Sluigi
265398943Sluigistatic void
265498943Sluigishow_usage(void)
265598943Sluigi{
265698943Sluigi	fprintf(stderr, "usage: ipfw [options]\n"
265798943Sluigi"do \"ipfw -h\" or see ipfw manpage for details\n"
265898943Sluigi);
265998943Sluigi	exit(EX_USAGE);
266098943Sluigi}
266198943Sluigi
266298943Sluigistatic void
266398943Sluigihelp(void)
266498943Sluigi{
2665117328Sluigi	fprintf(stderr,
2666117328Sluigi"ipfw syntax summary (but please do read the ipfw(8) manpage):\n"
2667123495Sluigi"ipfw [-abcdefhnNqStTv] <command> where <command> is one of:\n"
2668117328Sluigi"add [num] [set N] [prob x] RULE-BODY\n"
2669117328Sluigi"{pipe|queue} N config PIPE-BODY\n"
2670117328Sluigi"[pipe|queue] {zero|delete|show} [N{,N}]\n"
2671165648Spiso"nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|reset|\n"
2672165648Spiso"		reverse|proxy_only|redirect_addr linkspec|\n"
2673165648Spiso"		redirect_port linkspec|redirect_proto linkspec}\n"
2674117328Sluigi"set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n"
2675130281Sru"table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n"
267698943Sluigi"\n"
2677136071Sgreen"RULE-BODY:	check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n"
2678149020Sbz"ACTION:	check-state | allow | count | deny | unreach{,6} CODE |\n"
2679149020Sbz"               skipto N | {divert|tee} PORT | forward ADDR |\n"
2680165648Spiso"               pipe N | queue N | nat N\n"
2681136071Sgreen"PARAMS: 	[log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n"
268298943Sluigi"ADDR:		[ MAC dst src ether_type ] \n"
2683145246Sbrooks"		[ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
2684145246Sbrooks"		[ ipv6|ip6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n"
2685130281Sru"IPADDR:	[not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n"
2686145246Sbrooks"IP6ADDR:	[not] { any | me | me6 | ip6/bits | IP6LIST }\n"
2687145246Sbrooks"IP6LIST:	{ ip6 | ip6/bits }[,IP6LIST]\n"
2688117544Sluigi"IPLIST:	{ ip | ip/bits | ip:mask }[,IPLIST]\n"
2689117544Sluigi"OPTION_LIST:	OPTION [OPTION_LIST]\n"
2690136247Sgreen"OPTION:	bridged | diverted | diverted-loopback | diverted-output |\n"
2691145246Sbrooks"	{dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n"
2692145246Sbrooks"	{dst-port|src-port} LIST |\n"
2693117328Sluigi"	estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n"
2694117328Sluigi"	iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n"
2695117328Sluigi"	ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n"
2696145246Sbrooks"	icmp6types LIST | ext6hdr LIST | flow-id N[,N] |\n"
2697117328Sluigi"	mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n"
2698117328Sluigi"	setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n"
2699136075Sgreen"	tcpdatalen LIST | verrevpath | versrcreach | antispoof\n"
270098943Sluigi);
270198943Sluigiexit(0);
270298943Sluigi}
270398943Sluigi
270498943Sluigi
270598943Sluigistatic int
270698943Sluigilookup_host (char *host, struct in_addr *ipaddr)
270798943Sluigi{
270898943Sluigi	struct hostent *he;
270998943Sluigi
271098943Sluigi	if (!inet_aton(host, ipaddr)) {
271198943Sluigi		if ((he = gethostbyname(host)) == NULL)
271298943Sluigi			return(-1);
271398943Sluigi		*ipaddr = *(struct in_addr *)he->h_addr_list[0];
271498943Sluigi	}
271598943Sluigi	return(0);
271698943Sluigi}
271798943Sluigi
271898943Sluigi/*
271998943Sluigi * fills the addr and mask fields in the instruction as appropriate from av.
272098943Sluigi * Update length as appropriate.
272198943Sluigi * The following formats are allowed:
272298943Sluigi *	me	returns O_IP_*_ME
272398943Sluigi *	1.2.3.4		single IP address
272498943Sluigi *	1.2.3.4:5.6.7.8	address:mask
272598943Sluigi *	1.2.3.4/24	address/mask
272698943Sluigi *	1.2.3.4/26{1,6,5,4,23}	set of addresses in a subnet
2727117328Sluigi * We can have multiple comma-separated address/mask entries.
272898943Sluigi */
272998943Sluigistatic void
273098943Sluigifill_ip(ipfw_insn_ip *cmd, char *av)
273198943Sluigi{
2732117328Sluigi	int len = 0;
2733117328Sluigi	uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
273498943Sluigi
273598943Sluigi	cmd->o.len &= ~F_LEN_MASK;	/* zero len */
273698943Sluigi
2737140271Sbrooks	if (_substrcmp(av, "any") == 0)
273898943Sluigi		return;
273998943Sluigi
2740140271Sbrooks	if (_substrcmp(av, "me") == 0) {
274198943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn);
274298943Sluigi		return;
274398943Sluigi	}
274498943Sluigi
2745140271Sbrooks	if (strncmp(av, "table(", 6) == 0) {
2746130281Sru		char *p = strchr(av + 6, ',');
2747130281Sru
2748130281Sru		if (p)
2749130281Sru			*p++ = '\0';
2750130281Sru		cmd->o.opcode = O_IP_DST_LOOKUP;
2751130281Sru		cmd->o.arg1 = strtoul(av + 6, NULL, 0);
2752130281Sru		if (p) {
2753130281Sru			cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2754130281Sru			d[0] = strtoul(p, NULL, 0);
2755130281Sru		} else
2756130281Sru			cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2757130281Sru		return;
2758130281Sru	}
2759130281Sru
2760117328Sluigi    while (av) {
2761117328Sluigi	/*
2762117328Sluigi	 * After the address we can have '/' or ':' indicating a mask,
2763117328Sluigi	 * ',' indicating another address follows, '{' indicating a
2764117328Sluigi	 * set of addresses of unspecified size.
2765117328Sluigi	 */
2766165851Smlaier	char *t = NULL, *p = strpbrk(av, "/:,{");
2767117328Sluigi	int masklen;
2768165851Smlaier	char md, nd;
2769117328Sluigi
277098943Sluigi	if (p) {
277198943Sluigi		md = *p;
277298943Sluigi		*p++ = '\0';
2773165851Smlaier		if ((t = strpbrk(p, ",{")) != NULL) {
2774165851Smlaier			nd = *t;
2775165851Smlaier			*t = '\0';
2776165851Smlaier		}
2777117328Sluigi	} else
2778117328Sluigi		md = '\0';
277998943Sluigi
2780117328Sluigi	if (lookup_host(av, (struct in_addr *)&d[0]) != 0)
278198943Sluigi		errx(EX_NOHOST, "hostname ``%s'' unknown", av);
278298943Sluigi	switch (md) {
278398943Sluigi	case ':':
2784117328Sluigi		if (!inet_aton(p, (struct in_addr *)&d[1]))
278598943Sluigi			errx(EX_DATAERR, "bad netmask ``%s''", p);
278698943Sluigi		break;
278798943Sluigi	case '/':
2788117328Sluigi		masklen = atoi(p);
2789117328Sluigi		if (masklen == 0)
2790117328Sluigi			d[1] = htonl(0);	/* mask */
2791117328Sluigi		else if (masklen > 32)
279298943Sluigi			errx(EX_DATAERR, "bad width ``%s''", p);
279398943Sluigi		else
2794117328Sluigi			d[1] = htonl(~0 << (32 - masklen));
279598943Sluigi		break;
2796117328Sluigi	case '{':	/* no mask, assume /24 and put back the '{' */
2797117328Sluigi		d[1] = htonl(~0 << (32 - 24));
2798117328Sluigi		*(--p) = md;
2799117328Sluigi		break;
2800117328Sluigi
2801117328Sluigi	case ',':	/* single address plus continuation */
2802117328Sluigi		*(--p) = md;
2803117328Sluigi		/* FALLTHROUGH */
2804117328Sluigi	case 0:		/* initialization value */
280598943Sluigi	default:
2806117328Sluigi		d[1] = htonl(~0);	/* force /32 */
280798943Sluigi		break;
280898943Sluigi	}
2809117328Sluigi	d[0] &= d[1];		/* mask base address with mask */
2810165851Smlaier	if (t)
2811165851Smlaier		*t = nd;
2812117328Sluigi	/* find next separator */
281398943Sluigi	if (p)
2814117328Sluigi		p = strpbrk(p, ",{");
2815117328Sluigi	if (p && *p == '{') {
2816117328Sluigi		/*
2817117328Sluigi		 * We have a set of addresses. They are stored as follows:
2818117328Sluigi		 *   arg1	is the set size (powers of 2, 2..256)
2819117328Sluigi		 *   addr	is the base address IN HOST FORMAT
2820117328Sluigi		 *   mask..	is an array of arg1 bits (rounded up to
2821117328Sluigi		 *		the next multiple of 32) with bits set
2822117328Sluigi		 *		for each host in the map.
2823117328Sluigi		 */
2824117328Sluigi		uint32_t *map = (uint32_t *)&cmd->mask;
282598943Sluigi		int low, high;
2826117577Sluigi		int i = contigmask((uint8_t *)&(d[1]), 32);
282798943Sluigi
2828117328Sluigi		if (len > 0)
2829117328Sluigi			errx(EX_DATAERR, "address set cannot be in a list");
2830117328Sluigi		if (i < 24 || i > 31)
2831117328Sluigi			errx(EX_DATAERR, "invalid set with mask %d\n", i);
2832117328Sluigi		cmd->o.arg1 = 1<<(32-i);	/* map length		*/
2833117328Sluigi		d[0] = ntohl(d[0]);		/* base addr in host format */
283498943Sluigi		cmd->o.opcode = O_IP_DST_SET;	/* default */
283598943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32;
2836101117Sluigi		for (i = 0; i < (cmd->o.arg1+31)/32 ; i++)
2837117328Sluigi			map[i] = 0;	/* clear map */
283898943Sluigi
2839117328Sluigi		av = p + 1;
2840117328Sluigi		low = d[0] & 0xff;
284198943Sluigi		high = low + cmd->o.arg1 - 1;
2842117328Sluigi		/*
2843117328Sluigi		 * Here, i stores the previous value when we specify a range
2844117328Sluigi		 * of addresses within a mask, e.g. 45-63. i = -1 means we
2845117328Sluigi		 * have no previous value.
2846117328Sluigi		 */
2847116716Sluigi		i = -1;	/* previous value in a range */
284898943Sluigi		while (isdigit(*av)) {
284998943Sluigi			char *s;
2850117328Sluigi			int a = strtol(av, &s, 0);
285198943Sluigi
2852117328Sluigi			if (s == av) { /* no parameter */
2853117328Sluigi			    if (*av != '}')
2854117328Sluigi				errx(EX_DATAERR, "set not closed\n");
2855117328Sluigi			    if (i != -1)
2856117328Sluigi				errx(EX_DATAERR, "incomplete range %d-", i);
2857117328Sluigi			    break;
2858117328Sluigi			}
2859117328Sluigi			if (a < low || a > high)
2860117328Sluigi			    errx(EX_DATAERR, "addr %d out of range [%d-%d]\n",
286198943Sluigi				a, low, high);
286298943Sluigi			a -= low;
2863116716Sluigi			if (i == -1)	/* no previous in range */
2864116716Sluigi			    i = a;
2865116716Sluigi			else {		/* check that range is valid */
2866116716Sluigi			    if (i > a)
2867116716Sluigi				errx(EX_DATAERR, "invalid range %d-%d",
2868116716Sluigi					i+low, a+low);
2869116716Sluigi			    if (*s == '-')
2870116716Sluigi				errx(EX_DATAERR, "double '-' in range");
2871116716Sluigi			}
2872116716Sluigi			for (; i <= a; i++)
2873117328Sluigi			    map[i/32] |= 1<<(i & 31);
2874116716Sluigi			i = -1;
2875116716Sluigi			if (*s == '-')
2876116716Sluigi			    i = a;
2877117328Sluigi			else if (*s == '}')
2878116716Sluigi			    break;
287998943Sluigi			av = s+1;
288098943Sluigi		}
288198943Sluigi		return;
288298943Sluigi	}
2883117328Sluigi	av = p;
2884117328Sluigi	if (av)			/* then *av must be a ',' */
2885117328Sluigi		av++;
288698943Sluigi
2887117328Sluigi	/* Check this entry */
2888117328Sluigi	if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */
2889117328Sluigi		/*
2890117328Sluigi		 * 'any' turns the entire list into a NOP.
2891117328Sluigi		 * 'not any' never matches, so it is removed from the
2892117328Sluigi		 * list unless it is the only item, in which case we
2893117328Sluigi		 * report an error.
2894117328Sluigi		 */
2895117328Sluigi		if (cmd->o.len & F_NOT) {	/* "not any" never matches */
2896117328Sluigi			if (av == NULL && len == 0) /* only this entry */
2897117328Sluigi				errx(EX_DATAERR, "not any never matches");
2898117328Sluigi		}
2899117328Sluigi		/* else do nothing and skip this entry */
2900128067Smaxim		return;
2901117328Sluigi	}
2902117328Sluigi	/* A single IP can be stored in an optimized format */
2903117328Sluigi	if (d[1] == IP_MASK_ALL && av == NULL && len == 0) {
290498943Sluigi		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2905117328Sluigi		return;
2906117328Sluigi	}
2907117328Sluigi	len += 2;	/* two words... */
2908117328Sluigi	d += 2;
2909117328Sluigi    } /* end while */
2910162363Sjhay    if (len + 1 > F_LEN_MASK)
2911162363Sjhay	errx(EX_DATAERR, "address list too long");
2912117328Sluigi    cmd->o.len |= len+1;
291398943Sluigi}
291498943Sluigi
291598943Sluigi
2916145246Sbrooks/* Try to find ipv6 address by hostname */
2917145246Sbrooksstatic int
2918145246Sbrookslookup_host6 (char *host, struct in6_addr *ip6addr)
2919145246Sbrooks{
2920145246Sbrooks	struct hostent *he;
2921145246Sbrooks
2922145246Sbrooks	if (!inet_pton(AF_INET6, host, ip6addr)) {
2923145246Sbrooks		if ((he = gethostbyname2(host, AF_INET6)) == NULL)
2924145246Sbrooks			return(-1);
2925145246Sbrooks		memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr));
2926145246Sbrooks	}
2927145246Sbrooks	return(0);
2928145246Sbrooks}
2929145246Sbrooks
2930145246Sbrooks
2931145246Sbrooks/* n2mask sets n bits of the mask */
2932145246Sbrooksstatic void
2933145246Sbrooksn2mask(struct in6_addr *mask, int n)
2934145246Sbrooks{
2935145246Sbrooks	static int	minimask[9] =
2936145246Sbrooks	    { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
2937145246Sbrooks	u_char		*p;
2938145246Sbrooks
2939145246Sbrooks	memset(mask, 0, sizeof(struct in6_addr));
2940145246Sbrooks	p = (u_char *) mask;
2941145246Sbrooks	for (; n > 0; p++, n -= 8) {
2942145246Sbrooks		if (n >= 8)
2943145246Sbrooks			*p = 0xff;
2944145246Sbrooks		else
2945145246Sbrooks			*p = minimask[n];
2946145246Sbrooks	}
2947145246Sbrooks	return;
2948145246Sbrooks}
2949145246Sbrooks
2950145246Sbrooks
295198943Sluigi/*
2952145246Sbrooks * fill the addr and mask fields in the instruction as appropriate from av.
2953145246Sbrooks * Update length as appropriate.
2954145246Sbrooks * The following formats are allowed:
2955145246Sbrooks *     any     matches any IP6. Actually returns an empty instruction.
2956145246Sbrooks *     me      returns O_IP6_*_ME
2957145246Sbrooks *
2958145246Sbrooks *     03f1::234:123:0342                single IP6 addres
2959145246Sbrooks *     03f1::234:123:0342/24            address/mask
2960145246Sbrooks *     03f1::234:123:0342/24,03f1::234:123:0343/               List of address
2961145246Sbrooks *
2962145246Sbrooks * Set of address (as in ipv6) not supported because ipv6 address
2963145246Sbrooks * are typically random past the initial prefix.
2964145246Sbrooks * Return 1 on success, 0 on failure.
2965145246Sbrooks */
2966145246Sbrooksstatic int
2967145246Sbrooksfill_ip6(ipfw_insn_ip6 *cmd, char *av)
2968145246Sbrooks{
2969145246Sbrooks	int len = 0;
2970145246Sbrooks	struct in6_addr *d = &(cmd->addr6);
2971145246Sbrooks	/*
2972145246Sbrooks	 * Needed for multiple address.
2973145246Sbrooks	 * Note d[1] points to struct in6_add r mask6 of cmd
2974145246Sbrooks	 */
2975145246Sbrooks
2976145246Sbrooks       cmd->o.len &= ~F_LEN_MASK;	/* zero len */
2977145246Sbrooks
2978145246Sbrooks       if (strcmp(av, "any") == 0)
2979145246Sbrooks	       return (1);
2980145246Sbrooks
2981145246Sbrooks
2982145246Sbrooks       if (strcmp(av, "me") == 0) {	/* Set the data for "me" opt*/
2983145246Sbrooks	       cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2984145246Sbrooks	       return (1);
2985145246Sbrooks       }
2986145246Sbrooks
2987145246Sbrooks       if (strcmp(av, "me6") == 0) {	/* Set the data for "me" opt*/
2988145246Sbrooks	       cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2989145246Sbrooks	       return (1);
2990145246Sbrooks       }
2991145246Sbrooks
2992145246Sbrooks       av = strdup(av);
2993145246Sbrooks       while (av) {
2994145246Sbrooks		/*
2995145246Sbrooks		 * After the address we can have '/' indicating a mask,
2996145246Sbrooks		 * or ',' indicating another address follows.
2997145246Sbrooks		 */
2998145246Sbrooks
2999145246Sbrooks		char *p;
3000145246Sbrooks		int masklen;
3001145246Sbrooks		char md = '\0';
3002145246Sbrooks
3003145246Sbrooks		if ((p = strpbrk(av, "/,")) ) {
3004145246Sbrooks			md = *p;	/* save the separator */
3005145246Sbrooks			*p = '\0';	/* terminate address string */
3006145246Sbrooks			p++;		/* and skip past it */
3007145246Sbrooks		}
3008145246Sbrooks		/* now p points to NULL, mask or next entry */
3009145246Sbrooks
3010145246Sbrooks		/* lookup stores address in *d as a side effect */
3011145246Sbrooks		if (lookup_host6(av, d) != 0) {
3012145246Sbrooks			/* XXX: failed. Free memory and go */
3013145246Sbrooks			errx(EX_DATAERR, "bad address \"%s\"", av);
3014145246Sbrooks		}
3015145246Sbrooks		/* next, look at the mask, if any */
3016145246Sbrooks		masklen = (md == '/') ? atoi(p) : 128;
3017145246Sbrooks		if (masklen > 128 || masklen < 0)
3018145246Sbrooks			errx(EX_DATAERR, "bad width \"%s\''", p);
3019145246Sbrooks		else
3020145246Sbrooks			n2mask(&d[1], masklen);
3021145246Sbrooks
3022145246Sbrooks		APPLY_MASK(d, &d[1])   /* mask base address with mask */
3023145246Sbrooks
3024145246Sbrooks		/* find next separator */
3025145246Sbrooks
3026145246Sbrooks		if (md == '/') {	/* find separator past the mask */
3027145246Sbrooks			p = strpbrk(p, ",");
3028145246Sbrooks			if (p != NULL)
3029145246Sbrooks				p++;
3030145246Sbrooks		}
3031145246Sbrooks		av = p;
3032145246Sbrooks
3033145246Sbrooks		/* Check this entry */
3034145246Sbrooks		if (masklen == 0) {
3035145246Sbrooks			/*
3036145246Sbrooks			 * 'any' turns the entire list into a NOP.
3037145246Sbrooks			 * 'not any' never matches, so it is removed from the
3038145246Sbrooks			 * list unless it is the only item, in which case we
3039145246Sbrooks			 * report an error.
3040145246Sbrooks			 */
3041145246Sbrooks			if (cmd->o.len & F_NOT && av == NULL && len == 0)
3042145246Sbrooks				errx(EX_DATAERR, "not any never matches");
3043145246Sbrooks			continue;
3044145246Sbrooks		}
3045145246Sbrooks
3046145246Sbrooks		/*
3047145246Sbrooks		 * A single IP can be stored alone
3048145246Sbrooks		 */
3049145246Sbrooks		if (masklen == 128 && av == NULL && len == 0) {
3050145246Sbrooks			len = F_INSN_SIZE(struct in6_addr);
3051145246Sbrooks			break;
3052145246Sbrooks		}
3053145246Sbrooks
3054145246Sbrooks		/* Update length and pointer to arguments */
3055145246Sbrooks		len += F_INSN_SIZE(struct in6_addr)*2;
3056145246Sbrooks		d += 2;
3057145246Sbrooks	} /* end while */
3058145246Sbrooks
3059145246Sbrooks	/*
3060145246Sbrooks	 * Total length of the command, remember that 1 is the size of
3061145246Sbrooks	 * the base command.
3062145246Sbrooks	 */
3063162363Sjhay	if (len + 1 > F_LEN_MASK)
3064162363Sjhay		errx(EX_DATAERR, "address list too long");
3065145246Sbrooks	cmd->o.len |= len+1;
3066145246Sbrooks	free(av);
3067145246Sbrooks	return (1);
3068145246Sbrooks}
3069145246Sbrooks
3070145246Sbrooks/*
3071145246Sbrooks * fills command for ipv6 flow-id filtering
3072145246Sbrooks * note that the 20 bit flow number is stored in a array of u_int32_t
3073145246Sbrooks * it's supported lists of flow-id, so in the o.arg1 we store how many
3074145246Sbrooks * additional flow-id we want to filter, the basic is 1
3075145246Sbrooks */
3076145246Sbrooksvoid
3077145246Sbrooksfill_flow6( ipfw_insn_u32 *cmd, char *av )
3078145246Sbrooks{
3079145246Sbrooks	u_int32_t type;	 /* Current flow number */
3080145246Sbrooks	u_int16_t nflow = 0;    /* Current flow index */
3081145246Sbrooks	char *s = av;
3082145246Sbrooks	cmd->d[0] = 0;	  /* Initializing the base number*/
3083145246Sbrooks
3084145246Sbrooks	while (s) {
3085145246Sbrooks		av = strsep( &s, ",") ;
3086145246Sbrooks		type = strtoul(av, &av, 0);
3087145246Sbrooks		if (*av != ',' && *av != '\0')
3088145246Sbrooks			errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
3089145246Sbrooks		if (type > 0xfffff)
3090145246Sbrooks			errx(EX_DATAERR, "flow number out of range %s", av);
3091145246Sbrooks		cmd->d[nflow] |= type;
3092145246Sbrooks		nflow++;
3093145246Sbrooks	}
3094145246Sbrooks	if( nflow > 0 ) {
3095145246Sbrooks		cmd->o.opcode = O_FLOW6ID;
3096145246Sbrooks		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow;
3097145246Sbrooks		cmd->o.arg1 = nflow;
3098145246Sbrooks	}
3099145246Sbrooks	else {
3100145246Sbrooks		errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
3101145246Sbrooks	}
3102145246Sbrooks}
3103145246Sbrooks
3104145246Sbrooksstatic ipfw_insn *
3105145246Sbrooksadd_srcip6(ipfw_insn *cmd, char *av)
3106145246Sbrooks{
3107145246Sbrooks
3108145246Sbrooks	fill_ip6((ipfw_insn_ip6 *)cmd, av);
3109145246Sbrooks	if (F_LEN(cmd) == 0)				/* any */
3110145246Sbrooks		;
3111145246Sbrooks	if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) {	/* "me" */
3112145246Sbrooks		cmd->opcode = O_IP6_SRC_ME;
3113145246Sbrooks	} else if (F_LEN(cmd) ==
3114145246Sbrooks	    (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
3115145246Sbrooks		/* single IP, no mask*/
3116145246Sbrooks		cmd->opcode = O_IP6_SRC;
3117145246Sbrooks	} else {					/* addr/mask opt */
3118145246Sbrooks		cmd->opcode = O_IP6_SRC_MASK;
3119145246Sbrooks	}
3120145246Sbrooks	return cmd;
3121145246Sbrooks}
3122145246Sbrooks
3123145246Sbrooksstatic ipfw_insn *
3124145246Sbrooksadd_dstip6(ipfw_insn *cmd, char *av)
3125145246Sbrooks{
3126145246Sbrooks
3127145246Sbrooks	fill_ip6((ipfw_insn_ip6 *)cmd, av);
3128145246Sbrooks	if (F_LEN(cmd) == 0)				/* any */
3129145246Sbrooks		;
3130145246Sbrooks	if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) {	/* "me" */
3131145246Sbrooks		cmd->opcode = O_IP6_DST_ME;
3132145246Sbrooks	} else if (F_LEN(cmd) ==
3133145246Sbrooks	    (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
3134145246Sbrooks		/* single IP, no mask*/
3135145246Sbrooks		cmd->opcode = O_IP6_DST;
3136145246Sbrooks	} else {					/* addr/mask opt */
3137145246Sbrooks		cmd->opcode = O_IP6_DST_MASK;
3138145246Sbrooks	}
3139145246Sbrooks	return cmd;
3140145246Sbrooks}
3141145246Sbrooks
3142145246Sbrooks
3143145246Sbrooks/*
314498943Sluigi * helper function to process a set of flags and set bits in the
314598943Sluigi * appropriate masks.
314698943Sluigi */
314798943Sluigistatic void
314898943Sluigifill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
314998943Sluigi	struct _s_x *flags, char *p)
315098943Sluigi{
3151117328Sluigi	uint8_t set=0, clear=0;
315298943Sluigi
315398943Sluigi	while (p && *p) {
315498943Sluigi		char *q;	/* points to the separator */
315598943Sluigi		int val;
3156117328Sluigi		uint8_t *which;	/* mask we are working on */
315798943Sluigi
315898943Sluigi		if (*p == '!') {
315998943Sluigi			p++;
316098943Sluigi			which = &clear;
316198943Sluigi		} else
316298943Sluigi			which = &set;
316398943Sluigi		q = strchr(p, ',');
316498943Sluigi		if (q)
316598943Sluigi			*q++ = '\0';
316698943Sluigi		val = match_token(flags, p);
316798943Sluigi		if (val <= 0)
316898943Sluigi			errx(EX_DATAERR, "invalid flag %s", p);
3169117328Sluigi		*which |= (uint8_t)val;
317098943Sluigi		p = q;
317198943Sluigi	}
317298943Sluigi        cmd->opcode = opcode;
317398943Sluigi        cmd->len =  (cmd->len & (F_NOT | F_OR)) | 1;
317498943Sluigi        cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
317598943Sluigi}
317698943Sluigi
317798943Sluigi
317898943Sluigistatic void
317998943Sluigidelete(int ac, char *av[])
318098943Sluigi{
3181117328Sluigi	uint32_t rulenum;
3182117469Sluigi	struct dn_pipe p;
318398943Sluigi	int i;
318498943Sluigi	int exitval = EX_OK;
3185101628Sluigi	int do_set = 0;
318698943Sluigi
3187117469Sluigi	memset(&p, 0, sizeof p);
318898943Sluigi
318998943Sluigi	av++; ac--;
3190130013Scsjp	NEED1("missing rule specification");
3191140271Sbrooks	if (ac > 0 && _substrcmp(*av, "set") == 0) {
3192101978Sluigi		do_set = 1;	/* delete set */
3193101628Sluigi		ac--; av++;
3194101978Sluigi	}
319598943Sluigi
319698943Sluigi	/* Rule number */
319798943Sluigi	while (ac && isdigit(**av)) {
319898943Sluigi		i = atoi(*av); av++; ac--;
3199165648Spiso		if (do_nat) {
3200165648Spiso			exitval = do_cmd(IP_FW_NAT_DEL, &i, sizeof i);
3201165648Spiso			if (exitval) {
3202165648Spiso				exitval = EX_UNAVAILABLE;
3203165648Spiso				warn("rule %u not available", i);
3204165648Spiso			}
3205165648Spiso 		} else if (do_pipe) {
320698943Sluigi			if (do_pipe == 1)
3207117469Sluigi				p.pipe_nr = i;
320898943Sluigi			else
3209117469Sluigi				p.fs.fs_nr = i;
3210117469Sluigi			i = do_cmd(IP_DUMMYNET_DEL, &p, sizeof p);
321198943Sluigi			if (i) {
321298943Sluigi				exitval = 1;
321398943Sluigi				warn("rule %u: setsockopt(IP_DUMMYNET_DEL)",
3214117469Sluigi				    do_pipe == 1 ? p.pipe_nr : p.fs.fs_nr);
321598943Sluigi			}
321698943Sluigi		} else {
3217101978Sluigi			rulenum =  (i & 0xffff) | (do_set << 24);
3218117328Sluigi			i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum);
321998943Sluigi			if (i) {
322098943Sluigi				exitval = EX_UNAVAILABLE;
322198943Sluigi				warn("rule %u: setsockopt(IP_FW_DEL)",
322298943Sluigi				    rulenum);
322398943Sluigi			}
322498943Sluigi		}
322598943Sluigi	}
322698943Sluigi	if (exitval != EX_OK)
322798943Sluigi		exit(exitval);
322898943Sluigi}
322998943Sluigi
323098943Sluigi
323198943Sluigi/*
323298943Sluigi * fill the interface structure. We do not check the name as we can
323398943Sluigi * create interfaces dynamically, so checking them at insert time
323498943Sluigi * makes relatively little sense.
3235121816Sbrooks * Interface names containing '*', '?', or '[' are assumed to be shell
3236121816Sbrooks * patterns which match interfaces.
323798943Sluigi */
323898943Sluigistatic void
323998943Sluigifill_iface(ipfw_insn_if *cmd, char *arg)
324098943Sluigi{
324198943Sluigi	cmd->name[0] = '\0';
324298943Sluigi	cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
324398943Sluigi
324498943Sluigi	/* Parse the interface or address */
3245140271Sbrooks	if (strcmp(arg, "any") == 0)
324698943Sluigi		cmd->o.len = 0;		/* effectively ignore this command */
324798943Sluigi	else if (!isdigit(*arg)) {
3248121816Sbrooks		strlcpy(cmd->name, arg, sizeof(cmd->name));
3249121816Sbrooks		cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
325098943Sluigi	} else if (!inet_aton(arg, &cmd->p.ip))
325198943Sluigi		errx(EX_DATAERR, "bad ip address ``%s''", arg);
325298943Sluigi}
325398943Sluigi
3254165648Spiso/*
3255165648Spiso * Search for interface with name "ifn", and fill n accordingly:
3256165648Spiso *
3257165648Spiso * n->ip        ip address of interface "ifn"
3258165648Spiso * n->if_name   copy of interface name "ifn"
3259165648Spiso */
326098943Sluigistatic void
3261165648Spisoset_addr_dynamic(const char *ifn, struct cfg_nat *n)
3262165648Spiso{
3263165648Spiso	size_t needed;
3264165648Spiso	int mib[6];
3265165648Spiso	char *buf, *lim, *next;
3266165648Spiso	struct if_msghdr *ifm;
3267165648Spiso	struct ifa_msghdr *ifam;
3268165648Spiso	struct sockaddr_dl *sdl;
3269165648Spiso	struct sockaddr_in *sin;
3270165648Spiso	int ifIndex, ifMTU;
3271165648Spiso
3272165648Spiso	mib[0] = CTL_NET;
3273165648Spiso	mib[1] = PF_ROUTE;
3274165648Spiso	mib[2] = 0;
3275165648Spiso	mib[3] = AF_INET;
3276165648Spiso	mib[4] = NET_RT_IFLIST;
3277165648Spiso	mib[5] = 0;
3278165648Spiso/*
3279165648Spiso * Get interface data.
3280165648Spiso */
3281165648Spiso	if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
3282165648Spiso		err(1, "iflist-sysctl-estimate");
3283165648Spiso	if ((buf = malloc(needed)) == NULL)
3284165648Spiso		errx(1, "malloc failed");
3285165648Spiso	if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
3286165648Spiso		err(1, "iflist-sysctl-get");
3287165648Spiso	lim = buf + needed;
3288165648Spiso/*
3289165648Spiso * Loop through interfaces until one with
3290165648Spiso * given name is found. This is done to
3291165648Spiso * find correct interface index for routing
3292165648Spiso * message processing.
3293165648Spiso */
3294165648Spiso	ifIndex	= 0;
3295165648Spiso	next = buf;
3296165648Spiso	while (next < lim) {
3297165648Spiso		ifm = (struct if_msghdr *)next;
3298165648Spiso		next += ifm->ifm_msglen;
3299165648Spiso		if (ifm->ifm_version != RTM_VERSION) {
3300165648Spiso			if (verbose)
3301165648Spiso				warnx("routing message version %d "
3302165648Spiso				    "not understood", ifm->ifm_version);
3303165648Spiso			continue;
3304165648Spiso		}
3305165648Spiso		if (ifm->ifm_type == RTM_IFINFO) {
3306165648Spiso			sdl = (struct sockaddr_dl *)(ifm + 1);
3307165648Spiso			if (strlen(ifn) == sdl->sdl_nlen &&
3308165648Spiso			    strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
3309165648Spiso				ifIndex = ifm->ifm_index;
3310165648Spiso				ifMTU = ifm->ifm_data.ifi_mtu;
3311165648Spiso				break;
3312165648Spiso			}
3313165648Spiso		}
3314165648Spiso	}
3315165648Spiso	if (!ifIndex)
3316165648Spiso		errx(1, "unknown interface name %s", ifn);
3317165648Spiso/*
3318165648Spiso * Get interface address.
3319165648Spiso */
3320165648Spiso	sin = NULL;
3321165648Spiso	while (next < lim) {
3322165648Spiso		ifam = (struct ifa_msghdr *)next;
3323165648Spiso		next += ifam->ifam_msglen;
3324165648Spiso		if (ifam->ifam_version != RTM_VERSION) {
3325165648Spiso			if (verbose)
3326165648Spiso				warnx("routing message version %d "
3327165648Spiso				    "not understood", ifam->ifam_version);
3328165648Spiso			continue;
3329165648Spiso		}
3330165648Spiso		if (ifam->ifam_type != RTM_NEWADDR)
3331165648Spiso			break;
3332165648Spiso		if (ifam->ifam_addrs & RTA_IFA) {
3333165648Spiso			int i;
3334165648Spiso			char *cp = (char *)(ifam + 1);
3335165648Spiso
3336165648Spiso			for (i = 1; i < RTA_IFA; i <<= 1) {
3337165648Spiso				if (ifam->ifam_addrs & i)
3338165648Spiso					cp += SA_SIZE((struct sockaddr *)cp);
3339165648Spiso			}
3340165648Spiso			if (((struct sockaddr *)cp)->sa_family == AF_INET) {
3341165648Spiso				sin = (struct sockaddr_in *)cp;
3342165648Spiso				break;
3343165648Spiso			}
3344165648Spiso		}
3345165648Spiso	}
3346165648Spiso	if (sin == NULL)
3347165648Spiso		errx(1, "%s: cannot get interface address", ifn);
3348165648Spiso
3349165648Spiso	n->ip = sin->sin_addr;
3350165648Spiso	strncpy(n->if_name, ifn, IF_NAMESIZE);
3351165648Spiso
3352165648Spiso	free(buf);
3353165648Spiso}
3354165648Spiso
3355165648Spiso/*
3356165648Spiso * XXX - The following functions, macros and definitions come from natd.c:
3357165648Spiso * it would be better to move them outside natd.c, in a file
3358165648Spiso * (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live
3359165648Spiso * with it.
3360165648Spiso */
3361165648Spiso
3362165648Spiso/*
3363165648Spiso * Definition of a port range, and macros to deal with values.
3364165648Spiso * FORMAT:  HI 16-bits == first port in range, 0 == all ports.
3365165648Spiso *          LO 16-bits == number of ports in range
3366165648Spiso * NOTES:   - Port values are not stored in network byte order.
3367165648Spiso */
3368165648Spiso
3369165648Spiso#define port_range u_long
3370165648Spiso
3371165648Spiso#define GETLOPORT(x)     ((x) >> 0x10)
3372165648Spiso#define GETNUMPORTS(x)   ((x) & 0x0000ffff)
3373165648Spiso#define GETHIPORT(x)     (GETLOPORT((x)) + GETNUMPORTS((x)))
3374165648Spiso
3375165648Spiso/* Set y to be the low-port value in port_range variable x. */
3376165648Spiso#define SETLOPORT(x,y)   ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
3377165648Spiso
3378165648Spiso/* Set y to be the number of ports in port_range variable x. */
3379165648Spiso#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
3380165648Spiso
3381165648Spisostatic void
3382165648SpisoStrToAddr (const char* str, struct in_addr* addr)
3383165648Spiso{
3384165648Spiso	struct hostent* hp;
3385165648Spiso
3386165648Spiso	if (inet_aton (str, addr))
3387165648Spiso		return;
3388165648Spiso
3389165648Spiso	hp = gethostbyname (str);
3390165648Spiso	if (!hp)
3391165648Spiso		errx (1, "unknown host %s", str);
3392165648Spiso
3393165648Spiso	memcpy (addr, hp->h_addr, sizeof (struct in_addr));
3394165648Spiso}
3395165648Spiso
3396165648Spisostatic int
3397165648SpisoStrToPortRange (const char* str, const char* proto, port_range *portRange)
3398165648Spiso{
3399165648Spiso	char*           sep;
3400165648Spiso	struct servent*	sp;
3401165648Spiso	char*		end;
3402165648Spiso	u_short         loPort;
3403165648Spiso	u_short         hiPort;
3404165648Spiso
3405165648Spiso	/* First see if this is a service, return corresponding port if so. */
3406165648Spiso	sp = getservbyname (str,proto);
3407165648Spiso	if (sp) {
3408165648Spiso	        SETLOPORT(*portRange, ntohs(sp->s_port));
3409165648Spiso		SETNUMPORTS(*portRange, 1);
3410165648Spiso		return 0;
3411165648Spiso	}
3412165648Spiso
3413165648Spiso	/* Not a service, see if it's a single port or port range. */
3414165648Spiso	sep = strchr (str, '-');
3415165648Spiso	if (sep == NULL) {
3416165648Spiso	        SETLOPORT(*portRange, strtol(str, &end, 10));
3417165648Spiso		if (end != str) {
3418165648Spiso		        /* Single port. */
3419165648Spiso		        SETNUMPORTS(*portRange, 1);
3420165648Spiso			return 0;
3421165648Spiso		}
3422165648Spiso
3423165648Spiso		/* Error in port range field. */
3424165648Spiso		errx (EX_DATAERR, "%s/%s: unknown service", str, proto);
3425165648Spiso	}
3426165648Spiso
3427165648Spiso	/* Port range, get the values and sanity check. */
3428165648Spiso	sscanf (str, "%hu-%hu", &loPort, &hiPort);
3429165648Spiso	SETLOPORT(*portRange, loPort);
3430165648Spiso	SETNUMPORTS(*portRange, 0);	/* Error by default */
3431165648Spiso	if (loPort <= hiPort)
3432165648Spiso	        SETNUMPORTS(*portRange, hiPort - loPort + 1);
3433165648Spiso
3434165648Spiso	if (GETNUMPORTS(*portRange) == 0)
3435165648Spiso	        errx (EX_DATAERR, "invalid port range %s", str);
3436165648Spiso
3437165648Spiso	return 0;
3438165648Spiso}
3439165648Spiso
3440165648Spisostatic int
3441165648SpisoStrToProto (const char* str)
3442165648Spiso{
3443165648Spiso	if (!strcmp (str, "tcp"))
3444165648Spiso		return IPPROTO_TCP;
3445165648Spiso
3446165648Spiso	if (!strcmp (str, "udp"))
3447165648Spiso		return IPPROTO_UDP;
3448165648Spiso
3449165648Spiso	errx (EX_DATAERR, "unknown protocol %s. Expected tcp or udp", str);
3450165648Spiso}
3451165648Spiso
3452165648Spisostatic int
3453165648SpisoStrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
3454165648Spiso		       port_range *portRange)
3455165648Spiso{
3456165648Spiso	char*	ptr;
3457165648Spiso
3458165648Spiso	ptr = strchr (str, ':');
3459165648Spiso	if (!ptr)
3460165648Spiso		errx (EX_DATAERR, "%s is missing port number", str);
3461165648Spiso
3462165648Spiso	*ptr = '\0';
3463165648Spiso	++ptr;
3464165648Spiso
3465165648Spiso	StrToAddr (str, addr);
3466165648Spiso	return StrToPortRange (ptr, proto, portRange);
3467165648Spiso}
3468165648Spiso
3469165648Spiso/* End of stuff taken from natd.c. */
3470165648Spiso
3471165648Spiso#define INC_ARGCV() do {        \
3472165648Spiso	(*_av)++;               \
3473165648Spiso	(*_ac)--;               \
3474165648Spiso	av = *_av;              \
3475165648Spiso	ac = *_ac;              \
3476165648Spiso} while(0)
3477165648Spiso
3478165648Spiso/*
3479165648Spiso * The next 3 functions add support for the addr, port and proto redirect and
3480165648Spiso * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect()
3481165648Spiso * and SetupProtoRedirect() from natd.c.
3482165648Spiso *
3483165648Spiso * Every setup_* function fills at least one redirect entry
3484165648Spiso * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool)
3485165648Spiso * in buf.
3486165648Spiso *
3487165648Spiso * The format of data in buf is:
3488165648Spiso *
3489165648Spiso *
3490165648Spiso *     cfg_nat    cfg_redir    cfg_spool    ......  cfg_spool
3491165648Spiso *
3492165648Spiso *    -------------------------------------        ------------
3493165648Spiso *   |          | .....X ... |          |         |           |  .....
3494165648Spiso *    ------------------------------------- ...... ------------
3495165648Spiso *                     ^
3496165648Spiso *                spool_cnt       n=0       ......   n=(X-1)
3497165648Spiso *
3498165648Spiso * len points to the amount of available space in buf
3499165648Spiso * space counts the memory consumed by every function
3500165648Spiso *
3501165648Spiso * XXX - Every function get all the argv params so it
3502165648Spiso * has to check, in optional parameters, that the next
3503165648Spiso * args is a valid option for the redir entry and not
3504165648Spiso * another token. Only redir_port and redir_proto are
3505165648Spiso * affected by this.
3506165648Spiso */
3507165648Spiso
3508165648Spisostatic int
3509165648Spisosetup_redir_addr(char *spool_buf, int len,
3510165648Spiso		 int *_ac, char ***_av)
3511165648Spiso{
3512165648Spiso	char **av, *sep; /* Token separator. */
3513165648Spiso	/* Temporary buffer used to hold server pool ip's. */
3514165648Spiso	char tmp_spool_buf[NAT_BUF_LEN];
3515165648Spiso	int ac, i, space, lsnat;
3516165648Spiso	struct cfg_redir *r;
3517165648Spiso	struct cfg_spool *tmp;
3518165648Spiso
3519165648Spiso	av = *_av;
3520165648Spiso	ac = *_ac;
3521165648Spiso	space = 0;
3522165648Spiso	lsnat = 0;
3523165648Spiso	if (len >= SOF_REDIR) {
3524165648Spiso		r = (struct cfg_redir *)spool_buf;
3525165648Spiso		/* Skip cfg_redir at beginning of buf. */
3526165648Spiso		spool_buf = &spool_buf[SOF_REDIR];
3527165648Spiso		space = SOF_REDIR;
3528165648Spiso		len -= SOF_REDIR;
3529165648Spiso	} else
3530165648Spiso		goto nospace;
3531165648Spiso	r->mode = REDIR_ADDR;
3532165648Spiso	/* Extract local address. */
3533165648Spiso	if (ac == 0)
3534165648Spiso		errx(EX_DATAERR, "redirect_addr: missing local address");
3535165648Spiso	sep = strchr(*av, ',');
3536165648Spiso	if (sep) {		/* LSNAT redirection syntax. */
3537165648Spiso		r->laddr.s_addr = INADDR_NONE;
3538165648Spiso		/* Preserve av, copy spool servers to tmp_spool_buf. */
3539165648Spiso		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
3540165648Spiso		lsnat = 1;
3541165648Spiso	} else
3542165648Spiso		StrToAddr(*av, &r->laddr);
3543165648Spiso	INC_ARGCV();
3544165648Spiso
3545165648Spiso	/* Extract public address. */
3546165648Spiso	if (ac == 0)
3547165648Spiso		errx(EX_DATAERR, "redirect_addr: missing public address");
3548165648Spiso	StrToAddr(*av, &r->paddr);
3549165648Spiso	INC_ARGCV();
3550165648Spiso
3551165648Spiso	/* Setup LSNAT server pool. */
3552165648Spiso	if (sep) {
3553165648Spiso		sep = strtok(tmp_spool_buf, ",");
3554165648Spiso		while (sep != NULL) {
3555165648Spiso			tmp = (struct cfg_spool *)spool_buf;
3556165648Spiso			if (len < SOF_SPOOL)
3557165648Spiso				goto nospace;
3558165648Spiso			len -= SOF_SPOOL;
3559165648Spiso			space += SOF_SPOOL;
3560165648Spiso			StrToAddr(sep, &tmp->addr);
3561165648Spiso			tmp->port = ~0;
3562165648Spiso			r->spool_cnt++;
3563165648Spiso			/* Point to the next possible cfg_spool. */
3564165648Spiso			spool_buf = &spool_buf[SOF_SPOOL];
3565165648Spiso			sep = strtok(NULL, ",");
3566165648Spiso		}
3567165648Spiso	}
3568165648Spiso	return(space);
3569165648Spisonospace:
3570165648Spiso	errx(EX_DATAERR, "redirect_addr: buf is too small\n");
3571165648Spiso}
3572165648Spiso
3573165648Spisostatic int
3574165648Spisosetup_redir_port(char *spool_buf, int len,
3575165648Spiso		 int *_ac, char ***_av)
3576165648Spiso{
3577165648Spiso	char **av, *sep, *protoName;
3578165648Spiso	char tmp_spool_buf[NAT_BUF_LEN];
3579165648Spiso	int ac, space, lsnat;
3580165648Spiso	struct cfg_redir *r;
3581165648Spiso	struct cfg_spool *tmp;
3582165648Spiso	u_short numLocalPorts;
3583165648Spiso	port_range portRange;
3584165648Spiso
3585165648Spiso	av = *_av;
3586165648Spiso	ac = *_ac;
3587165648Spiso	space = 0;
3588165648Spiso	lsnat = 0;
3589165648Spiso	numLocalPorts = 0;
3590165648Spiso
3591165648Spiso	if (len >= SOF_REDIR) {
3592165648Spiso		r = (struct cfg_redir *)spool_buf;
3593165648Spiso		/* Skip cfg_redir at beginning of buf. */
3594165648Spiso		spool_buf = &spool_buf[SOF_REDIR];
3595165648Spiso		space = SOF_REDIR;
3596165648Spiso		len -= SOF_REDIR;
3597165648Spiso	} else
3598165648Spiso		goto nospace;
3599165648Spiso	r->mode = REDIR_PORT;
3600165648Spiso	/*
3601165648Spiso	 * Extract protocol.
3602165648Spiso	 */
3603165648Spiso	if (ac == 0)
3604165648Spiso		errx (EX_DATAERR, "redirect_port: missing protocol");
3605165648Spiso	r->proto = StrToProto(*av);
3606165648Spiso	protoName = *av;
3607165648Spiso	INC_ARGCV();
3608165648Spiso
3609165648Spiso	/*
3610165648Spiso	 * Extract local address.
3611165648Spiso	 */
3612165648Spiso	if (ac == 0)
3613165648Spiso		errx (EX_DATAERR, "redirect_port: missing local address");
3614165648Spiso
3615165648Spiso	sep = strchr(*av, ',');
3616165648Spiso	/* LSNAT redirection syntax. */
3617165648Spiso	if (sep) {
3618165648Spiso		r->laddr.s_addr = INADDR_NONE;
3619165648Spiso		r->lport = ~0;
3620165648Spiso		numLocalPorts = 1;
3621165648Spiso		/* Preserve av, copy spool servers to tmp_spool_buf. */
3622165648Spiso		strncpy(tmp_spool_buf, *av, strlen(*av)+1);
3623165648Spiso		lsnat = 1;
3624165648Spiso	} else {
3625165648Spiso		if (StrToAddrAndPortRange (*av, &r->laddr, protoName,
3626165648Spiso		    &portRange) != 0)
3627165648Spiso			errx(EX_DATAERR, "redirect_port:"
3628165648Spiso			    "invalid local port range");
3629165648Spiso
3630165648Spiso		r->lport = GETLOPORT(portRange);
3631165648Spiso		numLocalPorts = GETNUMPORTS(portRange);
3632165648Spiso	}
3633165648Spiso	INC_ARGCV();
3634165648Spiso
3635165648Spiso	/*
3636165648Spiso	 * Extract public port and optionally address.
3637165648Spiso	 */
3638165648Spiso	if (ac == 0)
3639165648Spiso		errx (EX_DATAERR, "redirect_port: missing public port");
3640165648Spiso
3641165648Spiso	sep = strchr (*av, ':');
3642165648Spiso	if (sep) {
3643165648Spiso	        if (StrToAddrAndPortRange (*av, &r->paddr, protoName,
3644165648Spiso		    &portRange) != 0)
3645165648Spiso		        errx(EX_DATAERR, "redirect_port:"
3646165648Spiso			    "invalid public port range");
3647165648Spiso	} else {
3648165648Spiso		r->paddr.s_addr = INADDR_ANY;
3649165648Spiso		if (StrToPortRange (*av, protoName, &portRange) != 0)
3650165648Spiso		        errx(EX_DATAERR, "redirect_port:"
3651165648Spiso			    "invalid public port range");
3652165648Spiso	}
3653165648Spiso
3654165648Spiso	r->pport = GETLOPORT(portRange);
3655165648Spiso	r->pport_cnt = GETNUMPORTS(portRange);
3656165648Spiso	INC_ARGCV();
3657165648Spiso
3658165648Spiso	/*
3659165648Spiso	 * Extract remote address and optionally port.
3660165648Spiso	 */
3661165648Spiso	/*
3662165648Spiso	 * NB: isalpha(**av) => we've to check that next parameter is really an
3663165648Spiso	 * option for this redirect entry, else stop here processing arg[cv].
3664165648Spiso	 */
3665165648Spiso	if (ac != 0 && !isalpha(**av)) {
3666165648Spiso		sep = strchr (*av, ':');
3667165648Spiso		if (sep) {
3668165648Spiso		        if (StrToAddrAndPortRange (*av, &r->raddr, protoName,
3669165648Spiso			    &portRange) != 0)
3670165648Spiso				errx(EX_DATAERR, "redirect_port:"
3671165648Spiso				    "invalid remote port range");
3672165648Spiso		} else {
3673165648Spiso		        SETLOPORT(portRange, 0);
3674165648Spiso			SETNUMPORTS(portRange, 1);
3675165648Spiso			StrToAddr (*av, &r->raddr);
3676165648Spiso		}
3677165648Spiso		INC_ARGCV();
3678165648Spiso	} else {
3679165648Spiso		SETLOPORT(portRange, 0);
3680165648Spiso		SETNUMPORTS(portRange, 1);
3681165648Spiso		r->raddr.s_addr = INADDR_ANY;
3682165648Spiso	}
3683165648Spiso	r->rport = GETLOPORT(portRange);
3684165648Spiso	r->rport_cnt = GETNUMPORTS(portRange);
3685165648Spiso
3686165648Spiso	/*
3687165648Spiso	 * Make sure port ranges match up, then add the redirect ports.
3688165648Spiso	 */
3689165648Spiso	if (numLocalPorts != r->pport_cnt)
3690165648Spiso	        errx(EX_DATAERR, "redirect_port:"
3691165648Spiso		    "port ranges must be equal in size");
3692165648Spiso
3693165648Spiso	/* Remote port range is allowed to be '0' which means all ports. */
3694165648Spiso	if (r->rport_cnt != numLocalPorts &&
3695165648Spiso	    (r->rport_cnt != 1 || r->rport != 0))
3696165648Spiso	        errx(EX_DATAERR, "redirect_port: remote port must"
3697165648Spiso		    "be 0 or equal to local port range in size");
3698165648Spiso
3699165648Spiso	/*
3700165648Spiso	 * Setup LSNAT server pool.
3701165648Spiso	 */
3702165648Spiso	if (lsnat) {
3703165648Spiso		sep = strtok(tmp_spool_buf, ",");
3704165648Spiso		while (sep != NULL) {
3705165648Spiso			tmp = (struct cfg_spool *)spool_buf;
3706165648Spiso			if (len < SOF_SPOOL)
3707165648Spiso				goto nospace;
3708165648Spiso			len -= SOF_SPOOL;
3709165648Spiso			space += SOF_SPOOL;
3710165648Spiso			if (StrToAddrAndPortRange(sep, &tmp->addr, protoName,
3711165648Spiso			    &portRange) != 0)
3712165648Spiso				errx(EX_DATAERR, "redirect_port:"
3713165648Spiso				    "invalid local port range");
3714165648Spiso			if (GETNUMPORTS(portRange) != 1)
3715165648Spiso				errx(EX_DATAERR, "redirect_port: local port"
3716165648Spiso				    "must be single in this context");
3717165648Spiso			tmp->port = GETLOPORT(portRange);
3718165648Spiso			r->spool_cnt++;
3719165648Spiso			/* Point to the next possible cfg_spool. */
3720165648Spiso			spool_buf = &spool_buf[SOF_SPOOL];
3721165648Spiso			sep = strtok(NULL, ",");
3722165648Spiso		}
3723165648Spiso	}
3724165648Spiso	return (space);
3725165648Spisonospace:
3726165648Spiso	errx(EX_DATAERR, "redirect_port: buf is too small\n");
3727165648Spiso}
3728165648Spiso
3729165648Spisostatic int
3730165648Spisosetup_redir_proto(char *spool_buf, int len,
3731165648Spiso		 int *_ac, char ***_av)
3732165648Spiso{
3733165648Spiso	char **av;
3734165648Spiso	int ac, i, space;
3735165648Spiso	struct protoent *protoent;
3736165648Spiso	struct cfg_redir *r;
3737165648Spiso
3738165648Spiso	av = *_av;
3739165648Spiso	ac = *_ac;
3740165648Spiso	if (len >= SOF_REDIR) {
3741165648Spiso		r = (struct cfg_redir *)spool_buf;
3742165648Spiso		/* Skip cfg_redir at beginning of buf. */
3743165648Spiso		spool_buf = &spool_buf[SOF_REDIR];
3744165648Spiso		space = SOF_REDIR;
3745165648Spiso		len -= SOF_REDIR;
3746165648Spiso	} else
3747165648Spiso		goto nospace;
3748165648Spiso	r->mode = REDIR_PROTO;
3749165648Spiso	/*
3750165648Spiso	 * Extract protocol.
3751165648Spiso	 */
3752165648Spiso	if (ac == 0)
3753165648Spiso		errx(EX_DATAERR, "redirect_proto: missing protocol");
3754165648Spiso
3755165648Spiso	protoent = getprotobyname(*av);
3756165648Spiso	if (protoent == NULL)
3757165648Spiso		errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av);
3758165648Spiso	else
3759165648Spiso		r->proto = protoent->p_proto;
3760165648Spiso
3761165648Spiso	INC_ARGCV();
3762165648Spiso
3763165648Spiso	/*
3764165648Spiso	 * Extract local address.
3765165648Spiso	 */
3766165648Spiso	if (ac == 0)
3767165648Spiso		errx(EX_DATAERR, "redirect_proto: missing local address");
3768165648Spiso	else
3769165648Spiso		StrToAddr(*av, &r->laddr);
3770165648Spiso
3771165648Spiso	INC_ARGCV();
3772165648Spiso
3773165648Spiso	/*
3774165648Spiso	 * Extract optional public address.
3775165648Spiso	 */
3776165648Spiso	if (ac == 0) {
3777165648Spiso		r->paddr.s_addr = INADDR_ANY;
3778165648Spiso		r->raddr.s_addr = INADDR_ANY;
3779165648Spiso	} else {
3780165648Spiso		/* see above in setup_redir_port() */
3781165648Spiso		if (!isalpha(**av)) {
3782165648Spiso			StrToAddr(*av, &r->paddr);
3783165648Spiso			INC_ARGCV();
3784165648Spiso
3785165648Spiso			/*
3786165648Spiso			 * Extract optional remote address.
3787165648Spiso			 */
3788165648Spiso			/* see above in setup_redir_port() */
3789165648Spiso			if (ac!=0 && !isalpha(**av)) {
3790165648Spiso				StrToAddr(*av, &r->raddr);
3791165648Spiso				INC_ARGCV();
3792165648Spiso			}
3793165648Spiso		}
3794165648Spiso	}
3795165648Spiso	return (space);
3796165648Spisonospace:
3797165648Spiso	errx(EX_DATAERR, "redirect_proto: buf is too small\n");
3798165648Spiso}
3799165648Spiso
3800165648Spisostatic void
3801165648Spisoshow_nat(int ac, char **av);
3802165648Spiso
3803165648Spisostatic void
3804165648Spisoprint_nat_config(char *buf) {
3805165648Spiso	struct cfg_nat *n;
3806165648Spiso	int i, cnt, flag, off;
3807165648Spiso	struct cfg_redir *t;
3808165648Spiso	struct cfg_spool *s;
3809165648Spiso	struct protoent *p;
3810165648Spiso
3811165648Spiso	n = (struct cfg_nat *)buf;
3812165648Spiso	flag = 1;
3813165648Spiso	off  = sizeof(*n);
3814165648Spiso	printf("ipfw nat %u config", n->id);
3815165648Spiso	if (strlen(n->if_name) != 0)
3816165648Spiso		printf(" if %s", n->if_name);
3817165648Spiso	else if (n->ip.s_addr != 0)
3818165648Spiso		printf(" ip %s", inet_ntoa(n->ip));
3819165648Spiso	while (n->mode != 0) {
3820165648Spiso		if (n->mode & PKT_ALIAS_LOG) {
3821165648Spiso			printf(" log");
3822165648Spiso			n->mode &= ~PKT_ALIAS_LOG;
3823165648Spiso		} else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
3824165648Spiso			printf(" deny_in");
3825165648Spiso			n->mode &= ~PKT_ALIAS_DENY_INCOMING;
3826165648Spiso		} else if (n->mode & PKT_ALIAS_SAME_PORTS) {
3827165648Spiso			printf(" same_ports");
3828165648Spiso			n->mode &= ~PKT_ALIAS_SAME_PORTS;
3829165648Spiso		} else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
3830165648Spiso			printf(" unreg_only");
3831165648Spiso			n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
3832165648Spiso		} else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
3833165648Spiso			printf(" reset");
3834165648Spiso			n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
3835165648Spiso		} else if (n->mode & PKT_ALIAS_REVERSE) {
3836165648Spiso			printf(" reverse");
3837165648Spiso			n->mode &= ~PKT_ALIAS_REVERSE;
3838165648Spiso		} else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
3839165648Spiso			printf(" proxy_only");
3840165648Spiso			n->mode &= ~PKT_ALIAS_PROXY_ONLY;
3841165648Spiso		}
3842165648Spiso	}
3843165648Spiso	/* Print all the redirect's data configuration. */
3844165648Spiso	for (cnt = 0; cnt < n->redir_cnt; cnt++) {
3845165648Spiso		t = (struct cfg_redir *)&buf[off];
3846165648Spiso		off += SOF_REDIR;
3847165648Spiso		switch (t->mode) {
3848165648Spiso		case REDIR_ADDR:
3849165648Spiso			printf(" redirect_addr");
3850165648Spiso			if (t->spool_cnt == 0)
3851165648Spiso				printf(" %s", inet_ntoa(t->laddr));
3852165648Spiso			else
3853165648Spiso				for (i = 0; i < t->spool_cnt; i++) {
3854165648Spiso					s = (struct cfg_spool *)&buf[off];
3855165648Spiso					if (i)
3856165648Spiso						printf(",");
3857165648Spiso					else
3858165648Spiso						printf(" ");
3859165648Spiso					printf("%s", inet_ntoa(s->addr));
3860165648Spiso					off += SOF_SPOOL;
3861165648Spiso				}
3862165648Spiso			printf(" %s", inet_ntoa(t->paddr));
3863165648Spiso			break;
3864165648Spiso		case REDIR_PORT:
3865165648Spiso			p = getprotobynumber(t->proto);
3866165648Spiso			printf(" redirect_port %s ", p->p_name);
3867165648Spiso			if (!t->spool_cnt) {
3868165648Spiso				printf("%s:%u", inet_ntoa(t->laddr), t->lport);
3869165648Spiso				if (t->pport_cnt > 1)
3870165648Spiso					printf("-%u", t->lport +
3871165648Spiso					    t->pport_cnt - 1);
3872165648Spiso			} else
3873165648Spiso				for (i=0; i < t->spool_cnt; i++) {
3874165648Spiso					s = (struct cfg_spool *)&buf[off];
3875165648Spiso					if (i)
3876165648Spiso						printf(",");
3877165648Spiso					printf("%s:%u", inet_ntoa(s->addr),
3878165648Spiso					    s->port);
3879165648Spiso					off += SOF_SPOOL;
3880165648Spiso				}
3881165648Spiso
3882165648Spiso			printf(" ");
3883165648Spiso			if (t->paddr.s_addr)
3884165648Spiso				printf("%s:", inet_ntoa(t->paddr));
3885165648Spiso			printf("%u", t->pport);
3886165648Spiso			if (!t->spool_cnt && t->pport_cnt > 1)
3887165648Spiso				printf("-%u", t->pport + t->pport_cnt - 1);
3888165648Spiso
3889165648Spiso			if (t->raddr.s_addr) {
3890165648Spiso				printf(" %s", inet_ntoa(t->raddr));
3891165648Spiso				if (t->rport) {
3892165648Spiso					printf(":%u", t->rport);
3893165648Spiso					if (!t->spool_cnt && t->rport_cnt > 1)
3894165648Spiso						printf("-%u", t->rport +
3895165648Spiso						    t->rport_cnt - 1);
3896165648Spiso				}
3897165648Spiso			}
3898165648Spiso			break;
3899165648Spiso		case REDIR_PROTO:
3900165648Spiso			p = getprotobynumber(t->proto);
3901165648Spiso			printf(" redirect_proto %s %s", p->p_name,
3902165648Spiso			    inet_ntoa(t->laddr));
3903165648Spiso			if (t->paddr.s_addr != 0) {
3904165648Spiso				printf(" %s", inet_ntoa(t->paddr));
3905165648Spiso				if (t->raddr.s_addr)
3906165648Spiso					printf(" %s", inet_ntoa(t->raddr));
3907165648Spiso			}
3908165648Spiso			break;
3909165648Spiso		default:
3910165648Spiso			errx(EX_DATAERR, "unknown redir mode");
3911165648Spiso			break;
3912165648Spiso		}
3913165648Spiso	}
3914165648Spiso	printf("\n");
3915165648Spiso}
3916165648Spiso
3917165648Spisostatic void
3918165648Spisoconfig_nat(int ac, char **av)
3919165648Spiso{
3920165648Spiso	struct cfg_nat *n;              /* Nat instance configuration. */
3921165648Spiso	struct in_addr ip;
3922165648Spiso	int i, len, off, tok;
3923165648Spiso	char *id, buf[NAT_BUF_LEN]; 	/* Buffer for serialized data. */
3924165648Spiso
3925165648Spiso	len = NAT_BUF_LEN;
3926165648Spiso	/* Offset in buf: save space for n at the beginning. */
3927165648Spiso	off = sizeof(*n);
3928165648Spiso	memset(buf, 0, sizeof(buf));
3929165648Spiso	n = (struct cfg_nat *)buf;
3930165648Spiso
3931165648Spiso	av++; ac--;
3932165648Spiso	/* Nat id. */
3933165648Spiso	if (ac && isdigit(**av)) {
3934165648Spiso		id = *av;
3935165648Spiso		i = atoi(*av);
3936165648Spiso		ac--; av++;
3937165648Spiso		n->id = i;
3938165648Spiso	} else
3939165648Spiso		errx(EX_DATAERR, "missing nat id");
3940165648Spiso	if (ac == 0)
3941165648Spiso		errx(EX_DATAERR, "missing option");
3942165648Spiso
3943165648Spiso	while (ac > 0) {
3944165648Spiso		tok = match_token(nat_params, *av);
3945165648Spiso		ac--; av++;
3946165648Spiso		switch (tok) {
3947165648Spiso		case TOK_IP:
3948165648Spiso			if (ac == 0)
3949165648Spiso				errx(EX_DATAERR, "missing option");
3950165648Spiso			if (!inet_aton(av[0], &(n->ip)))
3951165648Spiso				errx(EX_DATAERR, "bad ip address ``%s''",
3952165648Spiso				    av[0]);
3953165648Spiso			ac--; av++;
3954165648Spiso			break;
3955165648Spiso		case TOK_IF:
3956165648Spiso			set_addr_dynamic(av[0], n);
3957165648Spiso			ac--; av++;
3958165648Spiso			break;
3959165648Spiso		case TOK_ALOG:
3960165648Spiso			n->mode |= PKT_ALIAS_LOG;
3961165648Spiso			break;
3962165648Spiso		case TOK_DENY_INC:
3963165648Spiso			n->mode |= PKT_ALIAS_DENY_INCOMING;
3964165648Spiso			break;
3965165648Spiso		case TOK_SAME_PORTS:
3966165648Spiso			n->mode |= PKT_ALIAS_SAME_PORTS;
3967165648Spiso			break;
3968165648Spiso		case TOK_UNREG_ONLY:
3969165648Spiso			n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
3970165648Spiso			break;
3971165648Spiso		case TOK_RESET_ADDR:
3972165648Spiso			n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
3973165648Spiso			break;
3974165648Spiso		case TOK_ALIAS_REV:
3975165648Spiso			n->mode |= PKT_ALIAS_REVERSE;
3976165648Spiso			break;
3977165648Spiso		case TOK_PROXY_ONLY:
3978165648Spiso			n->mode |= PKT_ALIAS_PROXY_ONLY;
3979165648Spiso			break;
3980165648Spiso			/*
3981165648Spiso			 * All the setup_redir_* functions work directly in the final
3982165648Spiso			 * buffer, see above for details.
3983165648Spiso			 */
3984165648Spiso		case TOK_REDIR_ADDR:
3985165648Spiso		case TOK_REDIR_PORT:
3986165648Spiso		case TOK_REDIR_PROTO:
3987165648Spiso			switch (tok) {
3988165648Spiso			case TOK_REDIR_ADDR:
3989165648Spiso				i = setup_redir_addr(&buf[off], len, &ac, &av);
3990165648Spiso				break;
3991165648Spiso			case TOK_REDIR_PORT:
3992165648Spiso				i = setup_redir_port(&buf[off], len, &ac, &av);
3993165648Spiso				break;
3994165648Spiso			case TOK_REDIR_PROTO:
3995165648Spiso				i = setup_redir_proto(&buf[off], len, &ac, &av);
3996165648Spiso				break;
3997165648Spiso			}
3998165648Spiso			n->redir_cnt++;
3999165648Spiso			off += i;
4000165648Spiso			len -= i;
4001165648Spiso			break;
4002165648Spiso		default:
4003165648Spiso			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
4004165648Spiso		}
4005165648Spiso	}
4006165648Spiso
4007165648Spiso	i = do_cmd(IP_FW_NAT_CFG, buf, off);
4008165648Spiso	if (i)
4009165648Spiso		err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
4010165648Spiso
4011165648Spiso	/* After every modification, we show the resultant rule. */
4012165648Spiso	int _ac = 3;
4013165648Spiso	char *_av[] = {"show", "config", id};
4014165648Spiso	show_nat(_ac, _av);
4015165648Spiso}
4016165648Spiso
4017165648Spisostatic void
401898943Sluigiconfig_pipe(int ac, char **av)
401998943Sluigi{
4020117469Sluigi	struct dn_pipe p;
402198943Sluigi	int i;
402298943Sluigi	char *end;
402398943Sluigi	void *par = NULL;
402498943Sluigi
4025117469Sluigi	memset(&p, 0, sizeof p);
402698943Sluigi
402798943Sluigi	av++; ac--;
402898943Sluigi	/* Pipe number */
402998943Sluigi	if (ac && isdigit(**av)) {
403098943Sluigi		i = atoi(*av); av++; ac--;
403198943Sluigi		if (do_pipe == 1)
4032117469Sluigi			p.pipe_nr = i;
403398943Sluigi		else
4034117469Sluigi			p.fs.fs_nr = i;
403598943Sluigi	}
403699475Sluigi	while (ac > 0) {
403798943Sluigi		double d;
403898943Sluigi		int tok = match_token(dummynet_params, *av);
403998943Sluigi		ac--; av++;
404098943Sluigi
404198943Sluigi		switch(tok) {
4042101978Sluigi		case TOK_NOERROR:
4043117469Sluigi			p.fs.flags_fs |= DN_NOERROR;
4044101978Sluigi			break;
4045101978Sluigi
404698943Sluigi		case TOK_PLR:
404798943Sluigi			NEED1("plr needs argument 0..1\n");
404898943Sluigi			d = strtod(av[0], NULL);
404998943Sluigi			if (d > 1)
405098943Sluigi				d = 1;
405198943Sluigi			else if (d < 0)
405298943Sluigi				d = 0;
4053117469Sluigi			p.fs.plr = (int)(d*0x7fffffff);
405498943Sluigi			ac--; av++;
405598943Sluigi			break;
405698943Sluigi
405798943Sluigi		case TOK_QUEUE:
405898943Sluigi			NEED1("queue needs queue size\n");
405998943Sluigi			end = NULL;
4060117469Sluigi			p.fs.qsize = strtoul(av[0], &end, 0);
406198943Sluigi			if (*end == 'K' || *end == 'k') {
4062117469Sluigi				p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
4063117469Sluigi				p.fs.qsize *= 1024;
4064140271Sbrooks			} else if (*end == 'B' ||
4065140271Sbrooks			    _substrcmp2(end, "by", "bytes") == 0) {
4066117469Sluigi				p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
406798943Sluigi			}
406898943Sluigi			ac--; av++;
406998943Sluigi			break;
407098943Sluigi
407198943Sluigi		case TOK_BUCKETS:
407298943Sluigi			NEED1("buckets needs argument\n");
4073117469Sluigi			p.fs.rq_size = strtoul(av[0], NULL, 0);
407498943Sluigi			ac--; av++;
407598943Sluigi			break;
407698943Sluigi
407798943Sluigi		case TOK_MASK:
407898943Sluigi			NEED1("mask needs mask specifier\n");
407998943Sluigi			/*
408098943Sluigi			 * per-flow queue, mask is dst_ip, dst_port,
408198943Sluigi			 * src_ip, src_port, proto measured in bits
408298943Sluigi			 */
408398943Sluigi			par = NULL;
408498943Sluigi
4085145246Sbrooks			bzero(&p.fs.flow_mask, sizeof(p.fs.flow_mask));
408698943Sluigi			end = NULL;
408798943Sluigi
408898943Sluigi			while (ac >= 1) {
4089117328Sluigi			    uint32_t *p32 = NULL;
4090117328Sluigi			    uint16_t *p16 = NULL;
4091145246Sbrooks			    uint32_t *p20 = NULL;
4092145246Sbrooks			    struct in6_addr *pa6 = NULL;
4093145246Sbrooks			    uint32_t a;
409498943Sluigi
409598943Sluigi			    tok = match_token(dummynet_params, *av);
409698943Sluigi			    ac--; av++;
409798943Sluigi			    switch(tok) {
409898943Sluigi			    case TOK_ALL:
409998943Sluigi				    /*
410098943Sluigi				     * special case, all bits significant
410198943Sluigi				     */
4102117469Sluigi				    p.fs.flow_mask.dst_ip = ~0;
4103117469Sluigi				    p.fs.flow_mask.src_ip = ~0;
4104117469Sluigi				    p.fs.flow_mask.dst_port = ~0;
4105117469Sluigi				    p.fs.flow_mask.src_port = ~0;
4106117469Sluigi				    p.fs.flow_mask.proto = ~0;
4107145246Sbrooks				    n2mask(&(p.fs.flow_mask.dst_ip6), 128);
4108145246Sbrooks				    n2mask(&(p.fs.flow_mask.src_ip6), 128);
4109145246Sbrooks				    p.fs.flow_mask.flow_id6 = ~0;
4110117469Sluigi				    p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
411198943Sluigi				    goto end_mask;
411298943Sluigi
411398943Sluigi			    case TOK_DSTIP:
4114117469Sluigi				    p32 = &p.fs.flow_mask.dst_ip;
411598943Sluigi				    break;
411698943Sluigi
411798943Sluigi			    case TOK_SRCIP:
4118117469Sluigi				    p32 = &p.fs.flow_mask.src_ip;
411998943Sluigi				    break;
412098943Sluigi
4121145246Sbrooks			    case TOK_DSTIP6:
4122145246Sbrooks				    pa6 = &(p.fs.flow_mask.dst_ip6);
4123145246Sbrooks				    break;
4124145246Sbrooks
4125145246Sbrooks			    case TOK_SRCIP6:
4126145246Sbrooks				    pa6 = &(p.fs.flow_mask.src_ip6);
4127145246Sbrooks				    break;
4128145246Sbrooks
4129145246Sbrooks			    case TOK_FLOWID:
4130145246Sbrooks				    p20 = &p.fs.flow_mask.flow_id6;
4131145246Sbrooks				    break;
4132145246Sbrooks
413398943Sluigi			    case TOK_DSTPORT:
4134117469Sluigi				    p16 = &p.fs.flow_mask.dst_port;
413598943Sluigi				    break;
413698943Sluigi
413798943Sluigi			    case TOK_SRCPORT:
4138117469Sluigi				    p16 = &p.fs.flow_mask.src_port;
413998943Sluigi				    break;
414098943Sluigi
414198943Sluigi			    case TOK_PROTO:
414298943Sluigi				    break;
414398943Sluigi
414498943Sluigi			    default:
414598943Sluigi				    ac++; av--; /* backtrack */
414698943Sluigi				    goto end_mask;
414798943Sluigi			    }
414898943Sluigi			    if (ac < 1)
414998943Sluigi				    errx(EX_USAGE, "mask: value missing");
415098943Sluigi			    if (*av[0] == '/') {
415198943Sluigi				    a = strtoul(av[0]+1, &end, 0);
4152145246Sbrooks				    if (pa6 == NULL)
4153145246Sbrooks					    a = (a == 32) ? ~0 : (1 << a) - 1;
4154106505Smaxim			    } else
415599909Sluigi				    a = strtoul(av[0], &end, 0);
415698943Sluigi			    if (p32 != NULL)
415798943Sluigi				    *p32 = a;
415898943Sluigi			    else if (p16 != NULL) {
4159139821Sbrooks				    if (a > 0xFFFF)
416098943Sluigi					    errx(EX_DATAERR,
4161144687Sbrooks						"port mask must be 16 bit");
4162117328Sluigi				    *p16 = (uint16_t)a;
4163145246Sbrooks			    } else if (p20 != NULL) {
4164145246Sbrooks				    if (a > 0xfffff)
4165145246Sbrooks					errx(EX_DATAERR,
4166145246Sbrooks					    "flow_id mask must be 20 bit");
4167145246Sbrooks				    *p20 = (uint32_t)a;
4168145246Sbrooks			    } else if (pa6 != NULL) {
4169145246Sbrooks				    if (a < 0 || a > 128)
4170145246Sbrooks					errx(EX_DATAERR,
4171145246Sbrooks					    "in6addr invalid mask len");
4172145246Sbrooks				    else
4173145246Sbrooks					n2mask(pa6, a);
417498943Sluigi			    } else {
4175139821Sbrooks				    if (a > 0xFF)
417698943Sluigi					    errx(EX_DATAERR,
4177144687Sbrooks						"proto mask must be 8 bit");
4178117469Sluigi				    p.fs.flow_mask.proto = (uint8_t)a;
417998943Sluigi			    }
418098943Sluigi			    if (a != 0)
4181117469Sluigi				    p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
418298943Sluigi			    ac--; av++;
418398943Sluigi			} /* end while, config masks */
418498943Sluigiend_mask:
418598943Sluigi			break;
418698943Sluigi
418798943Sluigi		case TOK_RED:
418898943Sluigi		case TOK_GRED:
418998943Sluigi			NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
4190117469Sluigi			p.fs.flags_fs |= DN_IS_RED;
419198943Sluigi			if (tok == TOK_GRED)
4192117469Sluigi				p.fs.flags_fs |= DN_IS_GENTLE_RED;
419398943Sluigi			/*
419498943Sluigi			 * the format for parameters is w_q/min_th/max_th/max_p
419598943Sluigi			 */
419698943Sluigi			if ((end = strsep(&av[0], "/"))) {
419798943Sluigi			    double w_q = strtod(end, NULL);
419898943Sluigi			    if (w_q > 1 || w_q <= 0)
419998943Sluigi				errx(EX_DATAERR, "0 < w_q <= 1");
4200117469Sluigi			    p.fs.w_q = (int) (w_q * (1 << SCALE_RED));
420198943Sluigi			}
420298943Sluigi			if ((end = strsep(&av[0], "/"))) {
4203117469Sluigi			    p.fs.min_th = strtoul(end, &end, 0);
420498943Sluigi			    if (*end == 'K' || *end == 'k')
4205117469Sluigi				p.fs.min_th *= 1024;
420698943Sluigi			}
420798943Sluigi			if ((end = strsep(&av[0], "/"))) {
4208117469Sluigi			    p.fs.max_th = strtoul(end, &end, 0);
420998943Sluigi			    if (*end == 'K' || *end == 'k')
4210117469Sluigi				p.fs.max_th *= 1024;
421198943Sluigi			}
421298943Sluigi			if ((end = strsep(&av[0], "/"))) {
421398943Sluigi			    double max_p = strtod(end, NULL);
421498943Sluigi			    if (max_p > 1 || max_p <= 0)
421598943Sluigi				errx(EX_DATAERR, "0 < max_p <= 1");
4216117469Sluigi			    p.fs.max_p = (int)(max_p * (1 << SCALE_RED));
421798943Sluigi			}
421898943Sluigi			ac--; av++;
421998943Sluigi			break;
422098943Sluigi
422198943Sluigi		case TOK_DROPTAIL:
4222117469Sluigi			p.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
422398943Sluigi			break;
4224106505Smaxim
422598943Sluigi		case TOK_BW:
422698943Sluigi			NEED1("bw needs bandwidth or interface\n");
422798943Sluigi			if (do_pipe != 1)
422898943Sluigi			    errx(EX_DATAERR, "bandwidth only valid for pipes");
422998943Sluigi			/*
423098943Sluigi			 * set clocking interface or bandwidth value
423198943Sluigi			 */
423298943Sluigi			if (av[0][0] >= 'a' && av[0][0] <= 'z') {
4233117469Sluigi			    int l = sizeof(p.if_name)-1;
423498943Sluigi			    /* interface name */
4235117469Sluigi			    strncpy(p.if_name, av[0], l);
4236117469Sluigi			    p.if_name[l] = '\0';
4237117469Sluigi			    p.bandwidth = 0;
423898943Sluigi			} else {
4239117469Sluigi			    p.if_name[0] = '\0';
4240117469Sluigi			    p.bandwidth = strtoul(av[0], &end, 0);
424198943Sluigi			    if (*end == 'K' || *end == 'k') {
424298943Sluigi				end++;
4243117469Sluigi				p.bandwidth *= 1000;
424498943Sluigi			    } else if (*end == 'M') {
424598943Sluigi				end++;
4246117469Sluigi				p.bandwidth *= 1000000;
424798943Sluigi			    }
4248161550Sdwmalone			    if ((*end == 'B' &&
4249161550Sdwmalone				  _substrcmp2(end, "Bi", "Bit/s") != 0) ||
4250140271Sbrooks			        _substrcmp2(end, "by", "bytes") == 0)
4251117469Sluigi				p.bandwidth *= 8;
4252117469Sluigi			    if (p.bandwidth < 0)
425398943Sluigi				errx(EX_DATAERR, "bandwidth too large");
425498943Sluigi			}
425598943Sluigi			ac--; av++;
425698943Sluigi			break;
425798943Sluigi
425898943Sluigi		case TOK_DELAY:
425998943Sluigi			if (do_pipe != 1)
426098943Sluigi				errx(EX_DATAERR, "delay only valid for pipes");
426198943Sluigi			NEED1("delay needs argument 0..10000ms\n");
4262117469Sluigi			p.delay = strtoul(av[0], NULL, 0);
426398943Sluigi			ac--; av++;
426498943Sluigi			break;
426598943Sluigi
426698943Sluigi		case TOK_WEIGHT:
426798943Sluigi			if (do_pipe == 1)
426898943Sluigi				errx(EX_DATAERR,"weight only valid for queues");
426998943Sluigi			NEED1("weight needs argument 0..100\n");
4270117469Sluigi			p.fs.weight = strtoul(av[0], &end, 0);
427198943Sluigi			ac--; av++;
427298943Sluigi			break;
427398943Sluigi
427498943Sluigi		case TOK_PIPE:
427598943Sluigi			if (do_pipe == 1)
427698943Sluigi				errx(EX_DATAERR,"pipe only valid for queues");
427798943Sluigi			NEED1("pipe needs pipe_number\n");
4278117469Sluigi			p.fs.parent_nr = strtoul(av[0], &end, 0);
427998943Sluigi			ac--; av++;
428098943Sluigi			break;
428198943Sluigi
428298943Sluigi		default:
4283124924Smaxim			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
428498943Sluigi		}
428598943Sluigi	}
428698943Sluigi	if (do_pipe == 1) {
4287117469Sluigi		if (p.pipe_nr == 0)
428898943Sluigi			errx(EX_DATAERR, "pipe_nr must be > 0");
4289117469Sluigi		if (p.delay > 10000)
429098943Sluigi			errx(EX_DATAERR, "delay must be < 10000");
429198943Sluigi	} else { /* do_pipe == 2, queue */
4292117469Sluigi		if (p.fs.parent_nr == 0)
429398943Sluigi			errx(EX_DATAERR, "pipe must be > 0");
4294117469Sluigi		if (p.fs.weight >100)
429598943Sluigi			errx(EX_DATAERR, "weight must be <= 100");
429698943Sluigi	}
4297117469Sluigi	if (p.fs.flags_fs & DN_QSIZE_IS_BYTES) {
4298117469Sluigi		if (p.fs.qsize > 1024*1024)
429998943Sluigi			errx(EX_DATAERR, "queue size must be < 1MB");
430098943Sluigi	} else {
4301117469Sluigi		if (p.fs.qsize > 100)
430298943Sluigi			errx(EX_DATAERR, "2 <= queue size <= 100");
430398943Sluigi	}
4304117469Sluigi	if (p.fs.flags_fs & DN_IS_RED) {
430598943Sluigi		size_t len;
430698943Sluigi		int lookup_depth, avg_pkt_size;
430798943Sluigi		double s, idle, weight, w_q;
4308117469Sluigi		struct clockinfo ck;
430998943Sluigi		int t;
431098943Sluigi
4311117469Sluigi		if (p.fs.min_th >= p.fs.max_th)
431298943Sluigi		    errx(EX_DATAERR, "min_th %d must be < than max_th %d",
4313117469Sluigi			p.fs.min_th, p.fs.max_th);
4314117469Sluigi		if (p.fs.max_th == 0)
431598943Sluigi		    errx(EX_DATAERR, "max_th must be > 0");
431698943Sluigi
431798943Sluigi		len = sizeof(int);
431898943Sluigi		if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
431998943Sluigi			&lookup_depth, &len, NULL, 0) == -1)
432098943Sluigi
432198943Sluigi		    errx(1, "sysctlbyname(\"%s\")",
432298943Sluigi			"net.inet.ip.dummynet.red_lookup_depth");
432398943Sluigi		if (lookup_depth == 0)
432498943Sluigi		    errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth"
432598943Sluigi			" must be greater than zero");
432698943Sluigi
432798943Sluigi		len = sizeof(int);
432898943Sluigi		if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
432998943Sluigi			&avg_pkt_size, &len, NULL, 0) == -1)
433098943Sluigi
433198943Sluigi		    errx(1, "sysctlbyname(\"%s\")",
433298943Sluigi			"net.inet.ip.dummynet.red_avg_pkt_size");
433398943Sluigi		if (avg_pkt_size == 0)
433498943Sluigi			errx(EX_DATAERR,
433598943Sluigi			    "net.inet.ip.dummynet.red_avg_pkt_size must"
433698943Sluigi			    " be greater than zero");
433798943Sluigi
433898943Sluigi		len = sizeof(struct clockinfo);
4339117469Sluigi		if (sysctlbyname("kern.clockrate", &ck, &len, NULL, 0) == -1)
434098943Sluigi			errx(1, "sysctlbyname(\"%s\")", "kern.clockrate");
434198943Sluigi
434298943Sluigi		/*
434398943Sluigi		 * Ticks needed for sending a medium-sized packet.
434498943Sluigi		 * Unfortunately, when we are configuring a WF2Q+ queue, we
434598943Sluigi		 * do not have bandwidth information, because that is stored
434698943Sluigi		 * in the parent pipe, and also we have multiple queues
434798943Sluigi		 * competing for it. So we set s=0, which is not very
434898943Sluigi		 * correct. But on the other hand, why do we want RED with
434998943Sluigi		 * WF2Q+ ?
435098943Sluigi		 */
4351117469Sluigi		if (p.bandwidth==0) /* this is a WF2Q+ queue */
435298943Sluigi			s = 0;
435398943Sluigi		else
4354117469Sluigi			s = ck.hz * avg_pkt_size * 8 / p.bandwidth;
435598943Sluigi
435698943Sluigi		/*
435798943Sluigi		 * max idle time (in ticks) before avg queue size becomes 0.
435898943Sluigi		 * NOTA:  (3/w_q) is approx the value x so that
435998943Sluigi		 * (1-w_q)^x < 10^-3.
436098943Sluigi		 */
4361117469Sluigi		w_q = ((double)p.fs.w_q) / (1 << SCALE_RED);
436298943Sluigi		idle = s * 3. / w_q;
4363117469Sluigi		p.fs.lookup_step = (int)idle / lookup_depth;
4364117469Sluigi		if (!p.fs.lookup_step)
4365117469Sluigi			p.fs.lookup_step = 1;
436698943Sluigi		weight = 1 - w_q;
4367117469Sluigi		for (t = p.fs.lookup_step; t > 0; --t)
436898943Sluigi			weight *= weight;
4369117469Sluigi		p.fs.lookup_weight = (int)(weight * (1 << SCALE_RED));
437098943Sluigi	}
4371117469Sluigi	i = do_cmd(IP_DUMMYNET_CONFIGURE, &p, sizeof p);
437298943Sluigi	if (i)
437398943Sluigi		err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
437498943Sluigi}
437598943Sluigi
437698943Sluigistatic void
4377169424Smaximget_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask)
437898943Sluigi{
437998943Sluigi	int i, l;
4380169424Smaxim	char *ap, *ptr, *optr;
4381169424Smaxim	struct ether_addr *mac;
4382169424Smaxim	const char *macset = "0123456789abcdefABCDEF:";
438398943Sluigi
4384169424Smaxim	if (strcmp(p, "any") == 0) {
4385169424Smaxim		for (i = 0; i < ETHER_ADDR_LEN; i++)
4386169424Smaxim			addr[i] = mask[i] = 0;
438798943Sluigi		return;
4388169424Smaxim	}
438998943Sluigi
4390169424Smaxim	optr = ptr = strdup(p);
4391169424Smaxim	if ((ap = strsep(&ptr, "&/")) != NULL && *ap != 0) {
4392169424Smaxim		l = strlen(ap);
4393169424Smaxim		if (strspn(ap, macset) != l || (mac = ether_aton(ap)) == NULL)
4394169424Smaxim			errx(EX_DATAERR, "Incorrect MAC address");
4395169424Smaxim		bcopy(mac, addr, ETHER_ADDR_LEN);
4396169424Smaxim	} else
4397169424Smaxim		errx(EX_DATAERR, "Incorrect MAC address");
4398169424Smaxim
4399169424Smaxim	if (ptr != NULL) { /* we have mask? */
4400169424Smaxim		if (p[ptr - optr - 1] == '/') { /* mask len */
4401169424Smaxim			l = strtol(ptr, &ap, 10);
4402169424Smaxim			if (*ap != 0 || l > ETHER_ADDR_LEN * 8 || l < 0)
4403169424Smaxim				errx(EX_DATAERR, "Incorrect mask length");
4404169424Smaxim			for (i = 0; l > 0 && i < ETHER_ADDR_LEN; l -= 8, i++)
4405169424Smaxim				mask[i] = (l >= 8) ? 0xff: (~0) << (8 - l);
4406169424Smaxim		} else { /* mask */
4407169424Smaxim			l = strlen(ptr);
4408169424Smaxim			if (strspn(ptr, macset) != l ||
4409169424Smaxim			    (mac = ether_aton(ptr)) == NULL)
4410169424Smaxim				errx(EX_DATAERR, "Incorrect mask");
4411169424Smaxim			bcopy(mac, mask, ETHER_ADDR_LEN);
441298943Sluigi		}
4413169424Smaxim	} else { /* default mask: ff:ff:ff:ff:ff:ff */
4414169424Smaxim		for (i = 0; i < ETHER_ADDR_LEN; i++)
441598943Sluigi			mask[i] = 0xff;
441698943Sluigi	}
4417169424Smaxim	for (i = 0; i < ETHER_ADDR_LEN; i++)
441898943Sluigi		addr[i] &= mask[i];
4419169424Smaxim
4420169424Smaxim	free(optr);
442198943Sluigi}
442298943Sluigi
442398943Sluigi/*
442498943Sluigi * helper function, updates the pointer to cmd with the length
442598943Sluigi * of the current command, and also cleans up the first word of
442698943Sluigi * the new command in case it has been clobbered before.
442798943Sluigi */
442898943Sluigistatic ipfw_insn *
442998943Sluiginext_cmd(ipfw_insn *cmd)
443098943Sluigi{
443198943Sluigi	cmd += F_LEN(cmd);
443298943Sluigi	bzero(cmd, sizeof(*cmd));
443398943Sluigi	return cmd;
443498943Sluigi}
443598943Sluigi
443698943Sluigi/*
4437117469Sluigi * Takes arguments and copies them into a comment
4438117469Sluigi */
4439117469Sluigistatic void
4440117469Sluigifill_comment(ipfw_insn *cmd, int ac, char **av)
4441117469Sluigi{
4442117469Sluigi	int i, l;
4443117469Sluigi	char *p = (char *)(cmd + 1);
4444117577Sluigi
4445117469Sluigi	cmd->opcode = O_NOP;
4446117469Sluigi	cmd->len =  (cmd->len & (F_NOT | F_OR));
4447117469Sluigi
4448117469Sluigi	/* Compute length of comment string. */
4449117469Sluigi	for (i = 0, l = 0; i < ac; i++)
4450117469Sluigi		l += strlen(av[i]) + 1;
4451117469Sluigi	if (l == 0)
4452117469Sluigi		return;
4453117469Sluigi	if (l > 84)
4454117469Sluigi		errx(EX_DATAERR,
4455117469Sluigi		    "comment too long (max 80 chars)");
4456117469Sluigi	l = 1 + (l+3)/4;
4457117469Sluigi	cmd->len =  (cmd->len & (F_NOT | F_OR)) | l;
4458117469Sluigi	for (i = 0; i < ac; i++) {
4459117469Sluigi		strcpy(p, av[i]);
4460117469Sluigi		p += strlen(av[i]);
4461117469Sluigi		*p++ = ' ';
4462117469Sluigi	}
4463117469Sluigi	*(--p) = '\0';
4464117469Sluigi}
4465117577Sluigi
4466117469Sluigi/*
446798943Sluigi * A function to fill simple commands of size 1.
446898943Sluigi * Existing flags are preserved.
446998943Sluigi */
447098943Sluigistatic void
4471117328Sluigifill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg)
447298943Sluigi{
447398943Sluigi	cmd->opcode = opcode;
447498943Sluigi	cmd->len =  ((cmd->len | flags) & (F_NOT | F_OR)) | 1;
447598943Sluigi	cmd->arg1 = arg;
447698943Sluigi}
447798943Sluigi
447898943Sluigi/*
447998943Sluigi * Fetch and add the MAC address and type, with masks. This generates one or
448098943Sluigi * two microinstructions, and returns the pointer to the last one.
448198943Sluigi */
448298943Sluigistatic ipfw_insn *
448398943Sluigiadd_mac(ipfw_insn *cmd, int ac, char *av[])
448498943Sluigi{
4485102087Sluigi	ipfw_insn_mac *mac;
448698943Sluigi
4487102087Sluigi	if (ac < 2)
4488102098Sluigi		errx(EX_DATAERR, "MAC dst src");
448998943Sluigi
449098943Sluigi	cmd->opcode = O_MACADDR2;
449198943Sluigi	cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
449298943Sluigi
449398943Sluigi	mac = (ipfw_insn_mac *)cmd;
4494101978Sluigi	get_mac_addr_mask(av[0], mac->addr, mac->mask);	/* dst */
4495169424Smaxim	get_mac_addr_mask(av[1], &(mac->addr[ETHER_ADDR_LEN]),
4496169424Smaxim	    &(mac->mask[ETHER_ADDR_LEN])); /* src */
4497102087Sluigi	return cmd;
4498102087Sluigi}
449998943Sluigi
4500102087Sluigistatic ipfw_insn *
4501102087Sluigiadd_mactype(ipfw_insn *cmd, int ac, char *av)
4502102087Sluigi{
4503102087Sluigi	if (ac < 1)
4504102087Sluigi		errx(EX_DATAERR, "missing MAC type");
4505102087Sluigi	if (strcmp(av, "any") != 0) { /* we have a non-null type */
4506102087Sluigi		fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE);
450798943Sluigi		cmd->opcode = O_MAC_TYPE;
4508102087Sluigi		return cmd;
4509102087Sluigi	} else
4510102087Sluigi		return NULL;
4511102087Sluigi}
451298943Sluigi
4513102087Sluigistatic ipfw_insn *
4514152923Sumeadd_proto0(ipfw_insn *cmd, char *av, u_char *protop)
4515102087Sluigi{
4516102087Sluigi	struct protoent *pe;
4517152923Sume	char *ep;
4518152923Sume	int proto;
4519102087Sluigi
4520156315Sume	proto = strtol(av, &ep, 10);
4521156315Sume	if (*ep != '\0' || proto <= 0) {
4522152923Sume		if ((pe = getprotobyname(av)) == NULL)
4523152923Sume			return NULL;
4524152923Sume		proto = pe->p_proto;
4525152923Sume	}
4526145246Sbrooks
4527152923Sume	fill_cmd(cmd, O_PROTO, 0, proto);
4528152923Sume	*protop = proto;
4529152923Sume	return cmd;
4530152923Sume}
4531152923Sume
4532152923Sumestatic ipfw_insn *
4533152923Sumeadd_proto(ipfw_insn *cmd, char *av, u_char *protop)
4534152923Sume{
4535152923Sume	u_char proto = IPPROTO_IP;
4536152923Sume
4537156315Sume	if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
4538146894Smlaier		; /* do not set O_IP4 nor O_IP6 */
4539152923Sume	else if (strcmp(av, "ip4") == 0)
4540152923Sume		/* explicit "just IPv4" rule */
4541152923Sume		fill_cmd(cmd, O_IP4, 0, 0);
4542152923Sume	else if (strcmp(av, "ip6") == 0) {
4543152923Sume		/* explicit "just IPv6" rule */
4544152923Sume		proto = IPPROTO_IPV6;
4545152923Sume		fill_cmd(cmd, O_IP6, 0, 0);
4546152923Sume	} else
4547152923Sume		return add_proto0(cmd, av, protop);
4548152923Sume
4549152923Sume	*protop = proto;
4550152923Sume	return cmd;
4551152923Sume}
4552152923Sume
4553152923Sumestatic ipfw_insn *
4554152923Sumeadd_proto_compat(ipfw_insn *cmd, char *av, u_char *protop)
4555152923Sume{
4556152923Sume	u_char proto = IPPROTO_IP;
4557152923Sume
4558152923Sume	if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
4559152923Sume		; /* do not set O_IP4 nor O_IP6 */
4560146894Smlaier	else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0)
4561146894Smlaier		/* explicit "just IPv4" rule */
4562146894Smlaier		fill_cmd(cmd, O_IP4, 0, 0);
4563146894Smlaier	else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) {
4564146894Smlaier		/* explicit "just IPv6" rule */
4565152923Sume		proto = IPPROTO_IPV6;
4566146894Smlaier		fill_cmd(cmd, O_IP6, 0, 0);
4567152923Sume	} else
4568152923Sume		return add_proto0(cmd, av, protop);
4569145246Sbrooks
4570152923Sume	*protop = proto;
457198943Sluigi	return cmd;
457298943Sluigi}
457398943Sluigi
4574102087Sluigistatic ipfw_insn *
4575102087Sluigiadd_srcip(ipfw_insn *cmd, char *av)
4576102087Sluigi{
4577102087Sluigi	fill_ip((ipfw_insn_ip *)cmd, av);
4578102087Sluigi	if (cmd->opcode == O_IP_DST_SET)			/* set */
4579102087Sluigi		cmd->opcode = O_IP_SRC_SET;
4580130281Sru	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
4581130281Sru		cmd->opcode = O_IP_SRC_LOOKUP;
4582102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
4583102087Sluigi		cmd->opcode = O_IP_SRC_ME;
4584102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
4585102087Sluigi		cmd->opcode = O_IP_SRC;
4586117328Sluigi	else							/* addr/mask */
4587102087Sluigi		cmd->opcode = O_IP_SRC_MASK;
4588102087Sluigi	return cmd;
4589102087Sluigi}
4590102087Sluigi
4591102087Sluigistatic ipfw_insn *
4592102087Sluigiadd_dstip(ipfw_insn *cmd, char *av)
4593102087Sluigi{
4594102087Sluigi	fill_ip((ipfw_insn_ip *)cmd, av);
4595102087Sluigi	if (cmd->opcode == O_IP_DST_SET)			/* set */
4596102087Sluigi		;
4597130281Sru	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
4598130281Sru		;
4599102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
4600102087Sluigi		cmd->opcode = O_IP_DST_ME;
4601102087Sluigi	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
4602102087Sluigi		cmd->opcode = O_IP_DST;
4603117328Sluigi	else							/* addr/mask */
4604102087Sluigi		cmd->opcode = O_IP_DST_MASK;
4605102087Sluigi	return cmd;
4606102087Sluigi}
4607102087Sluigi
4608102087Sluigistatic ipfw_insn *
4609102087Sluigiadd_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
4610102087Sluigi{
4611140271Sbrooks	if (_substrcmp(av, "any") == 0) {
4612102087Sluigi		return NULL;
4613102087Sluigi	} else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) {
4614102087Sluigi		/* XXX todo: check that we have a protocol with ports */
4615102087Sluigi		cmd->opcode = opcode;
4616102087Sluigi		return cmd;
4617102087Sluigi	}
4618102087Sluigi	return NULL;
4619102087Sluigi}
4620102087Sluigi
4621145246Sbrooksstatic ipfw_insn *
4622145246Sbrooksadd_src(ipfw_insn *cmd, char *av, u_char proto)
4623145246Sbrooks{
4624145246Sbrooks	struct in6_addr a;
4625158553Smlaier	char *host, *ch;
4626158553Smlaier	ipfw_insn *ret = NULL;
4627145246Sbrooks
4628158553Smlaier	if ((host = strdup(av)) == NULL)
4629158553Smlaier		return NULL;
4630158553Smlaier	if ((ch = strrchr(host, '/')) != NULL)
4631158553Smlaier		*ch = '\0';
4632158553Smlaier
4633145246Sbrooks	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
4634158553Smlaier	    inet_pton(AF_INET6, host, &a))
4635158553Smlaier		ret = add_srcip6(cmd, av);
4636145246Sbrooks	/* XXX: should check for IPv4, not !IPv6 */
4637161483Sdwmalone	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
4638161483Sdwmalone	    !inet_pton(AF_INET6, host, &a)))
4639158553Smlaier		ret = add_srcip(cmd, av);
4640161483Sdwmalone	if (ret == NULL && strcmp(av, "any") != 0)
4641158553Smlaier		ret = cmd;
4642145246Sbrooks
4643158553Smlaier	free(host);
4644158553Smlaier	return ret;
4645145246Sbrooks}
4646145246Sbrooks
4647145246Sbrooksstatic ipfw_insn *
4648145246Sbrooksadd_dst(ipfw_insn *cmd, char *av, u_char proto)
4649145246Sbrooks{
4650145246Sbrooks	struct in6_addr a;
4651158553Smlaier	char *host, *ch;
4652158553Smlaier	ipfw_insn *ret = NULL;
4653145246Sbrooks
4654158553Smlaier	if ((host = strdup(av)) == NULL)
4655158553Smlaier		return NULL;
4656158553Smlaier	if ((ch = strrchr(host, '/')) != NULL)
4657158553Smlaier		*ch = '\0';
4658158553Smlaier
4659145246Sbrooks	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
4660158553Smlaier	    inet_pton(AF_INET6, host, &a))
4661158553Smlaier		ret = add_dstip6(cmd, av);
4662145246Sbrooks	/* XXX: should check for IPv4, not !IPv6 */
4663161483Sdwmalone	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
4664161483Sdwmalone	    !inet_pton(AF_INET6, host, &a)))
4665158553Smlaier		ret = add_dstip(cmd, av);
4666161483Sdwmalone	if (ret == NULL && strcmp(av, "any") != 0)
4667158553Smlaier		ret = cmd;
4668145246Sbrooks
4669158553Smlaier	free(host);
4670158553Smlaier	return ret;
4671145246Sbrooks}
4672145246Sbrooks
467398943Sluigi/*
467498943Sluigi * Parse arguments and assemble the microinstructions which make up a rule.
467598943Sluigi * Rules are added into the 'rulebuf' and then copied in the correct order
467698943Sluigi * into the actual rule.
467798943Sluigi *
4678136071Sgreen * The syntax for a rule starts with the action, followed by
4679136071Sgreen * optional action parameters, and the various match patterns.
4680108533Sschweikh * In the assembled microcode, the first opcode must be an O_PROBE_STATE
468198943Sluigi * (generated if the rule includes a keep-state option), then the
4682136071Sgreen * various match patterns, log/altq actions, and the actual action.
4683106505Smaxim *
468498943Sluigi */
468598943Sluigistatic void
468698943Sluigiadd(int ac, char *av[])
468798943Sluigi{
468898943Sluigi	/*
468998943Sluigi	 * rules are added into the 'rulebuf' and then copied in
469098943Sluigi	 * the correct order into the actual rule.
469198943Sluigi	 * Some things that need to go out of order (prob, action etc.)
469298943Sluigi	 * go into actbuf[].
469398943Sluigi	 */
4694117328Sluigi	static uint32_t rulebuf[255], actbuf[255], cmdbuf[255];
469598943Sluigi
4696117469Sluigi	ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
4697102087Sluigi	ipfw_insn *first_cmd;	/* first match pattern */
469898943Sluigi
469998943Sluigi	struct ip_fw *rule;
470098943Sluigi
470198943Sluigi	/*
470298943Sluigi	 * various flags used to record that we entered some fields.
470398943Sluigi	 */
4704101116Sluigi	ipfw_insn *have_state = NULL;	/* check-state or keep-state */
4705158879Soleg	ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL;
4706134475Smaxim	size_t len;
470798943Sluigi
470898943Sluigi	int i;
470998943Sluigi
471098943Sluigi	int open_par = 0;	/* open parenthesis ( */
471198943Sluigi
471298943Sluigi	/* proto is here because it is used to fetch ports */
471398943Sluigi	u_char proto = IPPROTO_IP;	/* default protocol */
471498943Sluigi
4715107289Sluigi	double match_prob = 1; /* match probability, default is always match */
4716107289Sluigi
471798943Sluigi	bzero(actbuf, sizeof(actbuf));		/* actions go here */
471898943Sluigi	bzero(cmdbuf, sizeof(cmdbuf));
471998943Sluigi	bzero(rulebuf, sizeof(rulebuf));
472098943Sluigi
472198943Sluigi	rule = (struct ip_fw *)rulebuf;
472298943Sluigi	cmd = (ipfw_insn *)cmdbuf;
472398943Sluigi	action = (ipfw_insn *)actbuf;
472498943Sluigi
472598943Sluigi	av++; ac--;
472698943Sluigi
472798943Sluigi	/* [rule N]	-- Rule number optional */
472898943Sluigi	if (ac && isdigit(**av)) {
472998943Sluigi		rule->rulenum = atoi(*av);
473098943Sluigi		av++;
473198943Sluigi		ac--;
473298943Sluigi	}
473398943Sluigi
4734117655Sluigi	/* [set N]	-- set number (0..RESVD_SET), optional */
4735140271Sbrooks	if (ac > 1 && _substrcmp(*av, "set") == 0) {
4736101628Sluigi		int set = strtoul(av[1], NULL, 10);
4737117655Sluigi		if (set < 0 || set > RESVD_SET)
4738101628Sluigi			errx(EX_DATAERR, "illegal set %s", av[1]);
4739101628Sluigi		rule->set = set;
4740101628Sluigi		av += 2; ac -= 2;
4741101628Sluigi	}
4742101628Sluigi
474398943Sluigi	/* [prob D]	-- match probability, optional */
4744140271Sbrooks	if (ac > 1 && _substrcmp(*av, "prob") == 0) {
4745107289Sluigi		match_prob = strtod(av[1], NULL);
474698943Sluigi
4747107289Sluigi		if (match_prob <= 0 || match_prob > 1)
474898943Sluigi			errx(EX_DATAERR, "illegal match prob. %s", av[1]);
474998943Sluigi		av += 2; ac -= 2;
475098943Sluigi	}
475198943Sluigi
475298943Sluigi	/* action	-- mandatory */
475398943Sluigi	NEED1("missing action");
475498943Sluigi	i = match_token(rule_actions, *av);
475598943Sluigi	ac--; av++;
475698943Sluigi	action->len = 1;	/* default */
475798943Sluigi	switch(i) {
475898943Sluigi	case TOK_CHECKSTATE:
4759101116Sluigi		have_state = action;
476098943Sluigi		action->opcode = O_CHECK_STATE;
476198943Sluigi		break;
476298943Sluigi
476398943Sluigi	case TOK_ACCEPT:
476498943Sluigi		action->opcode = O_ACCEPT;
476598943Sluigi		break;
476698943Sluigi
476798943Sluigi	case TOK_DENY:
476898943Sluigi		action->opcode = O_DENY;
476999475Sluigi		action->arg1 = 0;
477098943Sluigi		break;
477198943Sluigi
477299475Sluigi	case TOK_REJECT:
477399475Sluigi		action->opcode = O_REJECT;
477499475Sluigi		action->arg1 = ICMP_UNREACH_HOST;
477599475Sluigi		break;
477699475Sluigi
477799475Sluigi	case TOK_RESET:
477899475Sluigi		action->opcode = O_REJECT;
477999475Sluigi		action->arg1 = ICMP_REJECT_RST;
478099475Sluigi		break;
478199475Sluigi
4782149020Sbz	case TOK_RESET6:
4783149020Sbz		action->opcode = O_UNREACH6;
4784149020Sbz		action->arg1 = ICMP6_UNREACH_RST;
4785149020Sbz		break;
4786149020Sbz
478799475Sluigi	case TOK_UNREACH:
478899475Sluigi		action->opcode = O_REJECT;
478999475Sluigi		NEED1("missing reject code");
479099475Sluigi		fill_reject_code(&action->arg1, *av);
479199475Sluigi		ac--; av++;
479299475Sluigi		break;
479399475Sluigi
4794149020Sbz	case TOK_UNREACH6:
4795149020Sbz		action->opcode = O_UNREACH6;
4796149020Sbz		NEED1("missing unreach code");
4797149020Sbz		fill_unreach6_code(&action->arg1, *av);
4798149020Sbz		ac--; av++;
4799149020Sbz		break;
4800149020Sbz
480198943Sluigi	case TOK_COUNT:
480298943Sluigi		action->opcode = O_COUNT;
480398943Sluigi		break;
480498943Sluigi
480598943Sluigi	case TOK_QUEUE:
4806153374Sglebius		action->opcode = O_QUEUE;
4807153374Sglebius		goto chkarg;
480898943Sluigi	case TOK_PIPE:
4809153374Sglebius		action->opcode = O_PIPE;
4810153374Sglebius		goto chkarg;
481198943Sluigi	case TOK_SKIPTO:
4812153374Sglebius		action->opcode = O_SKIPTO;
4813153374Sglebius		goto chkarg;
4814153374Sglebius	case TOK_NETGRAPH:
4815153374Sglebius		action->opcode = O_NETGRAPH;
4816153374Sglebius		goto chkarg;
4817153374Sglebius	case TOK_NGTEE:
4818153374Sglebius		action->opcode = O_NGTEE;
4819153374Sglebius		goto chkarg;
482098943Sluigi	case TOK_DIVERT:
4821153374Sglebius		action->opcode = O_DIVERT;
4822153374Sglebius		goto chkarg;
482398943Sluigi	case TOK_TEE:
4824153374Sglebius		action->opcode = O_TEE;
4825153374Sglebiuschkarg:
4826153374Sglebius		if (!ac)
4827153374Sglebius			errx(EX_USAGE, "missing argument for %s", *(av - 1));
4828153374Sglebius		if (isdigit(**av)) {
4829153374Sglebius			action->arg1 = strtoul(*av, NULL, 10);
4830153374Sglebius			if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG)
4831153374Sglebius				errx(EX_DATAERR, "illegal argument for %s",
4832153374Sglebius				    *(av - 1));
4833153374Sglebius		} else if (_substrcmp(*av, TABLEARG) == 0) {
4834153374Sglebius			action->arg1 = IP_FW_TABLEARG;
4835153374Sglebius		} else if (i == TOK_DIVERT || i == TOK_TEE) {
483698943Sluigi			struct servent *s;
483798943Sluigi			setservent(1);
483898943Sluigi			s = getservbyname(av[0], "divert");
483998943Sluigi			if (s != NULL)
484098943Sluigi				action->arg1 = ntohs(s->s_port);
484198943Sluigi			else
484298943Sluigi				errx(EX_DATAERR, "illegal divert/tee port");
4843153374Sglebius		} else
4844153374Sglebius			errx(EX_DATAERR, "illegal argument for %s", *(av - 1));
484598943Sluigi		ac--; av++;
484698943Sluigi		break;
484798943Sluigi
484898943Sluigi	case TOK_FORWARD: {
484998943Sluigi		ipfw_insn_sa *p = (ipfw_insn_sa *)action;
485098943Sluigi		char *s, *end;
485198943Sluigi
485298943Sluigi		NEED1("missing forward address[:port]");
485398943Sluigi
485498943Sluigi		action->opcode = O_FORWARD_IP;
485598943Sluigi		action->len = F_INSN_SIZE(ipfw_insn_sa);
485698943Sluigi
485798943Sluigi		p->sa.sin_len = sizeof(struct sockaddr_in);
485898943Sluigi		p->sa.sin_family = AF_INET;
485998943Sluigi		p->sa.sin_port = 0;
486098943Sluigi		/*
486198943Sluigi		 * locate the address-port separator (':' or ',')
486298943Sluigi		 */
486398943Sluigi		s = strchr(*av, ':');
486498943Sluigi		if (s == NULL)
486598943Sluigi			s = strchr(*av, ',');
486698943Sluigi		if (s != NULL) {
486798943Sluigi			*(s++) = '\0';
486898943Sluigi			i = strtoport(s, &end, 0 /* base */, 0 /* proto */);
486998943Sluigi			if (s == end)
487098943Sluigi				errx(EX_DATAERR,
487198943Sluigi				    "illegal forwarding port ``%s''", s);
4872103241Sluigi			p->sa.sin_port = (u_short)i;
487398943Sluigi		}
4874161456Sjulian		if (_substrcmp(*av, "tablearg") == 0)
4875161456Sjulian			p->sa.sin_addr.s_addr = INADDR_ANY;
4876161456Sjulian		else
4877161424Sjulian			lookup_host(*av, &(p->sa.sin_addr));
487898943Sluigi		ac--; av++;
487998943Sluigi		break;
4880161424Sjulian	    }
4881117469Sluigi	case TOK_COMMENT:
4882117469Sluigi		/* pretend it is a 'count' rule followed by the comment */
4883117469Sluigi		action->opcode = O_COUNT;
4884117469Sluigi		ac++; av--;	/* go back... */
4885117469Sluigi		break;
4886117469Sluigi
4887165648Spiso	case TOK_NAT:
4888165648Spiso 		action->opcode = O_NAT;
4889165648Spiso 		action->len = F_INSN_SIZE(ipfw_insn_nat);
4890165648Spiso 		NEED1("missing nat number");
4891165648Spiso 	        action->arg1 = strtoul(*av, NULL, 10);
4892165648Spiso 		ac--; av++;
4893165648Spiso 		break;
4894165648Spiso
489598943Sluigi	default:
4896102087Sluigi		errx(EX_DATAERR, "invalid action %s\n", av[-1]);
489798943Sluigi	}
489898943Sluigi	action = next_cmd(action);
489998943Sluigi
490098943Sluigi	/*
4901136071Sgreen	 * [altq queuename] -- altq tag, optional
490298943Sluigi	 * [log [logamount N]]	-- log, optional
490398943Sluigi	 *
4904136071Sgreen	 * If they exist, it go first in the cmdbuf, but then it is
490598943Sluigi	 * skipped in the copy section to the end of the buffer.
490698943Sluigi	 */
4907136071Sgreen	while (ac != 0 && (i = match_token(rule_action_params, *av)) != -1) {
4908136071Sgreen		ac--; av++;
4909136071Sgreen		switch (i) {
4910136071Sgreen		case TOK_LOG:
4911136071Sgreen		    {
4912136071Sgreen			ipfw_insn_log *c = (ipfw_insn_log *)cmd;
4913136071Sgreen			int l;
491498943Sluigi
4915136071Sgreen			if (have_log)
4916136071Sgreen				errx(EX_DATAERR,
4917136071Sgreen				    "log cannot be specified more than once");
4918136071Sgreen			have_log = (ipfw_insn *)c;
4919136071Sgreen			cmd->len = F_INSN_SIZE(ipfw_insn_log);
4920136071Sgreen			cmd->opcode = O_LOG;
4921140271Sbrooks			if (ac && _substrcmp(*av, "logamount") == 0) {
4922136071Sgreen				ac--; av++;
4923136071Sgreen				NEED1("logamount requires argument");
4924136071Sgreen				l = atoi(*av);
4925136071Sgreen				if (l < 0)
4926136071Sgreen					errx(EX_DATAERR,
4927136071Sgreen					    "logamount must be positive");
4928136071Sgreen				c->max_log = l;
4929136071Sgreen				ac--; av++;
4930136071Sgreen			} else {
4931136071Sgreen				len = sizeof(c->max_log);
4932136071Sgreen				if (sysctlbyname("net.inet.ip.fw.verbose_limit",
4933136071Sgreen				    &c->max_log, &len, NULL, 0) == -1)
4934136071Sgreen					errx(1, "sysctlbyname(\"%s\")",
4935136071Sgreen					    "net.inet.ip.fw.verbose_limit");
4936136071Sgreen			}
4937136071Sgreen		    }
4938136071Sgreen			break;
4939136071Sgreen
4940136071Sgreen		case TOK_ALTQ:
4941136071Sgreen		    {
4942136071Sgreen			ipfw_insn_altq *a = (ipfw_insn_altq *)cmd;
4943136071Sgreen
4944136071Sgreen			NEED1("missing altq queue name");
4945136071Sgreen			if (have_altq)
4946136071Sgreen				errx(EX_DATAERR,
4947136071Sgreen				    "altq cannot be specified more than once");
4948136071Sgreen			have_altq = (ipfw_insn *)a;
4949136071Sgreen			cmd->len = F_INSN_SIZE(ipfw_insn_altq);
4950136071Sgreen			cmd->opcode = O_ALTQ;
4951136071Sgreen			fill_altq_qid(&a->qid, *av);
495298943Sluigi			ac--; av++;
4953136071Sgreen		    }
4954136071Sgreen			break;
4955136071Sgreen
4956158879Soleg		case TOK_TAG:
4957159636Soleg		case TOK_UNTAG: {
4958159636Soleg			uint16_t tag;
4959159636Soleg
4960158879Soleg			if (have_tag)
4961159636Soleg				errx(EX_USAGE, "tag and untag cannot be "
4962159636Soleg				    "specified more than once");
4963159636Soleg			GET_UINT_ARG(tag, 1, 65534, i, rule_action_params);
4964158879Soleg			have_tag = cmd;
4965159636Soleg			fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag);
4966158879Soleg			ac--; av++;
4967158879Soleg			break;
4968159636Soleg		}
4969158879Soleg
4970136071Sgreen		default:
4971136071Sgreen			abort();
497298943Sluigi		}
497398943Sluigi		cmd = next_cmd(cmd);
497498943Sluigi	}
497598943Sluigi
4976101116Sluigi	if (have_state)	/* must be a check-state, we are done */
497798943Sluigi		goto done;
497898943Sluigi
497998943Sluigi#define OR_START(target)					\
498098943Sluigi	if (ac && (*av[0] == '(' || *av[0] == '{')) {		\
498198943Sluigi		if (open_par)					\
498298943Sluigi			errx(EX_USAGE, "nested \"(\" not allowed\n"); \
4983101641Sluigi		prev = NULL;					\
498498943Sluigi		open_par = 1;					\
498598943Sluigi		if ( (av[0])[1] == '\0') {			\
498698943Sluigi			ac--; av++;				\
498798943Sluigi		} else						\
498898943Sluigi			(*av)++;				\
498998943Sluigi	}							\
499098943Sluigi	target:							\
499198943Sluigi
499298943Sluigi
499398943Sluigi#define	CLOSE_PAR						\
499498943Sluigi	if (open_par) {						\
499598943Sluigi		if (ac && (					\
4996140271Sbrooks		    strcmp(*av, ")") == 0 ||			\
4997140271Sbrooks		    strcmp(*av, "}") == 0)) {			\
4998101641Sluigi			prev = NULL;				\
499998943Sluigi			open_par = 0;				\
500098943Sluigi			ac--; av++;				\
500198943Sluigi		} else						\
500298943Sluigi			errx(EX_USAGE, "missing \")\"\n");	\
500398943Sluigi	}
5004106505Smaxim
500598943Sluigi#define NOT_BLOCK						\
5006140271Sbrooks	if (ac && _substrcmp(*av, "not") == 0) {		\
500798943Sluigi		if (cmd->len & F_NOT)				\
500898943Sluigi			errx(EX_USAGE, "double \"not\" not allowed\n"); \
500998943Sluigi		cmd->len |= F_NOT;				\
501098943Sluigi		ac--; av++;					\
501198943Sluigi	}
501298943Sluigi
501398943Sluigi#define OR_BLOCK(target)					\
5014140271Sbrooks	if (ac && _substrcmp(*av, "or") == 0) {		\
501598943Sluigi		if (prev == NULL || open_par == 0)		\
501698943Sluigi			errx(EX_DATAERR, "invalid OR block");	\
501798943Sluigi		prev->len |= F_OR;				\
501898943Sluigi		ac--; av++;					\
501998943Sluigi		goto target;					\
502098943Sluigi	}							\
502198943Sluigi	CLOSE_PAR;
502298943Sluigi
5023102087Sluigi	first_cmd = cmd;
5024102098Sluigi
5025102098Sluigi#if 0
502698943Sluigi	/*
5027102087Sluigi	 * MAC addresses, optional.
5028102087Sluigi	 * If we have this, we skip the part "proto from src to dst"
5029102087Sluigi	 * and jump straight to the option parsing.
5030102087Sluigi	 */
5031102087Sluigi	NOT_BLOCK;
5032102087Sluigi	NEED1("missing protocol");
5033140271Sbrooks	if (_substrcmp(*av, "MAC") == 0 ||
5034140271Sbrooks	    _substrcmp(*av, "mac") == 0) {
5035102087Sluigi		ac--; av++;	/* the "MAC" keyword */
5036102087Sluigi		add_mac(cmd, ac, av); /* exits in case of errors */
5037102087Sluigi		cmd = next_cmd(cmd);
5038102087Sluigi		ac -= 2; av += 2;	/* dst-mac and src-mac */
5039102087Sluigi		NOT_BLOCK;
5040102087Sluigi		NEED1("missing mac type");
5041102087Sluigi		if (add_mactype(cmd, ac, av[0]))
5042102087Sluigi			cmd = next_cmd(cmd);
5043102087Sluigi		ac--; av++;	/* any or mac-type */
5044102087Sluigi		goto read_options;
5045102087Sluigi	}
5046102098Sluigi#endif
5047102087Sluigi
5048102087Sluigi	/*
504998943Sluigi	 * protocol, mandatory
505098943Sluigi	 */
505198943Sluigi    OR_START(get_proto);
505298943Sluigi	NOT_BLOCK;
505398943Sluigi	NEED1("missing protocol");
5054152923Sume	if (add_proto_compat(cmd, *av, &proto)) {
5055102087Sluigi		av++; ac--;
5056147105Smlaier		if (F_LEN(cmd) != 0) {
5057102087Sluigi			prev = cmd;
5058102087Sluigi			cmd = next_cmd(cmd);
5059102087Sluigi		}
5060102098Sluigi	} else if (first_cmd != cmd) {
5061116438Smaxim		errx(EX_DATAERR, "invalid protocol ``%s''", *av);
5062102098Sluigi	} else
5063102098Sluigi		goto read_options;
506498943Sluigi    OR_BLOCK(get_proto);
506598943Sluigi
506698943Sluigi	/*
5067102087Sluigi	 * "from", mandatory
506898943Sluigi	 */
5069140271Sbrooks	if (!ac || _substrcmp(*av, "from") != 0)
507098943Sluigi		errx(EX_USAGE, "missing ``from''");
507198943Sluigi	ac--; av++;
507298943Sluigi
507398943Sluigi	/*
507498943Sluigi	 * source IP, mandatory
507598943Sluigi	 */
507698943Sluigi    OR_START(source_ip);
507798943Sluigi	NOT_BLOCK;	/* optional "not" */
507898943Sluigi	NEED1("missing source address");
5079145246Sbrooks	if (add_src(cmd, *av, proto)) {
5080102087Sluigi		ac--; av++;
5081102087Sluigi		if (F_LEN(cmd) != 0) {	/* ! any */
5082102087Sluigi			prev = cmd;
5083102087Sluigi			cmd = next_cmd(cmd);
5084102087Sluigi		}
5085145246Sbrooks	} else
5086145246Sbrooks		errx(EX_USAGE, "bad source address %s", *av);
508798943Sluigi    OR_BLOCK(source_ip);
508898943Sluigi
508998943Sluigi	/*
509098943Sluigi	 * source ports, optional
509198943Sluigi	 */
509298943Sluigi	NOT_BLOCK;	/* optional "not" */
5093101641Sluigi	if (ac) {
5094140271Sbrooks		if (_substrcmp(*av, "any") == 0 ||
5095102087Sluigi		    add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
5096102087Sluigi			ac--; av++;
5097102087Sluigi			if (F_LEN(cmd) != 0)
5098102087Sluigi				cmd = next_cmd(cmd);
5099101641Sluigi		}
510098943Sluigi	}
510198943Sluigi
510298943Sluigi	/*
5103102087Sluigi	 * "to", mandatory
510498943Sluigi	 */
5105140271Sbrooks	if (!ac || _substrcmp(*av, "to") != 0)
510698943Sluigi		errx(EX_USAGE, "missing ``to''");
510798943Sluigi	av++; ac--;
510898943Sluigi
510998943Sluigi	/*
511098943Sluigi	 * destination, mandatory
511198943Sluigi	 */
511298943Sluigi    OR_START(dest_ip);
511398943Sluigi	NOT_BLOCK;	/* optional "not" */
511498943Sluigi	NEED1("missing dst address");
5115145246Sbrooks	if (add_dst(cmd, *av, proto)) {
5116102087Sluigi		ac--; av++;
5117102087Sluigi		if (F_LEN(cmd) != 0) {	/* ! any */
5118102087Sluigi			prev = cmd;
5119102087Sluigi			cmd = next_cmd(cmd);
5120102087Sluigi		}
5121145246Sbrooks	} else
5122145246Sbrooks		errx( EX_USAGE, "bad destination address %s", *av);
512398943Sluigi    OR_BLOCK(dest_ip);
512498943Sluigi
512598943Sluigi	/*
512698943Sluigi	 * dest. ports, optional
512798943Sluigi	 */
512898943Sluigi	NOT_BLOCK;	/* optional "not" */
5129101641Sluigi	if (ac) {
5130140271Sbrooks		if (_substrcmp(*av, "any") == 0 ||
5131102087Sluigi		    add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
5132102087Sluigi			ac--; av++;
5133102087Sluigi			if (F_LEN(cmd) != 0)
5134102087Sluigi				cmd = next_cmd(cmd);
5135101641Sluigi		}
513698943Sluigi	}
513798943Sluigi
513898943Sluigiread_options:
5139102087Sluigi	if (ac && first_cmd == cmd) {
5140102087Sluigi		/*
5141102087Sluigi		 * nothing specified so far, store in the rule to ease
5142102087Sluigi		 * printout later.
5143102087Sluigi		 */
5144102087Sluigi		 rule->_pad = 1;
5145102087Sluigi	}
514698943Sluigi	prev = NULL;
514798943Sluigi	while (ac) {
5148101641Sluigi		char *s;
5149101641Sluigi		ipfw_insn_u32 *cmd32;	/* alias for cmd */
515098943Sluigi
5151101641Sluigi		s = *av;
5152101641Sluigi		cmd32 = (ipfw_insn_u32 *)cmd;
5153101641Sluigi
515498943Sluigi		if (*s == '!') {	/* alternate syntax for NOT */
515598943Sluigi			if (cmd->len & F_NOT)
515698943Sluigi				errx(EX_USAGE, "double \"not\" not allowed\n");
515798943Sluigi			cmd->len = F_NOT;
515898943Sluigi			s++;
515998943Sluigi		}
516098943Sluigi		i = match_token(rule_options, s);
516198943Sluigi		ac--; av++;
516298943Sluigi		switch(i) {
516398943Sluigi		case TOK_NOT:
516498943Sluigi			if (cmd->len & F_NOT)
516598943Sluigi				errx(EX_USAGE, "double \"not\" not allowed\n");
516698943Sluigi			cmd->len = F_NOT;
516798943Sluigi			break;
516898943Sluigi
516998943Sluigi		case TOK_OR:
5170101641Sluigi			if (open_par == 0 || prev == NULL)
517198943Sluigi				errx(EX_USAGE, "invalid \"or\" block\n");
517298943Sluigi			prev->len |= F_OR;
517398943Sluigi			break;
5174101641Sluigi
5175101641Sluigi		case TOK_STARTBRACE:
5176101641Sluigi			if (open_par)
5177101641Sluigi				errx(EX_USAGE, "+nested \"(\" not allowed\n");
5178101641Sluigi			open_par = 1;
5179101641Sluigi			break;
5180101641Sluigi
5181101641Sluigi		case TOK_ENDBRACE:
5182101641Sluigi			if (!open_par)
5183101641Sluigi				errx(EX_USAGE, "+missing \")\"\n");
5184101641Sluigi			open_par = 0;
5185102087Sluigi			prev = NULL;
5186101641Sluigi        		break;
5187101641Sluigi
518898943Sluigi		case TOK_IN:
518998943Sluigi			fill_cmd(cmd, O_IN, 0, 0);
519098943Sluigi			break;
519198943Sluigi
519298943Sluigi		case TOK_OUT:
519398943Sluigi			cmd->len ^= F_NOT; /* toggle F_NOT */
519498943Sluigi			fill_cmd(cmd, O_IN, 0, 0);
519598943Sluigi			break;
519698943Sluigi
5197136073Sgreen		case TOK_DIVERTED:
5198136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 3);
5199136073Sgreen			break;
5200136073Sgreen
5201136073Sgreen		case TOK_DIVERTEDLOOPBACK:
5202136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 1);
5203136073Sgreen			break;
5204136073Sgreen
5205136073Sgreen		case TOK_DIVERTEDOUTPUT:
5206136073Sgreen			fill_cmd(cmd, O_DIVERTED, 0, 2);
5207136073Sgreen			break;
5208136073Sgreen
520998943Sluigi		case TOK_FRAG:
521098943Sluigi			fill_cmd(cmd, O_FRAG, 0, 0);
521198943Sluigi			break;
521298943Sluigi
521398943Sluigi		case TOK_LAYER2:
521498943Sluigi			fill_cmd(cmd, O_LAYER2, 0, 0);
521598943Sluigi			break;
521698943Sluigi
521798943Sluigi		case TOK_XMIT:
521898943Sluigi		case TOK_RECV:
521998943Sluigi		case TOK_VIA:
522098943Sluigi			NEED1("recv, xmit, via require interface name"
522198943Sluigi				" or address");
522298943Sluigi			fill_iface((ipfw_insn_if *)cmd, av[0]);
522398943Sluigi			ac--; av++;
522498943Sluigi			if (F_LEN(cmd) == 0)	/* not a valid address */
522598943Sluigi				break;
522698943Sluigi			if (i == TOK_XMIT)
522798943Sluigi				cmd->opcode = O_XMIT;
522898943Sluigi			else if (i == TOK_RECV)
522998943Sluigi				cmd->opcode = O_RECV;
523098943Sluigi			else if (i == TOK_VIA)
523198943Sluigi				cmd->opcode = O_VIA;
523298943Sluigi			break;
523398943Sluigi
523499475Sluigi		case TOK_ICMPTYPES:
523599475Sluigi			NEED1("icmptypes requires list of types");
523699475Sluigi			fill_icmptypes((ipfw_insn_u32 *)cmd, *av);
523799475Sluigi			av++; ac--;
523899475Sluigi			break;
5239145246Sbrooks
5240145246Sbrooks		case TOK_ICMP6TYPES:
5241145246Sbrooks			NEED1("icmptypes requires list of types");
5242145246Sbrooks			fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av);
5243145246Sbrooks			av++; ac--;
5244145246Sbrooks			break;
524599475Sluigi
524698943Sluigi		case TOK_IPTTL:
524798943Sluigi			NEED1("ipttl requires TTL");
5248116690Sluigi			if (strpbrk(*av, "-,")) {
5249116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPTTL))
5250116690Sluigi				errx(EX_DATAERR, "invalid ipttl %s", *av);
5251116690Sluigi			} else
5252116690Sluigi			    fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
525398943Sluigi			ac--; av++;
525498943Sluigi			break;
525598943Sluigi
525698943Sluigi		case TOK_IPID:
5257116690Sluigi			NEED1("ipid requires id");
5258116690Sluigi			if (strpbrk(*av, "-,")) {
5259116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPID))
5260116690Sluigi				errx(EX_DATAERR, "invalid ipid %s", *av);
5261116690Sluigi			} else
5262116690Sluigi			    fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0));
526398943Sluigi			ac--; av++;
526498943Sluigi			break;
526598943Sluigi
526698943Sluigi		case TOK_IPLEN:
526798943Sluigi			NEED1("iplen requires length");
5268116690Sluigi			if (strpbrk(*av, "-,")) {
5269116690Sluigi			    if (!add_ports(cmd, *av, 0, O_IPLEN))
5270116690Sluigi				errx(EX_DATAERR, "invalid ip len %s", *av);
5271116690Sluigi			} else
5272116690Sluigi			    fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0));
527398943Sluigi			ac--; av++;
527498943Sluigi			break;
527598943Sluigi
527698943Sluigi		case TOK_IPVER:
527798943Sluigi			NEED1("ipver requires version");
527898943Sluigi			fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0));
527998943Sluigi			ac--; av++;
528098943Sluigi			break;
528198943Sluigi
528299475Sluigi		case TOK_IPPRECEDENCE:
528399475Sluigi			NEED1("ipprecedence requires value");
528499475Sluigi			fill_cmd(cmd, O_IPPRECEDENCE, 0,
528599475Sluigi			    (strtoul(*av, NULL, 0) & 7) << 5);
528699475Sluigi			ac--; av++;
528799475Sluigi			break;
528899475Sluigi
528998943Sluigi		case TOK_IPOPTS:
529098943Sluigi			NEED1("missing argument for ipoptions");
5291101116Sluigi			fill_flags(cmd, O_IPOPT, f_ipopts, *av);
529298943Sluigi			ac--; av++;
529398943Sluigi			break;
529498943Sluigi
529599475Sluigi		case TOK_IPTOS:
529699475Sluigi			NEED1("missing argument for iptos");
5297101116Sluigi			fill_flags(cmd, O_IPTOS, f_iptos, *av);
529899475Sluigi			ac--; av++;
529999475Sluigi			break;
530099475Sluigi
530198943Sluigi		case TOK_UID:
530298943Sluigi			NEED1("uid requires argument");
530398943Sluigi		    {
530498943Sluigi			char *end;
530598943Sluigi			uid_t uid;
530698943Sluigi			struct passwd *pwd;
530798943Sluigi
530898943Sluigi			cmd->opcode = O_UID;
530998943Sluigi			uid = strtoul(*av, &end, 0);
531098943Sluigi			pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av);
531198943Sluigi			if (pwd == NULL)
531298943Sluigi				errx(EX_DATAERR, "uid \"%s\" nonexistent", *av);
5313106504Smaxim			cmd32->d[0] = pwd->pw_uid;
5314135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
531598943Sluigi			ac--; av++;
531698943Sluigi		    }
531798943Sluigi			break;
531898943Sluigi
531998943Sluigi		case TOK_GID:
532098943Sluigi			NEED1("gid requires argument");
532198943Sluigi		    {
532298943Sluigi			char *end;
532398943Sluigi			gid_t gid;
532498943Sluigi			struct group *grp;
532598943Sluigi
532698943Sluigi			cmd->opcode = O_GID;
532798943Sluigi			gid = strtoul(*av, &end, 0);
532898943Sluigi			grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av);
532998943Sluigi			if (grp == NULL)
533098943Sluigi				errx(EX_DATAERR, "gid \"%s\" nonexistent", *av);
5331106504Smaxim			cmd32->d[0] = grp->gr_gid;
5332135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
533398943Sluigi			ac--; av++;
533498943Sluigi		    }
533598943Sluigi			break;
533698943Sluigi
5337133600Scsjp		case TOK_JAIL:
5338133600Scsjp			NEED1("jail requires argument");
5339133600Scsjp		    {
5340133600Scsjp			char *end;
5341133600Scsjp			int jid;
5342133600Scsjp
5343133600Scsjp			cmd->opcode = O_JAIL;
5344133600Scsjp			jid = (int)strtol(*av, &end, 0);
5345133600Scsjp			if (jid < 0 || *end != '\0')
5346133600Scsjp				errx(EX_DATAERR, "jail requires prison ID");
5347135554Scsjp			cmd32->d[0] = (uint32_t)jid;
5348135089Scsjp			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
5349133600Scsjp			ac--; av++;
5350133600Scsjp		    }
5351133600Scsjp			break;
5352133600Scsjp
535398943Sluigi		case TOK_ESTAB:
535498943Sluigi			fill_cmd(cmd, O_ESTAB, 0, 0);
535598943Sluigi			break;
535698943Sluigi
535798943Sluigi		case TOK_SETUP:
535898943Sluigi			fill_cmd(cmd, O_TCPFLAGS, 0,
535998943Sluigi				(TH_SYN) | ( (TH_ACK) & 0xff) <<8 );
536098943Sluigi			break;
536198943Sluigi
5362136075Sgreen		case TOK_TCPDATALEN:
5363136075Sgreen			NEED1("tcpdatalen requires length");
5364136075Sgreen			if (strpbrk(*av, "-,")) {
5365136075Sgreen			    if (!add_ports(cmd, *av, 0, O_TCPDATALEN))
5366136075Sgreen				errx(EX_DATAERR, "invalid tcpdata len %s", *av);
5367136075Sgreen			} else
5368136075Sgreen			    fill_cmd(cmd, O_TCPDATALEN, 0,
5369136075Sgreen				    strtoul(*av, NULL, 0));
5370136075Sgreen			ac--; av++;
5371136075Sgreen			break;
5372136075Sgreen
537398943Sluigi		case TOK_TCPOPTS:
537498943Sluigi			NEED1("missing argument for tcpoptions");
537598943Sluigi			fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av);
537698943Sluigi			ac--; av++;
537798943Sluigi			break;
537898943Sluigi
537998943Sluigi		case TOK_TCPSEQ:
538098943Sluigi		case TOK_TCPACK:
538198943Sluigi			NEED1("tcpseq/tcpack requires argument");
538298943Sluigi			cmd->len = F_INSN_SIZE(ipfw_insn_u32);
538398943Sluigi			cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK;
538498943Sluigi			cmd32->d[0] = htonl(strtoul(*av, NULL, 0));
538598943Sluigi			ac--; av++;
538698943Sluigi			break;
538798943Sluigi
538898943Sluigi		case TOK_TCPWIN:
538998943Sluigi			NEED1("tcpwin requires length");
539098943Sluigi			fill_cmd(cmd, O_TCPWIN, 0,
539198943Sluigi			    htons(strtoul(*av, NULL, 0)));
539298943Sluigi			ac--; av++;
539398943Sluigi			break;
539498943Sluigi
539598943Sluigi		case TOK_TCPFLAGS:
539698943Sluigi			NEED1("missing argument for tcpflags");
539798943Sluigi			cmd->opcode = O_TCPFLAGS;
539898943Sluigi			fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av);
539998943Sluigi			ac--; av++;
540098943Sluigi			break;
540198943Sluigi
540298943Sluigi		case TOK_KEEPSTATE:
5403101641Sluigi			if (open_par)
5404101641Sluigi				errx(EX_USAGE, "keep-state cannot be part "
5405101641Sluigi				    "of an or block");
540699909Sluigi			if (have_state)
5407101116Sluigi				errx(EX_USAGE, "only one of keep-state "
540899909Sluigi					"and limit is allowed");
5409101116Sluigi			have_state = cmd;
541098943Sluigi			fill_cmd(cmd, O_KEEP_STATE, 0, 0);
541198943Sluigi			break;
541298943Sluigi
5413159636Soleg		case TOK_LIMIT: {
5414159636Soleg			ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
5415159636Soleg			int val;
5416159636Soleg
5417101641Sluigi			if (open_par)
5418159636Soleg				errx(EX_USAGE,
5419159636Soleg				    "limit cannot be part of an or block");
542099909Sluigi			if (have_state)
5421168819Smaxim				errx(EX_USAGE, "only one of keep-state and "
5422159636Soleg				    "limit is allowed");
5423101116Sluigi			have_state = cmd;
542498943Sluigi
542598943Sluigi			cmd->len = F_INSN_SIZE(ipfw_insn_limit);
542698943Sluigi			cmd->opcode = O_LIMIT;
5427159636Soleg			c->limit_mask = c->conn_limit = 0;
542898943Sluigi
5429159636Soleg			while (ac > 0) {
5430159636Soleg				if ((val = match_token(limit_masks, *av)) <= 0)
543198943Sluigi					break;
543298943Sluigi				c->limit_mask |= val;
543398943Sluigi				ac--; av++;
543498943Sluigi			}
5435159636Soleg
543698943Sluigi			if (c->limit_mask == 0)
5437159636Soleg				errx(EX_USAGE, "limit: missing limit mask");
5438159636Soleg
5439159636Soleg			GET_UINT_ARG(c->conn_limit, 1, 65534, TOK_LIMIT,
5440159636Soleg			    rule_options);
5441159636Soleg
544298943Sluigi			ac--; av++;
544398943Sluigi			break;
5444159636Soleg		}
544598943Sluigi
5446102087Sluigi		case TOK_PROTO:
5447102087Sluigi			NEED1("missing protocol");
5448145246Sbrooks			if (add_proto(cmd, *av, &proto)) {
5449102087Sluigi				ac--; av++;
5450102098Sluigi			} else
5451116438Smaxim				errx(EX_DATAERR, "invalid protocol ``%s''",
5452116438Smaxim				    *av);
5453102087Sluigi			break;
5454106505Smaxim
5455102087Sluigi		case TOK_SRCIP:
5456102087Sluigi			NEED1("missing source IP");
5457102087Sluigi			if (add_srcip(cmd, *av)) {
5458102087Sluigi				ac--; av++;
5459102087Sluigi			}
5460102087Sluigi			break;
5461102087Sluigi
5462102087Sluigi		case TOK_DSTIP:
5463102087Sluigi			NEED1("missing destination IP");
5464102087Sluigi			if (add_dstip(cmd, *av)) {
5465102087Sluigi				ac--; av++;
5466102087Sluigi			}
5467102087Sluigi			break;
5468102087Sluigi
5469145246Sbrooks		case TOK_SRCIP6:
5470145246Sbrooks			NEED1("missing source IP6");
5471145246Sbrooks			if (add_srcip6(cmd, *av)) {
5472145246Sbrooks				ac--; av++;
5473145246Sbrooks			}
5474145246Sbrooks			break;
5475145246Sbrooks
5476145246Sbrooks		case TOK_DSTIP6:
5477145246Sbrooks			NEED1("missing destination IP6");
5478145246Sbrooks			if (add_dstip6(cmd, *av)) {
5479145246Sbrooks				ac--; av++;
5480145246Sbrooks			}
5481145246Sbrooks			break;
5482145246Sbrooks
5483102087Sluigi		case TOK_SRCPORT:
5484102087Sluigi			NEED1("missing source port");
5485140271Sbrooks			if (_substrcmp(*av, "any") == 0 ||
5486102087Sluigi			    add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
5487102087Sluigi				ac--; av++;
5488102087Sluigi			} else
5489102087Sluigi				errx(EX_DATAERR, "invalid source port %s", *av);
5490102087Sluigi			break;
5491102087Sluigi
5492102087Sluigi		case TOK_DSTPORT:
5493102087Sluigi			NEED1("missing destination port");
5494140271Sbrooks			if (_substrcmp(*av, "any") == 0 ||
5495102087Sluigi			    add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
5496102087Sluigi				ac--; av++;
5497102087Sluigi			} else
5498102087Sluigi				errx(EX_DATAERR, "invalid destination port %s",
5499102087Sluigi				    *av);
5500102087Sluigi			break;
5501102087Sluigi
5502102087Sluigi		case TOK_MAC:
5503102087Sluigi			if (add_mac(cmd, ac, av)) {
5504102087Sluigi				ac -= 2; av += 2;
5505102087Sluigi			}
5506102087Sluigi			break;
5507102087Sluigi
5508102087Sluigi		case TOK_MACTYPE:
5509102087Sluigi			NEED1("missing mac type");
5510102087Sluigi			if (!add_mactype(cmd, ac, *av))
5511116438Smaxim				errx(EX_DATAERR, "invalid mac type %s", *av);
5512102087Sluigi			ac--; av++;
5513102087Sluigi			break;
5514102087Sluigi
5515112250Scjc		case TOK_VERREVPATH:
5516112250Scjc			fill_cmd(cmd, O_VERREVPATH, 0, 0);
5517112250Scjc			break;
5518116919Sluigi
5519128575Sandre		case TOK_VERSRCREACH:
5520128575Sandre			fill_cmd(cmd, O_VERSRCREACH, 0, 0);
5521128575Sandre			break;
5522128575Sandre
5523133387Sandre		case TOK_ANTISPOOF:
5524133387Sandre			fill_cmd(cmd, O_ANTISPOOF, 0, 0);
5525133387Sandre			break;
5526133387Sandre
5527117241Sluigi		case TOK_IPSEC:
5528117241Sluigi			fill_cmd(cmd, O_IPSEC, 0, 0);
5529117241Sluigi			break;
5530117241Sluigi
5531145246Sbrooks		case TOK_IPV6:
5532145246Sbrooks			fill_cmd(cmd, O_IP6, 0, 0);
5533145246Sbrooks			break;
5534145246Sbrooks
5535146894Smlaier		case TOK_IPV4:
5536146894Smlaier			fill_cmd(cmd, O_IP4, 0, 0);
5537146894Smlaier			break;
5538146894Smlaier
5539145246Sbrooks		case TOK_EXT6HDR:
5540145246Sbrooks			fill_ext6hdr( cmd, *av );
5541145246Sbrooks			ac--; av++;
5542145246Sbrooks			break;
5543145246Sbrooks
5544145246Sbrooks		case TOK_FLOWID:
5545145246Sbrooks			if (proto != IPPROTO_IPV6 )
5546145246Sbrooks				errx( EX_USAGE, "flow-id filter is active "
5547145246Sbrooks				    "only for ipv6 protocol\n");
5548145246Sbrooks			fill_flow6( (ipfw_insn_u32 *) cmd, *av );
5549145246Sbrooks			ac--; av++;
5550145246Sbrooks			break;
5551145246Sbrooks
5552117469Sluigi		case TOK_COMMENT:
5553117469Sluigi			fill_comment(cmd, ac, av);
5554117469Sluigi			av += ac;
5555117469Sluigi			ac = 0;
5556117469Sluigi			break;
5557117469Sluigi
5558158879Soleg		case TOK_TAGGED:
5559159636Soleg			if (ac > 0 && strpbrk(*av, "-,")) {
5560158879Soleg				if (!add_ports(cmd, *av, 0, O_TAGGED))
5561159636Soleg					errx(EX_DATAERR, "tagged: invalid tag"
5562159636Soleg					    " list: %s", *av);
5563158879Soleg			}
5564159636Soleg			else {
5565159636Soleg				uint16_t tag;
5566159636Soleg
5567159636Soleg				GET_UINT_ARG(tag, 1, 65534, TOK_TAGGED,
5568159636Soleg				    rule_options);
5569159636Soleg				fill_cmd(cmd, O_TAGGED, 0, tag);
5570159636Soleg			}
5571158879Soleg			ac--; av++;
5572158879Soleg			break;
5573158879Soleg
557498943Sluigi		default:
557598943Sluigi			errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
557698943Sluigi		}
557798943Sluigi		if (F_LEN(cmd) > 0) {	/* prepare to advance */
557898943Sluigi			prev = cmd;
557998943Sluigi			cmd = next_cmd(cmd);
558098943Sluigi		}
558198943Sluigi	}
558298943Sluigi
558398943Sluigidone:
558498943Sluigi	/*
558598943Sluigi	 * Now copy stuff into the rule.
558698943Sluigi	 * If we have a keep-state option, the first instruction
558798943Sluigi	 * must be a PROBE_STATE (which is generated here).
558898943Sluigi	 * If we have a LOG option, it was stored as the first command,
558998943Sluigi	 * and now must be moved to the top of the action part.
559098943Sluigi	 */
559198943Sluigi	dst = (ipfw_insn *)rule->cmd;
559298943Sluigi
559398943Sluigi	/*
5594107289Sluigi	 * First thing to write into the command stream is the match probability.
5595107289Sluigi	 */
5596107289Sluigi	if (match_prob != 1) { /* 1 means always match */
5597107289Sluigi		dst->opcode = O_PROB;
5598107289Sluigi		dst->len = 2;
5599107289Sluigi		*((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff);
5600107289Sluigi		dst += dst->len;
5601107289Sluigi	}
5602107289Sluigi
5603107289Sluigi	/*
560498943Sluigi	 * generate O_PROBE_STATE if necessary
560598943Sluigi	 */
5606101116Sluigi	if (have_state && have_state->opcode != O_CHECK_STATE) {
560798943Sluigi		fill_cmd(dst, O_PROBE_STATE, 0, 0);
560898943Sluigi		dst = next_cmd(dst);
560998943Sluigi	}
5610158879Soleg
5611158879Soleg	/* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */
561298943Sluigi	for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
561398943Sluigi		i = F_LEN(src);
561498943Sluigi
5615101116Sluigi		switch (src->opcode) {
5616101116Sluigi		case O_LOG:
5617101116Sluigi		case O_KEEP_STATE:
5618101116Sluigi		case O_LIMIT:
5619136071Sgreen		case O_ALTQ:
5620158879Soleg		case O_TAG:
5621101116Sluigi			break;
5622101116Sluigi		default:
5623117328Sluigi			bcopy(src, dst, i * sizeof(uint32_t));
562498943Sluigi			dst += i;
562598943Sluigi		}
562698943Sluigi	}
562798943Sluigi
562898943Sluigi	/*
5629101116Sluigi	 * put back the have_state command as last opcode
5630101116Sluigi	 */
5631101295Sluigi	if (have_state && have_state->opcode != O_CHECK_STATE) {
5632101116Sluigi		i = F_LEN(have_state);
5633117328Sluigi		bcopy(have_state, dst, i * sizeof(uint32_t));
5634101116Sluigi		dst += i;
5635101116Sluigi	}
5636101116Sluigi	/*
563798943Sluigi	 * start action section
563898943Sluigi	 */
563998943Sluigi	rule->act_ofs = dst - rule->cmd;
564098943Sluigi
5641158879Soleg	/* put back O_LOG, O_ALTQ, O_TAG if necessary */
5642136071Sgreen	if (have_log) {
5643136071Sgreen		i = F_LEN(have_log);
5644136071Sgreen		bcopy(have_log, dst, i * sizeof(uint32_t));
564598943Sluigi		dst += i;
564698943Sluigi	}
5647136071Sgreen	if (have_altq) {
5648136071Sgreen		i = F_LEN(have_altq);
5649136071Sgreen		bcopy(have_altq, dst, i * sizeof(uint32_t));
5650136071Sgreen		dst += i;
5651136071Sgreen	}
5652158879Soleg	if (have_tag) {
5653158879Soleg		i = F_LEN(have_tag);
5654158879Soleg		bcopy(have_tag, dst, i * sizeof(uint32_t));
5655158879Soleg		dst += i;
5656158879Soleg	}
565798943Sluigi	/*
565898943Sluigi	 * copy all other actions
565998943Sluigi	 */
566098943Sluigi	for (src = (ipfw_insn *)actbuf; src != action; src += i) {
566198943Sluigi		i = F_LEN(src);
5662117328Sluigi		bcopy(src, dst, i * sizeof(uint32_t));
566398943Sluigi		dst += i;
566498943Sluigi	}
566598943Sluigi
5666117328Sluigi	rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd);
5667117469Sluigi	i = (char *)dst - (char *)rule;
5668119740Stmm	if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1)
566998943Sluigi		err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
567098943Sluigi	if (!do_quiet)
5671117469Sluigi		show_ipfw(rule, 0, 0);
567298943Sluigi}
567398943Sluigi
567498943Sluigistatic void
5675117328Sluigizero(int ac, char *av[], int optname /* IP_FW_ZERO or IP_FW_RESETLOG */)
567698943Sluigi{
567798943Sluigi	int rulenum;
567898943Sluigi	int failed = EX_OK;
5679117469Sluigi	char const *name = optname == IP_FW_ZERO ?  "ZERO" : "RESETLOG";
568098943Sluigi
568198943Sluigi	av++; ac--;
568298943Sluigi
568398943Sluigi	if (!ac) {
568498943Sluigi		/* clear all entries */
5685117328Sluigi		if (do_cmd(optname, NULL, 0) < 0)
5686117328Sluigi			err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name);
568798943Sluigi		if (!do_quiet)
5688117328Sluigi			printf("%s.\n", optname == IP_FW_ZERO ?
5689117328Sluigi			    "Accounting cleared":"Logging counts reset");
569098943Sluigi
569198943Sluigi		return;
569298943Sluigi	}
569398943Sluigi
569498943Sluigi	while (ac) {
569598943Sluigi		/* Rule number */
569698943Sluigi		if (isdigit(**av)) {
569798943Sluigi			rulenum = atoi(*av);
569898943Sluigi			av++;
569998943Sluigi			ac--;
5700117328Sluigi			if (do_cmd(optname, &rulenum, sizeof rulenum)) {
5701117328Sluigi				warn("rule %u: setsockopt(IP_FW_%s)",
5702117328Sluigi				    rulenum, name);
570398943Sluigi				failed = EX_UNAVAILABLE;
570498943Sluigi			} else if (!do_quiet)
5705117328Sluigi				printf("Entry %d %s.\n", rulenum,
5706117328Sluigi				    optname == IP_FW_ZERO ?
5707117328Sluigi					"cleared" : "logging count reset");
570898943Sluigi		} else {
570998943Sluigi			errx(EX_USAGE, "invalid rule number ``%s''", *av);
571098943Sluigi		}
571198943Sluigi	}
571298943Sluigi	if (failed != EX_OK)
571398943Sluigi		exit(failed);
571498943Sluigi}
571598943Sluigi
571698943Sluigistatic void
5717117544Sluigiflush(int force)
571898943Sluigi{
571998943Sluigi	int cmd = do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH;
572098943Sluigi
5721117544Sluigi	if (!force && !do_quiet) { /* need to ask user */
572298943Sluigi		int c;
572398943Sluigi
572498943Sluigi		printf("Are you sure? [yn] ");
572598943Sluigi		fflush(stdout);
572698943Sluigi		do {
572798943Sluigi			c = toupper(getc(stdin));
572898943Sluigi			while (c != '\n' && getc(stdin) != '\n')
572998943Sluigi				if (feof(stdin))
573098943Sluigi					return; /* and do not flush */
573198943Sluigi		} while (c != 'Y' && c != 'N');
573298943Sluigi		printf("\n");
573398943Sluigi		if (c == 'N')	/* user said no */
573498943Sluigi			return;
573598943Sluigi	}
5736117328Sluigi	if (do_cmd(cmd, NULL, 0) < 0)
573798943Sluigi		err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
573898943Sluigi		    do_pipe ? "DUMMYNET" : "FW");
573998943Sluigi	if (!do_quiet)
574098943Sluigi		printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules");
574198943Sluigi}
574298943Sluigi
5743117469Sluigi/*
5744117544Sluigi * Free a the (locally allocated) copy of command line arguments.
5745117469Sluigi */
5746117544Sluigistatic void
5747117544Sluigifree_args(int ac, char **av)
5748117544Sluigi{
5749117544Sluigi	int i;
5750117544Sluigi
5751117544Sluigi	for (i=0; i < ac; i++)
5752117544Sluigi		free(av[i]);
5753117544Sluigi	free(av);
5754117544Sluigi}
5755117544Sluigi
5756117544Sluigi/*
5757130281Sru * This one handles all table-related commands
5758130281Sru * 	ipfw table N add addr[/masklen] [value]
5759130281Sru * 	ipfw table N delete addr[/masklen]
5760130281Sru * 	ipfw table N flush
5761130281Sru * 	ipfw table N list
5762130281Sru */
5763130281Srustatic void
5764130281Srutable_handler(int ac, char *av[])
5765130281Sru{
5766130281Sru	ipfw_table_entry ent;
5767130281Sru	ipfw_table *tbl;
5768130281Sru	int do_add;
5769130281Sru	char *p;
5770130281Sru	socklen_t l;
5771130281Sru	uint32_t a;
5772130281Sru
5773130281Sru	ac--; av++;
5774130281Sru	if (ac && isdigit(**av)) {
5775130281Sru		ent.tbl = atoi(*av);
5776130281Sru		ac--; av++;
5777130281Sru	} else
5778130281Sru		errx(EX_USAGE, "table number required");
5779130281Sru	NEED1("table needs command");
5780140271Sbrooks	if (_substrcmp(*av, "add") == 0 ||
5781140271Sbrooks	    _substrcmp(*av, "delete") == 0) {
5782130281Sru		do_add = **av == 'a';
5783130281Sru		ac--; av++;
5784130281Sru		if (!ac)
5785130281Sru			errx(EX_USAGE, "IP address required");
5786130281Sru		p = strchr(*av, '/');
5787130281Sru		if (p) {
5788130281Sru			*p++ = '\0';
5789130281Sru			ent.masklen = atoi(p);
5790130281Sru			if (ent.masklen > 32)
5791130281Sru				errx(EX_DATAERR, "bad width ``%s''", p);
5792130281Sru		} else
5793130281Sru			ent.masklen = 32;
5794130281Sru		if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0)
5795130298Sru			errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
5796130281Sru		ac--; av++;
5797161424Sjulian		if (do_add && ac) {
5798161424Sjulian			unsigned int tval;
5799161424Sjulian			/* isdigit is a bit of a hack here.. */
5800161424Sjulian			if (strchr(*av, (int)'.') == NULL && isdigit(**av))  {
5801161424Sjulian				ent.value = strtoul(*av, NULL, 0);
5802161424Sjulian			} else {
5803161424Sjulian		        	if (lookup_host(*av, (struct in_addr *)&tval) == 0) {
5804161424Sjulian					/* The value must be stored in host order	 *
5805161424Sjulian					 * so that the values < 65k can be distinguished */
5806161424Sjulian		       			ent.value = ntohl(tval);
5807161424Sjulian				} else {
5808161424Sjulian					errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
5809161424Sjulian				}
5810161424Sjulian			}
5811161424Sjulian		} else
5812130281Sru			ent.value = 0;
5813130281Sru		if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL,
5814157335Sjulian		    &ent, sizeof(ent)) < 0) {
5815155639Sjulian			/* If running silent, don't bomb out on these errors. */
5816155639Sjulian			if (!(do_quiet && (errno == (do_add ? EEXIST : ESRCH))))
5817155639Sjulian				err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
5818155639Sjulian				    do_add ? "ADD" : "DEL");
5819155639Sjulian			/* In silent mode, react to a failed add by deleting */
5820157332Sjulian			if (do_add) {
5821155639Sjulian				do_cmd(IP_FW_TABLE_DEL, &ent, sizeof(ent));
5822155639Sjulian				if (do_cmd(IP_FW_TABLE_ADD,
5823155639Sjulian				    &ent, sizeof(ent)) < 0)
5824155639Sjulian					err(EX_OSERR,
5825155639Sjulian				            "setsockopt(IP_FW_TABLE_ADD)");
5826157332Sjulian			}
5827157335Sjulian		}
5828140271Sbrooks	} else if (_substrcmp(*av, "flush") == 0) {
5829130281Sru		if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl, sizeof(ent.tbl)) < 0)
5830130281Sru			err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
5831140271Sbrooks	} else if (_substrcmp(*av, "list") == 0) {
5832130281Sru		a = ent.tbl;
5833130281Sru		l = sizeof(a);
5834130281Sru		if (do_cmd(IP_FW_TABLE_GETSIZE, &a, (uintptr_t)&l) < 0)
5835130281Sru			err(EX_OSERR, "getsockopt(IP_FW_TABLE_GETSIZE)");
5836130281Sru		l = sizeof(*tbl) + a * sizeof(ipfw_table_entry);
5837130281Sru		tbl = malloc(l);
5838130281Sru		if (tbl == NULL)
5839130281Sru			err(EX_OSERR, "malloc");
5840130281Sru		tbl->tbl = ent.tbl;
5841130281Sru		if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0)
5842130281Sru			err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)");
5843130281Sru		for (a = 0; a < tbl->cnt; a++) {
5844161424Sjulian			/* Heuristic to print it the right way */
5845161456Sjulian			/* values < 64k are printed as numbers */
5846161424Sjulian			unsigned int tval;
5847161424Sjulian			tval = tbl->ent[a].value;
5848161424Sjulian			if (tval > 0xffff) {
5849161424Sjulian			    char tbuf[128];
5850161456Sjulian			    strncpy(tbuf, inet_ntoa(*(struct in_addr *)
5851161456Sjulian				&tbl->ent[a].addr), 127);
5852161424Sjulian			    /* inet_ntoa expects host order */
5853161424Sjulian			    tval = htonl(tval);
5854161456Sjulian			    printf("%s/%u %s\n", tbuf, tbl->ent[a].masklen,
5855161456Sjulian			        inet_ntoa(*(struct in_addr *)&tval));
5856161424Sjulian			} else {
5857161424Sjulian			    printf("%s/%u %u\n",
5858161456Sjulian			        inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
5859161456Sjulian			        tbl->ent[a].masklen, tbl->ent[a].value);
5860161424Sjulian			}
5861130281Sru		}
5862130281Sru	} else
5863130281Sru		errx(EX_USAGE, "invalid table command %s", *av);
5864130281Sru}
5865130281Sru
5866165648Spisostatic void
5867165648Spisoshow_nat(int ac, char **av) {
5868165648Spiso	struct cfg_nat *n;
5869165648Spiso	struct cfg_redir *e;
5870165648Spiso	int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
5871165648Spiso	int nat_cnt, r;
5872165648Spiso	uint8_t *data, *p;
5873165648Spiso	char **lav, *endptr;
5874165648Spiso
5875165648Spiso	do_rule = 0;
5876165648Spiso	nalloc = 1024;
5877165648Spiso	size = 0;
5878165648Spiso	data = NULL;
5879165648Spiso	ac--; av++;
5880165648Spiso
5881165648Spiso	/* Parse parameters. */
5882165648Spiso	for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
5883165648Spiso		if (!strncmp(av[0], "config", strlen(av[0]))) {
5884165648Spiso			cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
5885165648Spiso			continue;
5886165648Spiso		}
5887165648Spiso		/* Convert command line rule #. */
5888165648Spiso		frule = lrule = strtoul(av[0], &endptr, 10);
5889165648Spiso		if (*endptr == '-')
5890165648Spiso			lrule = strtoul(endptr+1, &endptr, 10);
5891165648Spiso		if (lrule == 0)
5892165648Spiso			err(EX_USAGE, "invalid rule number: %s", av[0]);
5893165648Spiso		do_rule = 1;
5894165648Spiso	}
5895165648Spiso
5896165648Spiso	nbytes = nalloc;
5897165648Spiso	while (nbytes >= nalloc) {
5898165648Spiso		nalloc = nalloc * 2;
5899165648Spiso		nbytes = nalloc;
5900165648Spiso		if ((data = realloc(data, nbytes)) == NULL)
5901165648Spiso			err(EX_OSERR, "realloc");
5902165648Spiso		if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
5903165648Spiso			err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
5904165648Spiso			    (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
5905165648Spiso	}
5906165648Spiso	if (nbytes == 0)
5907165648Spiso		exit(0);
5908165648Spiso	if (do_cfg) {
5909165648Spiso		nat_cnt = *((int *)data);
5910165648Spiso		for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
5911165648Spiso			n = (struct cfg_nat *)&data[i];
5912165648Spiso			if (do_rule) {
5913165648Spiso				if (!(frule <= n->id && lrule >= n->id))
5914165648Spiso					continue;
5915165648Spiso			}
5916165648Spiso			print_nat_config(&data[i]);
5917165648Spiso			i += sizeof(struct cfg_nat);
5918165648Spiso			e = (struct cfg_redir *)&data[i];
5919165648Spiso			if (e->mode == REDIR_ADDR || e->mode == REDIR_PORT ||
5920165648Spiso			    e->mode == REDIR_PROTO)
5921165648Spiso				i += sizeof(struct cfg_redir) + e->spool_cnt *
5922165648Spiso				    sizeof(struct cfg_spool);
5923165648Spiso		}
5924165648Spiso	} else {
5925165648Spiso		for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
5926165648Spiso			p = &data[i];
5927165648Spiso			if (p == data + nbytes)
5928165648Spiso				break;
5929165648Spiso			bcopy(p, &r, sizeof(int));
5930165648Spiso			if (do_rule) {
5931165648Spiso				if (!(frule <= r && lrule >= r))
5932165648Spiso					continue;
5933165648Spiso			}
5934165648Spiso			printf("nat %u: %s\n", r, p+sizeof(int));
5935165648Spiso		}
5936165648Spiso	}
5937165648Spiso}
5938165648Spiso
5939130281Sru/*
5940117544Sluigi * Called with the arguments (excluding program name).
5941117544Sluigi * Returns 0 if successful, 1 if empty command, errx() in case of errors.
5942117544Sluigi */
594398943Sluigistatic int
5944117328Sluigiipfw_main(int oldac, char **oldav)
594598943Sluigi{
5946117469Sluigi	int ch, ac, save_ac;
5947117469Sluigi	char **av, **save_av;
5948117469Sluigi	int do_acct = 0;		/* Show packet/byte count */
5949117469Sluigi
5950117469Sluigi#define WHITESP		" \t\f\v\n\r"
5951117544Sluigi	if (oldac == 0)
5952117544Sluigi		return 1;
5953117544Sluigi	else if (oldac == 1) {
5954117328Sluigi		/*
5955117328Sluigi		 * If we are called with a single string, try to split it into
5956117328Sluigi		 * arguments for subsequent parsing.
5957117328Sluigi		 * But first, remove spaces after a ',', by copying the string
5958117328Sluigi		 * in-place.
5959117328Sluigi		 */
5960117469Sluigi		char *arg = oldav[0];	/* The string... */
5961117328Sluigi		int l = strlen(arg);
5962117328Sluigi		int copy = 0;		/* 1 if we need to copy, 0 otherwise */
5963117328Sluigi		int i, j;
5964117469Sluigi		for (i = j = 0; i < l; i++) {
5965117469Sluigi			if (arg[i] == '#')	/* comment marker */
5966117469Sluigi				break;
5967117328Sluigi			if (copy) {
5968117328Sluigi				arg[j++] = arg[i];
5969117469Sluigi				copy = !index("," WHITESP, arg[i]);
5970117328Sluigi			} else {
5971117469Sluigi				copy = !index(WHITESP, arg[i]);
5972117328Sluigi				if (copy)
5973117328Sluigi					arg[j++] = arg[i];
5974117328Sluigi			}
5975117469Sluigi		}
5976117328Sluigi		if (!copy && j > 0)	/* last char was a 'blank', remove it */
5977117328Sluigi			j--;
5978117328Sluigi		l = j;			/* the new argument length */
5979117328Sluigi		arg[j++] = '\0';
5980117469Sluigi		if (l == 0)		/* empty string! */
5981117544Sluigi			return 1;
5982117328Sluigi
5983117328Sluigi		/*
5984117328Sluigi		 * First, count number of arguments. Because of the previous
5985117469Sluigi		 * processing, this is just the number of blanks plus 1.
5986117328Sluigi		 */
5987117469Sluigi		for (i = 0, ac = 1; i < l; i++)
5988117469Sluigi			if (index(WHITESP, arg[i]) != NULL)
5989117328Sluigi				ac++;
5990117328Sluigi
5991117328Sluigi		av = calloc(ac, sizeof(char *));
5992117328Sluigi
5993117328Sluigi		/*
5994117328Sluigi		 * Second, copy arguments from cmd[] to av[]. For each one,
5995117328Sluigi		 * j is the initial character, i is the one past the end.
5996117328Sluigi		 */
5997117469Sluigi		for (ac = 0, i = j = 0; i < l; i++)
5998117469Sluigi			if (index(WHITESP, arg[i]) != NULL || i == l-1) {
5999117328Sluigi				if (i == l-1)
6000117328Sluigi					i++;
6001117328Sluigi				av[ac] = calloc(i-j+1, 1);
6002117328Sluigi				bcopy(arg+j, av[ac], i-j);
6003117328Sluigi				ac++;
6004117328Sluigi				j = i + 1;
6005117328Sluigi			}
6006117328Sluigi	} else {
6007117328Sluigi		/*
6008117328Sluigi		 * If an argument ends with ',' join with the next one.
6009117328Sluigi		 */
6010117328Sluigi		int first, i, l;
6011117328Sluigi
6012117328Sluigi		av = calloc(oldac, sizeof(char *));
6013117328Sluigi		for (first = i = ac = 0, l = 0; i < oldac; i++) {
6014117328Sluigi			char *arg = oldav[i];
6015117328Sluigi			int k = strlen(arg);
6016117328Sluigi
6017117328Sluigi			l += k;
6018117328Sluigi			if (arg[k-1] != ',' || i == oldac-1) {
6019117328Sluigi				/* Time to copy. */
6020117328Sluigi				av[ac] = calloc(l+1, 1);
6021117328Sluigi				for (l=0; first <= i; first++) {
6022117328Sluigi					strcat(av[ac]+l, oldav[first]);
6023117328Sluigi					l += strlen(oldav[first]);
6024117328Sluigi				}
6025117328Sluigi				ac++;
6026117328Sluigi				l = 0;
6027117328Sluigi				first = i+1;
6028117328Sluigi			}
6029117328Sluigi		}
6030117328Sluigi	}
6031117328Sluigi
603298943Sluigi	/* Set the force flag for non-interactive processes */
6033123804Smaxim	if (!do_force)
6034123804Smaxim		do_force = !isatty(STDIN_FILENO);
603598943Sluigi
6036117469Sluigi	/* Save arguments for final freeing of memory. */
6037117469Sluigi	save_ac = ac;
6038117469Sluigi	save_av = av;
6039117469Sluigi
6040117469Sluigi	optind = optreset = 0;
6041123495Sluigi	while ((ch = getopt(ac, av, "abcdefhnNqs:STtv")) != -1)
604298943Sluigi		switch (ch) {
604398943Sluigi		case 'a':
604498943Sluigi			do_acct = 1;
604598943Sluigi			break;
6046117328Sluigi
6047123495Sluigi		case 'b':
6048123495Sluigi			comment_only = 1;
6049123495Sluigi			do_compact = 1;
6050123495Sluigi			break;
6051123495Sluigi
6052102098Sluigi		case 'c':
6053102098Sluigi			do_compact = 1;
6054102098Sluigi			break;
6055117328Sluigi
605698943Sluigi		case 'd':
605798943Sluigi			do_dynamic = 1;
605898943Sluigi			break;
6059117328Sluigi
606098943Sluigi		case 'e':
606198943Sluigi			do_expired = 1;
606298943Sluigi			break;
6063117328Sluigi
606498943Sluigi		case 'f':
606598943Sluigi			do_force = 1;
606698943Sluigi			break;
6067117328Sluigi
6068117328Sluigi		case 'h': /* help */
6069117544Sluigi			free_args(save_ac, save_av);
6070117328Sluigi			help();
6071117328Sluigi			break;	/* NOTREACHED */
6072117328Sluigi
6073117328Sluigi		case 'n':
6074117328Sluigi			test_only = 1;
6075117328Sluigi			break;
6076117328Sluigi
607798943Sluigi		case 'N':
607898943Sluigi			do_resolv = 1;
607998943Sluigi			break;
6080117328Sluigi
608198943Sluigi		case 'q':
608298943Sluigi			do_quiet = 1;
608398943Sluigi			break;
6084117328Sluigi
6085117328Sluigi		case 's': /* sort */
6086117328Sluigi			do_sort = atoi(optarg);
6087117328Sluigi			break;
6088117328Sluigi
6089101628Sluigi		case 'S':
6090101628Sluigi			show_sets = 1;
6091101628Sluigi			break;
6092117328Sluigi
609398943Sluigi		case 't':
609498943Sluigi			do_time = 1;
609598943Sluigi			break;
6096117328Sluigi
6097117472Sluigi		case 'T':
6098117472Sluigi			do_time = 2;	/* numeric timestamp */
6099117472Sluigi			break;
6100117472Sluigi
610198943Sluigi		case 'v': /* verbose */
6102117328Sluigi			verbose = 1;
610398943Sluigi			break;
6104117328Sluigi
610598943Sluigi		default:
6106117544Sluigi			free_args(save_ac, save_av);
6107117544Sluigi			return 1;
610898943Sluigi		}
610998943Sluigi
611098943Sluigi	ac -= optind;
611198943Sluigi	av += optind;
611298943Sluigi	NEED1("bad arguments, for usage summary ``ipfw''");
611398943Sluigi
611498943Sluigi	/*
6115117544Sluigi	 * An undocumented behaviour of ipfw1 was to allow rule numbers first,
6116117544Sluigi	 * e.g. "100 add allow ..." instead of "add 100 allow ...".
6117117544Sluigi	 * In case, swap first and second argument to get the normal form.
6118117544Sluigi	 */
6119117544Sluigi	if (ac > 1 && isdigit(*av[0])) {
6120117544Sluigi		char *p = av[0];
6121117544Sluigi
6122117544Sluigi		av[0] = av[1];
6123117544Sluigi		av[1] = p;
6124117544Sluigi	}
6125117544Sluigi
6126117544Sluigi	/*
6127165648Spiso	 * Optional: pipe, queue or nat.
612898943Sluigi	 */
6129165648Spiso	do_nat = 0;
6130117821Smaxim	do_pipe = 0;
6131165648Spiso	if (!strncmp(*av, "nat", strlen(*av)))
6132165648Spiso 	        do_nat = 1;
6133165648Spiso 	else if (!strncmp(*av, "pipe", strlen(*av)))
613498943Sluigi		do_pipe = 1;
6135140271Sbrooks	else if (_substrcmp(*av, "queue") == 0)
613698943Sluigi		do_pipe = 2;
6137165648Spiso	if (do_pipe || do_nat) {
613898943Sluigi		ac--;
613998943Sluigi		av++;
614098943Sluigi	}
614198943Sluigi	NEED1("missing command");
614298943Sluigi
614398943Sluigi	/*
6144165648Spiso	 * For pipes, queues and nats we normally say 'nat|pipe NN config'
6145165648Spiso	 * but the code is easier to parse as 'nat|pipe config NN'
614698943Sluigi	 * so we swap the two arguments.
614798943Sluigi	 */
6148165648Spiso	if ((do_pipe || do_nat) && ac > 1 && isdigit(*av[0])) {
614998943Sluigi		char *p = av[0];
6150117544Sluigi
615198943Sluigi		av[0] = av[1];
615298943Sluigi		av[1] = p;
615398943Sluigi	}
6154117328Sluigi
6155140271Sbrooks	if (_substrcmp(*av, "add") == 0)
615698943Sluigi		add(ac, av);
6157165648Spiso	else if (do_nat && _substrcmp(*av, "show") == 0)
6158165648Spiso 		show_nat(ac, av);
6159140271Sbrooks	else if (do_pipe && _substrcmp(*av, "config") == 0)
616098943Sluigi		config_pipe(ac, av);
6161165648Spiso	else if (do_nat && _substrcmp(*av, "config") == 0)
6162165648Spiso 		config_nat(ac, av);
6163140271Sbrooks	else if (_substrcmp(*av, "delete") == 0)
616498943Sluigi		delete(ac, av);
6165140271Sbrooks	else if (_substrcmp(*av, "flush") == 0)
6166117544Sluigi		flush(do_force);
6167140271Sbrooks	else if (_substrcmp(*av, "zero") == 0)
6168117328Sluigi		zero(ac, av, IP_FW_ZERO);
6169140271Sbrooks	else if (_substrcmp(*av, "resetlog") == 0)
6170117328Sluigi		zero(ac, av, IP_FW_RESETLOG);
6171140271Sbrooks	else if (_substrcmp(*av, "print") == 0 ||
6172140271Sbrooks	         _substrcmp(*av, "list") == 0)
6173117469Sluigi		list(ac, av, do_acct);
6174140271Sbrooks	else if (_substrcmp(*av, "set") == 0)
6175101978Sluigi		sets_handler(ac, av);
6176140271Sbrooks	else if (_substrcmp(*av, "table") == 0)
6177130281Sru		table_handler(ac, av);
6178140271Sbrooks	else if (_substrcmp(*av, "enable") == 0)
6179109126Sdillon		sysctl_handler(ac, av, 1);
6180140271Sbrooks	else if (_substrcmp(*av, "disable") == 0)
6181109126Sdillon		sysctl_handler(ac, av, 0);
6182140271Sbrooks	else if (_substrcmp(*av, "show") == 0)
6183117469Sluigi		list(ac, av, 1 /* show counters */);
6184117469Sluigi	else
618598943Sluigi		errx(EX_USAGE, "bad command `%s'", *av);
6186117469Sluigi
6187117469Sluigi	/* Free memory allocated in the argument parsing. */
6188117544Sluigi	free_args(save_ac, save_av);
618998943Sluigi	return 0;
619098943Sluigi}
619198943Sluigi
619298943Sluigi
619398943Sluigistatic void
6194106505Smaximipfw_readfile(int ac, char *av[])
619598943Sluigi{
619698943Sluigi#define MAX_ARGS	32
619798943Sluigi	char	buf[BUFSIZ];
6198117469Sluigi	char	*cmd = NULL, *filename = av[ac-1];
6199117469Sluigi	int	c, lineno=0;
620098943Sluigi	FILE	*f = NULL;
620198943Sluigi	pid_t	preproc = 0;
620298943Sluigi
6203117469Sluigi	filename = av[ac-1];
6204117469Sluigi
6205123804Smaxim	while ((c = getopt(ac, av, "cfNnp:qS")) != -1) {
620698943Sluigi		switch(c) {
6207117469Sluigi		case 'c':
6208117469Sluigi			do_compact = 1;
6209117469Sluigi			break;
6210117469Sluigi
6211123804Smaxim		case 'f':
6212123804Smaxim			do_force = 1;
6213123804Smaxim			break;
6214123804Smaxim
6215117469Sluigi		case 'N':
6216117469Sluigi			do_resolv = 1;
6217117469Sluigi			break;
6218117469Sluigi
6219117328Sluigi		case 'n':
6220117328Sluigi			test_only = 1;
6221117328Sluigi			break;
6222117328Sluigi
622398943Sluigi		case 'p':
622498943Sluigi			cmd = optarg;
6225117469Sluigi			/*
6226117469Sluigi			 * Skip previous args and delete last one, so we
6227117469Sluigi			 * pass all but the last argument to the preprocessor
6228117469Sluigi			 * via av[optind-1]
6229117469Sluigi			 */
6230117469Sluigi			av += optind - 1;
6231117469Sluigi			ac -= optind - 1;
6232162773Smaxim			if (ac < 2)
6233162773Smaxim				errx(EX_USAGE, "no filename argument");
6234117469Sluigi			av[ac-1] = NULL;
6235117469Sluigi			fprintf(stderr, "command is %s\n", av[0]);
623698943Sluigi			break;
623798943Sluigi
623898943Sluigi		case 'q':
6239117469Sluigi			do_quiet = 1;
624098943Sluigi			break;
624198943Sluigi
6242117469Sluigi		case 'S':
6243117469Sluigi			show_sets = 1;
6244117469Sluigi			break;
6245117469Sluigi
624698943Sluigi		default:
624798943Sluigi			errx(EX_USAGE, "bad arguments, for usage"
624898943Sluigi			     " summary ``ipfw''");
624998943Sluigi		}
625098943Sluigi
6251117469Sluigi		if (cmd != NULL)
6252108231Skbyanc			break;
6253108231Skbyanc	}
6254108231Skbyanc
6255117469Sluigi	if (cmd == NULL && ac != optind + 1) {
6256117469Sluigi		fprintf(stderr, "ac %d, optind %d\n", ac, optind);
6257117469Sluigi		errx(EX_USAGE, "extraneous filename arguments");
6258108231Skbyanc	}
6259108231Skbyanc
6260117469Sluigi	if ((f = fopen(filename, "r")) == NULL)
6261117469Sluigi		err(EX_UNAVAILABLE, "fopen: %s", filename);
626298943Sluigi
6263117469Sluigi	if (cmd != NULL) {			/* pipe through preprocessor */
626498943Sluigi		int pipedes[2];
626598943Sluigi
626698943Sluigi		if (pipe(pipedes) == -1)
626798943Sluigi			err(EX_OSERR, "cannot create pipe");
626898943Sluigi
6269117469Sluigi		preproc = fork();
6270117469Sluigi		if (preproc == -1)
627198943Sluigi			err(EX_OSERR, "cannot fork");
627298943Sluigi
6273117577Sluigi		if (preproc == 0) {
6274117469Sluigi			/*
6275117469Sluigi			 * Child, will run the preprocessor with the
6276117469Sluigi			 * file on stdin and the pipe on stdout.
6277117469Sluigi			 */
627898943Sluigi			if (dup2(fileno(f), 0) == -1
627998943Sluigi			    || dup2(pipedes[1], 1) == -1)
628098943Sluigi				err(EX_OSERR, "dup2()");
628198943Sluigi			fclose(f);
628298943Sluigi			close(pipedes[1]);
628398943Sluigi			close(pipedes[0]);
6284117469Sluigi			execvp(cmd, av);
628598943Sluigi			err(EX_OSERR, "execvp(%s) failed", cmd);
6286117469Sluigi		} else { /* parent, will reopen f as the pipe */
628798943Sluigi			fclose(f);
628898943Sluigi			close(pipedes[1]);
628998943Sluigi			if ((f = fdopen(pipedes[0], "r")) == NULL) {
629098943Sluigi				int savederrno = errno;
629198943Sluigi
629298943Sluigi				(void)kill(preproc, SIGTERM);
629398943Sluigi				errno = savederrno;
629498943Sluigi				err(EX_OSERR, "fdopen()");
629598943Sluigi			}
629698943Sluigi		}
629798943Sluigi	}
629898943Sluigi
6299117469Sluigi	while (fgets(buf, BUFSIZ, f)) {		/* read commands */
6300117469Sluigi		char linename[10];
6301117469Sluigi		char *args[1];
6302117469Sluigi
630398943Sluigi		lineno++;
630498943Sluigi		sprintf(linename, "Line %d", lineno);
6305117328Sluigi		setprogname(linename); /* XXX */
6306117469Sluigi		args[0] = buf;
6307117469Sluigi		ipfw_main(1, args);
630898943Sluigi	}
630998943Sluigi	fclose(f);
6310117469Sluigi	if (cmd != NULL) {
6311117469Sluigi		int status;
6312117469Sluigi
631398943Sluigi		if (waitpid(preproc, &status, 0) == -1)
631498943Sluigi			errx(EX_OSERR, "waitpid()");
631598943Sluigi		if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
631698943Sluigi			errx(EX_UNAVAILABLE,
631798943Sluigi			    "preprocessor exited with status %d",
631898943Sluigi			    WEXITSTATUS(status));
631998943Sluigi		else if (WIFSIGNALED(status))
632098943Sluigi			errx(EX_UNAVAILABLE,
632198943Sluigi			    "preprocessor exited with signal %d",
632298943Sluigi			    WTERMSIG(status));
632398943Sluigi	}
632498943Sluigi}
632598943Sluigi
632698943Sluigiint
632798943Sluigimain(int ac, char *av[])
632898943Sluigi{
632998943Sluigi	/*
633098943Sluigi	 * If the last argument is an absolute pathname, interpret it
633198943Sluigi	 * as a file to be preprocessed.
633298943Sluigi	 */
633398943Sluigi
633498943Sluigi	if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0)
633598943Sluigi		ipfw_readfile(ac, av);
6336117544Sluigi	else {
6337117544Sluigi		if (ipfw_main(ac-1, av+1))
6338117544Sluigi			show_usage();
6339117544Sluigi	}
634098943Sluigi	return EX_OK;
634198943Sluigi}
6342