ipfw2.c revision 136071
1164190Sjkoshy/*
2176758Sjkoshy * Copyright (c) 2002-2003 Luigi Rizzo
3164190Sjkoshy * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
4164190Sjkoshy * Copyright (c) 1994 Ugen J.S.Antsilevich
5164190Sjkoshy *
6164190Sjkoshy * Idea and grammar partially left from:
7164190Sjkoshy * Copyright (c) 1993 Daniel Boulet
8164190Sjkoshy *
9164190Sjkoshy * Redistribution and use in source forms, with and without modification,
10164190Sjkoshy * are permitted provided that this entire comment appears intact.
11164190Sjkoshy *
12164190Sjkoshy * Redistribution in binary form may occur without any restrictions.
13164190Sjkoshy * Obviously, it would be nice if you gave credit where credit is due
14164190Sjkoshy * but requiring it would be too onerous.
15164190Sjkoshy *
16164190Sjkoshy * This software is provided ``AS IS'' without any warranties of any kind.
17164190Sjkoshy *
18164190Sjkoshy * NEW command line interface for IP firewall facility
19164190Sjkoshy *
20164190Sjkoshy * $FreeBSD: head/sbin/ipfw/ipfw2.c 136071 2004-10-03 00:17:46Z green $
21164190Sjkoshy */
22164190Sjkoshy
23164190Sjkoshy#include <sys/param.h>
24164190Sjkoshy#include <sys/mbuf.h>
25164190Sjkoshy#include <sys/socket.h>
26164190Sjkoshy#include <sys/sockio.h>
27164190Sjkoshy#include <sys/sysctl.h>
28164190Sjkoshy#include <sys/time.h>
29164190Sjkoshy#include <sys/wait.h>
30164190Sjkoshy#include <sys/queue.h>
31164190Sjkoshy
32164190Sjkoshy#include <ctype.h>
33164190Sjkoshy#include <err.h>
34164190Sjkoshy#include <errno.h>
35164190Sjkoshy#include <grp.h>
36164190Sjkoshy#include <limits.h>
37164190Sjkoshy#include <netdb.h>
38164190Sjkoshy#include <pwd.h>
39164190Sjkoshy#include <signal.h>
40164190Sjkoshy#include <stdio.h>
41164190Sjkoshy#include <stdlib.h>
42164190Sjkoshy#include <stdarg.h>
43164190Sjkoshy#include <string.h>
44247221Smarkj#include <timeconv.h>	/* XXX do we need this ? */
45164190Sjkoshy#include <unistd.h>
46247221Smarkj#include <sysexits.h>
47247221Smarkj#include <unistd.h>
48247221Smarkj#include <fcntl.h>
49247221Smarkj
50164190Sjkoshy#include <net/if.h>
51247221Smarkj#include <net/pfvar.h>
52164190Sjkoshy#include <netinet/in.h>
53247221Smarkj#include <netinet/in_systm.h>
54247221Smarkj#include <netinet/ip.h>
55247221Smarkj#include <netinet/ip_icmp.h>
56247221Smarkj#include <netinet/ip_fw.h>
57247221Smarkj#include <netinet/ip_dummynet.h>
58247221Smarkj#include <netinet/tcp.h>
59247221Smarkj#include <arpa/inet.h>
60247221Smarkj
61247221Smarkjint
62164190Sjkoshy		do_resolv,		/* Would try to resolve all */
63247221Smarkj		do_time,		/* Show time stamps */
64164190Sjkoshy		do_quiet,		/* Be quiet in add and flush */
65247221Smarkj		do_pipe,		/* this cmd refers to a pipe */
66247221Smarkj		do_sort,		/* field to sort results (0 = no) */
67247221Smarkj		do_dynamic,		/* display dynamic rules */
68247221Smarkj		do_expired,		/* display expired dynamic rules */
69247221Smarkj		do_compact,		/* show rules in compact mode */
70247221Smarkj		do_force,		/* do not ask for confirmation */
71247221Smarkj		show_sets,		/* display rule sets */
72247221Smarkj		test_only,		/* only check syntax */
73247221Smarkj		comment_only,		/* only print action and comment */
74247221Smarkj		verbose;
75247221Smarkj
76247221Smarkj#define	IP_MASK_ALL	0xffffffff
77164190Sjkoshy/*
78164190Sjkoshy * the following macro returns an error message if we run out of
79164190Sjkoshy * arguments.
80172088Sjkoshy */
81247221Smarkj#define NEED1(msg)      {if (!ac) errx(EX_USAGE, msg);}
82247221Smarkj
83164190Sjkoshy/*
84164190Sjkoshy * _s_x is a structure that stores a string <-> token pairs, used in
85247221Smarkj * various places in the parser. Entries are stored in arrays,
86164190Sjkoshy * with an entry with s=NULL as terminator.
87164190Sjkoshy * The search routines are match_token() and match_value().
88247221Smarkj * Often, an element with x=0 contains an error string.
89247221Smarkj *
90247221Smarkj */
91247221Smarkjstruct _s_x {
92164190Sjkoshy	char const *s;
93164190Sjkoshy	int x;
94164190Sjkoshy};
95164190Sjkoshy
96164190Sjkoshystatic struct _s_x f_tcpflags[] = {
97164190Sjkoshy	{ "syn", TH_SYN },
98164190Sjkoshy	{ "fin", TH_FIN },
99164190Sjkoshy	{ "ack", TH_ACK },
100247221Smarkj	{ "psh", TH_PUSH },
101247221Smarkj	{ "rst", TH_RST },
102164190Sjkoshy	{ "urg", TH_URG },
103247221Smarkj	{ "tcp flag", 0 },
104247221Smarkj	{ NULL,	0 }
105247221Smarkj};
106247221Smarkj
107247221Smarkjstatic struct _s_x f_tcpopts[] = {
108164190Sjkoshy	{ "mss",	IP_FW_TCPOPT_MSS },
109247221Smarkj	{ "maxseg",	IP_FW_TCPOPT_MSS },
110247221Smarkj	{ "window",	IP_FW_TCPOPT_WINDOW },
111247221Smarkj	{ "sack",	IP_FW_TCPOPT_SACK },
112247221Smarkj	{ "ts",		IP_FW_TCPOPT_TS },
113247221Smarkj	{ "timestamp",	IP_FW_TCPOPT_TS },
114164190Sjkoshy	{ "cc",		IP_FW_TCPOPT_CC },
115164190Sjkoshy	{ "tcp option",	0 },
116247221Smarkj	{ NULL,	0 }
117164190Sjkoshy};
118164190Sjkoshy
119164190Sjkoshy/*
120164190Sjkoshy * IP options span the range 0 to 255 so we need to remap them
121164190Sjkoshy * (though in fact only the low 5 bits are significant).
122164190Sjkoshy */
123164190Sjkoshystatic struct _s_x f_ipopts[] = {
124164190Sjkoshy	{ "ssrr",	IP_FW_IPOPT_SSRR},
125164190Sjkoshy	{ "lsrr",	IP_FW_IPOPT_LSRR},
126164190Sjkoshy	{ "rr",		IP_FW_IPOPT_RR},
127247221Smarkj	{ "ts",		IP_FW_IPOPT_TS},
128247221Smarkj	{ "ip option",	0 },
129247221Smarkj	{ NULL,	0 }
130247221Smarkj};
131247221Smarkj
132247221Smarkjstatic struct _s_x f_iptos[] = {
133247221Smarkj	{ "lowdelay",	IPTOS_LOWDELAY},
134247221Smarkj	{ "throughput",	IPTOS_THROUGHPUT},
135247221Smarkj	{ "reliability", IPTOS_RELIABILITY},
136247221Smarkj	{ "mincost",	IPTOS_MINCOST},
137247221Smarkj	{ "congestion",	IPTOS_CE},
138247221Smarkj	{ "ecntransport", IPTOS_ECT},
139247221Smarkj	{ "ip tos option", 0},
140247221Smarkj	{ NULL,	0 }
141247221Smarkj};
142247221Smarkj
143247221Smarkjstatic struct _s_x limit_masks[] = {
144247221Smarkj	{"all",		DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
145247221Smarkj	{"src-addr",	DYN_SRC_ADDR},
146247221Smarkj	{"src-port",	DYN_SRC_PORT},
147247221Smarkj	{"dst-addr",	DYN_DST_ADDR},
148247221Smarkj	{"dst-port",	DYN_DST_PORT},
149247221Smarkj	{NULL,		0}
150247221Smarkj};
151247221Smarkj
152247221Smarkj/*
153247221Smarkj * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines
154247221Smarkj * This is only used in this code.
155247221Smarkj */
156247221Smarkj#define IPPROTO_ETHERTYPE	0x1000
157247221Smarkjstatic struct _s_x ether_types[] = {
158247221Smarkj    /*
159247221Smarkj     * Note, we cannot use "-:&/" in the names because they are field
160247221Smarkj     * separators in the type specifications. Also, we use s = NULL as
161247221Smarkj     * end-delimiter, because a type of 0 can be legal.
162247221Smarkj     */
163247221Smarkj	{ "ip",		0x0800 },
164247221Smarkj	{ "ipv4",	0x0800 },
165247221Smarkj	{ "ipv6",	0x86dd },
166247221Smarkj	{ "arp",	0x0806 },
167247221Smarkj	{ "rarp",	0x8035 },
168247221Smarkj	{ "vlan",	0x8100 },
169247221Smarkj	{ "loop",	0x9000 },
170164190Sjkoshy	{ "trail",	0x1000 },
171212373Skaiw	{ "at",		0x809b },
172164190Sjkoshy	{ "atalk",	0x809b },
173164190Sjkoshy	{ "aarp",	0x80f3 },
174164190Sjkoshy	{ "pppoe_disc",	0x8863 },
175164190Sjkoshy	{ "pppoe_sess",	0x8864 },
176164190Sjkoshy	{ "ipx_8022",	0x00E0 },
177164190Sjkoshy	{ "ipx_8023",	0x0000 },
178164190Sjkoshy	{ "ipx_ii",	0x8137 },
179212373Skaiw	{ "ipx_snap",	0x8137 },
180164190Sjkoshy	{ "ipx",	0x8137 },
181164190Sjkoshy	{ "ns",		0x0600 },
182164190Sjkoshy	{ NULL,		0 }
183164190Sjkoshy};
184247221Smarkj
185247221Smarkjstatic void show_usage(void);
186247221Smarkj
187247221Smarkjenum tokens {
188247221Smarkj	TOK_NULL=0,
189247221Smarkj
190247221Smarkj	TOK_OR,
191247221Smarkj	TOK_NOT,
192247221Smarkj	TOK_STARTBRACE,
193164190Sjkoshy	TOK_ENDBRACE,
194247221Smarkj
195247221Smarkj	TOK_ACCEPT,
196247221Smarkj	TOK_COUNT,
197164190Sjkoshy	TOK_PIPE,
198164190Sjkoshy	TOK_QUEUE,
199164190Sjkoshy	TOK_DIVERT,
200164190Sjkoshy	TOK_TEE,
201212373Skaiw	TOK_FORWARD,
202164190Sjkoshy	TOK_SKIPTO,
203247221Smarkj	TOK_DENY,
204247221Smarkj	TOK_REJECT,
205247221Smarkj	TOK_RESET,
206164190Sjkoshy	TOK_UNREACH,
207247221Smarkj	TOK_CHECKSTATE,
208247221Smarkj
209247221Smarkj	TOK_ALTQ,
210247221Smarkj	TOK_LOG,
211247221Smarkj
212247221Smarkj	TOK_UID,
213247221Smarkj	TOK_GID,
214164190Sjkoshy	TOK_JAIL,
215164190Sjkoshy	TOK_IN,
216247221Smarkj	TOK_LIMIT,
217164190Sjkoshy	TOK_KEEPSTATE,
218164190Sjkoshy	TOK_LAYER2,
219164190Sjkoshy	TOK_OUT,
220164190Sjkoshy	TOK_XMIT,
221164190Sjkoshy	TOK_RECV,
222164190Sjkoshy	TOK_VIA,
223164190Sjkoshy	TOK_FRAG,
224164190Sjkoshy	TOK_IPOPTS,
225164190Sjkoshy	TOK_IPLEN,
226164190Sjkoshy	TOK_IPID,
227247221Smarkj	TOK_IPPRECEDENCE,
228247221Smarkj	TOK_IPTOS,
229164190Sjkoshy	TOK_IPTTL,
230247221Smarkj	TOK_IPVER,
231247221Smarkj	TOK_ESTAB,
232247221Smarkj	TOK_SETUP,
233247221Smarkj	TOK_TCPFLAGS,
234247221Smarkj	TOK_TCPOPTS,
235247221Smarkj	TOK_TCPSEQ,
236247221Smarkj	TOK_TCPACK,
237247221Smarkj	TOK_TCPWIN,
238164190Sjkoshy	TOK_ICMPTYPES,
239247221Smarkj	TOK_MAC,
240247221Smarkj	TOK_MACTYPE,
241247221Smarkj	TOK_VERREVPATH,
242247221Smarkj	TOK_VERSRCREACH,
243247221Smarkj	TOK_ANTISPOOF,
244247221Smarkj	TOK_IPSEC,
245247221Smarkj	TOK_COMMENT,
246247221Smarkj
247247221Smarkj	TOK_PLR,
248247221Smarkj	TOK_NOERROR,
249164190Sjkoshy	TOK_BUCKETS,
250247221Smarkj	TOK_DSTIP,
251164190Sjkoshy	TOK_SRCIP,
252247221Smarkj	TOK_DSTPORT,
253247221Smarkj	TOK_SRCPORT,
254247221Smarkj	TOK_ALL,
255247221Smarkj	TOK_MASK,
256247221Smarkj	TOK_BW,
257247221Smarkj	TOK_DELAY,
258247221Smarkj	TOK_RED,
259247221Smarkj	TOK_GRED,
260247221Smarkj	TOK_DROPTAIL,
261247221Smarkj	TOK_PROTO,
262247221Smarkj	TOK_WEIGHT,
263247221Smarkj};
264247221Smarkj
265247221Smarkjstruct _s_x dummynet_params[] = {
266247221Smarkj	{ "plr",		TOK_PLR },
267247221Smarkj	{ "noerror",		TOK_NOERROR },
268247221Smarkj	{ "buckets",		TOK_BUCKETS },
269247221Smarkj	{ "dst-ip",		TOK_DSTIP },
270247221Smarkj	{ "src-ip",		TOK_SRCIP },
271247221Smarkj	{ "dst-port",		TOK_DSTPORT },
272164190Sjkoshy	{ "src-port",		TOK_SRCPORT },
273164190Sjkoshy	{ "proto",		TOK_PROTO },
274247221Smarkj	{ "weight",		TOK_WEIGHT },
275247221Smarkj	{ "all",		TOK_ALL },
276247221Smarkj	{ "mask",		TOK_MASK },
277247221Smarkj	{ "droptail",		TOK_DROPTAIL },
278164190Sjkoshy	{ "red",		TOK_RED },
279164190Sjkoshy	{ "gred",		TOK_GRED },
280247221Smarkj	{ "bw",			TOK_BW },
281164190Sjkoshy	{ "bandwidth",		TOK_BW },
282164190Sjkoshy	{ "delay",		TOK_DELAY },
283164190Sjkoshy	{ "pipe",		TOK_PIPE },
284164190Sjkoshy	{ "queue",		TOK_QUEUE },
285164190Sjkoshy	{ "dummynet-params",	TOK_NULL },
286164190Sjkoshy	{ NULL, 0 }	/* terminator */
287164190Sjkoshy};
288164190Sjkoshy
289164190Sjkoshystruct _s_x rule_actions[] = {
290164190Sjkoshy	{ "accept",		TOK_ACCEPT },
291164190Sjkoshy	{ "pass",		TOK_ACCEPT },
292164190Sjkoshy	{ "allow",		TOK_ACCEPT },
293164190Sjkoshy	{ "permit",		TOK_ACCEPT },
294164190Sjkoshy	{ "count",		TOK_COUNT },
295164190Sjkoshy	{ "pipe",		TOK_PIPE },
296164190Sjkoshy	{ "queue",		TOK_QUEUE },
297164190Sjkoshy	{ "divert",		TOK_DIVERT },
298164190Sjkoshy	{ "tee",		TOK_TEE },
299164190Sjkoshy	{ "fwd",		TOK_FORWARD },
300164190Sjkoshy	{ "forward",		TOK_FORWARD },
301164190Sjkoshy	{ "skipto",		TOK_SKIPTO },
302164190Sjkoshy	{ "deny",		TOK_DENY },
303176758Sjkoshy	{ "drop",		TOK_DENY },
304176758Sjkoshy	{ "reject",		TOK_REJECT },
305176758Sjkoshy	{ "reset",		TOK_RESET },
306176758Sjkoshy	{ "unreach",		TOK_UNREACH },
307164190Sjkoshy	{ "check-state",	TOK_CHECKSTATE },
308164190Sjkoshy	{ "//",			TOK_COMMENT },
309176758Sjkoshy	{ NULL, 0 }	/* terminator */
310176758Sjkoshy};
311176758Sjkoshy
312176758Sjkoshystruct _s_x rule_action_params[] = {
313176758Sjkoshy	{ "altq",		TOK_ALTQ },
314164190Sjkoshy	{ "log",		TOK_LOG },
315176758Sjkoshy	{ NULL, 0 }	/* terminator */
316164190Sjkoshy};
317164190Sjkoshy
318164190Sjkoshystruct _s_x rule_options[] = {
319164190Sjkoshy	{ "uid",		TOK_UID },
320164190Sjkoshy	{ "gid",		TOK_GID },
321164190Sjkoshy	{ "jail",		TOK_JAIL },
322164190Sjkoshy	{ "in",			TOK_IN },
323164190Sjkoshy	{ "limit",		TOK_LIMIT },
324164190Sjkoshy	{ "keep-state",		TOK_KEEPSTATE },
325164190Sjkoshy	{ "bridged",		TOK_LAYER2 },
326164190Sjkoshy	{ "layer2",		TOK_LAYER2 },
327164190Sjkoshy	{ "out",		TOK_OUT },
328247221Smarkj	{ "xmit",		TOK_XMIT },
329247221Smarkj	{ "recv",		TOK_RECV },
330247221Smarkj	{ "via",		TOK_VIA },
331247221Smarkj	{ "fragment",		TOK_FRAG },
332164190Sjkoshy	{ "frag",		TOK_FRAG },
333164190Sjkoshy	{ "ipoptions",		TOK_IPOPTS },
334164190Sjkoshy	{ "ipopts",		TOK_IPOPTS },
335164190Sjkoshy	{ "iplen",		TOK_IPLEN },
336247221Smarkj	{ "ipid",		TOK_IPID },
337172088Sjkoshy	{ "ipprecedence",	TOK_IPPRECEDENCE },
338164190Sjkoshy	{ "iptos",		TOK_IPTOS },
339164190Sjkoshy	{ "ipttl",		TOK_IPTTL },
340164190Sjkoshy	{ "ipversion",		TOK_IPVER },
341164190Sjkoshy	{ "ipver",		TOK_IPVER },
342164190Sjkoshy	{ "estab",		TOK_ESTAB },
343164190Sjkoshy	{ "established",	TOK_ESTAB },
344164190Sjkoshy	{ "setup",		TOK_SETUP },
345247221Smarkj	{ "tcpflags",		TOK_TCPFLAGS },
346164190Sjkoshy	{ "tcpflgs",		TOK_TCPFLAGS },
347164190Sjkoshy	{ "tcpoptions",		TOK_TCPOPTS },
348164190Sjkoshy	{ "tcpopts",		TOK_TCPOPTS },
349164190Sjkoshy	{ "tcpseq",		TOK_TCPSEQ },
350164190Sjkoshy	{ "tcpack",		TOK_TCPACK },
351164190Sjkoshy	{ "tcpwin",		TOK_TCPWIN },
352164190Sjkoshy	{ "icmptype",		TOK_ICMPTYPES },
353164190Sjkoshy	{ "icmptypes",		TOK_ICMPTYPES },
354247221Smarkj	{ "dst-ip",		TOK_DSTIP },
355247221Smarkj	{ "src-ip",		TOK_SRCIP },
356247221Smarkj	{ "dst-port",		TOK_DSTPORT },
357247221Smarkj	{ "src-port",		TOK_SRCPORT },
358247221Smarkj	{ "proto",		TOK_PROTO },
359247221Smarkj	{ "MAC",		TOK_MAC },
360247221Smarkj	{ "mac",		TOK_MAC },
361247221Smarkj	{ "mac-type",		TOK_MACTYPE },
362247221Smarkj	{ "verrevpath",		TOK_VERREVPATH },
363247221Smarkj	{ "versrcreach",	TOK_VERSRCREACH },
364247221Smarkj	{ "antispoof",		TOK_ANTISPOOF },
365247221Smarkj	{ "ipsec",		TOK_IPSEC },
366247221Smarkj	{ "//",			TOK_COMMENT },
367247221Smarkj
368247221Smarkj	{ "not",		TOK_NOT },		/* pseudo option */
369164190Sjkoshy	{ "!", /* escape ? */	TOK_NOT },		/* pseudo option */
370164190Sjkoshy	{ "or",			TOK_OR },		/* pseudo option */
371164190Sjkoshy	{ "|", /* escape */	TOK_OR },		/* pseudo option */
372164190Sjkoshy	{ "{",			TOK_STARTBRACE },	/* pseudo option */
373164190Sjkoshy	{ "(",			TOK_STARTBRACE },	/* pseudo option */
374172088Sjkoshy	{ "}",			TOK_ENDBRACE },		/* pseudo option */
375172088Sjkoshy	{ ")",			TOK_ENDBRACE },		/* pseudo option */
376172088Sjkoshy	{ NULL, 0 }	/* terminator */
377172088Sjkoshy};
378172088Sjkoshy
379172088Sjkoshystatic __inline uint64_t
380172088Sjkoshyalign_uint64(uint64_t *pll) {
381172088Sjkoshy	uint64_t ret;
382172088Sjkoshy
383172088Sjkoshy	bcopy (pll, &ret, sizeof(ret));
384172088Sjkoshy	return ret;
385172088Sjkoshy}
386172088Sjkoshy
387172088Sjkoshy/*
388172088Sjkoshy * conditionally runs the command.
389172088Sjkoshy */
390172088Sjkoshystatic int
391172088Sjkoshydo_cmd(int optname, void *optval, uintptr_t optlen)
392172088Sjkoshy{
393172088Sjkoshy	static int s = -1;	/* the socket */
394172088Sjkoshy	int i;
395172088Sjkoshy
396172088Sjkoshy	if (test_only)
397164190Sjkoshy		return 0;
398164190Sjkoshy
399164190Sjkoshy	if (s == -1)
400164190Sjkoshy		s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
401164190Sjkoshy	if (s < 0)
402164190Sjkoshy		err(EX_UNAVAILABLE, "socket");
403164190Sjkoshy
404164190Sjkoshy	if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
405164190Sjkoshy	    optname == IP_FW_ADD || optname == IP_FW_TABLE_LIST ||
406164190Sjkoshy	    optname == IP_FW_TABLE_GETSIZE)
407164190Sjkoshy		i = getsockopt(s, IPPROTO_IP, optname, optval,
408164190Sjkoshy			(socklen_t *)optlen);
409164190Sjkoshy	else
410164190Sjkoshy		i = setsockopt(s, IPPROTO_IP, optname, optval, optlen);
411164190Sjkoshy	return i;
412164190Sjkoshy}
413164190Sjkoshy
414164190Sjkoshy/**
415164190Sjkoshy * match_token takes a table and a string, returns the value associated
416164190Sjkoshy * with the string (-1 in case of failure).
417164190Sjkoshy */
418164190Sjkoshystatic int
419164190Sjkoshymatch_token(struct _s_x *table, char *string)
420164190Sjkoshy{
421164190Sjkoshy	struct _s_x *pt;
422164190Sjkoshy	uint i = strlen(string);
423164190Sjkoshy
424164190Sjkoshy	for (pt = table ; i && pt->s != NULL ; pt++)
425164190Sjkoshy		if (strlen(pt->s) == i && !bcmp(string, pt->s, i))
426164190Sjkoshy			return pt->x;
427164190Sjkoshy	return -1;
428164190Sjkoshy}
429164190Sjkoshy
430164190Sjkoshy/**
431164190Sjkoshy * match_value takes a table and a value, returns the string associated
432164190Sjkoshy * with the value (NULL in case of failure).
433164190Sjkoshy */
434164190Sjkoshystatic char const *
435164190Sjkoshymatch_value(struct _s_x *p, int value)
436164190Sjkoshy{
437164190Sjkoshy	for (; p->s != NULL; p++)
438164190Sjkoshy		if (p->x == value)
439164190Sjkoshy			return p->s;
440164190Sjkoshy	return NULL;
441164190Sjkoshy}
442164190Sjkoshy
443164190Sjkoshy/*
444164190Sjkoshy * prints one port, symbolic or numeric
445164190Sjkoshy */
446164190Sjkoshystatic void
447164190Sjkoshyprint_port(int proto, uint16_t port)
448164190Sjkoshy{
449164190Sjkoshy
450164190Sjkoshy	if (proto == IPPROTO_ETHERTYPE) {
451164190Sjkoshy		char const *s;
452164190Sjkoshy
453164190Sjkoshy		if (do_resolv && (s = match_value(ether_types, port)) )
454164190Sjkoshy			printf("%s", s);
455164190Sjkoshy		else
456164190Sjkoshy			printf("0x%04x", port);
457164190Sjkoshy	} else {
458164190Sjkoshy		struct servent *se = NULL;
459164190Sjkoshy		if (do_resolv) {
460164190Sjkoshy			struct protoent *pe = getprotobynumber(proto);
461165535Sjkoshy
462165535Sjkoshy			se = getservbyport(htons(port), pe ? pe->p_name : NULL);
463164190Sjkoshy		}
464164190Sjkoshy		if (se)
465164190Sjkoshy			printf("%s", se->s_name);
466164190Sjkoshy		else
467164190Sjkoshy			printf("%d", port);
468164190Sjkoshy	}
469164190Sjkoshy}
470164190Sjkoshy
471164190Sjkoshystruct _s_x _port_name[] = {
472164190Sjkoshy	{"dst-port",	O_IP_DSTPORT},
473164190Sjkoshy	{"src-port",	O_IP_SRCPORT},
474164190Sjkoshy	{"ipid",	O_IPID},
475210326Skaiw	{"iplen",	O_IPLEN},
476210326Skaiw	{"ipttl",	O_IPTTL},
477164190Sjkoshy	{"mac-type",	O_MAC_TYPE},
478164190Sjkoshy	{NULL,		0}
479164190Sjkoshy};
480164190Sjkoshy
481164190Sjkoshy/*
482164190Sjkoshy * Print the values in a list 16-bit items of the types above.
483164190Sjkoshy * XXX todo: add support for mask.
484164190Sjkoshy */
485164190Sjkoshystatic void
486164190Sjkoshyprint_newports(ipfw_insn_u16 *cmd, int proto, int opcode)
487164190Sjkoshy{
488164190Sjkoshy	uint16_t *p = cmd->ports;
489164190Sjkoshy	int i;
490164190Sjkoshy	char const *sep;
491164190Sjkoshy
492164190Sjkoshy	if (cmd->o.len & F_NOT)
493164190Sjkoshy		printf(" not");
494164190Sjkoshy	if (opcode != 0) {
495164190Sjkoshy		sep = match_value(_port_name, opcode);
496164190Sjkoshy		if (sep == NULL)
497164190Sjkoshy			sep = "???";
498164190Sjkoshy		printf (" %s", sep);
499164190Sjkoshy	}
500164190Sjkoshy	sep = " ";
501164190Sjkoshy	for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
502164190Sjkoshy		printf(sep);
503164190Sjkoshy		print_port(proto, p[0]);
504164190Sjkoshy		if (p[0] != p[1]) {
505164190Sjkoshy			printf("-");
506164190Sjkoshy			print_port(proto, p[1]);
507164190Sjkoshy		}
508164190Sjkoshy		sep = ",";
509164190Sjkoshy	}
510164190Sjkoshy}
511164190Sjkoshy
512164190Sjkoshy/*
513164190Sjkoshy * Like strtol, but also translates service names into port numbers
514164190Sjkoshy * for some protocols.
515164190Sjkoshy * In particular:
516164190Sjkoshy *	proto == -1 disables the protocol check;
517164190Sjkoshy *	proto == IPPROTO_ETHERTYPE looks up an internal table
518164190Sjkoshy *	proto == <some value in /etc/protocols> matches the values there.
519164190Sjkoshy * Returns *end == s in case the parameter is not found.
520164190Sjkoshy */
521164190Sjkoshystatic int
522164190Sjkoshystrtoport(char *s, char **end, int base, int proto)
523164190Sjkoshy{
524164190Sjkoshy	char *p, *buf;
525210330Skaiw	char *s1;
526210330Skaiw	int i;
527210330Skaiw
528210330Skaiw	*end = s;		/* default - not found */
529210330Skaiw	if (*s == '\0')
530164190Sjkoshy		return 0;	/* not found */
531164190Sjkoshy
532164190Sjkoshy	if (isdigit(*s))
533164190Sjkoshy		return strtol(s, end, base);
534164190Sjkoshy
535172088Sjkoshy	/*
536172088Sjkoshy	 * find separator. '\\' escapes the next char.
537172088Sjkoshy	 */
538172088Sjkoshy	for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++)
539172088Sjkoshy		if (*s1 == '\\' && s1[1] != '\0')
540164190Sjkoshy			s1++;
541164190Sjkoshy
542164190Sjkoshy	buf = malloc(s1 - s + 1);
543164190Sjkoshy	if (buf == NULL)
544164190Sjkoshy		return 0;
545164190Sjkoshy
546164190Sjkoshy	/*
547164190Sjkoshy	 * copy into a buffer skipping backslashes
548164190Sjkoshy	 */
549164190Sjkoshy	for (p = s, i = 0; p != s1 ; p++)
550164190Sjkoshy		if (*p != '\\')
551164190Sjkoshy			buf[i++] = *p;
552164190Sjkoshy	buf[i++] = '\0';
553172088Sjkoshy
554172088Sjkoshy	if (proto == IPPROTO_ETHERTYPE) {
555164190Sjkoshy		i = match_token(ether_types, buf);
556164190Sjkoshy		free(buf);
557164190Sjkoshy		if (i != -1) {	/* found */
558164190Sjkoshy			*end = s1;
559165535Sjkoshy			return i;
560165535Sjkoshy		}
561165535Sjkoshy	} else {
562165535Sjkoshy		struct protoent *pe = NULL;
563165535Sjkoshy		struct servent *se;
564165535Sjkoshy
565165535Sjkoshy		if (proto != 0)
566164190Sjkoshy			pe = getprotobynumber(proto);
567164190Sjkoshy		setservent(1);
568164190Sjkoshy		se = getservbyname(buf, pe ? pe->p_name : NULL);
569164190Sjkoshy		free(buf);
570164190Sjkoshy		if (se != NULL) {
571164190Sjkoshy			*end = s1;
572164190Sjkoshy			return ntohs(se->s_port);
573164190Sjkoshy		}
574164190Sjkoshy	}
575164190Sjkoshy	return 0;	/* not found */
576164190Sjkoshy}
577164190Sjkoshy
578164190Sjkoshy/*
579164190Sjkoshy * Map between current altq queue id numbers and names.
580164190Sjkoshy */
581164190Sjkoshystatic int altq_fetched = 0;
582164190Sjkoshystatic TAILQ_HEAD(, pf_altq) altq_entries =
583164190Sjkoshy	TAILQ_HEAD_INITIALIZER(altq_entries);
584164190Sjkoshy
585164190Sjkoshystatic void
586164190Sjkoshyaltq_set_enabled(int enabled)
587164190Sjkoshy{
588164190Sjkoshy	int pffd;
589164190Sjkoshy
590164190Sjkoshy	pffd = open("/dev/pf", O_RDWR);
591164190Sjkoshy	if (pffd == -1)
592210328Skaiw		err(EX_UNAVAILABLE,
593164190Sjkoshy		    "altq support opening pf(4) control device");
594164190Sjkoshy	if (enabled) {
595164190Sjkoshy		if (ioctl(pffd, DIOCSTARTALTQ) != 0 && errno != EEXIST)
596210328Skaiw			err(EX_UNAVAILABLE, "enabling altq");
597164190Sjkoshy	} else {
598210328Skaiw		if (ioctl(pffd, DIOCSTOPALTQ) != 0 && errno != ENOENT)
599210328Skaiw			err(EX_UNAVAILABLE, "disabling altq");
600164190Sjkoshy	}
601210328Skaiw	close(pffd);
602210328Skaiw}
603164190Sjkoshy
604164190Sjkoshystatic void
605164190Sjkoshyaltq_fetch()
606164190Sjkoshy{
607210328Skaiw	struct pfioc_altq pfioc;
608164190Sjkoshy	struct pf_altq *altq;
609164190Sjkoshy	int pffd, mnr;
610164190Sjkoshy
611164190Sjkoshy	if (altq_fetched)
612164190Sjkoshy		return;
613164190Sjkoshy	altq_fetched = 1;
614164190Sjkoshy	pffd = open("/dev/pf", O_RDONLY);
615164190Sjkoshy	if (pffd == -1) {
616164190Sjkoshy		warn("altq support opening pf(4) control device");
617164190Sjkoshy		return;
618164190Sjkoshy	}
619164190Sjkoshy	bzero(&pfioc, sizeof(pfioc));
620164190Sjkoshy	if (ioctl(pffd, DIOCGETALTQS, &pfioc) != 0) {
621164190Sjkoshy		warn("altq support getting queue list");
622164190Sjkoshy		close(pffd);
623164190Sjkoshy		return;
624164190Sjkoshy	}
625164190Sjkoshy	mnr = pfioc.nr;
626164190Sjkoshy	for (pfioc.nr = 0; pfioc.nr < mnr; pfioc.nr++) {
627164190Sjkoshy		if (ioctl(pffd, DIOCGETALTQ, &pfioc) != 0) {
628164190Sjkoshy			if (errno == EBUSY)
629164190Sjkoshy				break;
630164190Sjkoshy			warn("altq support getting queue list");
631164190Sjkoshy			close(pffd);
632164190Sjkoshy			return;
633164190Sjkoshy		}
634164190Sjkoshy		if (pfioc.altq.qid == 0)
635164190Sjkoshy			continue;
636164190Sjkoshy		altq = malloc(sizeof(*altq));
637164190Sjkoshy		if (altq == NULL)
638164190Sjkoshy			err(EX_OSERR, "malloc");
639164190Sjkoshy		*altq = pfioc.altq;
640164190Sjkoshy		TAILQ_INSERT_TAIL(&altq_entries, altq, entries);
641164190Sjkoshy	}
642164190Sjkoshy	close(pffd);
643164190Sjkoshy}
644164190Sjkoshy
645164190Sjkoshystatic u_int32_t
646164190Sjkoshyaltq_name_to_qid(const char *name)
647164190Sjkoshy{
648164190Sjkoshy	struct pf_altq *altq;
649164190Sjkoshy
650164190Sjkoshy	altq_fetch();
651164190Sjkoshy	TAILQ_FOREACH(altq, &altq_entries, entries)
652164190Sjkoshy		if (strcmp(name, altq->qname) == 0)
653164190Sjkoshy			break;
654164190Sjkoshy	if (altq == NULL)
655164190Sjkoshy		errx(EX_DATAERR, "altq has no queue named `%s'", name);
656164190Sjkoshy	return altq->qid;
657164190Sjkoshy}
658164190Sjkoshy
659212373Skaiwstatic const char *
660212373Skaiwaltq_qid_to_name(u_int32_t qid)
661164190Sjkoshy{
662164190Sjkoshy	struct pf_altq *altq;
663164190Sjkoshy
664164190Sjkoshy	altq_fetch();
665164190Sjkoshy	TAILQ_FOREACH(altq, &altq_entries, entries)
666164190Sjkoshy		if (qid == altq->qid)
667164190Sjkoshy			break;
668164190Sjkoshy	if (altq == NULL)
669164190Sjkoshy		return NULL;
670164190Sjkoshy	return altq->qname;
671164190Sjkoshy}
672164190Sjkoshy
673212373Skaiwstatic void
674164190Sjkoshyfill_altq_qid(u_int32_t *qid, const char *av)
675164190Sjkoshy{
676164190Sjkoshy	*qid = altq_name_to_qid(av);
677164190Sjkoshy}
678164190Sjkoshy
679164190Sjkoshy/*
680164190Sjkoshy * Fill the body of the command with the list of port ranges.
681164190Sjkoshy */
682164190Sjkoshystatic int
683164190Sjkoshyfill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
684164190Sjkoshy{
685164190Sjkoshy	uint16_t a, b, *p = cmd->ports;
686164190Sjkoshy	int i = 0;
687164190Sjkoshy	char *s = av;
688164190Sjkoshy
689164190Sjkoshy	while (*s) {
690164190Sjkoshy		a = strtoport(av, &s, 0, proto);
691164190Sjkoshy		if (s == av) /* no parameter */
692164190Sjkoshy			break;
693164190Sjkoshy		if (*s == '-') { /* a range */
694164190Sjkoshy			av = s+1;
695164190Sjkoshy			b = strtoport(av, &s, 0, proto);
696164190Sjkoshy			if (s == av) /* no parameter */
697164190Sjkoshy				break;
698164190Sjkoshy			p[0] = a;
699164190Sjkoshy			p[1] = b;
700164190Sjkoshy		} else if (*s == ',' || *s == '\0' )
701164190Sjkoshy			p[0] = p[1] = a;
702164190Sjkoshy		else 	/* invalid separator */
703164190Sjkoshy			errx(EX_DATAERR, "invalid separator <%c> in <%s>\n",
704164190Sjkoshy				*s, av);
705164190Sjkoshy		i++;
706164190Sjkoshy		p += 2;
707164190Sjkoshy		av = s+1;
708164190Sjkoshy	}
709172088Sjkoshy	if (i > 0) {
710164190Sjkoshy		if (i+1 > F_LEN_MASK)
711164190Sjkoshy			errx(EX_DATAERR, "too many ports/ranges\n");
712164190Sjkoshy		cmd->o.len |= i+1; /* leave F_NOT and F_OR untouched */
713164190Sjkoshy	}
714164190Sjkoshy	return i;
715164190Sjkoshy}
716164190Sjkoshy
717164190Sjkoshystatic struct _s_x icmpcodes[] = {
718164190Sjkoshy      { "net",			ICMP_UNREACH_NET },
719164190Sjkoshy      { "host",			ICMP_UNREACH_HOST },
720164190Sjkoshy      { "protocol",		ICMP_UNREACH_PROTOCOL },
721164190Sjkoshy      { "port",			ICMP_UNREACH_PORT },
722164190Sjkoshy      { "needfrag",		ICMP_UNREACH_NEEDFRAG },
723164190Sjkoshy      { "srcfail",		ICMP_UNREACH_SRCFAIL },
724164190Sjkoshy      { "net-unknown",		ICMP_UNREACH_NET_UNKNOWN },
725164190Sjkoshy      { "host-unknown",		ICMP_UNREACH_HOST_UNKNOWN },
726164190Sjkoshy      { "isolated",		ICMP_UNREACH_ISOLATED },
727164190Sjkoshy      { "net-prohib",		ICMP_UNREACH_NET_PROHIB },
728164190Sjkoshy      { "host-prohib",		ICMP_UNREACH_HOST_PROHIB },
729164190Sjkoshy      { "tosnet",		ICMP_UNREACH_TOSNET },
730164190Sjkoshy      { "toshost",		ICMP_UNREACH_TOSHOST },
731164190Sjkoshy      { "filter-prohib",	ICMP_UNREACH_FILTER_PROHIB },
732164190Sjkoshy      { "host-precedence",	ICMP_UNREACH_HOST_PRECEDENCE },
733165535Sjkoshy      { "precedence-cutoff",	ICMP_UNREACH_PRECEDENCE_CUTOFF },
734165535Sjkoshy      { NULL, 0 }
735164190Sjkoshy};
736164190Sjkoshy
737164190Sjkoshystatic void
738164190Sjkoshyfill_reject_code(u_short *codep, char *str)
739164190Sjkoshy{
740164190Sjkoshy	int val;
741164190Sjkoshy	char *s;
742164190Sjkoshy
743164190Sjkoshy	val = strtoul(str, &s, 0);
744164190Sjkoshy	if (s == str || *s != '\0' || val >= 0x100)
745164190Sjkoshy		val = match_token(icmpcodes, str);
746164190Sjkoshy	if (val < 0)
747164190Sjkoshy		errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
748164190Sjkoshy	*codep = val;
749164190Sjkoshy	return;
750164190Sjkoshy}
751164190Sjkoshy
752164190Sjkoshystatic void
753164190Sjkoshyprint_reject_code(uint16_t code)
754164190Sjkoshy{
755164190Sjkoshy	char const *s = match_value(icmpcodes, code);
756164190Sjkoshy
757164190Sjkoshy	if (s != NULL)
758164190Sjkoshy		printf("unreach %s", s);
759164190Sjkoshy	else
760164190Sjkoshy		printf("unreach %u", code);
761164190Sjkoshy}
762164190Sjkoshy
763164190Sjkoshy/*
764164190Sjkoshy * Returns the number of bits set (from left) in a contiguous bitmask,
765164190Sjkoshy * or -1 if the mask is not contiguous.
766164190Sjkoshy * XXX this needs a proper fix.
767164190Sjkoshy * This effectively works on masks in big-endian (network) format.
768164190Sjkoshy * when compiled on little endian architectures.
769164190Sjkoshy *
770164190Sjkoshy * First bit is bit 7 of the first byte -- note, for MAC addresses,
771164190Sjkoshy * the first bit on the wire is bit 0 of the first byte.
772164190Sjkoshy * len is the max length in bits.
773164190Sjkoshy */
774164190Sjkoshystatic int
775164190Sjkoshycontigmask(uint8_t *p, int len)
776164190Sjkoshy{
777164190Sjkoshy	int i, n;
778164190Sjkoshy
779164190Sjkoshy	for (i=0; i<len ; i++)
780164190Sjkoshy		if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
781164190Sjkoshy			break;
782164190Sjkoshy	for (n=i+1; n < len; n++)
783210330Skaiw		if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
784164190Sjkoshy			return -1; /* mask not contiguous */
785164190Sjkoshy	return i;
786164190Sjkoshy}
787164190Sjkoshy
788164190Sjkoshy/*
789164190Sjkoshy * print flags set/clear in the two bitmasks passed as parameters.
790164190Sjkoshy * There is a specialized check for f_tcpflags.
791164190Sjkoshy */
792164190Sjkoshystatic void
793164190Sjkoshyprint_flags(char const *name, ipfw_insn *cmd, struct _s_x *list)
794164190Sjkoshy{
795164190Sjkoshy	char const *comma = "";
796164190Sjkoshy	int i;
797164190Sjkoshy	uint8_t set = cmd->arg1 & 0xff;
798164190Sjkoshy	uint8_t clear = (cmd->arg1 >> 8) & 0xff;
799164190Sjkoshy
800164190Sjkoshy	if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) {
801164190Sjkoshy		printf(" setup");
802164190Sjkoshy		return;
803164190Sjkoshy	}
804164190Sjkoshy
805164190Sjkoshy	printf(" %s ", name);
806164190Sjkoshy	for (i=0; list[i].x != 0; i++) {
807164190Sjkoshy		if (set & list[i].x) {
808164190Sjkoshy			set &= ~list[i].x;
809164190Sjkoshy			printf("%s%s", comma, list[i].s);
810164190Sjkoshy			comma = ",";
811164190Sjkoshy		}
812164190Sjkoshy		if (clear & list[i].x) {
813172088Sjkoshy			clear &= ~list[i].x;
814172088Sjkoshy			printf("%s!%s", comma, list[i].s);
815172088Sjkoshy			comma = ",";
816172088Sjkoshy		}
817164190Sjkoshy	}
818164190Sjkoshy}
819164190Sjkoshy
820164190Sjkoshy/*
821164190Sjkoshy * Print the ip address contained in a command.
822164190Sjkoshy */
823172088Sjkoshystatic void
824164190Sjkoshyprint_ip(ipfw_insn_ip *cmd, char const *s)
825164190Sjkoshy{
826164190Sjkoshy	struct hostent *he = NULL;
827164190Sjkoshy	int len = F_LEN((ipfw_insn *)cmd);
828164190Sjkoshy	uint32_t *a = ((ipfw_insn_u32 *)cmd)->d;
829164190Sjkoshy
830164190Sjkoshy	printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
831164190Sjkoshy
832164190Sjkoshy	if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) {
833164190Sjkoshy		printf("me");
834164190Sjkoshy		return;
835164190Sjkoshy	}
836164190Sjkoshy	if (cmd->o.opcode == O_IP_SRC_LOOKUP ||
837164190Sjkoshy	    cmd->o.opcode == O_IP_DST_LOOKUP) {
838164190Sjkoshy		printf("table(%u", ((ipfw_insn *)cmd)->arg1);
839164190Sjkoshy		if (len == F_INSN_SIZE(ipfw_insn_u32))
840164190Sjkoshy			printf(",%u", *a);
841177367Sjkoshy		printf(")");
842164190Sjkoshy		return;
843164190Sjkoshy	}
844164190Sjkoshy	if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) {
845164190Sjkoshy		uint32_t x, *map = (uint32_t *)&(cmd->mask);
846164190Sjkoshy		int i, j;
847172088Sjkoshy		char comma = '{';
848177367Sjkoshy
849172088Sjkoshy		x = cmd->o.arg1 - 1;
850172088Sjkoshy		x = htonl( ~x );
851164190Sjkoshy		cmd->addr.s_addr = htonl(cmd->addr.s_addr);
852164190Sjkoshy		printf("%s/%d", inet_ntoa(cmd->addr),
853164190Sjkoshy			contigmask((uint8_t *)&x, 32));
854164190Sjkoshy		x = cmd->addr.s_addr = htonl(cmd->addr.s_addr);
855164190Sjkoshy		x &= 0xff; /* base */
856164190Sjkoshy		/*
857164190Sjkoshy		 * Print bits and ranges.
858164190Sjkoshy		 * Locate first bit set (i), then locate first bit unset (j).
859164190Sjkoshy		 * If we have 3+ consecutive bits set, then print them as a
860164190Sjkoshy		 * range, otherwise only print the initial bit and rescan.
861164190Sjkoshy		 */
862164190Sjkoshy		for (i=0; i < cmd->o.arg1; i++)
863164190Sjkoshy			if (map[i/32] & (1<<(i & 31))) {
864164190Sjkoshy				for (j=i+1; j < cmd->o.arg1; j++)
865164190Sjkoshy					if (!(map[ j/32] & (1<<(j & 31))))
866164190Sjkoshy						break;
867164190Sjkoshy				printf("%c%d", comma, i+x);
868164190Sjkoshy				if (j>i+2) { /* range has at least 3 elements */
869164190Sjkoshy					printf("-%d", j-1+x);
870164190Sjkoshy					i = j-1;
871164190Sjkoshy				}
872164190Sjkoshy				comma = ',';
873164190Sjkoshy			}
874164190Sjkoshy		printf("}");
875164190Sjkoshy		return;
876164190Sjkoshy	}
877164190Sjkoshy	/*
878164190Sjkoshy	 * len == 2 indicates a single IP, whereas lists of 1 or more
879164190Sjkoshy	 * addr/mask pairs have len = (2n+1). We convert len to n so we
880164190Sjkoshy	 * use that to count the number of entries.
881164190Sjkoshy	 */
882164190Sjkoshy    for (len = len / 2; len > 0; len--, a += 2) {
883164190Sjkoshy	int mb =	/* mask length */
884164190Sjkoshy	    (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ?
885164190Sjkoshy		32 : contigmask((uint8_t *)&(a[1]), 32);
886164190Sjkoshy	if (mb == 32 && do_resolv)
887164190Sjkoshy		he = gethostbyaddr((char *)&(a[0]), sizeof(u_long), AF_INET);
888164190Sjkoshy	if (he != NULL)		/* resolved to name */
889164190Sjkoshy		printf("%s", he->h_name);
890164190Sjkoshy	else if (mb == 0)	/* any */
891164190Sjkoshy		printf("any");
892164190Sjkoshy	else {		/* numeric IP followed by some kind of mask */
893164190Sjkoshy		printf("%s", inet_ntoa( *((struct in_addr *)&a[0]) ) );
894164190Sjkoshy		if (mb < 0)
895164190Sjkoshy			printf(":%s", inet_ntoa( *((struct in_addr *)&a[1]) ) );
896164190Sjkoshy		else if (mb < 32)
897164190Sjkoshy			printf("/%d", mb);
898164190Sjkoshy	}
899164190Sjkoshy	if (len > 1)
900164190Sjkoshy		printf(",");
901164190Sjkoshy    }
902164190Sjkoshy}
903164190Sjkoshy
904164190Sjkoshy/*
905164190Sjkoshy * prints a MAC address/mask pair
906164190Sjkoshy */
907210347Skaiwstatic void
908210347Skaiwprint_mac(uint8_t *addr, uint8_t *mask)
909164190Sjkoshy{
910164190Sjkoshy	int l = contigmask(mask, 48);
911164190Sjkoshy
912210347Skaiw	if (l == 0)
913210347Skaiw		printf(" any");
914164190Sjkoshy	else {
915164190Sjkoshy		printf(" %02x:%02x:%02x:%02x:%02x:%02x",
916164190Sjkoshy		    addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
917164190Sjkoshy		if (l == -1)
918164190Sjkoshy			printf("&%02x:%02x:%02x:%02x:%02x:%02x",
919164190Sjkoshy			    mask[0], mask[1], mask[2],
920164190Sjkoshy			    mask[3], mask[4], mask[5]);
921164190Sjkoshy		else if (l < 48)
922164190Sjkoshy			printf("/%d", l);
923164190Sjkoshy	}
924164190Sjkoshy}
925164190Sjkoshy
926164190Sjkoshystatic void
927164190Sjkoshyfill_icmptypes(ipfw_insn_u32 *cmd, char *av)
928164190Sjkoshy{
929164190Sjkoshy	uint8_t type;
930164190Sjkoshy
931164190Sjkoshy	cmd->d[0] = 0;
932164190Sjkoshy	while (*av) {
933164190Sjkoshy		if (*av == ',')
934164190Sjkoshy			av++;
935164190Sjkoshy
936164190Sjkoshy		type = strtoul(av, &av, 0);
937164190Sjkoshy
938164190Sjkoshy		if (*av != ',' && *av != '\0')
939164190Sjkoshy			errx(EX_DATAERR, "invalid ICMP type");
940164190Sjkoshy
941164190Sjkoshy		if (type > 31)
942164190Sjkoshy			errx(EX_DATAERR, "ICMP type out of range");
943164190Sjkoshy
944164190Sjkoshy		cmd->d[0] |= 1 << type;
945164190Sjkoshy	}
946164190Sjkoshy	cmd->o.opcode = O_ICMPTYPE;
947164190Sjkoshy	cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
948164190Sjkoshy}
949164190Sjkoshy
950164190Sjkoshystatic void
951164190Sjkoshyprint_icmptypes(ipfw_insn_u32 *cmd)
952164190Sjkoshy{
953164190Sjkoshy	int i;
954164190Sjkoshy	char sep= ' ';
955164190Sjkoshy
956164190Sjkoshy	printf(" icmptypes");
957164190Sjkoshy	for (i = 0; i < 32; i++) {
958164190Sjkoshy		if ( (cmd->d[0] & (1 << (i))) == 0)
959164190Sjkoshy			continue;
960164190Sjkoshy		printf("%c%d", sep, i);
961164190Sjkoshy		sep = ',';
962164190Sjkoshy	}
963164190Sjkoshy}
964164190Sjkoshy
965164190Sjkoshy/*
966164190Sjkoshy * show_ipfw() prints the body of an ipfw rule.
967164190Sjkoshy * Because the standard rule has at least proto src_ip dst_ip, we use
968164190Sjkoshy * a helper function to produce these entries if not provided explicitly.
969 * The first argument is the list of fields we have, the second is
970 * the list of fields we want to be printed.
971 *
972 * Special cases if we have provided a MAC header:
973 *   + if the rule does not contain IP addresses/ports, do not print them;
974 *   + if the rule does not contain an IP proto, print "all" instead of "ip";
975 *
976 * Once we have 'have_options', IP header fields are printed as options.
977 */
978#define	HAVE_PROTO	0x0001
979#define	HAVE_SRCIP	0x0002
980#define	HAVE_DSTIP	0x0004
981#define	HAVE_MAC	0x0008
982#define	HAVE_MACTYPE	0x0010
983#define	HAVE_OPTIONS	0x8000
984
985#define	HAVE_IP		(HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP)
986static void
987show_prerequisites(int *flags, int want, int cmd)
988{
989	if (comment_only)
990		return;
991	if ( (*flags & HAVE_IP) == HAVE_IP)
992		*flags |= HAVE_OPTIONS;
993
994	if ( (*flags & (HAVE_MAC|HAVE_MACTYPE|HAVE_OPTIONS)) == HAVE_MAC &&
995	     cmd != O_MAC_TYPE) {
996		/*
997		 * mac-type was optimized out by the compiler,
998		 * restore it
999		 */
1000		printf(" any");
1001		*flags |= HAVE_MACTYPE | HAVE_OPTIONS;
1002		return;
1003	}
1004	if ( !(*flags & HAVE_OPTIONS)) {
1005		if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO))
1006			printf(" ip");
1007		if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
1008			printf(" from any");
1009		if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP))
1010			printf(" to any");
1011	}
1012	*flags |= want;
1013}
1014
1015static void
1016show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
1017{
1018	static int twidth = 0;
1019	int l;
1020	ipfw_insn *cmd;
1021	char *comment = NULL;	/* ptr to comment if we have one */
1022	int proto = 0;		/* default */
1023	int flags = 0;	/* prerequisites */
1024	ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */
1025	ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */
1026	int or_block = 0;	/* we are in an or block */
1027	uint32_t set_disable;
1028
1029	bcopy(&rule->next_rule, &set_disable, sizeof(set_disable));
1030
1031	if (set_disable & (1 << rule->set)) { /* disabled */
1032		if (!show_sets)
1033			return;
1034		else
1035			printf("# DISABLED ");
1036	}
1037	printf("%05u ", rule->rulenum);
1038
1039	if (pcwidth>0 || bcwidth>0)
1040		printf("%*llu %*llu ", pcwidth, align_uint64(&rule->pcnt),
1041		    bcwidth, align_uint64(&rule->bcnt));
1042
1043	if (do_time == 2)
1044		printf("%10u ", rule->timestamp);
1045	else if (do_time == 1) {
1046		char timestr[30];
1047		time_t t = (time_t)0;
1048
1049		if (twidth == 0) {
1050			strcpy(timestr, ctime(&t));
1051			*strchr(timestr, '\n') = '\0';
1052			twidth = strlen(timestr);
1053		}
1054		if (rule->timestamp) {
1055#if _FreeBSD_version < 500000 /* XXX check */
1056#define	_long_to_time(x)	(time_t)(x)
1057#endif
1058			t = _long_to_time(rule->timestamp);
1059
1060			strcpy(timestr, ctime(&t));
1061			*strchr(timestr, '\n') = '\0';
1062			printf("%s ", timestr);
1063		} else {
1064			printf("%*s", twidth, " ");
1065		}
1066	}
1067
1068	if (show_sets)
1069		printf("set %d ", rule->set);
1070
1071	/*
1072	 * print the optional "match probability"
1073	 */
1074	if (rule->cmd_len > 0) {
1075		cmd = rule->cmd ;
1076		if (cmd->opcode == O_PROB) {
1077			ipfw_insn_u32 *p = (ipfw_insn_u32 *)cmd;
1078			double d = 1.0 * p->d[0];
1079
1080			d = (d / 0x7fffffff);
1081			printf("prob %f ", d);
1082		}
1083	}
1084
1085	/*
1086	 * first print actions
1087	 */
1088        for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule);
1089			l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
1090		switch(cmd->opcode) {
1091		case O_CHECK_STATE:
1092			printf("check-state");
1093			flags = HAVE_IP; /* avoid printing anything else */
1094			break;
1095
1096		case O_ACCEPT:
1097			printf("allow");
1098			break;
1099
1100		case O_COUNT:
1101			printf("count");
1102			break;
1103
1104		case O_DENY:
1105			printf("deny");
1106			break;
1107
1108		case O_REJECT:
1109			if (cmd->arg1 == ICMP_REJECT_RST)
1110				printf("reset");
1111			else if (cmd->arg1 == ICMP_UNREACH_HOST)
1112				printf("reject");
1113			else
1114				print_reject_code(cmd->arg1);
1115			break;
1116
1117		case O_SKIPTO:
1118			printf("skipto %u", cmd->arg1);
1119			break;
1120
1121		case O_PIPE:
1122			printf("pipe %u", cmd->arg1);
1123			break;
1124
1125		case O_QUEUE:
1126			printf("queue %u", cmd->arg1);
1127			break;
1128
1129		case O_DIVERT:
1130			printf("divert %u", cmd->arg1);
1131			break;
1132
1133		case O_TEE:
1134			printf("tee %u", cmd->arg1);
1135			break;
1136
1137		case O_FORWARD_IP:
1138		    {
1139			ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
1140
1141			printf("fwd %s", inet_ntoa(s->sa.sin_addr));
1142			if (s->sa.sin_port)
1143				printf(",%d", s->sa.sin_port);
1144		    }
1145			break;
1146
1147		case O_LOG: /* O_LOG is printed last */
1148			logptr = (ipfw_insn_log *)cmd;
1149			break;
1150
1151		case O_ALTQ: /* O_ALTQ is printed after O_LOG */
1152			altqptr = (ipfw_insn_altq *)cmd;
1153			break;
1154
1155		default:
1156			printf("** unrecognized action %d len %d ",
1157				cmd->opcode, cmd->len);
1158		}
1159	}
1160	if (logptr) {
1161		if (logptr->max_log > 0)
1162			printf(" log logamount %d", logptr->max_log);
1163		else
1164			printf(" log");
1165	}
1166	if (altqptr) {
1167		const char *qname;
1168
1169		qname = altq_qid_to_name(altqptr->qid);
1170		if (qname == NULL)
1171			printf(" altq ?<%u>", altqptr->qid);
1172		else
1173			printf(" altq %s", qname);
1174	}
1175
1176	/*
1177	 * then print the body.
1178	 */
1179	if (rule->_pad & 1) {	/* empty rules before options */
1180		if (!do_compact)
1181			printf(" ip from any to any");
1182		flags |= HAVE_IP | HAVE_OPTIONS;
1183	}
1184
1185	if (comment_only)
1186		comment = "...";
1187
1188        for (l = rule->act_ofs, cmd = rule->cmd ;
1189			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
1190		/* useful alias */
1191		ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
1192
1193		if (comment_only) {
1194			if (cmd->opcode != O_NOP)
1195				continue;
1196			printf(" // %s\n", (char *)(cmd + 1));
1197			return;
1198		}
1199
1200		show_prerequisites(&flags, 0, cmd->opcode);
1201
1202		switch(cmd->opcode) {
1203		case O_PROB:
1204			break;	/* done already */
1205
1206		case O_PROBE_STATE:
1207			break; /* no need to print anything here */
1208
1209		case O_MACADDR2: {
1210			ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
1211
1212			if ((cmd->len & F_OR) && !or_block)
1213				printf(" {");
1214			if (cmd->len & F_NOT)
1215				printf(" not");
1216			printf(" MAC");
1217			flags |= HAVE_MAC;
1218			print_mac(m->addr, m->mask);
1219			print_mac(m->addr + 6, m->mask + 6);
1220			}
1221			break;
1222
1223		case O_MAC_TYPE:
1224			if ((cmd->len & F_OR) && !or_block)
1225				printf(" {");
1226			print_newports((ipfw_insn_u16 *)cmd, IPPROTO_ETHERTYPE,
1227				(flags & HAVE_OPTIONS) ? cmd->opcode : 0);
1228			flags |= HAVE_MAC | HAVE_MACTYPE | HAVE_OPTIONS;
1229			break;
1230
1231		case O_IP_SRC:
1232		case O_IP_SRC_LOOKUP:
1233		case O_IP_SRC_MASK:
1234		case O_IP_SRC_ME:
1235		case O_IP_SRC_SET:
1236			show_prerequisites(&flags, HAVE_PROTO, 0);
1237			if (!(flags & HAVE_SRCIP))
1238				printf(" from");
1239			if ((cmd->len & F_OR) && !or_block)
1240				printf(" {");
1241			print_ip((ipfw_insn_ip *)cmd,
1242				(flags & HAVE_OPTIONS) ? " src-ip" : "");
1243			flags |= HAVE_SRCIP;
1244			break;
1245
1246		case O_IP_DST:
1247		case O_IP_DST_LOOKUP:
1248		case O_IP_DST_MASK:
1249		case O_IP_DST_ME:
1250		case O_IP_DST_SET:
1251			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1252			if (!(flags & HAVE_DSTIP))
1253				printf(" to");
1254			if ((cmd->len & F_OR) && !or_block)
1255				printf(" {");
1256			print_ip((ipfw_insn_ip *)cmd,
1257				(flags & HAVE_OPTIONS) ? " dst-ip" : "");
1258			flags |= HAVE_DSTIP;
1259			break;
1260
1261		case O_IP_DSTPORT:
1262			show_prerequisites(&flags, HAVE_IP, 0);
1263		case O_IP_SRCPORT:
1264			show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1265			if ((cmd->len & F_OR) && !or_block)
1266				printf(" {");
1267			print_newports((ipfw_insn_u16 *)cmd, proto,
1268				(flags & HAVE_OPTIONS) ? cmd->opcode : 0);
1269			break;
1270
1271		case O_PROTO: {
1272			struct protoent *pe;
1273
1274			if ((cmd->len & F_OR) && !or_block)
1275				printf(" {");
1276			if (cmd->len & F_NOT)
1277				printf(" not");
1278			proto = cmd->arg1;
1279			pe = getprotobynumber(cmd->arg1);
1280			if (flags & HAVE_OPTIONS)
1281				printf(" proto");
1282			if (pe)
1283				printf(" %s", pe->p_name);
1284			else
1285				printf(" %u", cmd->arg1);
1286			}
1287			flags |= HAVE_PROTO;
1288			break;
1289
1290		default: /*options ... */
1291			show_prerequisites(&flags, HAVE_IP | HAVE_OPTIONS, 0);
1292			if ((cmd->len & F_OR) && !or_block)
1293				printf(" {");
1294			if (cmd->len & F_NOT && cmd->opcode != O_IN)
1295				printf(" not");
1296			switch(cmd->opcode) {
1297			case O_FRAG:
1298				printf(" frag");
1299				break;
1300
1301			case O_IN:
1302				printf(cmd->len & F_NOT ? " out" : " in");
1303				break;
1304
1305			case O_DIVERTED:
1306				switch (cmd->arg1) {
1307				case 3:
1308					printf(" diverted");
1309					break;
1310				case 1:
1311					printf(" diverted-loopback");
1312					break;
1313				case 2:
1314					printf(" diverted-output");
1315					break;
1316				default:
1317					printf(" diverted-?<%u>", cmd->arg1);
1318					break;
1319				}
1320				break;
1321
1322			case O_LAYER2:
1323				printf(" layer2");
1324				break;
1325			case O_XMIT:
1326			case O_RECV:
1327			case O_VIA: {
1328				char const *s;
1329				ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd;
1330
1331				if (cmd->opcode == O_XMIT)
1332					s = "xmit";
1333				else if (cmd->opcode == O_RECV)
1334					s = "recv";
1335				else /* if (cmd->opcode == O_VIA) */
1336					s = "via";
1337				if (cmdif->name[0] == '\0')
1338					printf(" %s %s", s,
1339					    inet_ntoa(cmdif->p.ip));
1340				printf(" %s %s", s, cmdif->name);
1341				}
1342				break;
1343
1344			case O_IPID:
1345				if (F_LEN(cmd) == 1)
1346				    printf(" ipid %u", cmd->arg1 );
1347				else
1348				    print_newports((ipfw_insn_u16 *)cmd, 0,
1349					O_IPID);
1350				break;
1351
1352			case O_IPTTL:
1353				if (F_LEN(cmd) == 1)
1354				    printf(" ipttl %u", cmd->arg1 );
1355				else
1356				    print_newports((ipfw_insn_u16 *)cmd, 0,
1357					O_IPTTL);
1358				break;
1359
1360			case O_IPVER:
1361				printf(" ipver %u", cmd->arg1 );
1362				break;
1363
1364			case O_IPPRECEDENCE:
1365				printf(" ipprecedence %u", (cmd->arg1) >> 5 );
1366				break;
1367
1368			case O_IPLEN:
1369				if (F_LEN(cmd) == 1)
1370				    printf(" iplen %u", cmd->arg1 );
1371				else
1372				    print_newports((ipfw_insn_u16 *)cmd, 0,
1373					O_IPLEN);
1374				break;
1375
1376			case O_IPOPT:
1377				print_flags("ipoptions", cmd, f_ipopts);
1378				break;
1379
1380			case O_IPTOS:
1381				print_flags("iptos", cmd, f_iptos);
1382				break;
1383
1384			case O_ICMPTYPE:
1385				print_icmptypes((ipfw_insn_u32 *)cmd);
1386				break;
1387
1388			case O_ESTAB:
1389				printf(" established");
1390				break;
1391
1392			case O_TCPFLAGS:
1393				print_flags("tcpflags", cmd, f_tcpflags);
1394				break;
1395
1396			case O_TCPOPTS:
1397				print_flags("tcpoptions", cmd, f_tcpopts);
1398				break;
1399
1400			case O_TCPWIN:
1401				printf(" tcpwin %d", ntohs(cmd->arg1));
1402				break;
1403
1404			case O_TCPACK:
1405				printf(" tcpack %d", ntohl(cmd32->d[0]));
1406				break;
1407
1408			case O_TCPSEQ:
1409				printf(" tcpseq %d", ntohl(cmd32->d[0]));
1410				break;
1411
1412			case O_UID:
1413			    {
1414				struct passwd *pwd = getpwuid(cmd32->d[0]);
1415
1416				if (pwd)
1417					printf(" uid %s", pwd->pw_name);
1418				else
1419					printf(" uid %u", cmd32->d[0]);
1420			    }
1421				break;
1422
1423			case O_GID:
1424			    {
1425				struct group *grp = getgrgid(cmd32->d[0]);
1426
1427				if (grp)
1428					printf(" gid %s", grp->gr_name);
1429				else
1430					printf(" gid %u", cmd32->d[0]);
1431			    }
1432				break;
1433
1434			case O_JAIL:
1435				printf(" jail %d", cmd32->d[0]);
1436				break;
1437
1438			case O_VERREVPATH:
1439				printf(" verrevpath");
1440				break;
1441
1442			case O_VERSRCREACH:
1443				printf(" versrcreach");
1444				break;
1445
1446			case O_ANTISPOOF:
1447				printf(" antispoof");
1448				break;
1449
1450			case O_IPSEC:
1451				printf(" ipsec");
1452				break;
1453
1454			case O_NOP:
1455				comment = (char *)(cmd + 1);
1456				break;
1457
1458			case O_KEEP_STATE:
1459				printf(" keep-state");
1460				break;
1461
1462			case O_LIMIT:
1463			    {
1464				struct _s_x *p = limit_masks;
1465				ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
1466				uint8_t x = c->limit_mask;
1467				char const *comma = " ";
1468
1469				printf(" limit");
1470				for (; p->x != 0 ; p++)
1471					if ((x & p->x) == p->x) {
1472						x &= ~p->x;
1473						printf("%s%s", comma, p->s);
1474						comma = ",";
1475					}
1476				printf(" %d", c->conn_limit);
1477			    }
1478				break;
1479
1480			default:
1481				printf(" [opcode %d len %d]",
1482				    cmd->opcode, cmd->len);
1483			}
1484		}
1485		if (cmd->len & F_OR) {
1486			printf(" or");
1487			or_block = 1;
1488		} else if (or_block) {
1489			printf(" }");
1490			or_block = 0;
1491		}
1492	}
1493	show_prerequisites(&flags, HAVE_IP, 0);
1494	if (comment)
1495		printf(" // %s", comment);
1496	printf("\n");
1497}
1498
1499static void
1500show_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth)
1501{
1502	struct protoent *pe;
1503	struct in_addr a;
1504	uint16_t rulenum;
1505
1506	if (!do_expired) {
1507		if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT))
1508			return;
1509	}
1510	bcopy(&d->rule, &rulenum, sizeof(rulenum));
1511	printf("%05d", rulenum);
1512	if (pcwidth>0 || bcwidth>0)
1513	    printf(" %*llu %*llu (%ds)", pcwidth,
1514		align_uint64(&d->pcnt), bcwidth,
1515		align_uint64(&d->bcnt), d->expire);
1516	switch (d->dyn_type) {
1517	case O_LIMIT_PARENT:
1518		printf(" PARENT %d", d->count);
1519		break;
1520	case O_LIMIT:
1521		printf(" LIMIT");
1522		break;
1523	case O_KEEP_STATE: /* bidir, no mask */
1524		printf(" STATE");
1525		break;
1526	}
1527
1528	if ((pe = getprotobynumber(d->id.proto)) != NULL)
1529		printf(" %s", pe->p_name);
1530	else
1531		printf(" proto %u", d->id.proto);
1532
1533	a.s_addr = htonl(d->id.src_ip);
1534	printf(" %s %d", inet_ntoa(a), d->id.src_port);
1535
1536	a.s_addr = htonl(d->id.dst_ip);
1537	printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port);
1538	printf("\n");
1539}
1540
1541static int
1542sort_q(const void *pa, const void *pb)
1543{
1544	int rev = (do_sort < 0);
1545	int field = rev ? -do_sort : do_sort;
1546	long long res = 0;
1547	const struct dn_flow_queue *a = pa;
1548	const struct dn_flow_queue *b = pb;
1549
1550	switch (field) {
1551	case 1: /* pkts */
1552		res = a->len - b->len;
1553		break;
1554	case 2: /* bytes */
1555		res = a->len_bytes - b->len_bytes;
1556		break;
1557
1558	case 3: /* tot pkts */
1559		res = a->tot_pkts - b->tot_pkts;
1560		break;
1561
1562	case 4: /* tot bytes */
1563		res = a->tot_bytes - b->tot_bytes;
1564		break;
1565	}
1566	if (res < 0)
1567		res = -1;
1568	if (res > 0)
1569		res = 1;
1570	return (int)(rev ? res : -res);
1571}
1572
1573static void
1574list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
1575{
1576	int l;
1577
1578	printf("    mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
1579	    fs->flow_mask.proto,
1580	    fs->flow_mask.src_ip, fs->flow_mask.src_port,
1581	    fs->flow_mask.dst_ip, fs->flow_mask.dst_port);
1582	if (fs->rq_elements == 0)
1583		return;
1584
1585	printf("BKT Prot ___Source IP/port____ "
1586	    "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n");
1587	if (do_sort != 0)
1588		heapsort(q, fs->rq_elements, sizeof *q, sort_q);
1589	for (l = 0; l < fs->rq_elements; l++) {
1590		struct in_addr ina;
1591		struct protoent *pe;
1592
1593		ina.s_addr = htonl(q[l].id.src_ip);
1594		printf("%3d ", q[l].hash_slot);
1595		pe = getprotobynumber(q[l].id.proto);
1596		if (pe)
1597			printf("%-4s ", pe->p_name);
1598		else
1599			printf("%4u ", q[l].id.proto);
1600		printf("%15s/%-5d ",
1601		    inet_ntoa(ina), q[l].id.src_port);
1602		ina.s_addr = htonl(q[l].id.dst_ip);
1603		printf("%15s/%-5d ",
1604		    inet_ntoa(ina), q[l].id.dst_port);
1605		printf("%4qu %8qu %2u %4u %3u\n",
1606		    q[l].tot_pkts, q[l].tot_bytes,
1607		    q[l].len, q[l].len_bytes, q[l].drops);
1608		if (verbose)
1609			printf("   S %20qd  F %20qd\n",
1610			    q[l].S, q[l].F);
1611	}
1612}
1613
1614static void
1615print_flowset_parms(struct dn_flow_set *fs, char *prefix)
1616{
1617	int l;
1618	char qs[30];
1619	char plr[30];
1620	char red[90];	/* Display RED parameters */
1621
1622	l = fs->qsize;
1623	if (fs->flags_fs & DN_QSIZE_IS_BYTES) {
1624		if (l >= 8192)
1625			sprintf(qs, "%d KB", l / 1024);
1626		else
1627			sprintf(qs, "%d B", l);
1628	} else
1629		sprintf(qs, "%3d sl.", l);
1630	if (fs->plr)
1631		sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
1632	else
1633		plr[0] = '\0';
1634	if (fs->flags_fs & DN_IS_RED)	/* RED parameters */
1635		sprintf(red,
1636		    "\n\t  %cRED w_q %f min_th %d max_th %d max_p %f",
1637		    (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ',
1638		    1.0 * fs->w_q / (double)(1 << SCALE_RED),
1639		    SCALE_VAL(fs->min_th),
1640		    SCALE_VAL(fs->max_th),
1641		    1.0 * fs->max_p / (double)(1 << SCALE_RED));
1642	else
1643		sprintf(red, "droptail");
1644
1645	printf("%s %s%s %d queues (%d buckets) %s\n",
1646	    prefix, qs, plr, fs->rq_elements, fs->rq_size, red);
1647}
1648
1649static void
1650list_pipes(void *data, uint nbytes, int ac, char *av[])
1651{
1652	int rulenum;
1653	void *next = data;
1654	struct dn_pipe *p = (struct dn_pipe *) data;
1655	struct dn_flow_set *fs;
1656	struct dn_flow_queue *q;
1657	int l;
1658
1659	if (ac > 0)
1660		rulenum = strtoul(*av++, NULL, 10);
1661	else
1662		rulenum = 0;
1663	for (; nbytes >= sizeof *p; p = (struct dn_pipe *)next) {
1664		double b = p->bandwidth;
1665		char buf[30];
1666		char prefix[80];
1667
1668		if (p->next != (struct dn_pipe *)DN_IS_PIPE)
1669			break;	/* done with pipes, now queues */
1670
1671		/*
1672		 * compute length, as pipe have variable size
1673		 */
1674		l = sizeof(*p) + p->fs.rq_elements * sizeof(*q);
1675		next = (char *)p + l;
1676		nbytes -= l;
1677
1678		if ((rulenum != 0 && rulenum != p->pipe_nr) || do_pipe == 2)
1679			continue;
1680
1681		/*
1682		 * Print rate (or clocking interface)
1683		 */
1684		if (p->if_name[0] != '\0')
1685			sprintf(buf, "%s", p->if_name);
1686		else if (b == 0)
1687			sprintf(buf, "unlimited");
1688		else if (b >= 1000000)
1689			sprintf(buf, "%7.3f Mbit/s", b/1000000);
1690		else if (b >= 1000)
1691			sprintf(buf, "%7.3f Kbit/s", b/1000);
1692		else
1693			sprintf(buf, "%7.3f bit/s ", b);
1694
1695		sprintf(prefix, "%05d: %s %4d ms ",
1696		    p->pipe_nr, buf, p->delay);
1697		print_flowset_parms(&(p->fs), prefix);
1698		if (verbose)
1699			printf("   V %20qd\n", p->V >> MY_M);
1700
1701		q = (struct dn_flow_queue *)(p+1);
1702		list_queues(&(p->fs), q);
1703	}
1704	for (fs = next; nbytes >= sizeof *fs; fs = next) {
1705		char prefix[80];
1706
1707		if (fs->next != (struct dn_flow_set *)DN_IS_QUEUE)
1708			break;
1709		l = sizeof(*fs) + fs->rq_elements * sizeof(*q);
1710		next = (char *)fs + l;
1711		nbytes -= l;
1712
1713		if (rulenum != 0 && ((rulenum != fs->fs_nr && do_pipe == 2) ||
1714		    (rulenum != fs->parent_nr && do_pipe == 1))) {
1715			continue;
1716		}
1717
1718		q = (struct dn_flow_queue *)(fs+1);
1719		sprintf(prefix, "q%05d: weight %d pipe %d ",
1720		    fs->fs_nr, fs->weight, fs->parent_nr);
1721		print_flowset_parms(fs, prefix);
1722		list_queues(fs, q);
1723	}
1724}
1725
1726/*
1727 * This one handles all set-related commands
1728 * 	ipfw set { show | enable | disable }
1729 * 	ipfw set swap X Y
1730 * 	ipfw set move X to Y
1731 * 	ipfw set move rule X to Y
1732 */
1733static void
1734sets_handler(int ac, char *av[])
1735{
1736	uint32_t set_disable, masks[2];
1737	int i, nbytes;
1738	uint16_t rulenum;
1739	uint8_t cmd, new_set;
1740
1741	ac--;
1742	av++;
1743
1744	if (!ac)
1745		errx(EX_USAGE, "set needs command");
1746	if (!strncmp(*av, "show", strlen(*av)) ) {
1747		void *data;
1748		char const *msg;
1749
1750		nbytes = sizeof(struct ip_fw);
1751		if ((data = calloc(1, nbytes)) == NULL)
1752			err(EX_OSERR, "calloc");
1753		if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0)
1754			err(EX_OSERR, "getsockopt(IP_FW_GET)");
1755		bcopy(&((struct ip_fw *)data)->next_rule,
1756			&set_disable, sizeof(set_disable));
1757
1758		for (i = 0, msg = "disable" ; i < RESVD_SET; i++)
1759			if ((set_disable & (1<<i))) {
1760				printf("%s %d", msg, i);
1761				msg = "";
1762			}
1763		msg = (set_disable) ? " enable" : "enable";
1764		for (i = 0; i < RESVD_SET; i++)
1765			if (!(set_disable & (1<<i))) {
1766				printf("%s %d", msg, i);
1767				msg = "";
1768			}
1769		printf("\n");
1770	} else if (!strncmp(*av, "swap", strlen(*av))) {
1771		ac--; av++;
1772		if (ac != 2)
1773			errx(EX_USAGE, "set swap needs 2 set numbers\n");
1774		rulenum = atoi(av[0]);
1775		new_set = atoi(av[1]);
1776		if (!isdigit(*(av[0])) || rulenum > RESVD_SET)
1777			errx(EX_DATAERR, "invalid set number %s\n", av[0]);
1778		if (!isdigit(*(av[1])) || new_set > RESVD_SET)
1779			errx(EX_DATAERR, "invalid set number %s\n", av[1]);
1780		masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
1781		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
1782	} else if (!strncmp(*av, "move", strlen(*av))) {
1783		ac--; av++;
1784		if (ac && !strncmp(*av, "rule", strlen(*av))) {
1785			cmd = 2;
1786			ac--; av++;
1787		} else
1788			cmd = 3;
1789		if (ac != 3 || strncmp(av[1], "to", strlen(*av)))
1790			errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
1791		rulenum = atoi(av[0]);
1792		new_set = atoi(av[2]);
1793		if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) ||
1794			(cmd == 2 && rulenum == 65535) )
1795			errx(EX_DATAERR, "invalid source number %s\n", av[0]);
1796		if (!isdigit(*(av[2])) || new_set > RESVD_SET)
1797			errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
1798		masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
1799		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
1800	} else if (!strncmp(*av, "disable", strlen(*av)) ||
1801		   !strncmp(*av, "enable",  strlen(*av)) ) {
1802		int which = !strncmp(*av, "enable",  strlen(*av)) ? 1 : 0;
1803
1804		ac--; av++;
1805		masks[0] = masks[1] = 0;
1806
1807		while (ac) {
1808			if (isdigit(**av)) {
1809				i = atoi(*av);
1810				if (i < 0 || i > RESVD_SET)
1811					errx(EX_DATAERR,
1812					    "invalid set number %d\n", i);
1813				masks[which] |= (1<<i);
1814			} else if (!strncmp(*av, "disable", strlen(*av)))
1815				which = 0;
1816			else if (!strncmp(*av, "enable", strlen(*av)))
1817				which = 1;
1818			else
1819				errx(EX_DATAERR,
1820					"invalid set command %s\n", *av);
1821			av++; ac--;
1822		}
1823		if ( (masks[0] & masks[1]) != 0 )
1824			errx(EX_DATAERR,
1825			    "cannot enable and disable the same set\n");
1826
1827		i = do_cmd(IP_FW_DEL, masks, sizeof(masks));
1828		if (i)
1829			warn("set enable/disable: setsockopt(IP_FW_DEL)");
1830	} else
1831		errx(EX_USAGE, "invalid set command %s\n", *av);
1832}
1833
1834static void
1835sysctl_handler(int ac, char *av[], int which)
1836{
1837	ac--;
1838	av++;
1839
1840	if (ac == 0) {
1841		warnx("missing keyword to enable/disable\n");
1842	} else if (strncmp(*av, "firewall", strlen(*av)) == 0) {
1843		sysctlbyname("net.inet.ip.fw.enable", NULL, 0,
1844		    &which, sizeof(which));
1845	} else if (strncmp(*av, "one_pass", strlen(*av)) == 0) {
1846		sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0,
1847		    &which, sizeof(which));
1848	} else if (strncmp(*av, "debug", strlen(*av)) == 0) {
1849		sysctlbyname("net.inet.ip.fw.debug", NULL, 0,
1850		    &which, sizeof(which));
1851	} else if (strncmp(*av, "verbose", strlen(*av)) == 0) {
1852		sysctlbyname("net.inet.ip.fw.verbose", NULL, 0,
1853		    &which, sizeof(which));
1854	} else if (strncmp(*av, "dyn_keepalive", strlen(*av)) == 0) {
1855		sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0,
1856		    &which, sizeof(which));
1857	} else if (strncmp(*av, "altq", strlen(*av)) == 0) {
1858		altq_set_enabled(which);
1859	} else {
1860		warnx("unrecognize enable/disable keyword: %s\n", *av);
1861	}
1862}
1863
1864static void
1865list(int ac, char *av[], int show_counters)
1866{
1867	struct ip_fw *r;
1868	ipfw_dyn_rule *dynrules, *d;
1869
1870#define NEXT(r)	((struct ip_fw *)((char *)r + RULESIZE(r)))
1871	char *lim;
1872	void *data = NULL;
1873	int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width;
1874	int exitval = EX_OK;
1875	int lac;
1876	char **lav;
1877	u_long rnum, last;
1878	char *endptr;
1879	int seen = 0;
1880
1881	const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
1882	int nalloc = 1024;	/* start somewhere... */
1883
1884	last = 0;
1885
1886	if (test_only) {
1887		fprintf(stderr, "Testing only, list disabled\n");
1888		return;
1889	}
1890
1891	ac--;
1892	av++;
1893
1894	/* get rules or pipes from kernel, resizing array as necessary */
1895	nbytes = nalloc;
1896
1897	while (nbytes >= nalloc) {
1898		nalloc = nalloc * 2 + 200;
1899		nbytes = nalloc;
1900		if ((data = realloc(data, nbytes)) == NULL)
1901			err(EX_OSERR, "realloc");
1902		if (do_cmd(ocmd, data, (uintptr_t)&nbytes) < 0)
1903			err(EX_OSERR, "getsockopt(IP_%s_GET)",
1904				do_pipe ? "DUMMYNET" : "FW");
1905	}
1906
1907	if (do_pipe) {
1908		list_pipes(data, nbytes, ac, av);
1909		goto done;
1910	}
1911
1912	/*
1913	 * Count static rules. They have variable size so we
1914	 * need to scan the list to count them.
1915	 */
1916	for (nstat = 1, r = data, lim = (char *)data + nbytes;
1917		    r->rulenum < 65535 && (char *)r < lim;
1918		    ++nstat, r = NEXT(r) )
1919		; /* nothing */
1920
1921	/*
1922	 * Count dynamic rules. This is easier as they have
1923	 * fixed size.
1924	 */
1925	r = NEXT(r);
1926	dynrules = (ipfw_dyn_rule *)r ;
1927	n = (char *)r - (char *)data;
1928	ndyn = (nbytes - n) / sizeof *dynrules;
1929
1930	/* if showing stats, figure out column widths ahead of time */
1931	bcwidth = pcwidth = 0;
1932	if (show_counters) {
1933		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
1934			/* packet counter */
1935			width = snprintf(NULL, 0, "%llu",
1936			    align_uint64(&r->pcnt));
1937			if (width > pcwidth)
1938				pcwidth = width;
1939
1940			/* byte counter */
1941			width = snprintf(NULL, 0, "%llu",
1942			    align_uint64(&r->bcnt));
1943			if (width > bcwidth)
1944				bcwidth = width;
1945		}
1946	}
1947	if (do_dynamic && ndyn) {
1948		for (n = 0, d = dynrules; n < ndyn; n++, d++) {
1949			width = snprintf(NULL, 0, "%llu",
1950			    align_uint64(&d->pcnt));
1951			if (width > pcwidth)
1952				pcwidth = width;
1953
1954			width = snprintf(NULL, 0, "%llu",
1955			    align_uint64(&d->bcnt));
1956			if (width > bcwidth)
1957				bcwidth = width;
1958		}
1959	}
1960	/* if no rule numbers were specified, list all rules */
1961	if (ac == 0) {
1962		for (n = 0, r = data; n < nstat; n++, r = NEXT(r) )
1963			show_ipfw(r, pcwidth, bcwidth);
1964
1965		if (do_dynamic && ndyn) {
1966			printf("## Dynamic rules (%d):\n", ndyn);
1967			for (n = 0, d = dynrules; n < ndyn; n++, d++)
1968				show_dyn_ipfw(d, pcwidth, bcwidth);
1969		}
1970		goto done;
1971	}
1972
1973	/* display specific rules requested on command line */
1974
1975	for (lac = ac, lav = av; lac != 0; lac--) {
1976		/* convert command line rule # */
1977		last = rnum = strtoul(*lav++, &endptr, 10);
1978		if (*endptr == '-')
1979			last = strtoul(endptr+1, &endptr, 10);
1980		if (*endptr) {
1981			exitval = EX_USAGE;
1982			warnx("invalid rule number: %s", *(lav - 1));
1983			continue;
1984		}
1985		for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) {
1986			if (r->rulenum > last)
1987				break;
1988			if (r->rulenum >= rnum && r->rulenum <= last) {
1989				show_ipfw(r, pcwidth, bcwidth);
1990				seen = 1;
1991			}
1992		}
1993		if (!seen) {
1994			/* give precedence to other error(s) */
1995			if (exitval == EX_OK)
1996				exitval = EX_UNAVAILABLE;
1997			warnx("rule %lu does not exist", rnum);
1998		}
1999	}
2000
2001	if (do_dynamic && ndyn) {
2002		printf("## Dynamic rules:\n");
2003		for (lac = ac, lav = av; lac != 0; lac--) {
2004			rnum = strtoul(*lav++, &endptr, 10);
2005			if (*endptr == '-')
2006				last = strtoul(endptr+1, &endptr, 10);
2007			if (*endptr)
2008				/* already warned */
2009				continue;
2010			for (n = 0, d = dynrules; n < ndyn; n++, d++) {
2011				uint16_t rulenum;
2012
2013				bcopy(&d->rule, &rulenum, sizeof(rulenum));
2014				if (rulenum > rnum)
2015					break;
2016				if (r->rulenum >= rnum && r->rulenum <= last)
2017					show_dyn_ipfw(d, pcwidth, bcwidth);
2018			}
2019		}
2020	}
2021
2022	ac = 0;
2023
2024done:
2025	free(data);
2026
2027	if (exitval != EX_OK)
2028		exit(exitval);
2029#undef NEXT
2030}
2031
2032static void
2033show_usage(void)
2034{
2035	fprintf(stderr, "usage: ipfw [options]\n"
2036"do \"ipfw -h\" or see ipfw manpage for details\n"
2037);
2038	exit(EX_USAGE);
2039}
2040
2041static void
2042help(void)
2043{
2044	fprintf(stderr,
2045"ipfw syntax summary (but please do read the ipfw(8) manpage):\n"
2046"ipfw [-abcdefhnNqStTv] <command> where <command> is one of:\n"
2047"add [num] [set N] [prob x] RULE-BODY\n"
2048"{pipe|queue} N config PIPE-BODY\n"
2049"[pipe|queue] {zero|delete|show} [N{,N}]\n"
2050"set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n"
2051"table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n"
2052"\n"
2053"RULE-BODY:	check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n"
2054"ACTION:	check-state | allow | count | deny | reject | skipto N |\n"
2055"		{divert|tee} PORT | forward ADDR | pipe N | queue N\n"
2056"PARAMS: 	[log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n"
2057"ADDR:		[ MAC dst src ether_type ] \n"
2058"		[ from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
2059"IPADDR:	[not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n"
2060"IPLIST:	{ ip | ip/bits | ip:mask }[,IPLIST]\n"
2061"OPTION_LIST:	OPTION [OPTION_LIST]\n"
2062"OPTION:	bridged | {dst-ip|src-ip} ADDR | {dst-port|src-port} LIST |\n"
2063"	estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n"
2064"	iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n"
2065"	ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n"
2066"	mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n"
2067"	setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n"
2068"	verrevpath | versrcreach | antispoof\n"
2069);
2070exit(0);
2071}
2072
2073
2074static int
2075lookup_host (char *host, struct in_addr *ipaddr)
2076{
2077	struct hostent *he;
2078
2079	if (!inet_aton(host, ipaddr)) {
2080		if ((he = gethostbyname(host)) == NULL)
2081			return(-1);
2082		*ipaddr = *(struct in_addr *)he->h_addr_list[0];
2083	}
2084	return(0);
2085}
2086
2087/*
2088 * fills the addr and mask fields in the instruction as appropriate from av.
2089 * Update length as appropriate.
2090 * The following formats are allowed:
2091 *	any	matches any IP. Actually returns an empty instruction.
2092 *	me	returns O_IP_*_ME
2093 *	1.2.3.4		single IP address
2094 *	1.2.3.4:5.6.7.8	address:mask
2095 *	1.2.3.4/24	address/mask
2096 *	1.2.3.4/26{1,6,5,4,23}	set of addresses in a subnet
2097 * We can have multiple comma-separated address/mask entries.
2098 */
2099static void
2100fill_ip(ipfw_insn_ip *cmd, char *av)
2101{
2102	int len = 0;
2103	uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
2104
2105	cmd->o.len &= ~F_LEN_MASK;	/* zero len */
2106
2107	if (!strncmp(av, "any", strlen(av)))
2108		return;
2109
2110	if (!strncmp(av, "me", strlen(av))) {
2111		cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2112		return;
2113	}
2114
2115	if (!strncmp(av, "table(", 6)) {
2116		char *p = strchr(av + 6, ',');
2117
2118		if (p)
2119			*p++ = '\0';
2120		cmd->o.opcode = O_IP_DST_LOOKUP;
2121		cmd->o.arg1 = strtoul(av + 6, NULL, 0);
2122		if (p) {
2123			cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2124			d[0] = strtoul(p, NULL, 0);
2125		} else
2126			cmd->o.len |= F_INSN_SIZE(ipfw_insn);
2127		return;
2128	}
2129
2130    while (av) {
2131	/*
2132	 * After the address we can have '/' or ':' indicating a mask,
2133	 * ',' indicating another address follows, '{' indicating a
2134	 * set of addresses of unspecified size.
2135	 */
2136	char *p = strpbrk(av, "/:,{");
2137	int masklen;
2138	char md;
2139
2140	if (p) {
2141		md = *p;
2142		*p++ = '\0';
2143	} else
2144		md = '\0';
2145
2146	if (lookup_host(av, (struct in_addr *)&d[0]) != 0)
2147		errx(EX_NOHOST, "hostname ``%s'' unknown", av);
2148	switch (md) {
2149	case ':':
2150		if (!inet_aton(p, (struct in_addr *)&d[1]))
2151			errx(EX_DATAERR, "bad netmask ``%s''", p);
2152		break;
2153	case '/':
2154		masklen = atoi(p);
2155		if (masklen == 0)
2156			d[1] = htonl(0);	/* mask */
2157		else if (masklen > 32)
2158			errx(EX_DATAERR, "bad width ``%s''", p);
2159		else
2160			d[1] = htonl(~0 << (32 - masklen));
2161		break;
2162	case '{':	/* no mask, assume /24 and put back the '{' */
2163		d[1] = htonl(~0 << (32 - 24));
2164		*(--p) = md;
2165		break;
2166
2167	case ',':	/* single address plus continuation */
2168		*(--p) = md;
2169		/* FALLTHROUGH */
2170	case 0:		/* initialization value */
2171	default:
2172		d[1] = htonl(~0);	/* force /32 */
2173		break;
2174	}
2175	d[0] &= d[1];		/* mask base address with mask */
2176	/* find next separator */
2177	if (p)
2178		p = strpbrk(p, ",{");
2179	if (p && *p == '{') {
2180		/*
2181		 * We have a set of addresses. They are stored as follows:
2182		 *   arg1	is the set size (powers of 2, 2..256)
2183		 *   addr	is the base address IN HOST FORMAT
2184		 *   mask..	is an array of arg1 bits (rounded up to
2185		 *		the next multiple of 32) with bits set
2186		 *		for each host in the map.
2187		 */
2188		uint32_t *map = (uint32_t *)&cmd->mask;
2189		int low, high;
2190		int i = contigmask((uint8_t *)&(d[1]), 32);
2191
2192		if (len > 0)
2193			errx(EX_DATAERR, "address set cannot be in a list");
2194		if (i < 24 || i > 31)
2195			errx(EX_DATAERR, "invalid set with mask %d\n", i);
2196		cmd->o.arg1 = 1<<(32-i);	/* map length		*/
2197		d[0] = ntohl(d[0]);		/* base addr in host format */
2198		cmd->o.opcode = O_IP_DST_SET;	/* default */
2199		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32;
2200		for (i = 0; i < (cmd->o.arg1+31)/32 ; i++)
2201			map[i] = 0;	/* clear map */
2202
2203		av = p + 1;
2204		low = d[0] & 0xff;
2205		high = low + cmd->o.arg1 - 1;
2206		/*
2207		 * Here, i stores the previous value when we specify a range
2208		 * of addresses within a mask, e.g. 45-63. i = -1 means we
2209		 * have no previous value.
2210		 */
2211		i = -1;	/* previous value in a range */
2212		while (isdigit(*av)) {
2213			char *s;
2214			int a = strtol(av, &s, 0);
2215
2216			if (s == av) { /* no parameter */
2217			    if (*av != '}')
2218				errx(EX_DATAERR, "set not closed\n");
2219			    if (i != -1)
2220				errx(EX_DATAERR, "incomplete range %d-", i);
2221			    break;
2222			}
2223			if (a < low || a > high)
2224			    errx(EX_DATAERR, "addr %d out of range [%d-%d]\n",
2225				a, low, high);
2226			a -= low;
2227			if (i == -1)	/* no previous in range */
2228			    i = a;
2229			else {		/* check that range is valid */
2230			    if (i > a)
2231				errx(EX_DATAERR, "invalid range %d-%d",
2232					i+low, a+low);
2233			    if (*s == '-')
2234				errx(EX_DATAERR, "double '-' in range");
2235			}
2236			for (; i <= a; i++)
2237			    map[i/32] |= 1<<(i & 31);
2238			i = -1;
2239			if (*s == '-')
2240			    i = a;
2241			else if (*s == '}')
2242			    break;
2243			av = s+1;
2244		}
2245		return;
2246	}
2247	av = p;
2248	if (av)			/* then *av must be a ',' */
2249		av++;
2250
2251	/* Check this entry */
2252	if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */
2253		/*
2254		 * 'any' turns the entire list into a NOP.
2255		 * 'not any' never matches, so it is removed from the
2256		 * list unless it is the only item, in which case we
2257		 * report an error.
2258		 */
2259		if (cmd->o.len & F_NOT) {	/* "not any" never matches */
2260			if (av == NULL && len == 0) /* only this entry */
2261				errx(EX_DATAERR, "not any never matches");
2262		}
2263		/* else do nothing and skip this entry */
2264		return;
2265	}
2266	/* A single IP can be stored in an optimized format */
2267	if (d[1] == IP_MASK_ALL && av == NULL && len == 0) {
2268		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
2269		return;
2270	}
2271	len += 2;	/* two words... */
2272	d += 2;
2273    } /* end while */
2274    cmd->o.len |= len+1;
2275}
2276
2277
2278/*
2279 * helper function to process a set of flags and set bits in the
2280 * appropriate masks.
2281 */
2282static void
2283fill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
2284	struct _s_x *flags, char *p)
2285{
2286	uint8_t set=0, clear=0;
2287
2288	while (p && *p) {
2289		char *q;	/* points to the separator */
2290		int val;
2291		uint8_t *which;	/* mask we are working on */
2292
2293		if (*p == '!') {
2294			p++;
2295			which = &clear;
2296		} else
2297			which = &set;
2298		q = strchr(p, ',');
2299		if (q)
2300			*q++ = '\0';
2301		val = match_token(flags, p);
2302		if (val <= 0)
2303			errx(EX_DATAERR, "invalid flag %s", p);
2304		*which |= (uint8_t)val;
2305		p = q;
2306	}
2307        cmd->opcode = opcode;
2308        cmd->len =  (cmd->len & (F_NOT | F_OR)) | 1;
2309        cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
2310}
2311
2312
2313static void
2314delete(int ac, char *av[])
2315{
2316	uint32_t rulenum;
2317	struct dn_pipe p;
2318	int i;
2319	int exitval = EX_OK;
2320	int do_set = 0;
2321
2322	memset(&p, 0, sizeof p);
2323
2324	av++; ac--;
2325	NEED1("missing rule specification");
2326	if (ac > 0 && !strncmp(*av, "set", strlen(*av))) {
2327		do_set = 1;	/* delete set */
2328		ac--; av++;
2329	}
2330
2331	/* Rule number */
2332	while (ac && isdigit(**av)) {
2333		i = atoi(*av); av++; ac--;
2334		if (do_pipe) {
2335			if (do_pipe == 1)
2336				p.pipe_nr = i;
2337			else
2338				p.fs.fs_nr = i;
2339			i = do_cmd(IP_DUMMYNET_DEL, &p, sizeof p);
2340			if (i) {
2341				exitval = 1;
2342				warn("rule %u: setsockopt(IP_DUMMYNET_DEL)",
2343				    do_pipe == 1 ? p.pipe_nr : p.fs.fs_nr);
2344			}
2345		} else {
2346			rulenum =  (i & 0xffff) | (do_set << 24);
2347			i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum);
2348			if (i) {
2349				exitval = EX_UNAVAILABLE;
2350				warn("rule %u: setsockopt(IP_FW_DEL)",
2351				    rulenum);
2352			}
2353		}
2354	}
2355	if (exitval != EX_OK)
2356		exit(exitval);
2357}
2358
2359
2360/*
2361 * fill the interface structure. We do not check the name as we can
2362 * create interfaces dynamically, so checking them at insert time
2363 * makes relatively little sense.
2364 * Interface names containing '*', '?', or '[' are assumed to be shell
2365 * patterns which match interfaces.
2366 */
2367static void
2368fill_iface(ipfw_insn_if *cmd, char *arg)
2369{
2370	cmd->name[0] = '\0';
2371	cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
2372
2373	/* Parse the interface or address */
2374	if (!strcmp(arg, "any"))
2375		cmd->o.len = 0;		/* effectively ignore this command */
2376	else if (!isdigit(*arg)) {
2377		strlcpy(cmd->name, arg, sizeof(cmd->name));
2378		cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
2379	} else if (!inet_aton(arg, &cmd->p.ip))
2380		errx(EX_DATAERR, "bad ip address ``%s''", arg);
2381}
2382
2383static void
2384config_pipe(int ac, char **av)
2385{
2386	struct dn_pipe p;
2387	int i;
2388	char *end;
2389	uint32_t a;
2390	void *par = NULL;
2391
2392	memset(&p, 0, sizeof p);
2393
2394	av++; ac--;
2395	/* Pipe number */
2396	if (ac && isdigit(**av)) {
2397		i = atoi(*av); av++; ac--;
2398		if (do_pipe == 1)
2399			p.pipe_nr = i;
2400		else
2401			p.fs.fs_nr = i;
2402	}
2403	while (ac > 0) {
2404		double d;
2405		int tok = match_token(dummynet_params, *av);
2406		ac--; av++;
2407
2408		switch(tok) {
2409		case TOK_NOERROR:
2410			p.fs.flags_fs |= DN_NOERROR;
2411			break;
2412
2413		case TOK_PLR:
2414			NEED1("plr needs argument 0..1\n");
2415			d = strtod(av[0], NULL);
2416			if (d > 1)
2417				d = 1;
2418			else if (d < 0)
2419				d = 0;
2420			p.fs.plr = (int)(d*0x7fffffff);
2421			ac--; av++;
2422			break;
2423
2424		case TOK_QUEUE:
2425			NEED1("queue needs queue size\n");
2426			end = NULL;
2427			p.fs.qsize = strtoul(av[0], &end, 0);
2428			if (*end == 'K' || *end == 'k') {
2429				p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
2430				p.fs.qsize *= 1024;
2431			} else if (*end == 'B' || !strncmp(end, "by", 2)) {
2432				p.fs.flags_fs |= DN_QSIZE_IS_BYTES;
2433			}
2434			ac--; av++;
2435			break;
2436
2437		case TOK_BUCKETS:
2438			NEED1("buckets needs argument\n");
2439			p.fs.rq_size = strtoul(av[0], NULL, 0);
2440			ac--; av++;
2441			break;
2442
2443		case TOK_MASK:
2444			NEED1("mask needs mask specifier\n");
2445			/*
2446			 * per-flow queue, mask is dst_ip, dst_port,
2447			 * src_ip, src_port, proto measured in bits
2448			 */
2449			par = NULL;
2450
2451			p.fs.flow_mask.dst_ip = 0;
2452			p.fs.flow_mask.src_ip = 0;
2453			p.fs.flow_mask.dst_port = 0;
2454			p.fs.flow_mask.src_port = 0;
2455			p.fs.flow_mask.proto = 0;
2456			end = NULL;
2457
2458			while (ac >= 1) {
2459			    uint32_t *p32 = NULL;
2460			    uint16_t *p16 = NULL;
2461
2462			    tok = match_token(dummynet_params, *av);
2463			    ac--; av++;
2464			    switch(tok) {
2465			    case TOK_ALL:
2466				    /*
2467				     * special case, all bits significant
2468				     */
2469				    p.fs.flow_mask.dst_ip = ~0;
2470				    p.fs.flow_mask.src_ip = ~0;
2471				    p.fs.flow_mask.dst_port = ~0;
2472				    p.fs.flow_mask.src_port = ~0;
2473				    p.fs.flow_mask.proto = ~0;
2474				    p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
2475				    goto end_mask;
2476
2477			    case TOK_DSTIP:
2478				    p32 = &p.fs.flow_mask.dst_ip;
2479				    break;
2480
2481			    case TOK_SRCIP:
2482				    p32 = &p.fs.flow_mask.src_ip;
2483				    break;
2484
2485			    case TOK_DSTPORT:
2486				    p16 = &p.fs.flow_mask.dst_port;
2487				    break;
2488
2489			    case TOK_SRCPORT:
2490				    p16 = &p.fs.flow_mask.src_port;
2491				    break;
2492
2493			    case TOK_PROTO:
2494				    break;
2495
2496			    default:
2497				    ac++; av--; /* backtrack */
2498				    goto end_mask;
2499			    }
2500			    if (ac < 1)
2501				    errx(EX_USAGE, "mask: value missing");
2502			    if (*av[0] == '/') {
2503				    a = strtoul(av[0]+1, &end, 0);
2504				    a = (a == 32) ? ~0 : (1 << a) - 1;
2505			    } else
2506				    a = strtoul(av[0], &end, 0);
2507			    if (p32 != NULL)
2508				    *p32 = a;
2509			    else if (p16 != NULL) {
2510				    if (a > 65535)
2511					    errx(EX_DATAERR,
2512						"mask: must be 16 bit");
2513				    *p16 = (uint16_t)a;
2514			    } else {
2515				    if (a > 255)
2516					    errx(EX_DATAERR,
2517						"mask: must be 8 bit");
2518				    p.fs.flow_mask.proto = (uint8_t)a;
2519			    }
2520			    if (a != 0)
2521				    p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
2522			    ac--; av++;
2523			} /* end while, config masks */
2524end_mask:
2525			break;
2526
2527		case TOK_RED:
2528		case TOK_GRED:
2529			NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
2530			p.fs.flags_fs |= DN_IS_RED;
2531			if (tok == TOK_GRED)
2532				p.fs.flags_fs |= DN_IS_GENTLE_RED;
2533			/*
2534			 * the format for parameters is w_q/min_th/max_th/max_p
2535			 */
2536			if ((end = strsep(&av[0], "/"))) {
2537			    double w_q = strtod(end, NULL);
2538			    if (w_q > 1 || w_q <= 0)
2539				errx(EX_DATAERR, "0 < w_q <= 1");
2540			    p.fs.w_q = (int) (w_q * (1 << SCALE_RED));
2541			}
2542			if ((end = strsep(&av[0], "/"))) {
2543			    p.fs.min_th = strtoul(end, &end, 0);
2544			    if (*end == 'K' || *end == 'k')
2545				p.fs.min_th *= 1024;
2546			}
2547			if ((end = strsep(&av[0], "/"))) {
2548			    p.fs.max_th = strtoul(end, &end, 0);
2549			    if (*end == 'K' || *end == 'k')
2550				p.fs.max_th *= 1024;
2551			}
2552			if ((end = strsep(&av[0], "/"))) {
2553			    double max_p = strtod(end, NULL);
2554			    if (max_p > 1 || max_p <= 0)
2555				errx(EX_DATAERR, "0 < max_p <= 1");
2556			    p.fs.max_p = (int)(max_p * (1 << SCALE_RED));
2557			}
2558			ac--; av++;
2559			break;
2560
2561		case TOK_DROPTAIL:
2562			p.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
2563			break;
2564
2565		case TOK_BW:
2566			NEED1("bw needs bandwidth or interface\n");
2567			if (do_pipe != 1)
2568			    errx(EX_DATAERR, "bandwidth only valid for pipes");
2569			/*
2570			 * set clocking interface or bandwidth value
2571			 */
2572			if (av[0][0] >= 'a' && av[0][0] <= 'z') {
2573			    int l = sizeof(p.if_name)-1;
2574			    /* interface name */
2575			    strncpy(p.if_name, av[0], l);
2576			    p.if_name[l] = '\0';
2577			    p.bandwidth = 0;
2578			} else {
2579			    p.if_name[0] = '\0';
2580			    p.bandwidth = strtoul(av[0], &end, 0);
2581			    if (*end == 'K' || *end == 'k') {
2582				end++;
2583				p.bandwidth *= 1000;
2584			    } else if (*end == 'M') {
2585				end++;
2586				p.bandwidth *= 1000000;
2587			    }
2588			    if (*end == 'B' || !strncmp(end, "by", 2))
2589				p.bandwidth *= 8;
2590			    if (p.bandwidth < 0)
2591				errx(EX_DATAERR, "bandwidth too large");
2592			}
2593			ac--; av++;
2594			break;
2595
2596		case TOK_DELAY:
2597			if (do_pipe != 1)
2598				errx(EX_DATAERR, "delay only valid for pipes");
2599			NEED1("delay needs argument 0..10000ms\n");
2600			p.delay = strtoul(av[0], NULL, 0);
2601			ac--; av++;
2602			break;
2603
2604		case TOK_WEIGHT:
2605			if (do_pipe == 1)
2606				errx(EX_DATAERR,"weight only valid for queues");
2607			NEED1("weight needs argument 0..100\n");
2608			p.fs.weight = strtoul(av[0], &end, 0);
2609			ac--; av++;
2610			break;
2611
2612		case TOK_PIPE:
2613			if (do_pipe == 1)
2614				errx(EX_DATAERR,"pipe only valid for queues");
2615			NEED1("pipe needs pipe_number\n");
2616			p.fs.parent_nr = strtoul(av[0], &end, 0);
2617			ac--; av++;
2618			break;
2619
2620		default:
2621			errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
2622		}
2623	}
2624	if (do_pipe == 1) {
2625		if (p.pipe_nr == 0)
2626			errx(EX_DATAERR, "pipe_nr must be > 0");
2627		if (p.delay > 10000)
2628			errx(EX_DATAERR, "delay must be < 10000");
2629	} else { /* do_pipe == 2, queue */
2630		if (p.fs.parent_nr == 0)
2631			errx(EX_DATAERR, "pipe must be > 0");
2632		if (p.fs.weight >100)
2633			errx(EX_DATAERR, "weight must be <= 100");
2634	}
2635	if (p.fs.flags_fs & DN_QSIZE_IS_BYTES) {
2636		if (p.fs.qsize > 1024*1024)
2637			errx(EX_DATAERR, "queue size must be < 1MB");
2638	} else {
2639		if (p.fs.qsize > 100)
2640			errx(EX_DATAERR, "2 <= queue size <= 100");
2641	}
2642	if (p.fs.flags_fs & DN_IS_RED) {
2643		size_t len;
2644		int lookup_depth, avg_pkt_size;
2645		double s, idle, weight, w_q;
2646		struct clockinfo ck;
2647		int t;
2648
2649		if (p.fs.min_th >= p.fs.max_th)
2650		    errx(EX_DATAERR, "min_th %d must be < than max_th %d",
2651			p.fs.min_th, p.fs.max_th);
2652		if (p.fs.max_th == 0)
2653		    errx(EX_DATAERR, "max_th must be > 0");
2654
2655		len = sizeof(int);
2656		if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
2657			&lookup_depth, &len, NULL, 0) == -1)
2658
2659		    errx(1, "sysctlbyname(\"%s\")",
2660			"net.inet.ip.dummynet.red_lookup_depth");
2661		if (lookup_depth == 0)
2662		    errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth"
2663			" must be greater than zero");
2664
2665		len = sizeof(int);
2666		if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
2667			&avg_pkt_size, &len, NULL, 0) == -1)
2668
2669		    errx(1, "sysctlbyname(\"%s\")",
2670			"net.inet.ip.dummynet.red_avg_pkt_size");
2671		if (avg_pkt_size == 0)
2672			errx(EX_DATAERR,
2673			    "net.inet.ip.dummynet.red_avg_pkt_size must"
2674			    " be greater than zero");
2675
2676		len = sizeof(struct clockinfo);
2677		if (sysctlbyname("kern.clockrate", &ck, &len, NULL, 0) == -1)
2678			errx(1, "sysctlbyname(\"%s\")", "kern.clockrate");
2679
2680		/*
2681		 * Ticks needed for sending a medium-sized packet.
2682		 * Unfortunately, when we are configuring a WF2Q+ queue, we
2683		 * do not have bandwidth information, because that is stored
2684		 * in the parent pipe, and also we have multiple queues
2685		 * competing for it. So we set s=0, which is not very
2686		 * correct. But on the other hand, why do we want RED with
2687		 * WF2Q+ ?
2688		 */
2689		if (p.bandwidth==0) /* this is a WF2Q+ queue */
2690			s = 0;
2691		else
2692			s = ck.hz * avg_pkt_size * 8 / p.bandwidth;
2693
2694		/*
2695		 * max idle time (in ticks) before avg queue size becomes 0.
2696		 * NOTA:  (3/w_q) is approx the value x so that
2697		 * (1-w_q)^x < 10^-3.
2698		 */
2699		w_q = ((double)p.fs.w_q) / (1 << SCALE_RED);
2700		idle = s * 3. / w_q;
2701		p.fs.lookup_step = (int)idle / lookup_depth;
2702		if (!p.fs.lookup_step)
2703			p.fs.lookup_step = 1;
2704		weight = 1 - w_q;
2705		for (t = p.fs.lookup_step; t > 0; --t)
2706			weight *= weight;
2707		p.fs.lookup_weight = (int)(weight * (1 << SCALE_RED));
2708	}
2709	i = do_cmd(IP_DUMMYNET_CONFIGURE, &p, sizeof p);
2710	if (i)
2711		err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
2712}
2713
2714static void
2715get_mac_addr_mask(char *p, uint8_t *addr, uint8_t *mask)
2716{
2717	int i, l;
2718
2719	for (i=0; i<6; i++)
2720		addr[i] = mask[i] = 0;
2721	if (!strcmp(p, "any"))
2722		return;
2723
2724	for (i=0; *p && i<6;i++, p++) {
2725		addr[i] = strtol(p, &p, 16);
2726		if (*p != ':') /* we start with the mask */
2727			break;
2728	}
2729	if (*p == '/') { /* mask len */
2730		l = strtol(p+1, &p, 0);
2731		for (i=0; l>0; l -=8, i++)
2732			mask[i] = (l >=8) ? 0xff : (~0) << (8-l);
2733	} else if (*p == '&') { /* mask */
2734		for (i=0, p++; *p && i<6;i++, p++) {
2735			mask[i] = strtol(p, &p, 16);
2736			if (*p != ':')
2737				break;
2738		}
2739	} else if (*p == '\0') {
2740		for (i=0; i<6; i++)
2741			mask[i] = 0xff;
2742	}
2743	for (i=0; i<6; i++)
2744		addr[i] &= mask[i];
2745}
2746
2747/*
2748 * helper function, updates the pointer to cmd with the length
2749 * of the current command, and also cleans up the first word of
2750 * the new command in case it has been clobbered before.
2751 */
2752static ipfw_insn *
2753next_cmd(ipfw_insn *cmd)
2754{
2755	cmd += F_LEN(cmd);
2756	bzero(cmd, sizeof(*cmd));
2757	return cmd;
2758}
2759
2760/*
2761 * Takes arguments and copies them into a comment
2762 */
2763static void
2764fill_comment(ipfw_insn *cmd, int ac, char **av)
2765{
2766	int i, l;
2767	char *p = (char *)(cmd + 1);
2768
2769	cmd->opcode = O_NOP;
2770	cmd->len =  (cmd->len & (F_NOT | F_OR));
2771
2772	/* Compute length of comment string. */
2773	for (i = 0, l = 0; i < ac; i++)
2774		l += strlen(av[i]) + 1;
2775	if (l == 0)
2776		return;
2777	if (l > 84)
2778		errx(EX_DATAERR,
2779		    "comment too long (max 80 chars)");
2780	l = 1 + (l+3)/4;
2781	cmd->len =  (cmd->len & (F_NOT | F_OR)) | l;
2782	for (i = 0; i < ac; i++) {
2783		strcpy(p, av[i]);
2784		p += strlen(av[i]);
2785		*p++ = ' ';
2786	}
2787	*(--p) = '\0';
2788}
2789
2790/*
2791 * A function to fill simple commands of size 1.
2792 * Existing flags are preserved.
2793 */
2794static void
2795fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg)
2796{
2797	cmd->opcode = opcode;
2798	cmd->len =  ((cmd->len | flags) & (F_NOT | F_OR)) | 1;
2799	cmd->arg1 = arg;
2800}
2801
2802/*
2803 * Fetch and add the MAC address and type, with masks. This generates one or
2804 * two microinstructions, and returns the pointer to the last one.
2805 */
2806static ipfw_insn *
2807add_mac(ipfw_insn *cmd, int ac, char *av[])
2808{
2809	ipfw_insn_mac *mac;
2810
2811	if (ac < 2)
2812		errx(EX_DATAERR, "MAC dst src");
2813
2814	cmd->opcode = O_MACADDR2;
2815	cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
2816
2817	mac = (ipfw_insn_mac *)cmd;
2818	get_mac_addr_mask(av[0], mac->addr, mac->mask);	/* dst */
2819	get_mac_addr_mask(av[1], &(mac->addr[6]), &(mac->mask[6])); /* src */
2820	return cmd;
2821}
2822
2823static ipfw_insn *
2824add_mactype(ipfw_insn *cmd, int ac, char *av)
2825{
2826	if (ac < 1)
2827		errx(EX_DATAERR, "missing MAC type");
2828	if (strcmp(av, "any") != 0) { /* we have a non-null type */
2829		fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE);
2830		cmd->opcode = O_MAC_TYPE;
2831		return cmd;
2832	} else
2833		return NULL;
2834}
2835
2836static ipfw_insn *
2837add_proto(ipfw_insn *cmd, char *av)
2838{
2839	struct protoent *pe;
2840	u_char proto = 0;
2841
2842	if (!strncmp(av, "all", strlen(av)))
2843		; /* same as "ip" */
2844	else if ((proto = atoi(av)) > 0)
2845		; /* all done! */
2846	else if ((pe = getprotobyname(av)) != NULL)
2847		proto = pe->p_proto;
2848	else
2849		return NULL;
2850	if (proto != IPPROTO_IP)
2851		fill_cmd(cmd, O_PROTO, 0, proto);
2852	return cmd;
2853}
2854
2855static ipfw_insn *
2856add_srcip(ipfw_insn *cmd, char *av)
2857{
2858	fill_ip((ipfw_insn_ip *)cmd, av);
2859	if (cmd->opcode == O_IP_DST_SET)			/* set */
2860		cmd->opcode = O_IP_SRC_SET;
2861	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
2862		cmd->opcode = O_IP_SRC_LOOKUP;
2863	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
2864		cmd->opcode = O_IP_SRC_ME;
2865	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
2866		cmd->opcode = O_IP_SRC;
2867	else							/* addr/mask */
2868		cmd->opcode = O_IP_SRC_MASK;
2869	return cmd;
2870}
2871
2872static ipfw_insn *
2873add_dstip(ipfw_insn *cmd, char *av)
2874{
2875	fill_ip((ipfw_insn_ip *)cmd, av);
2876	if (cmd->opcode == O_IP_DST_SET)			/* set */
2877		;
2878	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
2879		;
2880	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
2881		cmd->opcode = O_IP_DST_ME;
2882	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
2883		cmd->opcode = O_IP_DST;
2884	else							/* addr/mask */
2885		cmd->opcode = O_IP_DST_MASK;
2886	return cmd;
2887}
2888
2889static ipfw_insn *
2890add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
2891{
2892	if (!strncmp(av, "any", strlen(av))) {
2893		return NULL;
2894	} else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) {
2895		/* XXX todo: check that we have a protocol with ports */
2896		cmd->opcode = opcode;
2897		return cmd;
2898	}
2899	return NULL;
2900}
2901
2902/*
2903 * Parse arguments and assemble the microinstructions which make up a rule.
2904 * Rules are added into the 'rulebuf' and then copied in the correct order
2905 * into the actual rule.
2906 *
2907 * The syntax for a rule starts with the action, followed by
2908 * optional action parameters, and the various match patterns.
2909 * In the assembled microcode, the first opcode must be an O_PROBE_STATE
2910 * (generated if the rule includes a keep-state option), then the
2911 * various match patterns, log/altq actions, and the actual action.
2912 *
2913 */
2914static void
2915add(int ac, char *av[])
2916{
2917	/*
2918	 * rules are added into the 'rulebuf' and then copied in
2919	 * the correct order into the actual rule.
2920	 * Some things that need to go out of order (prob, action etc.)
2921	 * go into actbuf[].
2922	 */
2923	static uint32_t rulebuf[255], actbuf[255], cmdbuf[255];
2924
2925	ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
2926	ipfw_insn *first_cmd;	/* first match pattern */
2927
2928	struct ip_fw *rule;
2929
2930	/*
2931	 * various flags used to record that we entered some fields.
2932	 */
2933	ipfw_insn *have_state = NULL;	/* check-state or keep-state */
2934	ipfw_insn *have_log = NULL, *have_altq = NULL;
2935	size_t len;
2936
2937	int i;
2938
2939	int open_par = 0;	/* open parenthesis ( */
2940
2941	/* proto is here because it is used to fetch ports */
2942	u_char proto = IPPROTO_IP;	/* default protocol */
2943
2944	double match_prob = 1; /* match probability, default is always match */
2945
2946	bzero(actbuf, sizeof(actbuf));		/* actions go here */
2947	bzero(cmdbuf, sizeof(cmdbuf));
2948	bzero(rulebuf, sizeof(rulebuf));
2949
2950	rule = (struct ip_fw *)rulebuf;
2951	cmd = (ipfw_insn *)cmdbuf;
2952	action = (ipfw_insn *)actbuf;
2953
2954	av++; ac--;
2955
2956	/* [rule N]	-- Rule number optional */
2957	if (ac && isdigit(**av)) {
2958		rule->rulenum = atoi(*av);
2959		av++;
2960		ac--;
2961	}
2962
2963	/* [set N]	-- set number (0..RESVD_SET), optional */
2964	if (ac > 1 && !strncmp(*av, "set", strlen(*av))) {
2965		int set = strtoul(av[1], NULL, 10);
2966		if (set < 0 || set > RESVD_SET)
2967			errx(EX_DATAERR, "illegal set %s", av[1]);
2968		rule->set = set;
2969		av += 2; ac -= 2;
2970	}
2971
2972	/* [prob D]	-- match probability, optional */
2973	if (ac > 1 && !strncmp(*av, "prob", strlen(*av))) {
2974		match_prob = strtod(av[1], NULL);
2975
2976		if (match_prob <= 0 || match_prob > 1)
2977			errx(EX_DATAERR, "illegal match prob. %s", av[1]);
2978		av += 2; ac -= 2;
2979	}
2980
2981	/* action	-- mandatory */
2982	NEED1("missing action");
2983	i = match_token(rule_actions, *av);
2984	ac--; av++;
2985	action->len = 1;	/* default */
2986	switch(i) {
2987	case TOK_CHECKSTATE:
2988		have_state = action;
2989		action->opcode = O_CHECK_STATE;
2990		break;
2991
2992	case TOK_ACCEPT:
2993		action->opcode = O_ACCEPT;
2994		break;
2995
2996	case TOK_DENY:
2997		action->opcode = O_DENY;
2998		action->arg1 = 0;
2999		break;
3000
3001	case TOK_REJECT:
3002		action->opcode = O_REJECT;
3003		action->arg1 = ICMP_UNREACH_HOST;
3004		break;
3005
3006	case TOK_RESET:
3007		action->opcode = O_REJECT;
3008		action->arg1 = ICMP_REJECT_RST;
3009		break;
3010
3011	case TOK_UNREACH:
3012		action->opcode = O_REJECT;
3013		NEED1("missing reject code");
3014		fill_reject_code(&action->arg1, *av);
3015		ac--; av++;
3016		break;
3017
3018	case TOK_COUNT:
3019		action->opcode = O_COUNT;
3020		break;
3021
3022	case TOK_QUEUE:
3023	case TOK_PIPE:
3024		action->len = F_INSN_SIZE(ipfw_insn_pipe);
3025	case TOK_SKIPTO:
3026		if (i == TOK_QUEUE)
3027			action->opcode = O_QUEUE;
3028		else if (i == TOK_PIPE)
3029			action->opcode = O_PIPE;
3030		else if (i == TOK_SKIPTO)
3031			action->opcode = O_SKIPTO;
3032		NEED1("missing skipto/pipe/queue number");
3033		action->arg1 = strtoul(*av, NULL, 10);
3034		av++; ac--;
3035		break;
3036
3037	case TOK_DIVERT:
3038	case TOK_TEE:
3039		action->opcode = (i == TOK_DIVERT) ? O_DIVERT : O_TEE;
3040		NEED1("missing divert/tee port");
3041		action->arg1 = strtoul(*av, NULL, 0);
3042		if (action->arg1 == 0) {
3043			struct servent *s;
3044			setservent(1);
3045			s = getservbyname(av[0], "divert");
3046			if (s != NULL)
3047				action->arg1 = ntohs(s->s_port);
3048			else
3049				errx(EX_DATAERR, "illegal divert/tee port");
3050		}
3051		ac--; av++;
3052		break;
3053
3054	case TOK_FORWARD: {
3055		ipfw_insn_sa *p = (ipfw_insn_sa *)action;
3056		char *s, *end;
3057
3058		NEED1("missing forward address[:port]");
3059
3060		action->opcode = O_FORWARD_IP;
3061		action->len = F_INSN_SIZE(ipfw_insn_sa);
3062
3063		p->sa.sin_len = sizeof(struct sockaddr_in);
3064		p->sa.sin_family = AF_INET;
3065		p->sa.sin_port = 0;
3066		/*
3067		 * locate the address-port separator (':' or ',')
3068		 */
3069		s = strchr(*av, ':');
3070		if (s == NULL)
3071			s = strchr(*av, ',');
3072		if (s != NULL) {
3073			*(s++) = '\0';
3074			i = strtoport(s, &end, 0 /* base */, 0 /* proto */);
3075			if (s == end)
3076				errx(EX_DATAERR,
3077				    "illegal forwarding port ``%s''", s);
3078			p->sa.sin_port = (u_short)i;
3079		}
3080		lookup_host(*av, &(p->sa.sin_addr));
3081		}
3082		ac--; av++;
3083		break;
3084
3085	case TOK_COMMENT:
3086		/* pretend it is a 'count' rule followed by the comment */
3087		action->opcode = O_COUNT;
3088		ac++; av--;	/* go back... */
3089		break;
3090
3091	default:
3092		errx(EX_DATAERR, "invalid action %s\n", av[-1]);
3093	}
3094	action = next_cmd(action);
3095
3096	/*
3097	 * [altq queuename] -- altq tag, optional
3098	 * [log [logamount N]]	-- log, optional
3099	 *
3100	 * If they exist, it go first in the cmdbuf, but then it is
3101	 * skipped in the copy section to the end of the buffer.
3102	 */
3103	while (ac != 0 && (i = match_token(rule_action_params, *av)) != -1) {
3104		ac--; av++;
3105		switch (i) {
3106		case TOK_LOG:
3107		    {
3108			ipfw_insn_log *c = (ipfw_insn_log *)cmd;
3109			int l;
3110
3111			if (have_log)
3112				errx(EX_DATAERR,
3113				    "log cannot be specified more than once");
3114			have_log = (ipfw_insn *)c;
3115			cmd->len = F_INSN_SIZE(ipfw_insn_log);
3116			cmd->opcode = O_LOG;
3117			if (ac && !strncmp(*av, "logamount", strlen(*av))) {
3118				ac--; av++;
3119				NEED1("logamount requires argument");
3120				l = atoi(*av);
3121				if (l < 0)
3122					errx(EX_DATAERR,
3123					    "logamount must be positive");
3124				c->max_log = l;
3125				ac--; av++;
3126			} else {
3127				len = sizeof(c->max_log);
3128				if (sysctlbyname("net.inet.ip.fw.verbose_limit",
3129				    &c->max_log, &len, NULL, 0) == -1)
3130					errx(1, "sysctlbyname(\"%s\")",
3131					    "net.inet.ip.fw.verbose_limit");
3132			}
3133		    }
3134			break;
3135
3136		case TOK_ALTQ:
3137		    {
3138			ipfw_insn_altq *a = (ipfw_insn_altq *)cmd;
3139
3140			NEED1("missing altq queue name");
3141			if (have_altq)
3142				errx(EX_DATAERR,
3143				    "altq cannot be specified more than once");
3144			have_altq = (ipfw_insn *)a;
3145			cmd->len = F_INSN_SIZE(ipfw_insn_altq);
3146			cmd->opcode = O_ALTQ;
3147			fill_altq_qid(&a->qid, *av);
3148			ac--; av++;
3149		    }
3150			break;
3151
3152		default:
3153			abort();
3154		}
3155		cmd = next_cmd(cmd);
3156	}
3157
3158	if (have_state)	/* must be a check-state, we are done */
3159		goto done;
3160
3161#define OR_START(target)					\
3162	if (ac && (*av[0] == '(' || *av[0] == '{')) {		\
3163		if (open_par)					\
3164			errx(EX_USAGE, "nested \"(\" not allowed\n"); \
3165		prev = NULL;					\
3166		open_par = 1;					\
3167		if ( (av[0])[1] == '\0') {			\
3168			ac--; av++;				\
3169		} else						\
3170			(*av)++;				\
3171	}							\
3172	target:							\
3173
3174
3175#define	CLOSE_PAR						\
3176	if (open_par) {						\
3177		if (ac && (					\
3178		    !strncmp(*av, ")", strlen(*av)) ||		\
3179		    !strncmp(*av, "}", strlen(*av)) )) {	\
3180			prev = NULL;				\
3181			open_par = 0;				\
3182			ac--; av++;				\
3183		} else						\
3184			errx(EX_USAGE, "missing \")\"\n");	\
3185	}
3186
3187#define NOT_BLOCK						\
3188	if (ac && !strncmp(*av, "not", strlen(*av))) {		\
3189		if (cmd->len & F_NOT)				\
3190			errx(EX_USAGE, "double \"not\" not allowed\n"); \
3191		cmd->len |= F_NOT;				\
3192		ac--; av++;					\
3193	}
3194
3195#define OR_BLOCK(target)					\
3196	if (ac && !strncmp(*av, "or", strlen(*av))) {		\
3197		if (prev == NULL || open_par == 0)		\
3198			errx(EX_DATAERR, "invalid OR block");	\
3199		prev->len |= F_OR;				\
3200		ac--; av++;					\
3201		goto target;					\
3202	}							\
3203	CLOSE_PAR;
3204
3205	first_cmd = cmd;
3206
3207#if 0
3208	/*
3209	 * MAC addresses, optional.
3210	 * If we have this, we skip the part "proto from src to dst"
3211	 * and jump straight to the option parsing.
3212	 */
3213	NOT_BLOCK;
3214	NEED1("missing protocol");
3215	if (!strncmp(*av, "MAC", strlen(*av)) ||
3216	    !strncmp(*av, "mac", strlen(*av))) {
3217		ac--; av++;	/* the "MAC" keyword */
3218		add_mac(cmd, ac, av); /* exits in case of errors */
3219		cmd = next_cmd(cmd);
3220		ac -= 2; av += 2;	/* dst-mac and src-mac */
3221		NOT_BLOCK;
3222		NEED1("missing mac type");
3223		if (add_mactype(cmd, ac, av[0]))
3224			cmd = next_cmd(cmd);
3225		ac--; av++;	/* any or mac-type */
3226		goto read_options;
3227	}
3228#endif
3229
3230	/*
3231	 * protocol, mandatory
3232	 */
3233    OR_START(get_proto);
3234	NOT_BLOCK;
3235	NEED1("missing protocol");
3236	if (add_proto(cmd, *av)) {
3237		av++; ac--;
3238		if (F_LEN(cmd) == 0)	/* plain IP */
3239			proto = 0;
3240		else {
3241			proto = cmd->arg1;
3242			prev = cmd;
3243			cmd = next_cmd(cmd);
3244		}
3245	} else if (first_cmd != cmd) {
3246		errx(EX_DATAERR, "invalid protocol ``%s''", *av);
3247	} else
3248		goto read_options;
3249    OR_BLOCK(get_proto);
3250
3251	/*
3252	 * "from", mandatory
3253	 */
3254	if (!ac || strncmp(*av, "from", strlen(*av)))
3255		errx(EX_USAGE, "missing ``from''");
3256	ac--; av++;
3257
3258	/*
3259	 * source IP, mandatory
3260	 */
3261    OR_START(source_ip);
3262	NOT_BLOCK;	/* optional "not" */
3263	NEED1("missing source address");
3264	if (add_srcip(cmd, *av)) {
3265		ac--; av++;
3266		if (F_LEN(cmd) != 0) {	/* ! any */
3267			prev = cmd;
3268			cmd = next_cmd(cmd);
3269		}
3270	}
3271    OR_BLOCK(source_ip);
3272
3273	/*
3274	 * source ports, optional
3275	 */
3276	NOT_BLOCK;	/* optional "not" */
3277	if (ac) {
3278		if (!strncmp(*av, "any", strlen(*av)) ||
3279		    add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
3280			ac--; av++;
3281			if (F_LEN(cmd) != 0)
3282				cmd = next_cmd(cmd);
3283		}
3284	}
3285
3286	/*
3287	 * "to", mandatory
3288	 */
3289	if (!ac || strncmp(*av, "to", strlen(*av)))
3290		errx(EX_USAGE, "missing ``to''");
3291	av++; ac--;
3292
3293	/*
3294	 * destination, mandatory
3295	 */
3296    OR_START(dest_ip);
3297	NOT_BLOCK;	/* optional "not" */
3298	NEED1("missing dst address");
3299	if (add_dstip(cmd, *av)) {
3300		ac--; av++;
3301		if (F_LEN(cmd) != 0) {	/* ! any */
3302			prev = cmd;
3303			cmd = next_cmd(cmd);
3304		}
3305	}
3306    OR_BLOCK(dest_ip);
3307
3308	/*
3309	 * dest. ports, optional
3310	 */
3311	NOT_BLOCK;	/* optional "not" */
3312	if (ac) {
3313		if (!strncmp(*av, "any", strlen(*av)) ||
3314		    add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
3315			ac--; av++;
3316			if (F_LEN(cmd) != 0)
3317				cmd = next_cmd(cmd);
3318		}
3319	}
3320
3321read_options:
3322	if (ac && first_cmd == cmd) {
3323		/*
3324		 * nothing specified so far, store in the rule to ease
3325		 * printout later.
3326		 */
3327		 rule->_pad = 1;
3328	}
3329	prev = NULL;
3330	while (ac) {
3331		char *s;
3332		ipfw_insn_u32 *cmd32;	/* alias for cmd */
3333
3334		s = *av;
3335		cmd32 = (ipfw_insn_u32 *)cmd;
3336
3337		if (*s == '!') {	/* alternate syntax for NOT */
3338			if (cmd->len & F_NOT)
3339				errx(EX_USAGE, "double \"not\" not allowed\n");
3340			cmd->len = F_NOT;
3341			s++;
3342		}
3343		i = match_token(rule_options, s);
3344		ac--; av++;
3345		switch(i) {
3346		case TOK_NOT:
3347			if (cmd->len & F_NOT)
3348				errx(EX_USAGE, "double \"not\" not allowed\n");
3349			cmd->len = F_NOT;
3350			break;
3351
3352		case TOK_OR:
3353			if (open_par == 0 || prev == NULL)
3354				errx(EX_USAGE, "invalid \"or\" block\n");
3355			prev->len |= F_OR;
3356			break;
3357
3358		case TOK_STARTBRACE:
3359			if (open_par)
3360				errx(EX_USAGE, "+nested \"(\" not allowed\n");
3361			open_par = 1;
3362			break;
3363
3364		case TOK_ENDBRACE:
3365			if (!open_par)
3366				errx(EX_USAGE, "+missing \")\"\n");
3367			open_par = 0;
3368			prev = NULL;
3369        		break;
3370
3371		case TOK_IN:
3372			fill_cmd(cmd, O_IN, 0, 0);
3373			break;
3374
3375		case TOK_OUT:
3376			cmd->len ^= F_NOT; /* toggle F_NOT */
3377			fill_cmd(cmd, O_IN, 0, 0);
3378			break;
3379
3380		case TOK_FRAG:
3381			fill_cmd(cmd, O_FRAG, 0, 0);
3382			break;
3383
3384		case TOK_LAYER2:
3385			fill_cmd(cmd, O_LAYER2, 0, 0);
3386			break;
3387
3388		case TOK_XMIT:
3389		case TOK_RECV:
3390		case TOK_VIA:
3391			NEED1("recv, xmit, via require interface name"
3392				" or address");
3393			fill_iface((ipfw_insn_if *)cmd, av[0]);
3394			ac--; av++;
3395			if (F_LEN(cmd) == 0)	/* not a valid address */
3396				break;
3397			if (i == TOK_XMIT)
3398				cmd->opcode = O_XMIT;
3399			else if (i == TOK_RECV)
3400				cmd->opcode = O_RECV;
3401			else if (i == TOK_VIA)
3402				cmd->opcode = O_VIA;
3403			break;
3404
3405		case TOK_ICMPTYPES:
3406			NEED1("icmptypes requires list of types");
3407			fill_icmptypes((ipfw_insn_u32 *)cmd, *av);
3408			av++; ac--;
3409			break;
3410
3411		case TOK_IPTTL:
3412			NEED1("ipttl requires TTL");
3413			if (strpbrk(*av, "-,")) {
3414			    if (!add_ports(cmd, *av, 0, O_IPTTL))
3415				errx(EX_DATAERR, "invalid ipttl %s", *av);
3416			} else
3417			    fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
3418			ac--; av++;
3419			break;
3420
3421		case TOK_IPID:
3422			NEED1("ipid requires id");
3423			if (strpbrk(*av, "-,")) {
3424			    if (!add_ports(cmd, *av, 0, O_IPID))
3425				errx(EX_DATAERR, "invalid ipid %s", *av);
3426			} else
3427			    fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0));
3428			ac--; av++;
3429			break;
3430
3431		case TOK_IPLEN:
3432			NEED1("iplen requires length");
3433			if (strpbrk(*av, "-,")) {
3434			    if (!add_ports(cmd, *av, 0, O_IPLEN))
3435				errx(EX_DATAERR, "invalid ip len %s", *av);
3436			} else
3437			    fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0));
3438			ac--; av++;
3439			break;
3440
3441		case TOK_IPVER:
3442			NEED1("ipver requires version");
3443			fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0));
3444			ac--; av++;
3445			break;
3446
3447		case TOK_IPPRECEDENCE:
3448			NEED1("ipprecedence requires value");
3449			fill_cmd(cmd, O_IPPRECEDENCE, 0,
3450			    (strtoul(*av, NULL, 0) & 7) << 5);
3451			ac--; av++;
3452			break;
3453
3454		case TOK_IPOPTS:
3455			NEED1("missing argument for ipoptions");
3456			fill_flags(cmd, O_IPOPT, f_ipopts, *av);
3457			ac--; av++;
3458			break;
3459
3460		case TOK_IPTOS:
3461			NEED1("missing argument for iptos");
3462			fill_flags(cmd, O_IPTOS, f_iptos, *av);
3463			ac--; av++;
3464			break;
3465
3466		case TOK_UID:
3467			NEED1("uid requires argument");
3468		    {
3469			char *end;
3470			uid_t uid;
3471			struct passwd *pwd;
3472
3473			cmd->opcode = O_UID;
3474			uid = strtoul(*av, &end, 0);
3475			pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av);
3476			if (pwd == NULL)
3477				errx(EX_DATAERR, "uid \"%s\" nonexistent", *av);
3478			cmd32->d[0] = pwd->pw_uid;
3479			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
3480			ac--; av++;
3481		    }
3482			break;
3483
3484		case TOK_GID:
3485			NEED1("gid requires argument");
3486		    {
3487			char *end;
3488			gid_t gid;
3489			struct group *grp;
3490
3491			cmd->opcode = O_GID;
3492			gid = strtoul(*av, &end, 0);
3493			grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av);
3494			if (grp == NULL)
3495				errx(EX_DATAERR, "gid \"%s\" nonexistent", *av);
3496			cmd32->d[0] = grp->gr_gid;
3497			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
3498			ac--; av++;
3499		    }
3500			break;
3501
3502		case TOK_JAIL:
3503			NEED1("jail requires argument");
3504		    {
3505			char *end;
3506			int jid;
3507
3508			cmd->opcode = O_JAIL;
3509			jid = (int)strtol(*av, &end, 0);
3510			if (jid < 0 || *end != '\0')
3511				errx(EX_DATAERR, "jail requires prison ID");
3512			cmd32->d[0] = (uint32_t)jid;
3513			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
3514			ac--; av++;
3515		    }
3516			break;
3517
3518		case TOK_ESTAB:
3519			fill_cmd(cmd, O_ESTAB, 0, 0);
3520			break;
3521
3522		case TOK_SETUP:
3523			fill_cmd(cmd, O_TCPFLAGS, 0,
3524				(TH_SYN) | ( (TH_ACK) & 0xff) <<8 );
3525			break;
3526
3527		case TOK_TCPOPTS:
3528			NEED1("missing argument for tcpoptions");
3529			fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av);
3530			ac--; av++;
3531			break;
3532
3533		case TOK_TCPSEQ:
3534		case TOK_TCPACK:
3535			NEED1("tcpseq/tcpack requires argument");
3536			cmd->len = F_INSN_SIZE(ipfw_insn_u32);
3537			cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK;
3538			cmd32->d[0] = htonl(strtoul(*av, NULL, 0));
3539			ac--; av++;
3540			break;
3541
3542		case TOK_TCPWIN:
3543			NEED1("tcpwin requires length");
3544			fill_cmd(cmd, O_TCPWIN, 0,
3545			    htons(strtoul(*av, NULL, 0)));
3546			ac--; av++;
3547			break;
3548
3549		case TOK_TCPFLAGS:
3550			NEED1("missing argument for tcpflags");
3551			cmd->opcode = O_TCPFLAGS;
3552			fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av);
3553			ac--; av++;
3554			break;
3555
3556		case TOK_KEEPSTATE:
3557			if (open_par)
3558				errx(EX_USAGE, "keep-state cannot be part "
3559				    "of an or block");
3560			if (have_state)
3561				errx(EX_USAGE, "only one of keep-state "
3562					"and limit is allowed");
3563			have_state = cmd;
3564			fill_cmd(cmd, O_KEEP_STATE, 0, 0);
3565			break;
3566
3567		case TOK_LIMIT:
3568			if (open_par)
3569				errx(EX_USAGE, "limit cannot be part "
3570				    "of an or block");
3571			if (have_state)
3572				errx(EX_USAGE, "only one of keep-state "
3573					"and limit is allowed");
3574			NEED1("limit needs mask and # of connections");
3575			have_state = cmd;
3576		    {
3577			ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
3578
3579			cmd->len = F_INSN_SIZE(ipfw_insn_limit);
3580			cmd->opcode = O_LIMIT;
3581			c->limit_mask = 0;
3582			c->conn_limit = 0;
3583			for (; ac >1 ;) {
3584				int val;
3585
3586				val = match_token(limit_masks, *av);
3587				if (val <= 0)
3588					break;
3589				c->limit_mask |= val;
3590				ac--; av++;
3591			}
3592			c->conn_limit = atoi(*av);
3593			if (c->conn_limit == 0)
3594				errx(EX_USAGE, "limit: limit must be >0");
3595			if (c->limit_mask == 0)
3596				errx(EX_USAGE, "missing limit mask");
3597			ac--; av++;
3598		    }
3599			break;
3600
3601		case TOK_PROTO:
3602			NEED1("missing protocol");
3603			if (add_proto(cmd, *av)) {
3604				proto = cmd->arg1;
3605				ac--; av++;
3606			} else
3607				errx(EX_DATAERR, "invalid protocol ``%s''",
3608				    *av);
3609			break;
3610
3611		case TOK_SRCIP:
3612			NEED1("missing source IP");
3613			if (add_srcip(cmd, *av)) {
3614				ac--; av++;
3615			}
3616			break;
3617
3618		case TOK_DSTIP:
3619			NEED1("missing destination IP");
3620			if (add_dstip(cmd, *av)) {
3621				ac--; av++;
3622			}
3623			break;
3624
3625		case TOK_SRCPORT:
3626			NEED1("missing source port");
3627			if (!strncmp(*av, "any", strlen(*av)) ||
3628			    add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
3629				ac--; av++;
3630			} else
3631				errx(EX_DATAERR, "invalid source port %s", *av);
3632			break;
3633
3634		case TOK_DSTPORT:
3635			NEED1("missing destination port");
3636			if (!strncmp(*av, "any", strlen(*av)) ||
3637			    add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
3638				ac--; av++;
3639			} else
3640				errx(EX_DATAERR, "invalid destination port %s",
3641				    *av);
3642			break;
3643
3644		case TOK_MAC:
3645			if (add_mac(cmd, ac, av)) {
3646				ac -= 2; av += 2;
3647			}
3648			break;
3649
3650		case TOK_MACTYPE:
3651			NEED1("missing mac type");
3652			if (!add_mactype(cmd, ac, *av))
3653				errx(EX_DATAERR, "invalid mac type %s", *av);
3654			ac--; av++;
3655			break;
3656
3657		case TOK_VERREVPATH:
3658			fill_cmd(cmd, O_VERREVPATH, 0, 0);
3659			break;
3660
3661		case TOK_VERSRCREACH:
3662			fill_cmd(cmd, O_VERSRCREACH, 0, 0);
3663			break;
3664
3665		case TOK_ANTISPOOF:
3666			fill_cmd(cmd, O_ANTISPOOF, 0, 0);
3667			break;
3668
3669		case TOK_IPSEC:
3670			fill_cmd(cmd, O_IPSEC, 0, 0);
3671			break;
3672
3673		case TOK_COMMENT:
3674			fill_comment(cmd, ac, av);
3675			av += ac;
3676			ac = 0;
3677			break;
3678
3679		default:
3680			errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
3681		}
3682		if (F_LEN(cmd) > 0) {	/* prepare to advance */
3683			prev = cmd;
3684			cmd = next_cmd(cmd);
3685		}
3686	}
3687
3688done:
3689	/*
3690	 * Now copy stuff into the rule.
3691	 * If we have a keep-state option, the first instruction
3692	 * must be a PROBE_STATE (which is generated here).
3693	 * If we have a LOG option, it was stored as the first command,
3694	 * and now must be moved to the top of the action part.
3695	 */
3696	dst = (ipfw_insn *)rule->cmd;
3697
3698	/*
3699	 * First thing to write into the command stream is the match probability.
3700	 */
3701	if (match_prob != 1) { /* 1 means always match */
3702		dst->opcode = O_PROB;
3703		dst->len = 2;
3704		*((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff);
3705		dst += dst->len;
3706	}
3707
3708	/*
3709	 * generate O_PROBE_STATE if necessary
3710	 */
3711	if (have_state && have_state->opcode != O_CHECK_STATE) {
3712		fill_cmd(dst, O_PROBE_STATE, 0, 0);
3713		dst = next_cmd(dst);
3714	}
3715	/*
3716	 * copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ
3717	 */
3718	for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
3719		i = F_LEN(src);
3720
3721		switch (src->opcode) {
3722		case O_LOG:
3723		case O_KEEP_STATE:
3724		case O_LIMIT:
3725		case O_ALTQ:
3726			break;
3727		default:
3728			bcopy(src, dst, i * sizeof(uint32_t));
3729			dst += i;
3730		}
3731	}
3732
3733	/*
3734	 * put back the have_state command as last opcode
3735	 */
3736	if (have_state && have_state->opcode != O_CHECK_STATE) {
3737		i = F_LEN(have_state);
3738		bcopy(have_state, dst, i * sizeof(uint32_t));
3739		dst += i;
3740	}
3741	/*
3742	 * start action section
3743	 */
3744	rule->act_ofs = dst - rule->cmd;
3745
3746	/*
3747	 * put back O_LOG, O_ALTQ if necessary
3748	 */
3749	if (have_log) {
3750		i = F_LEN(have_log);
3751		bcopy(have_log, dst, i * sizeof(uint32_t));
3752		dst += i;
3753	}
3754	if (have_altq) {
3755		i = F_LEN(have_altq);
3756		bcopy(have_altq, dst, i * sizeof(uint32_t));
3757		dst += i;
3758	}
3759	/*
3760	 * copy all other actions
3761	 */
3762	for (src = (ipfw_insn *)actbuf; src != action; src += i) {
3763		i = F_LEN(src);
3764		bcopy(src, dst, i * sizeof(uint32_t));
3765		dst += i;
3766	}
3767
3768	rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd);
3769	i = (char *)dst - (char *)rule;
3770	if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1)
3771		err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
3772	if (!do_quiet)
3773		show_ipfw(rule, 0, 0);
3774}
3775
3776static void
3777zero(int ac, char *av[], int optname /* IP_FW_ZERO or IP_FW_RESETLOG */)
3778{
3779	int rulenum;
3780	int failed = EX_OK;
3781	char const *name = optname == IP_FW_ZERO ?  "ZERO" : "RESETLOG";
3782
3783	av++; ac--;
3784
3785	if (!ac) {
3786		/* clear all entries */
3787		if (do_cmd(optname, NULL, 0) < 0)
3788			err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name);
3789		if (!do_quiet)
3790			printf("%s.\n", optname == IP_FW_ZERO ?
3791			    "Accounting cleared":"Logging counts reset");
3792
3793		return;
3794	}
3795
3796	while (ac) {
3797		/* Rule number */
3798		if (isdigit(**av)) {
3799			rulenum = atoi(*av);
3800			av++;
3801			ac--;
3802			if (do_cmd(optname, &rulenum, sizeof rulenum)) {
3803				warn("rule %u: setsockopt(IP_FW_%s)",
3804				    rulenum, name);
3805				failed = EX_UNAVAILABLE;
3806			} else if (!do_quiet)
3807				printf("Entry %d %s.\n", rulenum,
3808				    optname == IP_FW_ZERO ?
3809					"cleared" : "logging count reset");
3810		} else {
3811			errx(EX_USAGE, "invalid rule number ``%s''", *av);
3812		}
3813	}
3814	if (failed != EX_OK)
3815		exit(failed);
3816}
3817
3818static void
3819flush(int force)
3820{
3821	int cmd = do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH;
3822
3823	if (!force && !do_quiet) { /* need to ask user */
3824		int c;
3825
3826		printf("Are you sure? [yn] ");
3827		fflush(stdout);
3828		do {
3829			c = toupper(getc(stdin));
3830			while (c != '\n' && getc(stdin) != '\n')
3831				if (feof(stdin))
3832					return; /* and do not flush */
3833		} while (c != 'Y' && c != 'N');
3834		printf("\n");
3835		if (c == 'N')	/* user said no */
3836			return;
3837	}
3838	if (do_cmd(cmd, NULL, 0) < 0)
3839		err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
3840		    do_pipe ? "DUMMYNET" : "FW");
3841	if (!do_quiet)
3842		printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules");
3843}
3844
3845/*
3846 * Free a the (locally allocated) copy of command line arguments.
3847 */
3848static void
3849free_args(int ac, char **av)
3850{
3851	int i;
3852
3853	for (i=0; i < ac; i++)
3854		free(av[i]);
3855	free(av);
3856}
3857
3858/*
3859 * This one handles all table-related commands
3860 * 	ipfw table N add addr[/masklen] [value]
3861 * 	ipfw table N delete addr[/masklen]
3862 * 	ipfw table N flush
3863 * 	ipfw table N list
3864 */
3865static void
3866table_handler(int ac, char *av[])
3867{
3868	ipfw_table_entry ent;
3869	ipfw_table *tbl;
3870	int do_add;
3871	char *p;
3872	socklen_t l;
3873	uint32_t a;
3874
3875	ac--; av++;
3876	if (ac && isdigit(**av)) {
3877		ent.tbl = atoi(*av);
3878		ac--; av++;
3879	} else
3880		errx(EX_USAGE, "table number required");
3881	NEED1("table needs command");
3882	if (strncmp(*av, "add", strlen(*av)) == 0 ||
3883	    strncmp(*av, "delete", strlen(*av)) == 0) {
3884		do_add = **av == 'a';
3885		ac--; av++;
3886		if (!ac)
3887			errx(EX_USAGE, "IP address required");
3888		p = strchr(*av, '/');
3889		if (p) {
3890			*p++ = '\0';
3891			ent.masklen = atoi(p);
3892			if (ent.masklen > 32)
3893				errx(EX_DATAERR, "bad width ``%s''", p);
3894		} else
3895			ent.masklen = 32;
3896		if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0)
3897			errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
3898		ac--; av++;
3899		if (do_add && ac)
3900			ent.value = strtoul(*av, NULL, 0);
3901		else
3902			ent.value = 0;
3903		if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL,
3904		    &ent, sizeof(ent)) < 0)
3905			err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
3906			    do_add ? "ADD" : "DEL");
3907	} else if (strncmp(*av, "flush", strlen(*av)) == 0) {
3908		if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl, sizeof(ent.tbl)) < 0)
3909			err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
3910	} else if (strncmp(*av, "list", strlen(*av)) == 0) {
3911		a = ent.tbl;
3912		l = sizeof(a);
3913		if (do_cmd(IP_FW_TABLE_GETSIZE, &a, (uintptr_t)&l) < 0)
3914			err(EX_OSERR, "getsockopt(IP_FW_TABLE_GETSIZE)");
3915		l = sizeof(*tbl) + a * sizeof(ipfw_table_entry);
3916		tbl = malloc(l);
3917		if (tbl == NULL)
3918			err(EX_OSERR, "malloc");
3919		tbl->tbl = ent.tbl;
3920		if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0)
3921			err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)");
3922		for (a = 0; a < tbl->cnt; a++) {
3923			printf("%s/%u %u\n",
3924			    inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
3925			    tbl->ent[a].masklen, tbl->ent[a].value);
3926		}
3927	} else
3928		errx(EX_USAGE, "invalid table command %s", *av);
3929}
3930
3931/*
3932 * Called with the arguments (excluding program name).
3933 * Returns 0 if successful, 1 if empty command, errx() in case of errors.
3934 */
3935static int
3936ipfw_main(int oldac, char **oldav)
3937{
3938	int ch, ac, save_ac;
3939	char **av, **save_av;
3940	int do_acct = 0;		/* Show packet/byte count */
3941
3942#define WHITESP		" \t\f\v\n\r"
3943	if (oldac == 0)
3944		return 1;
3945	else if (oldac == 1) {
3946		/*
3947		 * If we are called with a single string, try to split it into
3948		 * arguments for subsequent parsing.
3949		 * But first, remove spaces after a ',', by copying the string
3950		 * in-place.
3951		 */
3952		char *arg = oldav[0];	/* The string... */
3953		int l = strlen(arg);
3954		int copy = 0;		/* 1 if we need to copy, 0 otherwise */
3955		int i, j;
3956		for (i = j = 0; i < l; i++) {
3957			if (arg[i] == '#')	/* comment marker */
3958				break;
3959			if (copy) {
3960				arg[j++] = arg[i];
3961				copy = !index("," WHITESP, arg[i]);
3962			} else {
3963				copy = !index(WHITESP, arg[i]);
3964				if (copy)
3965					arg[j++] = arg[i];
3966			}
3967		}
3968		if (!copy && j > 0)	/* last char was a 'blank', remove it */
3969			j--;
3970		l = j;			/* the new argument length */
3971		arg[j++] = '\0';
3972		if (l == 0)		/* empty string! */
3973			return 1;
3974
3975		/*
3976		 * First, count number of arguments. Because of the previous
3977		 * processing, this is just the number of blanks plus 1.
3978		 */
3979		for (i = 0, ac = 1; i < l; i++)
3980			if (index(WHITESP, arg[i]) != NULL)
3981				ac++;
3982
3983		av = calloc(ac, sizeof(char *));
3984
3985		/*
3986		 * Second, copy arguments from cmd[] to av[]. For each one,
3987		 * j is the initial character, i is the one past the end.
3988		 */
3989		for (ac = 0, i = j = 0; i < l; i++)
3990			if (index(WHITESP, arg[i]) != NULL || i == l-1) {
3991				if (i == l-1)
3992					i++;
3993				av[ac] = calloc(i-j+1, 1);
3994				bcopy(arg+j, av[ac], i-j);
3995				ac++;
3996				j = i + 1;
3997			}
3998	} else {
3999		/*
4000		 * If an argument ends with ',' join with the next one.
4001		 */
4002		int first, i, l;
4003
4004		av = calloc(oldac, sizeof(char *));
4005		for (first = i = ac = 0, l = 0; i < oldac; i++) {
4006			char *arg = oldav[i];
4007			int k = strlen(arg);
4008
4009			l += k;
4010			if (arg[k-1] != ',' || i == oldac-1) {
4011				/* Time to copy. */
4012				av[ac] = calloc(l+1, 1);
4013				for (l=0; first <= i; first++) {
4014					strcat(av[ac]+l, oldav[first]);
4015					l += strlen(oldav[first]);
4016				}
4017				ac++;
4018				l = 0;
4019				first = i+1;
4020			}
4021		}
4022	}
4023
4024	/* Set the force flag for non-interactive processes */
4025	if (!do_force)
4026		do_force = !isatty(STDIN_FILENO);
4027
4028	/* Save arguments for final freeing of memory. */
4029	save_ac = ac;
4030	save_av = av;
4031
4032	optind = optreset = 0;
4033	while ((ch = getopt(ac, av, "abcdefhnNqs:STtv")) != -1)
4034		switch (ch) {
4035		case 'a':
4036			do_acct = 1;
4037			break;
4038
4039		case 'b':
4040			comment_only = 1;
4041			do_compact = 1;
4042			break;
4043
4044		case 'c':
4045			do_compact = 1;
4046			break;
4047
4048		case 'd':
4049			do_dynamic = 1;
4050			break;
4051
4052		case 'e':
4053			do_expired = 1;
4054			break;
4055
4056		case 'f':
4057			do_force = 1;
4058			break;
4059
4060		case 'h': /* help */
4061			free_args(save_ac, save_av);
4062			help();
4063			break;	/* NOTREACHED */
4064
4065		case 'n':
4066			test_only = 1;
4067			break;
4068
4069		case 'N':
4070			do_resolv = 1;
4071			break;
4072
4073		case 'q':
4074			do_quiet = 1;
4075			break;
4076
4077		case 's': /* sort */
4078			do_sort = atoi(optarg);
4079			break;
4080
4081		case 'S':
4082			show_sets = 1;
4083			break;
4084
4085		case 't':
4086			do_time = 1;
4087			break;
4088
4089		case 'T':
4090			do_time = 2;	/* numeric timestamp */
4091			break;
4092
4093		case 'v': /* verbose */
4094			verbose = 1;
4095			break;
4096
4097		default:
4098			free_args(save_ac, save_av);
4099			return 1;
4100		}
4101
4102	ac -= optind;
4103	av += optind;
4104	NEED1("bad arguments, for usage summary ``ipfw''");
4105
4106	/*
4107	 * An undocumented behaviour of ipfw1 was to allow rule numbers first,
4108	 * e.g. "100 add allow ..." instead of "add 100 allow ...".
4109	 * In case, swap first and second argument to get the normal form.
4110	 */
4111	if (ac > 1 && isdigit(*av[0])) {
4112		char *p = av[0];
4113
4114		av[0] = av[1];
4115		av[1] = p;
4116	}
4117
4118	/*
4119	 * optional: pipe or queue
4120	 */
4121	do_pipe = 0;
4122	if (!strncmp(*av, "pipe", strlen(*av)))
4123		do_pipe = 1;
4124	else if (!strncmp(*av, "queue", strlen(*av)))
4125		do_pipe = 2;
4126	if (do_pipe) {
4127		ac--;
4128		av++;
4129	}
4130	NEED1("missing command");
4131
4132	/*
4133	 * For pipes and queues we normally say 'pipe NN config'
4134	 * but the code is easier to parse as 'pipe config NN'
4135	 * so we swap the two arguments.
4136	 */
4137	if (do_pipe > 0 && ac > 1 && isdigit(*av[0])) {
4138		char *p = av[0];
4139
4140		av[0] = av[1];
4141		av[1] = p;
4142	}
4143
4144	if (!strncmp(*av, "add", strlen(*av)))
4145		add(ac, av);
4146	else if (do_pipe && !strncmp(*av, "config", strlen(*av)))
4147		config_pipe(ac, av);
4148	else if (!strncmp(*av, "delete", strlen(*av)))
4149		delete(ac, av);
4150	else if (!strncmp(*av, "flush", strlen(*av)))
4151		flush(do_force);
4152	else if (!strncmp(*av, "zero", strlen(*av)))
4153		zero(ac, av, IP_FW_ZERO);
4154	else if (!strncmp(*av, "resetlog", strlen(*av)))
4155		zero(ac, av, IP_FW_RESETLOG);
4156	else if (!strncmp(*av, "print", strlen(*av)) ||
4157	         !strncmp(*av, "list", strlen(*av)))
4158		list(ac, av, do_acct);
4159	else if (!strncmp(*av, "set", strlen(*av)))
4160		sets_handler(ac, av);
4161	else if (!strncmp(*av, "table", strlen(*av)))
4162		table_handler(ac, av);
4163	else if (!strncmp(*av, "enable", strlen(*av)))
4164		sysctl_handler(ac, av, 1);
4165	else if (!strncmp(*av, "disable", strlen(*av)))
4166		sysctl_handler(ac, av, 0);
4167	else if (!strncmp(*av, "show", strlen(*av)))
4168		list(ac, av, 1 /* show counters */);
4169	else
4170		errx(EX_USAGE, "bad command `%s'", *av);
4171
4172	/* Free memory allocated in the argument parsing. */
4173	free_args(save_ac, save_av);
4174	return 0;
4175}
4176
4177
4178static void
4179ipfw_readfile(int ac, char *av[])
4180{
4181#define MAX_ARGS	32
4182	char	buf[BUFSIZ];
4183	char	*cmd = NULL, *filename = av[ac-1];
4184	int	c, lineno=0;
4185	FILE	*f = NULL;
4186	pid_t	preproc = 0;
4187
4188	filename = av[ac-1];
4189
4190	while ((c = getopt(ac, av, "cfNnp:qS")) != -1) {
4191		switch(c) {
4192		case 'c':
4193			do_compact = 1;
4194			break;
4195
4196		case 'f':
4197			do_force = 1;
4198			break;
4199
4200		case 'N':
4201			do_resolv = 1;
4202			break;
4203
4204		case 'n':
4205			test_only = 1;
4206			break;
4207
4208		case 'p':
4209			cmd = optarg;
4210			/*
4211			 * Skip previous args and delete last one, so we
4212			 * pass all but the last argument to the preprocessor
4213			 * via av[optind-1]
4214			 */
4215			av += optind - 1;
4216			ac -= optind - 1;
4217			av[ac-1] = NULL;
4218			fprintf(stderr, "command is %s\n", av[0]);
4219			break;
4220
4221		case 'q':
4222			do_quiet = 1;
4223			break;
4224
4225		case 'S':
4226			show_sets = 1;
4227			break;
4228
4229		default:
4230			errx(EX_USAGE, "bad arguments, for usage"
4231			     " summary ``ipfw''");
4232		}
4233
4234		if (cmd != NULL)
4235			break;
4236	}
4237
4238	if (cmd == NULL && ac != optind + 1) {
4239		fprintf(stderr, "ac %d, optind %d\n", ac, optind);
4240		errx(EX_USAGE, "extraneous filename arguments");
4241	}
4242
4243	if ((f = fopen(filename, "r")) == NULL)
4244		err(EX_UNAVAILABLE, "fopen: %s", filename);
4245
4246	if (cmd != NULL) {			/* pipe through preprocessor */
4247		int pipedes[2];
4248
4249		if (pipe(pipedes) == -1)
4250			err(EX_OSERR, "cannot create pipe");
4251
4252		preproc = fork();
4253		if (preproc == -1)
4254			err(EX_OSERR, "cannot fork");
4255
4256		if (preproc == 0) {
4257			/*
4258			 * Child, will run the preprocessor with the
4259			 * file on stdin and the pipe on stdout.
4260			 */
4261			if (dup2(fileno(f), 0) == -1
4262			    || dup2(pipedes[1], 1) == -1)
4263				err(EX_OSERR, "dup2()");
4264			fclose(f);
4265			close(pipedes[1]);
4266			close(pipedes[0]);
4267			execvp(cmd, av);
4268			err(EX_OSERR, "execvp(%s) failed", cmd);
4269		} else { /* parent, will reopen f as the pipe */
4270			fclose(f);
4271			close(pipedes[1]);
4272			if ((f = fdopen(pipedes[0], "r")) == NULL) {
4273				int savederrno = errno;
4274
4275				(void)kill(preproc, SIGTERM);
4276				errno = savederrno;
4277				err(EX_OSERR, "fdopen()");
4278			}
4279		}
4280	}
4281
4282	while (fgets(buf, BUFSIZ, f)) {		/* read commands */
4283		char linename[10];
4284		char *args[1];
4285
4286		lineno++;
4287		sprintf(linename, "Line %d", lineno);
4288		setprogname(linename); /* XXX */
4289		args[0] = buf;
4290		ipfw_main(1, args);
4291	}
4292	fclose(f);
4293	if (cmd != NULL) {
4294		int status;
4295
4296		if (waitpid(preproc, &status, 0) == -1)
4297			errx(EX_OSERR, "waitpid()");
4298		if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
4299			errx(EX_UNAVAILABLE,
4300			    "preprocessor exited with status %d",
4301			    WEXITSTATUS(status));
4302		else if (WIFSIGNALED(status))
4303			errx(EX_UNAVAILABLE,
4304			    "preprocessor exited with signal %d",
4305			    WTERMSIG(status));
4306	}
4307}
4308
4309int
4310main(int ac, char *av[])
4311{
4312	/*
4313	 * If the last argument is an absolute pathname, interpret it
4314	 * as a file to be preprocessed.
4315	 */
4316
4317	if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0)
4318		ipfw_readfile(ac, av);
4319	else {
4320		if (ipfw_main(ac-1, av+1))
4321			show_usage();
4322	}
4323	return EX_OK;
4324}
4325