ipfw2.c revision 342164
1/*
2 * Copyright (c) 2002-2003 Luigi Rizzo
3 * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
4 * Copyright (c) 1994 Ugen J.S.Antsilevich
5 *
6 * Idea and grammar partially left from:
7 * Copyright (c) 1993 Daniel Boulet
8 *
9 * Redistribution and use in source forms, with and without modification,
10 * are permitted provided that this entire comment appears intact.
11 *
12 * Redistribution in binary form may occur without any restrictions.
13 * Obviously, it would be nice if you gave credit where credit is due
14 * but requiring it would be too onerous.
15 *
16 * This software is provided ``AS IS'' without any warranties of any kind.
17 *
18 * NEW command line interface for IP firewall facility
19 *
20 * $FreeBSD: stable/11/sbin/ipfw/ipfw2.c 342164 2018-12-17 10:43:23Z ae $
21 */
22
23#include <sys/types.h>
24#include <sys/param.h>
25#include <sys/socket.h>
26#include <sys/sockio.h>
27#include <sys/sysctl.h>
28
29#include "ipfw2.h"
30
31#include <ctype.h>
32#include <err.h>
33#include <errno.h>
34#include <grp.h>
35#include <jail.h>
36#include <netdb.h>
37#include <pwd.h>
38#include <stdio.h>
39#include <stdarg.h>
40#include <stdint.h>
41#include <stdlib.h>
42#include <string.h>
43#include <sysexits.h>
44#include <time.h>	/* ctime */
45#include <timeconv.h>	/* _long_to_time */
46#include <unistd.h>
47#include <fcntl.h>
48#include <stddef.h>	/* offsetof */
49
50#include <net/ethernet.h>
51#include <net/if.h>		/* only IFNAMSIZ */
52#include <netinet/in.h>
53#include <netinet/in_systm.h>	/* only n_short, n_long */
54#include <netinet/ip.h>
55#include <netinet/ip_icmp.h>
56#include <netinet/ip_fw.h>
57#include <netinet/tcp.h>
58#include <arpa/inet.h>
59
60struct cmdline_opts co;	/* global options */
61
62struct format_opts {
63	int bcwidth;
64	int pcwidth;
65	int show_counters;
66	int show_time;		/* show timestamp */
67	uint32_t set_mask;	/* enabled sets mask */
68	uint32_t flags;		/* request flags */
69	uint32_t first;		/* first rule to request */
70	uint32_t last;		/* last rule to request */
71	uint32_t dcnt;		/* number of dynamic states */
72	ipfw_obj_ctlv *tstate;	/* table state data */
73};
74
75int resvd_set_number = RESVD_SET;
76
77int ipfw_socket = -1;
78
79#define	CHECK_LENGTH(v, len) do {				\
80	if ((v) < (len))					\
81		errx(EX_DATAERR, "Rule too long");		\
82	} while (0)
83/*
84 * Check if we have enough space in cmd buffer. Note that since
85 * first 8? u32 words are reserved by reserved header, full cmd
86 * buffer can't be used, so we need to protect from buffer overrun
87 * only. At the beginning, cblen is less than actual buffer size by
88 * size of ipfw_insn_u32 instruction + 1 u32 work. This eliminates need
89 * for checking small instructions fitting in given range.
90 * We also (ab)use the fact that ipfw_insn is always the first field
91 * for any custom instruction.
92 */
93#define	CHECK_CMDLEN	CHECK_LENGTH(cblen, F_LEN((ipfw_insn *)cmd))
94
95#define GET_UINT_ARG(arg, min, max, tok, s_x) do {			\
96	if (!av[0])							\
97		errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \
98	if (_substrcmp(*av, "tablearg") == 0) {				\
99		arg = IP_FW_TARG;					\
100		break;							\
101	}								\
102									\
103	{								\
104	long _xval;							\
105	char *end;							\
106									\
107	_xval = strtol(*av, &end, 10);					\
108									\
109	if (!isdigit(**av) || *end != '\0' || (_xval == 0 && errno == EINVAL)) \
110		errx(EX_DATAERR, "%s: invalid argument: %s",		\
111		    match_value(s_x, tok), *av);			\
112									\
113	if (errno == ERANGE || _xval < min || _xval > max)		\
114		errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \
115		    match_value(s_x, tok), min, max, *av);		\
116									\
117	if (_xval == IP_FW_TARG)					\
118		errx(EX_DATAERR, "%s: illegal argument value: %s",	\
119		    match_value(s_x, tok), *av);			\
120	arg = _xval;							\
121	}								\
122} while (0)
123
124static struct _s_x f_tcpflags[] = {
125	{ "syn", TH_SYN },
126	{ "fin", TH_FIN },
127	{ "ack", TH_ACK },
128	{ "psh", TH_PUSH },
129	{ "rst", TH_RST },
130	{ "urg", TH_URG },
131	{ "tcp flag", 0 },
132	{ NULL,	0 }
133};
134
135static struct _s_x f_tcpopts[] = {
136	{ "mss",	IP_FW_TCPOPT_MSS },
137	{ "maxseg",	IP_FW_TCPOPT_MSS },
138	{ "window",	IP_FW_TCPOPT_WINDOW },
139	{ "sack",	IP_FW_TCPOPT_SACK },
140	{ "ts",		IP_FW_TCPOPT_TS },
141	{ "timestamp",	IP_FW_TCPOPT_TS },
142	{ "cc",		IP_FW_TCPOPT_CC },
143	{ "tcp option",	0 },
144	{ NULL,	0 }
145};
146
147/*
148 * IP options span the range 0 to 255 so we need to remap them
149 * (though in fact only the low 5 bits are significant).
150 */
151static struct _s_x f_ipopts[] = {
152	{ "ssrr",	IP_FW_IPOPT_SSRR},
153	{ "lsrr",	IP_FW_IPOPT_LSRR},
154	{ "rr",		IP_FW_IPOPT_RR},
155	{ "ts",		IP_FW_IPOPT_TS},
156	{ "ip option",	0 },
157	{ NULL,	0 }
158};
159
160static struct _s_x f_iptos[] = {
161	{ "lowdelay",	IPTOS_LOWDELAY},
162	{ "throughput",	IPTOS_THROUGHPUT},
163	{ "reliability", IPTOS_RELIABILITY},
164	{ "mincost",	IPTOS_MINCOST},
165	{ "congestion",	IPTOS_ECN_CE},
166	{ "ecntransport", IPTOS_ECN_ECT0},
167	{ "ip tos option", 0},
168	{ NULL,	0 }
169};
170
171struct _s_x f_ipdscp[] = {
172	{ "af11", IPTOS_DSCP_AF11 >> 2 },	/* 001010 */
173	{ "af12", IPTOS_DSCP_AF12 >> 2 },	/* 001100 */
174	{ "af13", IPTOS_DSCP_AF13 >> 2 },	/* 001110 */
175	{ "af21", IPTOS_DSCP_AF21 >> 2 },	/* 010010 */
176	{ "af22", IPTOS_DSCP_AF22 >> 2 },	/* 010100 */
177	{ "af23", IPTOS_DSCP_AF23 >> 2 },	/* 010110 */
178	{ "af31", IPTOS_DSCP_AF31 >> 2 },	/* 011010 */
179	{ "af32", IPTOS_DSCP_AF32 >> 2 },	/* 011100 */
180	{ "af33", IPTOS_DSCP_AF33 >> 2 },	/* 011110 */
181	{ "af41", IPTOS_DSCP_AF41 >> 2 },	/* 100010 */
182	{ "af42", IPTOS_DSCP_AF42 >> 2 },	/* 100100 */
183	{ "af43", IPTOS_DSCP_AF43 >> 2 },	/* 100110 */
184	{ "be", IPTOS_DSCP_CS0 >> 2 }, 	/* 000000 */
185	{ "ef", IPTOS_DSCP_EF >> 2 },	/* 101110 */
186	{ "cs0", IPTOS_DSCP_CS0 >> 2 },	/* 000000 */
187	{ "cs1", IPTOS_DSCP_CS1 >> 2 },	/* 001000 */
188	{ "cs2", IPTOS_DSCP_CS2 >> 2 },	/* 010000 */
189	{ "cs3", IPTOS_DSCP_CS3 >> 2 },	/* 011000 */
190	{ "cs4", IPTOS_DSCP_CS4 >> 2 },	/* 100000 */
191	{ "cs5", IPTOS_DSCP_CS5 >> 2 },	/* 101000 */
192	{ "cs6", IPTOS_DSCP_CS6 >> 2 },	/* 110000 */
193	{ "cs7", IPTOS_DSCP_CS7 >> 2 },	/* 100000 */
194	{ NULL, 0 }
195};
196
197static struct _s_x limit_masks[] = {
198	{"all",		DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
199	{"src-addr",	DYN_SRC_ADDR},
200	{"src-port",	DYN_SRC_PORT},
201	{"dst-addr",	DYN_DST_ADDR},
202	{"dst-port",	DYN_DST_PORT},
203	{NULL,		0}
204};
205
206/*
207 * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines
208 * This is only used in this code.
209 */
210#define IPPROTO_ETHERTYPE	0x1000
211static struct _s_x ether_types[] = {
212    /*
213     * Note, we cannot use "-:&/" in the names because they are field
214     * separators in the type specifications. Also, we use s = NULL as
215     * end-delimiter, because a type of 0 can be legal.
216     */
217	{ "ip",		0x0800 },
218	{ "ipv4",	0x0800 },
219	{ "ipv6",	0x86dd },
220	{ "arp",	0x0806 },
221	{ "rarp",	0x8035 },
222	{ "vlan",	0x8100 },
223	{ "loop",	0x9000 },
224	{ "trail",	0x1000 },
225	{ "at",		0x809b },
226	{ "atalk",	0x809b },
227	{ "aarp",	0x80f3 },
228	{ "pppoe_disc",	0x8863 },
229	{ "pppoe_sess",	0x8864 },
230	{ "ipx_8022",	0x00E0 },
231	{ "ipx_8023",	0x0000 },
232	{ "ipx_ii",	0x8137 },
233	{ "ipx_snap",	0x8137 },
234	{ "ipx",	0x8137 },
235	{ "ns",		0x0600 },
236	{ NULL,		0 }
237};
238
239static struct _s_x rule_eactions[] = {
240	{ "nat64lsn",		TOK_NAT64LSN },
241	{ "nat64stl",		TOK_NAT64STL },
242	{ "nptv6",		TOK_NPTV6 },
243	{ "tcp-setmss",		TOK_TCPSETMSS },
244	{ NULL, 0 }	/* terminator */
245};
246
247static struct _s_x rule_actions[] = {
248	{ "abort6",		TOK_ABORT6 },
249	{ "abort",		TOK_ABORT },
250	{ "accept",		TOK_ACCEPT },
251	{ "pass",		TOK_ACCEPT },
252	{ "allow",		TOK_ACCEPT },
253	{ "permit",		TOK_ACCEPT },
254	{ "count",		TOK_COUNT },
255	{ "pipe",		TOK_PIPE },
256	{ "queue",		TOK_QUEUE },
257	{ "divert",		TOK_DIVERT },
258	{ "tee",		TOK_TEE },
259	{ "netgraph",		TOK_NETGRAPH },
260	{ "ngtee",		TOK_NGTEE },
261	{ "fwd",		TOK_FORWARD },
262	{ "forward",		TOK_FORWARD },
263	{ "skipto",		TOK_SKIPTO },
264	{ "deny",		TOK_DENY },
265	{ "drop",		TOK_DENY },
266	{ "reject",		TOK_REJECT },
267	{ "reset6",		TOK_RESET6 },
268	{ "reset",		TOK_RESET },
269	{ "unreach6",		TOK_UNREACH6 },
270	{ "unreach",		TOK_UNREACH },
271	{ "check-state",	TOK_CHECKSTATE },
272	{ "//",			TOK_COMMENT },
273	{ "nat",		TOK_NAT },
274	{ "reass",		TOK_REASS },
275	{ "setfib",		TOK_SETFIB },
276	{ "setdscp",		TOK_SETDSCP },
277	{ "call",		TOK_CALL },
278	{ "return",		TOK_RETURN },
279	{ "eaction",		TOK_EACTION },
280	{ "tcp-setmss",		TOK_TCPSETMSS },
281	{ NULL, 0 }	/* terminator */
282};
283
284static struct _s_x rule_action_params[] = {
285	{ "altq",		TOK_ALTQ },
286	{ "log",		TOK_LOG },
287	{ "tag",		TOK_TAG },
288	{ "untag",		TOK_UNTAG },
289	{ NULL, 0 }	/* terminator */
290};
291
292/*
293 * The 'lookup' instruction accepts one of the following arguments.
294 * -1 is a terminator for the list.
295 * Arguments are passed as v[1] in O_DST_LOOKUP options.
296 */
297static int lookup_key[] = {
298	TOK_DSTIP, TOK_SRCIP, TOK_DSTPORT, TOK_SRCPORT,
299	TOK_UID, TOK_JAIL, TOK_DSCP, -1 };
300
301static struct _s_x rule_options[] = {
302	{ "tagged",		TOK_TAGGED },
303	{ "uid",		TOK_UID },
304	{ "gid",		TOK_GID },
305	{ "jail",		TOK_JAIL },
306	{ "in",			TOK_IN },
307	{ "limit",		TOK_LIMIT },
308	{ "set-limit",		TOK_SETLIMIT },
309	{ "keep-state",		TOK_KEEPSTATE },
310	{ "record-state",	TOK_RECORDSTATE },
311	{ "bridged",		TOK_LAYER2 },
312	{ "layer2",		TOK_LAYER2 },
313	{ "out",		TOK_OUT },
314	{ "diverted",		TOK_DIVERTED },
315	{ "diverted-loopback",	TOK_DIVERTEDLOOPBACK },
316	{ "diverted-output",	TOK_DIVERTEDOUTPUT },
317	{ "xmit",		TOK_XMIT },
318	{ "recv",		TOK_RECV },
319	{ "via",		TOK_VIA },
320	{ "fragment",		TOK_FRAG },
321	{ "frag",		TOK_FRAG },
322	{ "fib",		TOK_FIB },
323	{ "ipoptions",		TOK_IPOPTS },
324	{ "ipopts",		TOK_IPOPTS },
325	{ "iplen",		TOK_IPLEN },
326	{ "ipid",		TOK_IPID },
327	{ "ipprecedence",	TOK_IPPRECEDENCE },
328	{ "dscp",		TOK_DSCP },
329	{ "iptos",		TOK_IPTOS },
330	{ "ipttl",		TOK_IPTTL },
331	{ "ipversion",		TOK_IPVER },
332	{ "ipver",		TOK_IPVER },
333	{ "estab",		TOK_ESTAB },
334	{ "established",	TOK_ESTAB },
335	{ "setup",		TOK_SETUP },
336	{ "sockarg",		TOK_SOCKARG },
337	{ "tcpdatalen",		TOK_TCPDATALEN },
338	{ "tcpflags",		TOK_TCPFLAGS },
339	{ "tcpflgs",		TOK_TCPFLAGS },
340	{ "tcpoptions",		TOK_TCPOPTS },
341	{ "tcpopts",		TOK_TCPOPTS },
342	{ "tcpseq",		TOK_TCPSEQ },
343	{ "tcpack",		TOK_TCPACK },
344	{ "tcpwin",		TOK_TCPWIN },
345	{ "icmptype",		TOK_ICMPTYPES },
346	{ "icmptypes",		TOK_ICMPTYPES },
347	{ "dst-ip",		TOK_DSTIP },
348	{ "src-ip",		TOK_SRCIP },
349	{ "dst-port",		TOK_DSTPORT },
350	{ "src-port",		TOK_SRCPORT },
351	{ "proto",		TOK_PROTO },
352	{ "MAC",		TOK_MAC },
353	{ "mac",		TOK_MAC },
354	{ "mac-type",		TOK_MACTYPE },
355	{ "verrevpath",		TOK_VERREVPATH },
356	{ "versrcreach",	TOK_VERSRCREACH },
357	{ "antispoof",		TOK_ANTISPOOF },
358	{ "ipsec",		TOK_IPSEC },
359	{ "icmp6type",		TOK_ICMP6TYPES },
360	{ "icmp6types",		TOK_ICMP6TYPES },
361	{ "ext6hdr",		TOK_EXT6HDR},
362	{ "flow-id",		TOK_FLOWID},
363	{ "ipv6",		TOK_IPV6},
364	{ "ip6",		TOK_IPV6},
365	{ "ipv4",		TOK_IPV4},
366	{ "ip4",		TOK_IPV4},
367	{ "dst-ipv6",		TOK_DSTIP6},
368	{ "dst-ip6",		TOK_DSTIP6},
369	{ "src-ipv6",		TOK_SRCIP6},
370	{ "src-ip6",		TOK_SRCIP6},
371	{ "lookup",		TOK_LOOKUP},
372	{ "flow",		TOK_FLOW},
373	{ "defer-action",	TOK_SKIPACTION },
374	{ "defer-immediate-action",	TOK_SKIPACTION },
375	{ "//",			TOK_COMMENT },
376
377	{ "not",		TOK_NOT },		/* pseudo option */
378	{ "!", /* escape ? */	TOK_NOT },		/* pseudo option */
379	{ "or",			TOK_OR },		/* pseudo option */
380	{ "|", /* escape */	TOK_OR },		/* pseudo option */
381	{ "{",			TOK_STARTBRACE },	/* pseudo option */
382	{ "(",			TOK_STARTBRACE },	/* pseudo option */
383	{ "}",			TOK_ENDBRACE },		/* pseudo option */
384	{ ")",			TOK_ENDBRACE },		/* pseudo option */
385	{ NULL, 0 }	/* terminator */
386};
387
388void bprint_uint_arg(struct buf_pr *bp, const char *str, uint32_t arg);
389static int ipfw_get_config(struct cmdline_opts *co, struct format_opts *fo,
390    ipfw_cfg_lheader **pcfg, size_t *psize);
391static int ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo,
392    ipfw_cfg_lheader *cfg, size_t sz, int ac, char **av);
393static void ipfw_list_tifaces(void);
394
395struct tidx;
396static uint16_t pack_object(struct tidx *tstate, char *name, int otype);
397static uint16_t pack_table(struct tidx *tstate, char *name);
398
399static char *table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx);
400static void object_sort_ctlv(ipfw_obj_ctlv *ctlv);
401static char *object_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx,
402    uint16_t type);
403
404/*
405 * Simple string buffer API.
406 * Used to simplify buffer passing between function and for
407 * transparent overrun handling.
408 */
409
410/*
411 * Allocates new buffer of given size @sz.
412 *
413 * Returns 0 on success.
414 */
415int
416bp_alloc(struct buf_pr *b, size_t size)
417{
418	memset(b, 0, sizeof(struct buf_pr));
419
420	if ((b->buf = calloc(1, size)) == NULL)
421		return (ENOMEM);
422
423	b->ptr = b->buf;
424	b->size = size;
425	b->avail = b->size;
426
427	return (0);
428}
429
430void
431bp_free(struct buf_pr *b)
432{
433
434	free(b->buf);
435}
436
437/*
438 * Flushes buffer so new writer start from beginning.
439 */
440void
441bp_flush(struct buf_pr *b)
442{
443
444	b->ptr = b->buf;
445	b->avail = b->size;
446	b->buf[0] = '\0';
447}
448
449/*
450 * Print message specified by @format and args.
451 * Automatically manage buffer space and transparently handle
452 * buffer overruns.
453 *
454 * Returns number of bytes that should have been printed.
455 */
456int
457bprintf(struct buf_pr *b, char *format, ...)
458{
459	va_list args;
460	int i;
461
462	va_start(args, format);
463
464	i = vsnprintf(b->ptr, b->avail, format, args);
465	va_end(args);
466
467	if (i > b->avail || i < 0) {
468		/* Overflow or print error */
469		b->avail = 0;
470	} else {
471		b->ptr += i;
472		b->avail -= i;
473	}
474
475	b->needed += i;
476
477	return (i);
478}
479
480/*
481 * Special values printer for tablearg-aware opcodes.
482 */
483void
484bprint_uint_arg(struct buf_pr *bp, const char *str, uint32_t arg)
485{
486
487	if (str != NULL)
488		bprintf(bp, "%s", str);
489	if (arg == IP_FW_TARG)
490		bprintf(bp, "tablearg");
491	else
492		bprintf(bp, "%u", arg);
493}
494
495/*
496 * Helper routine to print a possibly unaligned uint64_t on
497 * various platform. If width > 0, print the value with
498 * the desired width, followed by a space;
499 * otherwise, return the required width.
500 */
501int
502pr_u64(struct buf_pr *b, uint64_t *pd, int width)
503{
504#ifdef TCC
505#define U64_FMT "I64"
506#else
507#define U64_FMT "llu"
508#endif
509	uint64_t u;
510	unsigned long long d;
511
512	bcopy (pd, &u, sizeof(u));
513	d = u;
514	return (width > 0) ?
515		bprintf(b, "%*" U64_FMT " ", width, d) :
516		snprintf(NULL, 0, "%" U64_FMT, d) ;
517#undef U64_FMT
518}
519
520
521void *
522safe_calloc(size_t number, size_t size)
523{
524	void *ret = calloc(number, size);
525
526	if (ret == NULL)
527		err(EX_OSERR, "calloc");
528	return ret;
529}
530
531void *
532safe_realloc(void *ptr, size_t size)
533{
534	void *ret = realloc(ptr, size);
535
536	if (ret == NULL)
537		err(EX_OSERR, "realloc");
538	return ret;
539}
540
541/*
542 * Compare things like interface or table names.
543 */
544int
545stringnum_cmp(const char *a, const char *b)
546{
547	int la, lb;
548
549	la = strlen(a);
550	lb = strlen(b);
551
552	if (la > lb)
553		return (1);
554	else if (la < lb)
555		return (-01);
556
557	return (strcmp(a, b));
558}
559
560
561/*
562 * conditionally runs the command.
563 * Selected options or negative -> getsockopt
564 */
565int
566do_cmd(int optname, void *optval, uintptr_t optlen)
567{
568	int i;
569
570	if (co.test_only)
571		return 0;
572
573	if (ipfw_socket == -1)
574		ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
575	if (ipfw_socket < 0)
576		err(EX_UNAVAILABLE, "socket");
577
578	if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
579	    optname == IP_FW_ADD || optname == IP_FW3 ||
580	    optname == IP_FW_NAT_GET_CONFIG ||
581	    optname < 0 ||
582	    optname == IP_FW_NAT_GET_LOG) {
583		if (optname < 0)
584			optname = -optname;
585		i = getsockopt(ipfw_socket, IPPROTO_IP, optname, optval,
586			(socklen_t *)optlen);
587	} else {
588		i = setsockopt(ipfw_socket, IPPROTO_IP, optname, optval, optlen);
589	}
590	return i;
591}
592
593/*
594 * do_set3 - pass ipfw control cmd to kernel
595 * @optname: option name
596 * @optval: pointer to option data
597 * @optlen: option length
598 *
599 * Assumes op3 header is already embedded.
600 * Calls setsockopt() with IP_FW3 as kernel-visible opcode.
601 * Returns 0 on success or errno otherwise.
602 */
603int
604do_set3(int optname, ip_fw3_opheader *op3, size_t optlen)
605{
606
607	if (co.test_only)
608		return (0);
609
610	if (ipfw_socket == -1)
611		ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
612	if (ipfw_socket < 0)
613		err(EX_UNAVAILABLE, "socket");
614
615	op3->opcode = optname;
616
617	return (setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen));
618}
619
620/*
621 * do_get3 - pass ipfw control cmd to kernel
622 * @optname: option name
623 * @optval: pointer to option data
624 * @optlen: pointer to option length
625 *
626 * Assumes op3 header is already embedded.
627 * Calls getsockopt() with IP_FW3 as kernel-visible opcode.
628 * Returns 0 on success or errno otherwise.
629 */
630int
631do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen)
632{
633	int error;
634	socklen_t len;
635
636	if (co.test_only)
637		return (0);
638
639	if (ipfw_socket == -1)
640		ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
641	if (ipfw_socket < 0)
642		err(EX_UNAVAILABLE, "socket");
643
644	op3->opcode = optname;
645
646	len = *optlen;
647	error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, &len);
648	*optlen = len;
649
650	return (error);
651}
652
653/**
654 * match_token takes a table and a string, returns the value associated
655 * with the string (-1 in case of failure).
656 */
657int
658match_token(struct _s_x *table, const char *string)
659{
660	struct _s_x *pt;
661	uint i = strlen(string);
662
663	for (pt = table ; i && pt->s != NULL ; pt++)
664		if (strlen(pt->s) == i && !bcmp(string, pt->s, i))
665			return pt->x;
666	return (-1);
667}
668
669/**
670 * match_token_relaxed takes a table and a string, returns the value associated
671 * with the string for the best match.
672 *
673 * Returns:
674 * value from @table for matched records
675 * -1 for non-matched records
676 * -2 if more than one records match @string.
677 */
678int
679match_token_relaxed(struct _s_x *table, const char *string)
680{
681	struct _s_x *pt, *m;
682	int i, c;
683
684	i = strlen(string);
685	c = 0;
686
687	for (pt = table ; i != 0 && pt->s != NULL ; pt++) {
688		if (strncmp(pt->s, string, i) != 0)
689			continue;
690		m = pt;
691		c++;
692	}
693
694	if (c == 1)
695		return (m->x);
696
697	return (c > 0 ? -2: -1);
698}
699
700int
701get_token(struct _s_x *table, const char *string, const char *errbase)
702{
703	int tcmd;
704
705	if ((tcmd = match_token_relaxed(table, string)) < 0)
706		errx(EX_USAGE, "%s %s %s",
707		    (tcmd == 0) ? "invalid" : "ambiguous", errbase, string);
708
709	return (tcmd);
710}
711
712/**
713 * match_value takes a table and a value, returns the string associated
714 * with the value (NULL in case of failure).
715 */
716char const *
717match_value(struct _s_x *p, int value)
718{
719	for (; p->s != NULL; p++)
720		if (p->x == value)
721			return p->s;
722	return NULL;
723}
724
725size_t
726concat_tokens(char *buf, size_t bufsize, struct _s_x *table, char *delimiter)
727{
728	struct _s_x *pt;
729	int l;
730	size_t sz;
731
732	for (sz = 0, pt = table ; pt->s != NULL; pt++) {
733		l = snprintf(buf + sz, bufsize - sz, "%s%s",
734		    (sz == 0) ? "" : delimiter, pt->s);
735		sz += l;
736		bufsize += l;
737		if (sz > bufsize)
738			return (bufsize);
739	}
740
741	return (sz);
742}
743
744/*
745 * helper function to process a set of flags and set bits in the
746 * appropriate masks.
747 */
748int
749fill_flags(struct _s_x *flags, char *p, char **e, uint32_t *set,
750    uint32_t *clear)
751{
752	char *q;	/* points to the separator */
753	int val;
754	uint32_t *which;	/* mask we are working on */
755
756	while (p && *p) {
757		if (*p == '!') {
758			p++;
759			which = clear;
760		} else
761			which = set;
762		q = strchr(p, ',');
763		if (q)
764			*q++ = '\0';
765		val = match_token(flags, p);
766		if (val <= 0) {
767			if (e != NULL)
768				*e = p;
769			return (-1);
770		}
771		*which |= (uint32_t)val;
772		p = q;
773	}
774	return (0);
775}
776
777void
778print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint32_t set)
779{
780	char const *comma = "";
781	int i, l;
782
783	for (i = 0; list[i].x != 0; i++) {
784		if ((set & list[i].x) == 0)
785			continue;
786
787		set &= ~list[i].x;
788		l = snprintf(buf, sz, "%s%s", comma, list[i].s);
789		if (l >= sz)
790			return;
791		comma = ",";
792		buf += l;
793		sz -=l;
794	}
795}
796
797/*
798 * _substrcmp takes two strings and returns 1 if they do not match,
799 * and 0 if they match exactly or the first string is a sub-string
800 * of the second.  A warning is printed to stderr in the case that the
801 * first string is a sub-string of the second.
802 *
803 * This function will be removed in the future through the usual
804 * deprecation process.
805 */
806int
807_substrcmp(const char *str1, const char* str2)
808{
809
810	if (strncmp(str1, str2, strlen(str1)) != 0)
811		return 1;
812
813	if (strlen(str1) != strlen(str2))
814		warnx("DEPRECATED: '%s' matched '%s' as a sub-string",
815		    str1, str2);
816	return 0;
817}
818
819/*
820 * _substrcmp2 takes three strings and returns 1 if the first two do not match,
821 * and 0 if they match exactly or the second string is a sub-string
822 * of the first.  A warning is printed to stderr in the case that the
823 * first string does not match the third.
824 *
825 * This function exists to warn about the bizarre construction
826 * strncmp(str, "by", 2) which is used to allow people to use a shortcut
827 * for "bytes".  The problem is that in addition to accepting "by",
828 * "byt", "byte", and "bytes", it also excepts "by_rabid_dogs" and any
829 * other string beginning with "by".
830 *
831 * This function will be removed in the future through the usual
832 * deprecation process.
833 */
834int
835_substrcmp2(const char *str1, const char* str2, const char* str3)
836{
837
838	if (strncmp(str1, str2, strlen(str2)) != 0)
839		return 1;
840
841	if (strcmp(str1, str3) != 0)
842		warnx("DEPRECATED: '%s' matched '%s'",
843		    str1, str3);
844	return 0;
845}
846
847/*
848 * prints one port, symbolic or numeric
849 */
850static void
851print_port(struct buf_pr *bp, int proto, uint16_t port)
852{
853
854	if (proto == IPPROTO_ETHERTYPE) {
855		char const *s;
856
857		if (co.do_resolv && (s = match_value(ether_types, port)) )
858			bprintf(bp, "%s", s);
859		else
860			bprintf(bp, "0x%04x", port);
861	} else {
862		struct servent *se = NULL;
863		if (co.do_resolv) {
864			struct protoent *pe = getprotobynumber(proto);
865
866			se = getservbyport(htons(port), pe ? pe->p_name : NULL);
867		}
868		if (se)
869			bprintf(bp, "%s", se->s_name);
870		else
871			bprintf(bp, "%d", port);
872	}
873}
874
875static struct _s_x _port_name[] = {
876	{"dst-port",	O_IP_DSTPORT},
877	{"src-port",	O_IP_SRCPORT},
878	{"ipid",	O_IPID},
879	{"iplen",	O_IPLEN},
880	{"ipttl",	O_IPTTL},
881	{"mac-type",	O_MAC_TYPE},
882	{"tcpdatalen",	O_TCPDATALEN},
883	{"tcpwin",	O_TCPWIN},
884	{"tagged",	O_TAGGED},
885	{NULL,		0}
886};
887
888/*
889 * Print the values in a list 16-bit items of the types above.
890 * XXX todo: add support for mask.
891 */
892static void
893print_newports(struct buf_pr *bp, ipfw_insn_u16 *cmd, int proto, int opcode)
894{
895	uint16_t *p = cmd->ports;
896	int i;
897	char const *sep;
898
899	if (opcode != 0) {
900		sep = match_value(_port_name, opcode);
901		if (sep == NULL)
902			sep = "???";
903		bprintf(bp, " %s", sep);
904	}
905	sep = " ";
906	for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
907		bprintf(bp, "%s", sep);
908		print_port(bp, proto, p[0]);
909		if (p[0] != p[1]) {
910			bprintf(bp, "-");
911			print_port(bp, proto, p[1]);
912		}
913		sep = ",";
914	}
915}
916
917/*
918 * Like strtol, but also translates service names into port numbers
919 * for some protocols.
920 * In particular:
921 *	proto == -1 disables the protocol check;
922 *	proto == IPPROTO_ETHERTYPE looks up an internal table
923 *	proto == <some value in /etc/protocols> matches the values there.
924 * Returns *end == s in case the parameter is not found.
925 */
926static int
927strtoport(char *s, char **end, int base, int proto)
928{
929	char *p, *buf;
930	char *s1;
931	int i;
932
933	*end = s;		/* default - not found */
934	if (*s == '\0')
935		return 0;	/* not found */
936
937	if (isdigit(*s))
938		return strtol(s, end, base);
939
940	/*
941	 * find separator. '\\' escapes the next char.
942	 */
943	for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++)
944		if (*s1 == '\\' && s1[1] != '\0')
945			s1++;
946
947	buf = safe_calloc(s1 - s + 1, 1);
948
949	/*
950	 * copy into a buffer skipping backslashes
951	 */
952	for (p = s, i = 0; p != s1 ; p++)
953		if (*p != '\\')
954			buf[i++] = *p;
955	buf[i++] = '\0';
956
957	if (proto == IPPROTO_ETHERTYPE) {
958		i = match_token(ether_types, buf);
959		free(buf);
960		if (i != -1) {	/* found */
961			*end = s1;
962			return i;
963		}
964	} else {
965		struct protoent *pe = NULL;
966		struct servent *se;
967
968		if (proto != 0)
969			pe = getprotobynumber(proto);
970		setservent(1);
971		se = getservbyname(buf, pe ? pe->p_name : NULL);
972		free(buf);
973		if (se != NULL) {
974			*end = s1;
975			return ntohs(se->s_port);
976		}
977	}
978	return 0;	/* not found */
979}
980
981/*
982 * Fill the body of the command with the list of port ranges.
983 */
984static int
985fill_newports(ipfw_insn_u16 *cmd, char *av, int proto, int cblen)
986{
987	uint16_t a, b, *p = cmd->ports;
988	int i = 0;
989	char *s = av;
990
991	while (*s) {
992		a = strtoport(av, &s, 0, proto);
993		if (s == av) 			/* empty or invalid argument */
994			return (0);
995
996		CHECK_LENGTH(cblen, i + 2);
997
998		switch (*s) {
999		case '-':			/* a range */
1000			av = s + 1;
1001			b = strtoport(av, &s, 0, proto);
1002			/* Reject expressions like '1-abc' or '1-2-3'. */
1003			if (s == av || (*s != ',' && *s != '\0'))
1004				return (0);
1005			p[0] = a;
1006			p[1] = b;
1007			break;
1008		case ',':			/* comma separated list */
1009		case '\0':
1010			p[0] = p[1] = a;
1011			break;
1012		default:
1013			warnx("port list: invalid separator <%c> in <%s>",
1014				*s, av);
1015			return (0);
1016		}
1017
1018		i++;
1019		p += 2;
1020		av = s + 1;
1021	}
1022	if (i > 0) {
1023		if (i + 1 > F_LEN_MASK)
1024			errx(EX_DATAERR, "too many ports/ranges\n");
1025		cmd->o.len |= i + 1;	/* leave F_NOT and F_OR untouched */
1026	}
1027	return (i);
1028}
1029
1030/*
1031 * Fill the body of the command with the list of DiffServ codepoints.
1032 */
1033static void
1034fill_dscp(ipfw_insn *cmd, char *av, int cblen)
1035{
1036	uint32_t *low, *high;
1037	char *s = av, *a;
1038	int code;
1039
1040	cmd->opcode = O_DSCP;
1041	cmd->len |= F_INSN_SIZE(ipfw_insn_u32) + 1;
1042
1043	CHECK_CMDLEN;
1044
1045	low = (uint32_t *)(cmd + 1);
1046	high = low + 1;
1047
1048	*low = 0;
1049	*high = 0;
1050
1051	while (s != NULL) {
1052		a = strchr(s, ',');
1053
1054		if (a != NULL)
1055			*a++ = '\0';
1056
1057		if (isalpha(*s)) {
1058			if ((code = match_token(f_ipdscp, s)) == -1)
1059				errx(EX_DATAERR, "Unknown DSCP code");
1060		} else {
1061			code = strtoul(s, NULL, 10);
1062			if (code < 0 || code > 63)
1063				errx(EX_DATAERR, "Invalid DSCP value");
1064		}
1065
1066		if (code >= 32)
1067			*high |= 1 << (code - 32);
1068		else
1069			*low |= 1 << code;
1070
1071		s = a;
1072	}
1073}
1074
1075static struct _s_x icmpcodes[] = {
1076      { "net",			ICMP_UNREACH_NET },
1077      { "host",			ICMP_UNREACH_HOST },
1078      { "protocol",		ICMP_UNREACH_PROTOCOL },
1079      { "port",			ICMP_UNREACH_PORT },
1080      { "needfrag",		ICMP_UNREACH_NEEDFRAG },
1081      { "srcfail",		ICMP_UNREACH_SRCFAIL },
1082      { "net-unknown",		ICMP_UNREACH_NET_UNKNOWN },
1083      { "host-unknown",		ICMP_UNREACH_HOST_UNKNOWN },
1084      { "isolated",		ICMP_UNREACH_ISOLATED },
1085      { "net-prohib",		ICMP_UNREACH_NET_PROHIB },
1086      { "host-prohib",		ICMP_UNREACH_HOST_PROHIB },
1087      { "tosnet",		ICMP_UNREACH_TOSNET },
1088      { "toshost",		ICMP_UNREACH_TOSHOST },
1089      { "filter-prohib",	ICMP_UNREACH_FILTER_PROHIB },
1090      { "host-precedence",	ICMP_UNREACH_HOST_PRECEDENCE },
1091      { "precedence-cutoff",	ICMP_UNREACH_PRECEDENCE_CUTOFF },
1092      { NULL, 0 }
1093};
1094
1095static void
1096fill_reject_code(u_short *codep, char *str)
1097{
1098	int val;
1099	char *s;
1100
1101	val = strtoul(str, &s, 0);
1102	if (s == str || *s != '\0' || val >= 0x100)
1103		val = match_token(icmpcodes, str);
1104	if (val < 0)
1105		errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
1106	*codep = val;
1107	return;
1108}
1109
1110static void
1111print_reject_code(struct buf_pr *bp, uint16_t code)
1112{
1113	char const *s;
1114
1115	if ((s = match_value(icmpcodes, code)) != NULL)
1116		bprintf(bp, "unreach %s", s);
1117	else
1118		bprintf(bp, "unreach %u", code);
1119}
1120
1121/*
1122 * Returns the number of bits set (from left) in a contiguous bitmask,
1123 * or -1 if the mask is not contiguous.
1124 * XXX this needs a proper fix.
1125 * This effectively works on masks in big-endian (network) format.
1126 * when compiled on little endian architectures.
1127 *
1128 * First bit is bit 7 of the first byte -- note, for MAC addresses,
1129 * the first bit on the wire is bit 0 of the first byte.
1130 * len is the max length in bits.
1131 */
1132int
1133contigmask(uint8_t *p, int len)
1134{
1135	int i, n;
1136
1137	for (i=0; i<len ; i++)
1138		if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
1139			break;
1140	for (n=i+1; n < len; n++)
1141		if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
1142			return -1; /* mask not contiguous */
1143	return i;
1144}
1145
1146/*
1147 * print flags set/clear in the two bitmasks passed as parameters.
1148 * There is a specialized check for f_tcpflags.
1149 */
1150static void
1151print_flags(struct buf_pr *bp, char const *name, ipfw_insn *cmd,
1152    struct _s_x *list)
1153{
1154	char const *comma = "";
1155	int i;
1156	uint8_t set = cmd->arg1 & 0xff;
1157	uint8_t clear = (cmd->arg1 >> 8) & 0xff;
1158
1159	if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) {
1160		bprintf(bp, " setup");
1161		return;
1162	}
1163
1164	bprintf(bp, " %s ", name);
1165	for (i=0; list[i].x != 0; i++) {
1166		if (set & list[i].x) {
1167			set &= ~list[i].x;
1168			bprintf(bp, "%s%s", comma, list[i].s);
1169			comma = ",";
1170		}
1171		if (clear & list[i].x) {
1172			clear &= ~list[i].x;
1173			bprintf(bp, "%s!%s", comma, list[i].s);
1174			comma = ",";
1175		}
1176	}
1177}
1178
1179
1180/*
1181 * Print the ip address contained in a command.
1182 */
1183static void
1184print_ip(struct buf_pr *bp, const struct format_opts *fo, ipfw_insn_ip *cmd)
1185{
1186	struct hostent *he = NULL;
1187	struct in_addr *ia;
1188	uint32_t len = F_LEN((ipfw_insn *)cmd);
1189	uint32_t *a = ((ipfw_insn_u32 *)cmd)->d;
1190	char *t;
1191
1192	bprintf(bp, " ");
1193	if (cmd->o.opcode == O_IP_DST_LOOKUP && len > F_INSN_SIZE(ipfw_insn_u32)) {
1194		uint32_t d = a[1];
1195		const char *arg = "<invalid>";
1196
1197		if (d < sizeof(lookup_key)/sizeof(lookup_key[0]))
1198			arg = match_value(rule_options, lookup_key[d]);
1199		t = table_search_ctlv(fo->tstate, ((ipfw_insn *)cmd)->arg1);
1200		bprintf(bp, "lookup %s %s", arg, t);
1201		return;
1202	}
1203	if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) {
1204		bprintf(bp, "me");
1205		return;
1206	}
1207	if (cmd->o.opcode == O_IP_SRC_LOOKUP ||
1208	    cmd->o.opcode == O_IP_DST_LOOKUP) {
1209		t = table_search_ctlv(fo->tstate, ((ipfw_insn *)cmd)->arg1);
1210		bprintf(bp, "table(%s", t);
1211		if (len == F_INSN_SIZE(ipfw_insn_u32))
1212			bprintf(bp, ",%u", *a);
1213		bprintf(bp, ")");
1214		return;
1215	}
1216	if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) {
1217		uint32_t x, *map = (uint32_t *)&(cmd->mask);
1218		int i, j;
1219		char comma = '{';
1220
1221		x = cmd->o.arg1 - 1;
1222		x = htonl( ~x );
1223		cmd->addr.s_addr = htonl(cmd->addr.s_addr);
1224		bprintf(bp, "%s/%d", inet_ntoa(cmd->addr),
1225			contigmask((uint8_t *)&x, 32));
1226		x = cmd->addr.s_addr = htonl(cmd->addr.s_addr);
1227		x &= 0xff; /* base */
1228		/*
1229		 * Print bits and ranges.
1230		 * Locate first bit set (i), then locate first bit unset (j).
1231		 * If we have 3+ consecutive bits set, then print them as a
1232		 * range, otherwise only print the initial bit and rescan.
1233		 */
1234		for (i=0; i < cmd->o.arg1; i++)
1235			if (map[i/32] & (1<<(i & 31))) {
1236				for (j=i+1; j < cmd->o.arg1; j++)
1237					if (!(map[ j/32] & (1<<(j & 31))))
1238						break;
1239				bprintf(bp, "%c%d", comma, i+x);
1240				if (j>i+2) { /* range has at least 3 elements */
1241					bprintf(bp, "-%d", j-1+x);
1242					i = j-1;
1243				}
1244				comma = ',';
1245			}
1246		bprintf(bp, "}");
1247		return;
1248	}
1249	/*
1250	 * len == 2 indicates a single IP, whereas lists of 1 or more
1251	 * addr/mask pairs have len = (2n+1). We convert len to n so we
1252	 * use that to count the number of entries.
1253	 */
1254    for (len = len / 2; len > 0; len--, a += 2) {
1255	int mb =	/* mask length */
1256	    (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ?
1257		32 : contigmask((uint8_t *)&(a[1]), 32);
1258	if (mb == 32 && co.do_resolv)
1259		he = gethostbyaddr((char *)&(a[0]), sizeof(in_addr_t),
1260		    AF_INET);
1261	if (he != NULL)		/* resolved to name */
1262		bprintf(bp, "%s", he->h_name);
1263	else if (mb == 0)	/* any */
1264		bprintf(bp, "any");
1265	else {		/* numeric IP followed by some kind of mask */
1266		ia = (struct in_addr *)&a[0];
1267		bprintf(bp, "%s", inet_ntoa(*ia));
1268		if (mb < 0) {
1269			ia = (struct in_addr *)&a[1];
1270			bprintf(bp, ":%s", inet_ntoa(*ia));
1271		} else if (mb < 32)
1272			bprintf(bp, "/%d", mb);
1273	}
1274	if (len > 1)
1275		bprintf(bp, ",");
1276    }
1277}
1278
1279/*
1280 * prints a MAC address/mask pair
1281 */
1282static void
1283format_mac(struct buf_pr *bp, uint8_t *addr, uint8_t *mask)
1284{
1285	int l = contigmask(mask, 48);
1286
1287	if (l == 0)
1288		bprintf(bp, " any");
1289	else {
1290		bprintf(bp, " %02x:%02x:%02x:%02x:%02x:%02x",
1291		    addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
1292		if (l == -1)
1293			bprintf(bp, "&%02x:%02x:%02x:%02x:%02x:%02x",
1294			    mask[0], mask[1], mask[2],
1295			    mask[3], mask[4], mask[5]);
1296		else if (l < 48)
1297			bprintf(bp, "/%d", l);
1298	}
1299}
1300
1301static void
1302print_mac(struct buf_pr *bp, ipfw_insn_mac *mac)
1303{
1304
1305	bprintf(bp, " MAC");
1306	format_mac(bp, mac->addr, mac->mask);
1307	format_mac(bp, mac->addr + 6, mac->mask + 6);
1308}
1309
1310static void
1311fill_icmptypes(ipfw_insn_u32 *cmd, char *av)
1312{
1313	uint8_t type;
1314
1315	cmd->d[0] = 0;
1316	while (*av) {
1317		if (*av == ',')
1318			av++;
1319
1320		type = strtoul(av, &av, 0);
1321
1322		if (*av != ',' && *av != '\0')
1323			errx(EX_DATAERR, "invalid ICMP type");
1324
1325		if (type > 31)
1326			errx(EX_DATAERR, "ICMP type out of range");
1327
1328		cmd->d[0] |= 1 << type;
1329	}
1330	cmd->o.opcode = O_ICMPTYPE;
1331	cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
1332}
1333
1334static void
1335print_icmptypes(struct buf_pr *bp, ipfw_insn_u32 *cmd)
1336{
1337	int i;
1338	char sep= ' ';
1339
1340	bprintf(bp, " icmptypes");
1341	for (i = 0; i < 32; i++) {
1342		if ( (cmd->d[0] & (1 << (i))) == 0)
1343			continue;
1344		bprintf(bp, "%c%d", sep, i);
1345		sep = ',';
1346	}
1347}
1348
1349static void
1350print_dscp(struct buf_pr *bp, ipfw_insn_u32 *cmd)
1351{
1352	int i = 0;
1353	uint32_t *v;
1354	char sep= ' ';
1355	const char *code;
1356
1357	bprintf(bp, " dscp");
1358	v = cmd->d;
1359	while (i < 64) {
1360		if (*v & (1 << i)) {
1361			if ((code = match_value(f_ipdscp, i)) != NULL)
1362				bprintf(bp, "%c%s", sep, code);
1363			else
1364				bprintf(bp, "%c%d", sep, i);
1365			sep = ',';
1366		}
1367
1368		if ((++i % 32) == 0)
1369			v++;
1370	}
1371}
1372
1373#define	insntod(cmd, type)	((ipfw_insn_ ## type *)(cmd))
1374struct show_state {
1375	struct ip_fw_rule	*rule;
1376	const ipfw_insn		*eaction;
1377	uint8_t			*printed;
1378	int			flags;
1379#define	HAVE_PROTO		0x0001
1380#define	HAVE_SRCIP		0x0002
1381#define	HAVE_DSTIP		0x0004
1382#define	HAVE_PROBE_STATE	0x0008
1383	int			proto;
1384	int			or_block;
1385};
1386
1387static int
1388init_show_state(struct show_state *state, struct ip_fw_rule *rule)
1389{
1390
1391	state->printed = calloc(rule->cmd_len, sizeof(uint8_t));
1392	if (state->printed == NULL)
1393		return (ENOMEM);
1394	state->rule = rule;
1395	state->eaction = NULL;
1396	state->flags = 0;
1397	state->proto = 0;
1398	state->or_block = 0;
1399	return (0);
1400}
1401
1402static void
1403free_show_state(struct show_state *state)
1404{
1405
1406	free(state->printed);
1407}
1408
1409static uint8_t
1410is_printed_opcode(struct show_state *state, const ipfw_insn *cmd)
1411{
1412
1413	return (state->printed[cmd - state->rule->cmd]);
1414}
1415
1416static void
1417mark_printed(struct show_state *state, const ipfw_insn *cmd)
1418{
1419
1420	state->printed[cmd - state->rule->cmd] = 1;
1421}
1422
1423static void
1424print_limit_mask(struct buf_pr *bp, const ipfw_insn_limit *limit)
1425{
1426	struct _s_x *p = limit_masks;
1427	char const *comma = " ";
1428	uint8_t x;
1429
1430	for (x = limit->limit_mask; p->x != 0; p++) {
1431		if ((x & p->x) == p->x) {
1432			x &= ~p->x;
1433			bprintf(bp, "%s%s", comma, p->s);
1434			comma = ",";
1435		}
1436	}
1437	bprint_uint_arg(bp, " ", limit->conn_limit);
1438}
1439
1440static int
1441print_instruction(struct buf_pr *bp, const struct format_opts *fo,
1442    struct show_state *state, ipfw_insn *cmd)
1443{
1444	struct protoent *pe;
1445	struct passwd *pwd;
1446	struct group *grp;
1447	const char *s;
1448	double d;
1449
1450	if (is_printed_opcode(state, cmd))
1451		return (0);
1452	if ((cmd->len & F_OR) != 0 && state->or_block == 0)
1453		bprintf(bp, " {");
1454	if (cmd->opcode != O_IN && (cmd->len & F_NOT) != 0)
1455		bprintf(bp, " not");
1456
1457	switch (cmd->opcode) {
1458	case O_PROB:
1459		d = 1.0 * insntod(cmd, u32)->d[0] / 0x7fffffff;
1460		bprintf(bp, "prob %f ", d);
1461		break;
1462	case O_PROBE_STATE: /* no need to print anything here */
1463		state->flags |= HAVE_PROBE_STATE;
1464		break;
1465	case O_IP_SRC:
1466	case O_IP_SRC_LOOKUP:
1467	case O_IP_SRC_MASK:
1468	case O_IP_SRC_ME:
1469	case O_IP_SRC_SET:
1470		if (state->flags & HAVE_SRCIP)
1471			bprintf(bp, " src-ip");
1472		print_ip(bp, fo, insntod(cmd, ip));
1473		break;
1474	case O_IP_DST:
1475	case O_IP_DST_LOOKUP:
1476	case O_IP_DST_MASK:
1477	case O_IP_DST_ME:
1478	case O_IP_DST_SET:
1479		if (state->flags & HAVE_DSTIP)
1480			bprintf(bp, " dst-ip");
1481		print_ip(bp, fo, insntod(cmd, ip));
1482		break;
1483	case O_IP6_SRC:
1484	case O_IP6_SRC_MASK:
1485	case O_IP6_SRC_ME:
1486		if (state->flags & HAVE_SRCIP)
1487			bprintf(bp, " src-ip6");
1488		print_ip6(bp, insntod(cmd, ip6));
1489		break;
1490	case O_IP6_DST:
1491	case O_IP6_DST_MASK:
1492	case O_IP6_DST_ME:
1493		if (state->flags & HAVE_DSTIP)
1494			bprintf(bp, " dst-ip6");
1495		print_ip6(bp, insntod(cmd, ip6));
1496		break;
1497	case O_FLOW6ID:
1498		print_flow6id(bp, insntod(cmd, u32));
1499		break;
1500	case O_IP_DSTPORT:
1501	case O_IP_SRCPORT:
1502		print_newports(bp, insntod(cmd, u16), state->proto,
1503		    (state->flags & (HAVE_SRCIP | HAVE_DSTIP)) ==
1504		    (HAVE_SRCIP | HAVE_DSTIP) ?  cmd->opcode: 0);
1505		break;
1506	case O_PROTO:
1507		pe = getprotobynumber(cmd->arg1);
1508		if (state->flags & HAVE_PROTO)
1509			bprintf(bp, " proto");
1510		if (pe != NULL)
1511			bprintf(bp, " %s", pe->p_name);
1512		else
1513			bprintf(bp, " %u", cmd->arg1);
1514		state->proto = cmd->arg1;
1515		break;
1516	case O_MACADDR2:
1517		print_mac(bp, insntod(cmd, mac));
1518		break;
1519	case O_MAC_TYPE:
1520		print_newports(bp, insntod(cmd, u16),
1521		    IPPROTO_ETHERTYPE, cmd->opcode);
1522		break;
1523	case O_FRAG:
1524		bprintf(bp, " frag");
1525		break;
1526	case O_FIB:
1527		bprintf(bp, " fib %u", cmd->arg1);
1528		break;
1529	case O_SOCKARG:
1530		bprintf(bp, " sockarg");
1531		break;
1532	case O_IN:
1533		bprintf(bp, cmd->len & F_NOT ? " out" : " in");
1534		break;
1535	case O_DIVERTED:
1536		switch (cmd->arg1) {
1537		case 3:
1538			bprintf(bp, " diverted");
1539			break;
1540		case 2:
1541			bprintf(bp, " diverted-output");
1542			break;
1543		case 1:
1544			bprintf(bp, " diverted-loopback");
1545			break;
1546		default:
1547			bprintf(bp, " diverted-?<%u>", cmd->arg1);
1548			break;
1549		}
1550		break;
1551	case O_LAYER2:
1552		bprintf(bp, " layer2");
1553		break;
1554	case O_XMIT:
1555	case O_RECV:
1556	case O_VIA:
1557		if (cmd->opcode == O_XMIT)
1558			s = "xmit";
1559		else if (cmd->opcode == O_RECV)
1560			s = "recv";
1561		else /* if (cmd->opcode == O_VIA) */
1562			s = "via";
1563		switch (insntod(cmd, if)->name[0]) {
1564		case '\0':
1565			bprintf(bp, " %s %s", s,
1566			    inet_ntoa(insntod(cmd, if)->p.ip));
1567			break;
1568		case '\1':
1569			bprintf(bp, " %s table(%s)", s,
1570			    table_search_ctlv(fo->tstate,
1571			    insntod(cmd, if)->p.kidx));
1572			break;
1573		default:
1574			bprintf(bp, " %s %s", s,
1575			    insntod(cmd, if)->name);
1576		}
1577		break;
1578	case O_IP_FLOW_LOOKUP:
1579		s = table_search_ctlv(fo->tstate, cmd->arg1);
1580		bprintf(bp, " flow table(%s", s);
1581		if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))
1582			bprintf(bp, ",%u", insntod(cmd, u32)->d[0]);
1583		bprintf(bp, ")");
1584		break;
1585	case O_IPID:
1586	case O_IPTTL:
1587	case O_IPLEN:
1588	case O_TCPDATALEN:
1589	case O_TCPWIN:
1590		if (F_LEN(cmd) == 1) {
1591			switch (cmd->opcode) {
1592			case O_IPID:
1593				s = "ipid";
1594				break;
1595			case O_IPTTL:
1596				s = "ipttl";
1597				break;
1598			case O_IPLEN:
1599				s = "iplen";
1600				break;
1601			case O_TCPDATALEN:
1602				s = "tcpdatalen";
1603				break;
1604			case O_TCPWIN:
1605				s = "tcpwin";
1606				break;
1607			}
1608			bprintf(bp, " %s %u", s, cmd->arg1);
1609		} else
1610			print_newports(bp, insntod(cmd, u16), 0,
1611			    cmd->opcode);
1612		break;
1613	case O_IPVER:
1614		bprintf(bp, " ipver %u", cmd->arg1);
1615		break;
1616	case O_IPPRECEDENCE:
1617		bprintf(bp, " ipprecedence %u", cmd->arg1 >> 5);
1618		break;
1619	case O_DSCP:
1620		print_dscp(bp, insntod(cmd, u32));
1621		break;
1622	case O_IPOPT:
1623		print_flags(bp, "ipoptions", cmd, f_ipopts);
1624		break;
1625	case O_IPTOS:
1626		print_flags(bp, "iptos", cmd, f_iptos);
1627		break;
1628	case O_ICMPTYPE:
1629		print_icmptypes(bp, insntod(cmd, u32));
1630		break;
1631	case O_ESTAB:
1632		bprintf(bp, " established");
1633		break;
1634	case O_TCPFLAGS:
1635		print_flags(bp, "tcpflags", cmd, f_tcpflags);
1636		break;
1637	case O_TCPOPTS:
1638		print_flags(bp, "tcpoptions", cmd, f_tcpopts);
1639		break;
1640	case O_TCPACK:
1641		bprintf(bp, " tcpack %d",
1642		    ntohl(insntod(cmd, u32)->d[0]));
1643		break;
1644	case O_TCPSEQ:
1645		bprintf(bp, " tcpseq %d",
1646		    ntohl(insntod(cmd, u32)->d[0]));
1647		break;
1648	case O_UID:
1649		pwd = getpwuid(insntod(cmd, u32)->d[0]);
1650		if (pwd != NULL)
1651			bprintf(bp, " uid %s", pwd->pw_name);
1652		else
1653			bprintf(bp, " uid %u",
1654			    insntod(cmd, u32)->d[0]);
1655		break;
1656	case O_GID:
1657		grp = getgrgid(insntod(cmd, u32)->d[0]);
1658		if (grp != NULL)
1659			bprintf(bp, " gid %s", grp->gr_name);
1660		else
1661			bprintf(bp, " gid %u",
1662			    insntod(cmd, u32)->d[0]);
1663		break;
1664	case O_JAIL:
1665		bprintf(bp, " jail %d", insntod(cmd, u32)->d[0]);
1666		break;
1667	case O_VERREVPATH:
1668		bprintf(bp, " verrevpath");
1669		break;
1670	case O_VERSRCREACH:
1671		bprintf(bp, " versrcreach");
1672		break;
1673	case O_ANTISPOOF:
1674		bprintf(bp, " antispoof");
1675		break;
1676	case O_IPSEC:
1677		bprintf(bp, " ipsec");
1678		break;
1679	case O_NOP:
1680		bprintf(bp, " // %s", (char *)(cmd + 1));
1681		break;
1682	case O_KEEP_STATE:
1683		if (state->flags & HAVE_PROBE_STATE)
1684			bprintf(bp, " keep-state");
1685		else
1686			bprintf(bp, " record-state");
1687		bprintf(bp, " :%s",
1688		    object_search_ctlv(fo->tstate, cmd->arg1,
1689		    IPFW_TLV_STATE_NAME));
1690		break;
1691	case O_LIMIT:
1692		if (state->flags & HAVE_PROBE_STATE)
1693			bprintf(bp, " limit");
1694		else
1695			bprintf(bp, " set-limit");
1696		print_limit_mask(bp, insntod(cmd, limit));
1697		bprintf(bp, " :%s",
1698		    object_search_ctlv(fo->tstate, cmd->arg1,
1699		    IPFW_TLV_STATE_NAME));
1700		break;
1701	case O_IP6:
1702		bprintf(bp, " ip6");
1703		break;
1704	case O_IP4:
1705		bprintf(bp, " ip4");
1706		break;
1707	case O_ICMP6TYPE:
1708		print_icmp6types(bp, insntod(cmd, u32));
1709		break;
1710	case O_EXT_HDR:
1711		print_ext6hdr(bp, cmd);
1712		break;
1713	case O_TAGGED:
1714		if (F_LEN(cmd) == 1)
1715			bprint_uint_arg(bp, " tagged ", cmd->arg1);
1716		else
1717			print_newports(bp, insntod(cmd, u16),
1718				    0, O_TAGGED);
1719		break;
1720	case O_SKIP_ACTION:
1721		bprintf(bp, " defer-immediate-action");
1722		break;
1723	default:
1724		bprintf(bp, " [opcode %d len %d]", cmd->opcode,
1725		    cmd->len);
1726	}
1727	if (cmd->len & F_OR) {
1728		bprintf(bp, " or");
1729		state->or_block = 1;
1730	} else if (state->or_block != 0) {
1731		bprintf(bp, " }");
1732		state->or_block = 0;
1733	}
1734	mark_printed(state, cmd);
1735
1736	return (1);
1737}
1738
1739static ipfw_insn *
1740print_opcode(struct buf_pr *bp, struct format_opts *fo,
1741    struct show_state *state, int opcode)
1742{
1743	ipfw_insn *cmd;
1744	int l;
1745
1746	for (l = state->rule->act_ofs, cmd = state->rule->cmd;
1747	    l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
1748		/* We use zero opcode to print the rest of options */
1749		if (opcode >= 0 && cmd->opcode != opcode)
1750			continue;
1751		/*
1752		 * Skip O_NOP, when we printing the rest
1753		 * of options, it will be handled separately.
1754		 */
1755		if (cmd->opcode == O_NOP && opcode != O_NOP)
1756			continue;
1757		if (!print_instruction(bp, fo, state, cmd))
1758			continue;
1759		return (cmd);
1760	}
1761	return (NULL);
1762}
1763
1764static void
1765print_fwd(struct buf_pr *bp, const ipfw_insn *cmd)
1766{
1767	char buf[INET6_ADDRSTRLEN + IF_NAMESIZE + 2];
1768	ipfw_insn_sa6 *sa6;
1769	ipfw_insn_sa *sa;
1770	uint16_t port;
1771
1772	if (cmd->opcode == O_FORWARD_IP) {
1773		sa = insntod(cmd, sa);
1774		port = sa->sa.sin_port;
1775		if (sa->sa.sin_addr.s_addr == INADDR_ANY)
1776			bprintf(bp, "fwd tablearg");
1777		else
1778			bprintf(bp, "fwd %s", inet_ntoa(sa->sa.sin_addr));
1779	} else {
1780		sa6 = insntod(cmd, sa6);
1781		port = sa6->sa.sin6_port;
1782		bprintf(bp, "fwd ");
1783		if (getnameinfo((const struct sockaddr *)&sa6->sa,
1784		    sizeof(struct sockaddr_in6), buf, sizeof(buf), NULL, 0,
1785		    NI_NUMERICHOST) == 0)
1786			bprintf(bp, "%s", buf);
1787	}
1788	if (port != 0)
1789		bprintf(bp, ",%u", port);
1790}
1791
1792static int
1793print_action_instruction(struct buf_pr *bp, const struct format_opts *fo,
1794    struct show_state *state, const ipfw_insn *cmd)
1795{
1796	const char *s;
1797
1798	if (is_printed_opcode(state, cmd))
1799		return (0);
1800	switch (cmd->opcode) {
1801	case O_CHECK_STATE:
1802		bprintf(bp, "check-state");
1803		if (cmd->arg1 != 0)
1804			s = object_search_ctlv(fo->tstate, cmd->arg1,
1805			    IPFW_TLV_STATE_NAME);
1806		else
1807			s = NULL;
1808		bprintf(bp, " :%s", s ? s: "any");
1809		break;
1810	case O_ACCEPT:
1811		bprintf(bp, "allow");
1812		break;
1813	case O_COUNT:
1814		bprintf(bp, "count");
1815		break;
1816	case O_DENY:
1817		bprintf(bp, "deny");
1818		break;
1819	case O_REJECT:
1820		if (cmd->arg1 == ICMP_REJECT_RST)
1821			bprintf(bp, "reset");
1822		else if (cmd->arg1 == ICMP_REJECT_ABORT)
1823			bprintf(bp, "abort");
1824		else if (cmd->arg1 == ICMP_UNREACH_HOST)
1825			bprintf(bp, "reject");
1826		else
1827			print_reject_code(bp, cmd->arg1);
1828		break;
1829	case O_UNREACH6:
1830		if (cmd->arg1 == ICMP6_UNREACH_RST)
1831			bprintf(bp, "reset6");
1832		else if (cmd->arg1 == ICMP6_UNREACH_ABORT)
1833			bprintf(bp, "abort6");
1834		else
1835			print_unreach6_code(bp, cmd->arg1);
1836		break;
1837	case O_SKIPTO:
1838		bprint_uint_arg(bp, "skipto ", cmd->arg1);
1839		break;
1840	case O_PIPE:
1841		bprint_uint_arg(bp, "pipe ", cmd->arg1);
1842		break;
1843	case O_QUEUE:
1844		bprint_uint_arg(bp, "queue ", cmd->arg1);
1845		break;
1846	case O_DIVERT:
1847		bprint_uint_arg(bp, "divert ", cmd->arg1);
1848		break;
1849	case O_TEE:
1850		bprint_uint_arg(bp, "tee ", cmd->arg1);
1851		break;
1852	case O_NETGRAPH:
1853		bprint_uint_arg(bp, "netgraph ", cmd->arg1);
1854		break;
1855	case O_NGTEE:
1856		bprint_uint_arg(bp, "ngtee ", cmd->arg1);
1857		break;
1858	case O_FORWARD_IP:
1859	case O_FORWARD_IP6:
1860		print_fwd(bp, cmd);
1861		break;
1862	case O_LOG:
1863		if (insntod(cmd, log)->max_log > 0)
1864			bprintf(bp, " log logamount %d",
1865			    insntod(cmd, log)->max_log);
1866		else
1867			bprintf(bp, " log");
1868		break;
1869	case O_ALTQ:
1870#ifndef NO_ALTQ
1871		print_altq_cmd(bp, insntod(cmd, altq));
1872#endif
1873		break;
1874	case O_TAG:
1875		bprint_uint_arg(bp, cmd->len & F_NOT ? " untag ":
1876		    " tag ", cmd->arg1);
1877		break;
1878	case O_NAT:
1879		if (cmd->arg1 != IP_FW_NAT44_GLOBAL)
1880			bprint_uint_arg(bp, "nat ", cmd->arg1);
1881		else
1882			bprintf(bp, "nat global");
1883		break;
1884	case O_SETFIB:
1885		if (cmd->arg1 == IP_FW_TARG)
1886			bprint_uint_arg(bp, "setfib ", cmd->arg1);
1887		else
1888			bprintf(bp, "setfib %u", cmd->arg1 & 0x7FFF);
1889		break;
1890	case O_EXTERNAL_ACTION:
1891		/*
1892		 * The external action can consists of two following
1893		 * each other opcodes - O_EXTERNAL_ACTION and
1894		 * O_EXTERNAL_INSTANCE. The first contains the ID of
1895		 * name of external action. The second contains the ID
1896		 * of name of external action instance.
1897		 * NOTE: in case when external action has no named
1898		 * instances support, the second opcode isn't needed.
1899		 */
1900		state->eaction = cmd;
1901		s = object_search_ctlv(fo->tstate, cmd->arg1,
1902		    IPFW_TLV_EACTION);
1903		if (match_token(rule_eactions, s) != -1)
1904			bprintf(bp, "%s", s);
1905		else
1906			bprintf(bp, "eaction %s", s);
1907		break;
1908	case O_EXTERNAL_INSTANCE:
1909		if (state->eaction == NULL)
1910			break;
1911		/*
1912		 * XXX: we need to teach ipfw(9) to rewrite opcodes
1913		 * in the user buffer on rule addition. When we add
1914		 * the rule, we specify zero TLV type for
1915		 * O_EXTERNAL_INSTANCE object. To show correct
1916		 * rule after `ipfw add` we need to search instance
1917		 * name with zero type. But when we do `ipfw show`
1918		 * we calculate TLV type using IPFW_TLV_EACTION_NAME()
1919		 * macro.
1920		 */
1921		s = object_search_ctlv(fo->tstate, cmd->arg1, 0);
1922		if (s == NULL)
1923			s = object_search_ctlv(fo->tstate,
1924			    cmd->arg1, IPFW_TLV_EACTION_NAME(
1925			    state->eaction->arg1));
1926		bprintf(bp, " %s", s);
1927		break;
1928	case O_EXTERNAL_DATA:
1929		if (state->eaction == NULL)
1930			break;
1931		/*
1932		 * Currently we support data formatting only for
1933		 * external data with datalen u16. For unknown data
1934		 * print its size in bytes.
1935		 */
1936		if (cmd->len == F_INSN_SIZE(ipfw_insn))
1937			bprintf(bp, " %u", cmd->arg1);
1938		else
1939			bprintf(bp, " %ubytes",
1940			    cmd->len * sizeof(uint32_t));
1941		break;
1942	case O_SETDSCP:
1943		if (cmd->arg1 == IP_FW_TARG) {
1944			bprintf(bp, "setdscp tablearg");
1945			break;
1946		}
1947		s = match_value(f_ipdscp, cmd->arg1 & 0x3F);
1948		if (s != NULL)
1949			bprintf(bp, "setdscp %s", s);
1950		else
1951			bprintf(bp, "setdscp %u", cmd->arg1 & 0x3F);
1952		break;
1953	case O_REASS:
1954		bprintf(bp, "reass");
1955		break;
1956	case O_CALLRETURN:
1957		if (cmd->len & F_NOT)
1958			bprintf(bp, "return");
1959		else
1960			bprint_uint_arg(bp, "call ", cmd->arg1);
1961		break;
1962	default:
1963		bprintf(bp, "** unrecognized action %d len %d ",
1964			cmd->opcode, cmd->len);
1965	}
1966	mark_printed(state, cmd);
1967
1968	return (1);
1969}
1970
1971
1972static ipfw_insn *
1973print_action(struct buf_pr *bp, struct format_opts *fo,
1974    struct show_state *state, uint8_t opcode)
1975{
1976	ipfw_insn *cmd;
1977	int l;
1978
1979	for (l = state->rule->cmd_len - state->rule->act_ofs,
1980	    cmd = ACTION_PTR(state->rule); l > 0;
1981	    l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
1982		if (cmd->opcode != opcode)
1983			continue;
1984		if (!print_action_instruction(bp, fo, state, cmd))
1985			continue;
1986		return (cmd);
1987	}
1988	return (NULL);
1989}
1990
1991static void
1992print_proto(struct buf_pr *bp, struct format_opts *fo,
1993    struct show_state *state)
1994{
1995	ipfw_insn *cmd;
1996	int l, proto, ip4, ip6;
1997
1998	/* Count all O_PROTO, O_IP4, O_IP6 instructions. */
1999	proto = ip4 = ip6 = 0;
2000	for (l = state->rule->act_ofs, cmd = state->rule->cmd;
2001	    l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
2002		switch (cmd->opcode) {
2003		case O_PROTO:
2004			proto++;
2005			break;
2006		case O_IP4:
2007			ip4 = 1;
2008			if (cmd->len & F_OR)
2009				ip4++;
2010			break;
2011		case O_IP6:
2012			ip6 = 1;
2013			if (cmd->len & F_OR)
2014				ip6++;
2015			break;
2016		default:
2017			continue;
2018		}
2019	}
2020	if (proto == 0 && ip4 == 0 && ip6 == 0) {
2021		state->proto = IPPROTO_IP;
2022		state->flags |= HAVE_PROTO;
2023		bprintf(bp, " ip");
2024		return;
2025	}
2026	/* To handle the case { ip4 or ip6 }, print opcode with F_OR first */
2027	cmd = NULL;
2028	if (ip4 || ip6)
2029		cmd = print_opcode(bp, fo, state, ip4 > ip6 ? O_IP4: O_IP6);
2030	if (cmd != NULL && (cmd->len & F_OR))
2031		cmd = print_opcode(bp, fo, state, ip4 > ip6 ? O_IP6: O_IP4);
2032	if (cmd == NULL || (cmd->len & F_OR))
2033		for (l = proto; l > 0; l--) {
2034			cmd = print_opcode(bp, fo, state, O_PROTO);
2035			if (cmd == NULL || (cmd->len & F_OR) == 0)
2036				break;
2037		}
2038	/* Initialize proto, it is used by print_newports() */
2039	state->flags |= HAVE_PROTO;
2040	if (state->proto == 0 && ip6 != 0)
2041		state->proto = IPPROTO_IPV6;
2042}
2043
2044static int
2045match_opcode(int opcode, const int opcodes[], size_t nops)
2046{
2047	int i;
2048
2049	for (i = 0; i < nops; i++)
2050		if (opcode == opcodes[i])
2051			return (1);
2052	return (0);
2053}
2054
2055static void
2056print_address(struct buf_pr *bp, struct format_opts *fo,
2057    struct show_state *state, const int opcodes[], size_t nops, int portop,
2058    int flag)
2059{
2060	ipfw_insn *cmd;
2061	int count, l, portcnt, pf;
2062
2063	count = portcnt = 0;
2064	for (l = state->rule->act_ofs, cmd = state->rule->cmd;
2065	    l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
2066		if (match_opcode(cmd->opcode, opcodes, nops))
2067			count++;
2068		else if (cmd->opcode == portop)
2069			portcnt++;
2070	}
2071	if (count == 0)
2072		bprintf(bp, " any");
2073	for (l = state->rule->act_ofs, cmd = state->rule->cmd;
2074	    l > 0 && count > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
2075		if (!match_opcode(cmd->opcode, opcodes, nops))
2076			continue;
2077		print_instruction(bp, fo, state, cmd);
2078		if ((cmd->len & F_OR) == 0)
2079			break;
2080		count--;
2081	}
2082	/*
2083	 * If several O_IP_?PORT opcodes specified, leave them to the
2084	 * options section.
2085	 */
2086	if (portcnt == 1) {
2087		for (l = state->rule->act_ofs, cmd = state->rule->cmd, pf = 0;
2088		    l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
2089			if (cmd->opcode != portop) {
2090				pf = (cmd->len & F_OR);
2091				continue;
2092			}
2093			/* Print opcode iff it is not in OR block. */
2094			if (pf == 0 && (cmd->len & F_OR) == 0)
2095				print_instruction(bp, fo, state, cmd);
2096			break;
2097		}
2098	}
2099	state->flags |= flag;
2100}
2101
2102static const int action_opcodes[] = {
2103	O_CHECK_STATE, O_ACCEPT, O_COUNT, O_DENY, O_REJECT,
2104	O_UNREACH6, O_SKIPTO, O_PIPE, O_QUEUE, O_DIVERT, O_TEE,
2105	O_NETGRAPH, O_NGTEE, O_FORWARD_IP, O_FORWARD_IP6, O_NAT,
2106	O_SETFIB, O_SETDSCP, O_REASS, O_CALLRETURN,
2107	/* keep the following opcodes at the end of the list */
2108	O_EXTERNAL_ACTION, O_EXTERNAL_INSTANCE, O_EXTERNAL_DATA
2109};
2110
2111static const int modifier_opcodes[] = {
2112	O_LOG, O_ALTQ, O_TAG
2113};
2114
2115static const int src_opcodes[] = {
2116	O_IP_SRC, O_IP_SRC_LOOKUP, O_IP_SRC_MASK, O_IP_SRC_ME,
2117	O_IP_SRC_SET, O_IP6_SRC, O_IP6_SRC_MASK, O_IP6_SRC_ME
2118};
2119
2120static const int dst_opcodes[] = {
2121	O_IP_DST, O_IP_DST_LOOKUP, O_IP_DST_MASK, O_IP_DST_ME,
2122	O_IP_DST_SET, O_IP6_DST, O_IP6_DST_MASK, O_IP6_DST_ME
2123};
2124
2125static void
2126show_static_rule(struct cmdline_opts *co, struct format_opts *fo,
2127    struct buf_pr *bp, struct ip_fw_rule *rule, struct ip_fw_bcounter *cntr)
2128{
2129	struct show_state state;
2130	ipfw_insn *cmd;
2131	static int twidth = 0;
2132	int i;
2133
2134	/* Print # DISABLED or skip the rule */
2135	if ((fo->set_mask & (1 << rule->set)) == 0) {
2136		/* disabled mask */
2137		if (!co->show_sets)
2138			return;
2139		else
2140			bprintf(bp, "# DISABLED ");
2141	}
2142	if (init_show_state(&state, rule) != 0) {
2143		warn("init_show_state() failed");
2144		return;
2145	}
2146	bprintf(bp, "%05u ", rule->rulenum);
2147
2148	/* Print counters if enabled */
2149	if (fo->pcwidth > 0 || fo->bcwidth > 0) {
2150		pr_u64(bp, &cntr->pcnt, fo->pcwidth);
2151		pr_u64(bp, &cntr->bcnt, fo->bcwidth);
2152	}
2153
2154	/* Print timestamp */
2155	if (co->do_time == TIMESTAMP_NUMERIC)
2156		bprintf(bp, "%10u ", cntr->timestamp);
2157	else if (co->do_time == TIMESTAMP_STRING) {
2158		char timestr[30];
2159		time_t t = (time_t)0;
2160
2161		if (twidth == 0) {
2162			strcpy(timestr, ctime(&t));
2163			*strchr(timestr, '\n') = '\0';
2164			twidth = strlen(timestr);
2165		}
2166		if (cntr->timestamp > 0) {
2167			t = _long_to_time(cntr->timestamp);
2168
2169			strcpy(timestr, ctime(&t));
2170			*strchr(timestr, '\n') = '\0';
2171			bprintf(bp, "%s ", timestr);
2172		} else {
2173			bprintf(bp, "%*s", twidth, " ");
2174		}
2175	}
2176
2177	/* Print set number */
2178	if (co->show_sets)
2179		bprintf(bp, "set %d ", rule->set);
2180
2181	/* Print the optional "match probability" */
2182	cmd = print_opcode(bp, fo, &state, O_PROB);
2183	/* Print rule action */
2184	for (i = 0; i < nitems(action_opcodes); i++) {
2185		cmd = print_action(bp, fo, &state, action_opcodes[i]);
2186		if (cmd == NULL)
2187			continue;
2188		/* Handle special cases */
2189		switch (cmd->opcode) {
2190		case O_CHECK_STATE:
2191			goto end;
2192		case O_EXTERNAL_ACTION:
2193		case O_EXTERNAL_INSTANCE:
2194			/* External action can have several instructions */
2195			continue;
2196		}
2197		break;
2198	}
2199	/* Print rule modifiers */
2200	for (i = 0; i < nitems(modifier_opcodes); i++)
2201		print_action(bp, fo, &state, modifier_opcodes[i]);
2202	/*
2203	 * Print rule body
2204	 */
2205	if (co->comment_only != 0)
2206		goto end;
2207
2208	if (rule->flags & IPFW_RULE_JUSTOPTS) {
2209		state.flags |= HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP;
2210		goto justopts;
2211	}
2212
2213	print_proto(bp, fo, &state);
2214
2215	/* Print source */
2216	bprintf(bp, " from");
2217	print_address(bp, fo, &state, src_opcodes, nitems(src_opcodes),
2218	    O_IP_SRCPORT, HAVE_SRCIP);
2219
2220	/* Print destination */
2221	bprintf(bp, " to");
2222	print_address(bp, fo, &state, dst_opcodes, nitems(dst_opcodes),
2223	    O_IP_DSTPORT, HAVE_DSTIP);
2224
2225justopts:
2226	/* Print the rest of options */
2227	while (print_opcode(bp, fo, &state, -1))
2228		;
2229end:
2230	/* Print comment at the end */
2231	cmd = print_opcode(bp, fo, &state, O_NOP);
2232	if (co->comment_only != 0 && cmd == NULL)
2233		bprintf(bp, " // ...");
2234	bprintf(bp, "\n");
2235	free_show_state(&state);
2236}
2237
2238static void
2239show_dyn_state(struct cmdline_opts *co, struct format_opts *fo,
2240    struct buf_pr *bp, ipfw_dyn_rule *d)
2241{
2242	struct protoent *pe;
2243	struct in_addr a;
2244	uint16_t rulenum;
2245	char buf[INET6_ADDRSTRLEN];
2246
2247	if (!co->do_expired) {
2248		if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT))
2249			return;
2250	}
2251	bcopy(&d->rule, &rulenum, sizeof(rulenum));
2252	bprintf(bp, "%05d", rulenum);
2253	if (fo->pcwidth > 0 || fo->bcwidth > 0) {
2254		bprintf(bp, " ");
2255		pr_u64(bp, &d->pcnt, fo->pcwidth);
2256		pr_u64(bp, &d->bcnt, fo->bcwidth);
2257		bprintf(bp, "(%ds)", d->expire);
2258	}
2259	switch (d->dyn_type) {
2260	case O_LIMIT_PARENT:
2261		bprintf(bp, " PARENT %d", d->count);
2262		break;
2263	case O_LIMIT:
2264		bprintf(bp, " LIMIT");
2265		break;
2266	case O_KEEP_STATE: /* bidir, no mask */
2267		bprintf(bp, " STATE");
2268		break;
2269	}
2270
2271	if ((pe = getprotobynumber(d->id.proto)) != NULL)
2272		bprintf(bp, " %s", pe->p_name);
2273	else
2274		bprintf(bp, " proto %u", d->id.proto);
2275
2276	if (d->id.addr_type == 4) {
2277		a.s_addr = htonl(d->id.src_ip);
2278		bprintf(bp, " %s %d", inet_ntoa(a), d->id.src_port);
2279
2280		a.s_addr = htonl(d->id.dst_ip);
2281		bprintf(bp, " <-> %s %d", inet_ntoa(a), d->id.dst_port);
2282	} else if (d->id.addr_type == 6) {
2283		bprintf(bp, " %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf,
2284		    sizeof(buf)), d->id.src_port);
2285		bprintf(bp, " <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6,
2286		    buf, sizeof(buf)), d->id.dst_port);
2287	} else
2288		bprintf(bp, " UNKNOWN <-> UNKNOWN");
2289	if (d->kidx != 0)
2290		bprintf(bp, " :%s", object_search_ctlv(fo->tstate,
2291		    d->kidx, IPFW_TLV_STATE_NAME));
2292}
2293
2294static int
2295do_range_cmd(int cmd, ipfw_range_tlv *rt)
2296{
2297	ipfw_range_header rh;
2298	size_t sz;
2299
2300	memset(&rh, 0, sizeof(rh));
2301	memcpy(&rh.range, rt, sizeof(*rt));
2302	rh.range.head.length = sizeof(*rt);
2303	rh.range.head.type = IPFW_TLV_RANGE;
2304	sz = sizeof(rh);
2305
2306	if (do_get3(cmd, &rh.opheader, &sz) != 0)
2307		return (-1);
2308	/* Save number of matched objects */
2309	rt->new_set = rh.range.new_set;
2310	return (0);
2311}
2312
2313/*
2314 * This one handles all set-related commands
2315 * 	ipfw set { show | enable | disable }
2316 * 	ipfw set swap X Y
2317 * 	ipfw set move X to Y
2318 * 	ipfw set move rule X to Y
2319 */
2320void
2321ipfw_sets_handler(char *av[])
2322{
2323	ipfw_range_tlv rt;
2324	char *msg;
2325	size_t size;
2326	uint32_t masks[2];
2327	int i;
2328	uint16_t rulenum;
2329	uint8_t cmd;
2330
2331	av++;
2332	memset(&rt, 0, sizeof(rt));
2333
2334	if (av[0] == NULL)
2335		errx(EX_USAGE, "set needs command");
2336	if (_substrcmp(*av, "show") == 0) {
2337		struct format_opts fo;
2338		ipfw_cfg_lheader *cfg;
2339
2340		memset(&fo, 0, sizeof(fo));
2341		if (ipfw_get_config(&co, &fo, &cfg, &size) != 0)
2342			err(EX_OSERR, "requesting config failed");
2343
2344		for (i = 0, msg = "disable"; i < RESVD_SET; i++)
2345			if ((cfg->set_mask & (1<<i)) == 0) {
2346				printf("%s %d", msg, i);
2347				msg = "";
2348			}
2349		msg = (cfg->set_mask != (uint32_t)-1) ? " enable" : "enable";
2350		for (i = 0; i < RESVD_SET; i++)
2351			if ((cfg->set_mask & (1<<i)) != 0) {
2352				printf("%s %d", msg, i);
2353				msg = "";
2354			}
2355		printf("\n");
2356		free(cfg);
2357	} else if (_substrcmp(*av, "swap") == 0) {
2358		av++;
2359		if ( av[0] == NULL || av[1] == NULL )
2360			errx(EX_USAGE, "set swap needs 2 set numbers\n");
2361		rt.set = atoi(av[0]);
2362		rt.new_set = atoi(av[1]);
2363		if (!isdigit(*(av[0])) || rt.set > RESVD_SET)
2364			errx(EX_DATAERR, "invalid set number %s\n", av[0]);
2365		if (!isdigit(*(av[1])) || rt.new_set > RESVD_SET)
2366			errx(EX_DATAERR, "invalid set number %s\n", av[1]);
2367		i = do_range_cmd(IP_FW_SET_SWAP, &rt);
2368	} else if (_substrcmp(*av, "move") == 0) {
2369		av++;
2370		if (av[0] && _substrcmp(*av, "rule") == 0) {
2371			rt.flags = IPFW_RCFLAG_RANGE; /* move rules to new set */
2372			cmd = IP_FW_XMOVE;
2373			av++;
2374		} else
2375			cmd = IP_FW_SET_MOVE; /* Move set to new one */
2376		if (av[0] == NULL || av[1] == NULL || av[2] == NULL ||
2377				av[3] != NULL ||  _substrcmp(av[1], "to") != 0)
2378			errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
2379		rulenum = atoi(av[0]);
2380		rt.new_set = atoi(av[2]);
2381		if (cmd == IP_FW_XMOVE) {
2382			rt.start_rule = rulenum;
2383			rt.end_rule = rulenum;
2384		} else
2385			rt.set = rulenum;
2386		rt.new_set = atoi(av[2]);
2387		if (!isdigit(*(av[0])) || (cmd == 3 && rt.set > RESVD_SET) ||
2388			(cmd == 2 && rt.start_rule == IPFW_DEFAULT_RULE) )
2389			errx(EX_DATAERR, "invalid source number %s\n", av[0]);
2390		if (!isdigit(*(av[2])) || rt.new_set > RESVD_SET)
2391			errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
2392		i = do_range_cmd(cmd, &rt);
2393		if (i < 0)
2394			err(EX_OSERR, "failed to move %s",
2395			    cmd == IP_FW_SET_MOVE ? "set": "rule");
2396	} else if (_substrcmp(*av, "disable") == 0 ||
2397		   _substrcmp(*av, "enable") == 0 ) {
2398		int which = _substrcmp(*av, "enable") == 0 ? 1 : 0;
2399
2400		av++;
2401		masks[0] = masks[1] = 0;
2402
2403		while (av[0]) {
2404			if (isdigit(**av)) {
2405				i = atoi(*av);
2406				if (i < 0 || i > RESVD_SET)
2407					errx(EX_DATAERR,
2408					    "invalid set number %d\n", i);
2409				masks[which] |= (1<<i);
2410			} else if (_substrcmp(*av, "disable") == 0)
2411				which = 0;
2412			else if (_substrcmp(*av, "enable") == 0)
2413				which = 1;
2414			else
2415				errx(EX_DATAERR,
2416					"invalid set command %s\n", *av);
2417			av++;
2418		}
2419		if ( (masks[0] & masks[1]) != 0 )
2420			errx(EX_DATAERR,
2421			    "cannot enable and disable the same set\n");
2422
2423		rt.set = masks[0];
2424		rt.new_set = masks[1];
2425		i = do_range_cmd(IP_FW_SET_ENABLE, &rt);
2426		if (i)
2427			warn("set enable/disable: setsockopt(IP_FW_SET_ENABLE)");
2428	} else
2429		errx(EX_USAGE, "invalid set command %s\n", *av);
2430}
2431
2432void
2433ipfw_sysctl_handler(char *av[], int which)
2434{
2435	av++;
2436
2437	if (av[0] == NULL) {
2438		warnx("missing keyword to enable/disable\n");
2439	} else if (_substrcmp(*av, "firewall") == 0) {
2440		sysctlbyname("net.inet.ip.fw.enable", NULL, 0,
2441		    &which, sizeof(which));
2442		sysctlbyname("net.inet6.ip6.fw.enable", NULL, 0,
2443		    &which, sizeof(which));
2444	} else if (_substrcmp(*av, "one_pass") == 0) {
2445		sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0,
2446		    &which, sizeof(which));
2447	} else if (_substrcmp(*av, "debug") == 0) {
2448		sysctlbyname("net.inet.ip.fw.debug", NULL, 0,
2449		    &which, sizeof(which));
2450	} else if (_substrcmp(*av, "verbose") == 0) {
2451		sysctlbyname("net.inet.ip.fw.verbose", NULL, 0,
2452		    &which, sizeof(which));
2453	} else if (_substrcmp(*av, "dyn_keepalive") == 0) {
2454		sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0,
2455		    &which, sizeof(which));
2456#ifndef NO_ALTQ
2457	} else if (_substrcmp(*av, "altq") == 0) {
2458		altq_set_enabled(which);
2459#endif
2460	} else {
2461		warnx("unrecognize enable/disable keyword: %s\n", *av);
2462	}
2463}
2464
2465typedef void state_cb(struct cmdline_opts *co, struct format_opts *fo,
2466    void *arg, void *state);
2467
2468static void
2469prepare_format_dyn(struct cmdline_opts *co, struct format_opts *fo,
2470    void *arg, void *_state)
2471{
2472	ipfw_dyn_rule *d;
2473	int width;
2474	uint8_t set;
2475
2476	d = (ipfw_dyn_rule *)_state;
2477	/* Count _ALL_ states */
2478	fo->dcnt++;
2479
2480	if (fo->show_counters == 0)
2481		return;
2482
2483	if (co->use_set) {
2484		/* skip states from another set */
2485		bcopy((char *)&d->rule + sizeof(uint16_t), &set,
2486		    sizeof(uint8_t));
2487		if (set != co->use_set - 1)
2488			return;
2489	}
2490
2491	width = pr_u64(NULL, &d->pcnt, 0);
2492	if (width > fo->pcwidth)
2493		fo->pcwidth = width;
2494
2495	width = pr_u64(NULL, &d->bcnt, 0);
2496	if (width > fo->bcwidth)
2497		fo->bcwidth = width;
2498}
2499
2500static int
2501foreach_state(struct cmdline_opts *co, struct format_opts *fo,
2502    caddr_t base, size_t sz, state_cb dyn_bc, void *dyn_arg)
2503{
2504	int ttype;
2505	state_cb *fptr;
2506	void *farg;
2507	ipfw_obj_tlv *tlv;
2508	ipfw_obj_ctlv *ctlv;
2509
2510	fptr = NULL;
2511	ttype = 0;
2512
2513	while (sz > 0) {
2514		ctlv = (ipfw_obj_ctlv *)base;
2515		switch (ctlv->head.type) {
2516		case IPFW_TLV_DYNSTATE_LIST:
2517			base += sizeof(*ctlv);
2518			sz -= sizeof(*ctlv);
2519			ttype = IPFW_TLV_DYN_ENT;
2520			fptr = dyn_bc;
2521			farg = dyn_arg;
2522			break;
2523		default:
2524			return (sz);
2525		}
2526
2527		while (sz > 0) {
2528			tlv = (ipfw_obj_tlv *)base;
2529			if (tlv->type != ttype)
2530				break;
2531
2532			fptr(co, fo, farg, tlv + 1);
2533			sz -= tlv->length;
2534			base += tlv->length;
2535		}
2536	}
2537
2538	return (sz);
2539}
2540
2541static void
2542prepare_format_opts(struct cmdline_opts *co, struct format_opts *fo,
2543    ipfw_obj_tlv *rtlv, int rcnt, caddr_t dynbase, size_t dynsz)
2544{
2545	int bcwidth, pcwidth, width;
2546	int n;
2547	struct ip_fw_bcounter *cntr;
2548	struct ip_fw_rule *r;
2549
2550	bcwidth = 0;
2551	pcwidth = 0;
2552	if (fo->show_counters != 0) {
2553		for (n = 0; n < rcnt; n++,
2554		    rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) {
2555			cntr = (struct ip_fw_bcounter *)(rtlv + 1);
2556			r = (struct ip_fw_rule *)((caddr_t)cntr + cntr->size);
2557			/* skip rules from another set */
2558			if (co->use_set && r->set != co->use_set - 1)
2559				continue;
2560
2561			/* packet counter */
2562			width = pr_u64(NULL, &cntr->pcnt, 0);
2563			if (width > pcwidth)
2564				pcwidth = width;
2565
2566			/* byte counter */
2567			width = pr_u64(NULL, &cntr->bcnt, 0);
2568			if (width > bcwidth)
2569				bcwidth = width;
2570		}
2571	}
2572	fo->bcwidth = bcwidth;
2573	fo->pcwidth = pcwidth;
2574
2575	fo->dcnt = 0;
2576	if (co->do_dynamic && dynsz > 0)
2577		foreach_state(co, fo, dynbase, dynsz, prepare_format_dyn, NULL);
2578}
2579
2580static int
2581list_static_range(struct cmdline_opts *co, struct format_opts *fo,
2582    struct buf_pr *bp, ipfw_obj_tlv *rtlv, int rcnt)
2583{
2584	int n, seen;
2585	struct ip_fw_rule *r;
2586	struct ip_fw_bcounter *cntr;
2587	int c = 0;
2588
2589	for (n = seen = 0; n < rcnt; n++,
2590	    rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) {
2591
2592		if ((fo->show_counters | fo->show_time) != 0) {
2593			cntr = (struct ip_fw_bcounter *)(rtlv + 1);
2594			r = (struct ip_fw_rule *)((caddr_t)cntr + cntr->size);
2595		} else {
2596			cntr = NULL;
2597			r = (struct ip_fw_rule *)(rtlv + 1);
2598		}
2599		if (r->rulenum > fo->last)
2600			break;
2601		if (co->use_set && r->set != co->use_set - 1)
2602			continue;
2603		if (r->rulenum >= fo->first && r->rulenum <= fo->last) {
2604			show_static_rule(co, fo, bp, r, cntr);
2605			printf("%s", bp->buf);
2606			c += rtlv->length;
2607			bp_flush(bp);
2608			seen++;
2609		}
2610	}
2611
2612	return (seen);
2613}
2614
2615static void
2616list_dyn_state(struct cmdline_opts *co, struct format_opts *fo,
2617    void *_arg, void *_state)
2618{
2619	uint16_t rulenum;
2620	uint8_t set;
2621	ipfw_dyn_rule *d;
2622	struct buf_pr *bp;
2623
2624	d = (ipfw_dyn_rule *)_state;
2625	bp = (struct buf_pr *)_arg;
2626
2627	bcopy(&d->rule, &rulenum, sizeof(rulenum));
2628	if (rulenum > fo->last)
2629		return;
2630	if (co->use_set) {
2631		bcopy((char *)&d->rule + sizeof(uint16_t),
2632		      &set, sizeof(uint8_t));
2633		if (set != co->use_set - 1)
2634			return;
2635	}
2636	if (rulenum >= fo->first) {
2637		show_dyn_state(co, fo, bp, d);
2638		printf("%s\n", bp->buf);
2639		bp_flush(bp);
2640	}
2641}
2642
2643static int
2644list_dyn_range(struct cmdline_opts *co, struct format_opts *fo,
2645    struct buf_pr *bp, caddr_t base, size_t sz)
2646{
2647
2648	sz = foreach_state(co, fo, base, sz, list_dyn_state, bp);
2649	return (sz);
2650}
2651
2652void
2653ipfw_list(int ac, char *av[], int show_counters)
2654{
2655	ipfw_cfg_lheader *cfg;
2656	struct format_opts sfo;
2657	size_t sz;
2658	int error;
2659	int lac;
2660	char **lav;
2661	uint32_t rnum;
2662	char *endptr;
2663
2664	if (co.test_only) {
2665		fprintf(stderr, "Testing only, list disabled\n");
2666		return;
2667	}
2668	if (co.do_pipe) {
2669		dummynet_list(ac, av, show_counters);
2670		return;
2671	}
2672
2673	ac--;
2674	av++;
2675	memset(&sfo, 0, sizeof(sfo));
2676
2677	/* Determine rule range to request */
2678	if (ac > 0) {
2679		for (lac = ac, lav = av; lac != 0; lac--) {
2680			rnum = strtoul(*lav++, &endptr, 10);
2681			if (sfo.first == 0 || rnum < sfo.first)
2682				sfo.first = rnum;
2683
2684			if (*endptr == '-')
2685				rnum = strtoul(endptr + 1, &endptr, 10);
2686			if (sfo.last == 0 || rnum > sfo.last)
2687				sfo.last = rnum;
2688		}
2689	}
2690
2691	/* get configuraion from kernel */
2692	cfg = NULL;
2693	sfo.show_counters = show_counters;
2694	sfo.show_time = co.do_time;
2695	sfo.flags = IPFW_CFG_GET_STATIC;
2696	if (co.do_dynamic != 0)
2697		sfo.flags |= IPFW_CFG_GET_STATES;
2698	if ((sfo.show_counters | sfo.show_time) != 0)
2699		sfo.flags |= IPFW_CFG_GET_COUNTERS;
2700	if (ipfw_get_config(&co, &sfo, &cfg, &sz) != 0)
2701		err(EX_OSERR, "retrieving config failed");
2702
2703	error = ipfw_show_config(&co, &sfo, cfg, sz, ac, av);
2704
2705	free(cfg);
2706
2707	if (error != EX_OK)
2708		exit(error);
2709}
2710
2711static int
2712ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo,
2713    ipfw_cfg_lheader *cfg, size_t sz, int ac, char *av[])
2714{
2715	caddr_t dynbase;
2716	size_t dynsz;
2717	int rcnt;
2718	int exitval = EX_OK;
2719	int lac;
2720	char **lav;
2721	char *endptr;
2722	size_t readsz;
2723	struct buf_pr bp;
2724	ipfw_obj_ctlv *ctlv, *tstate;
2725	ipfw_obj_tlv *rbase;
2726
2727	/*
2728	 * Handle tablenames TLV first, if any
2729	 */
2730	tstate = NULL;
2731	rbase = NULL;
2732	dynbase = NULL;
2733	dynsz = 0;
2734	readsz = sizeof(*cfg);
2735	rcnt = 0;
2736
2737	fo->set_mask = cfg->set_mask;
2738
2739	ctlv = (ipfw_obj_ctlv *)(cfg + 1);
2740
2741	if (cfg->flags & IPFW_CFG_GET_STATIC) {
2742		/* We've requested static rules */
2743		if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) {
2744			object_sort_ctlv(ctlv);
2745			fo->tstate = ctlv;
2746			readsz += ctlv->head.length;
2747			ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv +
2748			    ctlv->head.length);
2749		}
2750
2751		if (ctlv->head.type == IPFW_TLV_RULE_LIST) {
2752			rbase = (ipfw_obj_tlv *)(ctlv + 1);
2753			rcnt = ctlv->count;
2754			readsz += ctlv->head.length;
2755			ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv +
2756			    ctlv->head.length);
2757		}
2758	}
2759
2760	if ((cfg->flags & IPFW_CFG_GET_STATES) && (readsz != sz))  {
2761		/* We may have some dynamic states */
2762		dynsz = sz - readsz;
2763		/* Skip empty header */
2764		if (dynsz != sizeof(ipfw_obj_ctlv))
2765			dynbase = (caddr_t)ctlv;
2766		else
2767			dynsz = 0;
2768	}
2769
2770	prepare_format_opts(co, fo, rbase, rcnt, dynbase, dynsz);
2771	bp_alloc(&bp, 4096);
2772
2773	/* if no rule numbers were specified, list all rules */
2774	if (ac == 0) {
2775		fo->first = 0;
2776		fo->last = IPFW_DEFAULT_RULE;
2777		list_static_range(co, fo, &bp, rbase, rcnt);
2778
2779		if (co->do_dynamic && dynsz > 0) {
2780			printf("## Dynamic rules (%d %zu):\n", fo->dcnt, dynsz);
2781			list_dyn_range(co, fo, &bp, dynbase, dynsz);
2782		}
2783
2784		bp_free(&bp);
2785		return (EX_OK);
2786	}
2787
2788	/* display specific rules requested on command line */
2789	for (lac = ac, lav = av; lac != 0; lac--) {
2790		/* convert command line rule # */
2791		fo->last = fo->first = strtoul(*lav++, &endptr, 10);
2792		if (*endptr == '-')
2793			fo->last = strtoul(endptr + 1, &endptr, 10);
2794		if (*endptr) {
2795			exitval = EX_USAGE;
2796			warnx("invalid rule number: %s", *(lav - 1));
2797			continue;
2798		}
2799
2800		if (list_static_range(co, fo, &bp, rbase, rcnt) == 0) {
2801			/* give precedence to other error(s) */
2802			if (exitval == EX_OK)
2803				exitval = EX_UNAVAILABLE;
2804			if (fo->first == fo->last)
2805				warnx("rule %u does not exist", fo->first);
2806			else
2807				warnx("no rules in range %u-%u",
2808				    fo->first, fo->last);
2809		}
2810	}
2811
2812	if (co->do_dynamic && dynsz > 0) {
2813		printf("## Dynamic rules:\n");
2814		for (lac = ac, lav = av; lac != 0; lac--) {
2815			fo->last = fo->first = strtoul(*lav++, &endptr, 10);
2816			if (*endptr == '-')
2817				fo->last = strtoul(endptr+1, &endptr, 10);
2818			if (*endptr)
2819				/* already warned */
2820				continue;
2821			list_dyn_range(co, fo, &bp, dynbase, dynsz);
2822		}
2823	}
2824
2825	bp_free(&bp);
2826	return (exitval);
2827}
2828
2829
2830/*
2831 * Retrieves current ipfw configuration of given type
2832 * and stores its pointer to @pcfg.
2833 *
2834 * Caller is responsible for freeing @pcfg.
2835 *
2836 * Returns 0 on success.
2837 */
2838
2839static int
2840ipfw_get_config(struct cmdline_opts *co, struct format_opts *fo,
2841    ipfw_cfg_lheader **pcfg, size_t *psize)
2842{
2843	ipfw_cfg_lheader *cfg;
2844	size_t sz;
2845	int i;
2846
2847
2848	if (co->test_only != 0) {
2849		fprintf(stderr, "Testing only, list disabled\n");
2850		return (0);
2851	}
2852
2853	/* Start with some data size */
2854	sz = 4096;
2855	cfg = NULL;
2856
2857	for (i = 0; i < 16; i++) {
2858		if (cfg != NULL)
2859			free(cfg);
2860		if ((cfg = calloc(1, sz)) == NULL)
2861			return (ENOMEM);
2862
2863		cfg->flags = fo->flags;
2864		cfg->start_rule = fo->first;
2865		cfg->end_rule = fo->last;
2866
2867		if (do_get3(IP_FW_XGET, &cfg->opheader, &sz) != 0) {
2868			if (errno != ENOMEM) {
2869				free(cfg);
2870				return (errno);
2871			}
2872
2873			/* Buffer size is not enough. Try to increase */
2874			sz = sz * 2;
2875			if (sz < cfg->size)
2876				sz = cfg->size;
2877			continue;
2878		}
2879
2880		*pcfg = cfg;
2881		*psize = sz;
2882		return (0);
2883	}
2884
2885	free(cfg);
2886	return (ENOMEM);
2887}
2888
2889static int
2890lookup_host (char *host, struct in_addr *ipaddr)
2891{
2892	struct hostent *he;
2893
2894	if (!inet_aton(host, ipaddr)) {
2895		if ((he = gethostbyname(host)) == NULL)
2896			return(-1);
2897		*ipaddr = *(struct in_addr *)he->h_addr_list[0];
2898	}
2899	return(0);
2900}
2901
2902struct tidx {
2903	ipfw_obj_ntlv *idx;
2904	uint32_t count;
2905	uint32_t size;
2906	uint16_t counter;
2907	uint8_t set;
2908};
2909
2910int
2911ipfw_check_object_name(const char *name)
2912{
2913	int c, i, l;
2914
2915	/*
2916	 * Check that name is null-terminated and contains
2917	 * valid symbols only. Valid mask is:
2918	 * [a-zA-Z0-9\-_\.]{1,63}
2919	 */
2920	l = strlen(name);
2921	if (l == 0 || l >= 64)
2922		return (EINVAL);
2923	for (i = 0; i < l; i++) {
2924		c = name[i];
2925		if (isalpha(c) || isdigit(c) || c == '_' ||
2926		    c == '-' || c == '.')
2927			continue;
2928		return (EINVAL);
2929	}
2930	return (0);
2931}
2932
2933static char *default_state_name = "default";
2934static int
2935state_check_name(const char *name)
2936{
2937
2938	if (ipfw_check_object_name(name) != 0)
2939		return (EINVAL);
2940	if (strcmp(name, "any") == 0)
2941		return (EINVAL);
2942	return (0);
2943}
2944
2945static int
2946eaction_check_name(const char *name)
2947{
2948
2949	if (ipfw_check_object_name(name) != 0)
2950		return (EINVAL);
2951	/* Restrict some 'special' names */
2952	if (match_token(rule_actions, name) != -1 &&
2953	    match_token(rule_action_params, name) != -1)
2954		return (EINVAL);
2955	return (0);
2956}
2957
2958static uint16_t
2959pack_object(struct tidx *tstate, char *name, int otype)
2960{
2961	int i;
2962	ipfw_obj_ntlv *ntlv;
2963
2964	for (i = 0; i < tstate->count; i++) {
2965		if (strcmp(tstate->idx[i].name, name) != 0)
2966			continue;
2967		if (tstate->idx[i].set != tstate->set)
2968			continue;
2969		if (tstate->idx[i].head.type != otype)
2970			continue;
2971
2972		return (tstate->idx[i].idx);
2973	}
2974
2975	if (tstate->count + 1 > tstate->size) {
2976		tstate->size += 4;
2977		tstate->idx = realloc(tstate->idx, tstate->size *
2978		    sizeof(ipfw_obj_ntlv));
2979		if (tstate->idx == NULL)
2980			return (0);
2981	}
2982
2983	ntlv = &tstate->idx[i];
2984	memset(ntlv, 0, sizeof(ipfw_obj_ntlv));
2985	strlcpy(ntlv->name, name, sizeof(ntlv->name));
2986	ntlv->head.type = otype;
2987	ntlv->head.length = sizeof(ipfw_obj_ntlv);
2988	ntlv->set = tstate->set;
2989	ntlv->idx = ++tstate->counter;
2990	tstate->count++;
2991
2992	return (ntlv->idx);
2993}
2994
2995static uint16_t
2996pack_table(struct tidx *tstate, char *name)
2997{
2998
2999	if (table_check_name(name) != 0)
3000		return (0);
3001
3002	return (pack_object(tstate, name, IPFW_TLV_TBL_NAME));
3003}
3004
3005void
3006fill_table(struct _ipfw_insn *cmd, char *av, uint8_t opcode,
3007    struct tidx *tstate)
3008{
3009	uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
3010	uint16_t uidx;
3011	char *p;
3012
3013	if ((p = strchr(av + 6, ')')) == NULL)
3014		errx(EX_DATAERR, "forgotten parenthesis: '%s'", av);
3015	*p = '\0';
3016	p = strchr(av + 6, ',');
3017	if (p)
3018		*p++ = '\0';
3019
3020	if ((uidx = pack_table(tstate, av + 6)) == 0)
3021		errx(EX_DATAERR, "Invalid table name: %s", av + 6);
3022
3023	cmd->opcode = opcode;
3024	cmd->arg1 = uidx;
3025	if (p) {
3026		cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
3027		d[0] = strtoul(p, NULL, 0);
3028	} else
3029		cmd->len |= F_INSN_SIZE(ipfw_insn);
3030}
3031
3032
3033/*
3034 * fills the addr and mask fields in the instruction as appropriate from av.
3035 * Update length as appropriate.
3036 * The following formats are allowed:
3037 *	me	returns O_IP_*_ME
3038 *	1.2.3.4		single IP address
3039 *	1.2.3.4:5.6.7.8	address:mask
3040 *	1.2.3.4/24	address/mask
3041 *	1.2.3.4/26{1,6,5,4,23}	set of addresses in a subnet
3042 * We can have multiple comma-separated address/mask entries.
3043 */
3044static void
3045fill_ip(ipfw_insn_ip *cmd, char *av, int cblen, struct tidx *tstate)
3046{
3047	int len = 0;
3048	uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
3049
3050	cmd->o.len &= ~F_LEN_MASK;	/* zero len */
3051
3052	if (_substrcmp(av, "any") == 0)
3053		return;
3054
3055	if (_substrcmp(av, "me") == 0) {
3056		cmd->o.len |= F_INSN_SIZE(ipfw_insn);
3057		return;
3058	}
3059
3060	if (strncmp(av, "table(", 6) == 0) {
3061		fill_table(&cmd->o, av, O_IP_DST_LOOKUP, tstate);
3062		return;
3063	}
3064
3065    while (av) {
3066	/*
3067	 * After the address we can have '/' or ':' indicating a mask,
3068	 * ',' indicating another address follows, '{' indicating a
3069	 * set of addresses of unspecified size.
3070	 */
3071	char *t = NULL, *p = strpbrk(av, "/:,{");
3072	int masklen;
3073	char md, nd = '\0';
3074
3075	CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn) + 2 + len);
3076
3077	if (p) {
3078		md = *p;
3079		*p++ = '\0';
3080		if ((t = strpbrk(p, ",{")) != NULL) {
3081			nd = *t;
3082			*t = '\0';
3083		}
3084	} else
3085		md = '\0';
3086
3087	if (lookup_host(av, (struct in_addr *)&d[0]) != 0)
3088		errx(EX_NOHOST, "hostname ``%s'' unknown", av);
3089	switch (md) {
3090	case ':':
3091		if (!inet_aton(p, (struct in_addr *)&d[1]))
3092			errx(EX_DATAERR, "bad netmask ``%s''", p);
3093		break;
3094	case '/':
3095		masklen = atoi(p);
3096		if (masklen == 0)
3097			d[1] = htonl(0U);	/* mask */
3098		else if (masklen > 32)
3099			errx(EX_DATAERR, "bad width ``%s''", p);
3100		else
3101			d[1] = htonl(~0U << (32 - masklen));
3102		break;
3103	case '{':	/* no mask, assume /24 and put back the '{' */
3104		d[1] = htonl(~0U << (32 - 24));
3105		*(--p) = md;
3106		break;
3107
3108	case ',':	/* single address plus continuation */
3109		*(--p) = md;
3110		/* FALLTHROUGH */
3111	case 0:		/* initialization value */
3112	default:
3113		d[1] = htonl(~0U);	/* force /32 */
3114		break;
3115	}
3116	d[0] &= d[1];		/* mask base address with mask */
3117	if (t)
3118		*t = nd;
3119	/* find next separator */
3120	if (p)
3121		p = strpbrk(p, ",{");
3122	if (p && *p == '{') {
3123		/*
3124		 * We have a set of addresses. They are stored as follows:
3125		 *   arg1	is the set size (powers of 2, 2..256)
3126		 *   addr	is the base address IN HOST FORMAT
3127		 *   mask..	is an array of arg1 bits (rounded up to
3128		 *		the next multiple of 32) with bits set
3129		 *		for each host in the map.
3130		 */
3131		uint32_t *map = (uint32_t *)&cmd->mask;
3132		int low, high;
3133		int i = contigmask((uint8_t *)&(d[1]), 32);
3134
3135		if (len > 0)
3136			errx(EX_DATAERR, "address set cannot be in a list");
3137		if (i < 24 || i > 31)
3138			errx(EX_DATAERR, "invalid set with mask %d\n", i);
3139		cmd->o.arg1 = 1<<(32-i);	/* map length		*/
3140		d[0] = ntohl(d[0]);		/* base addr in host format */
3141		cmd->o.opcode = O_IP_DST_SET;	/* default */
3142		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32;
3143		for (i = 0; i < (cmd->o.arg1+31)/32 ; i++)
3144			map[i] = 0;	/* clear map */
3145
3146		av = p + 1;
3147		low = d[0] & 0xff;
3148		high = low + cmd->o.arg1 - 1;
3149		/*
3150		 * Here, i stores the previous value when we specify a range
3151		 * of addresses within a mask, e.g. 45-63. i = -1 means we
3152		 * have no previous value.
3153		 */
3154		i = -1;	/* previous value in a range */
3155		while (isdigit(*av)) {
3156			char *s;
3157			int a = strtol(av, &s, 0);
3158
3159			if (s == av) { /* no parameter */
3160			    if (*av != '}')
3161				errx(EX_DATAERR, "set not closed\n");
3162			    if (i != -1)
3163				errx(EX_DATAERR, "incomplete range %d-", i);
3164			    break;
3165			}
3166			if (a < low || a > high)
3167			    errx(EX_DATAERR, "addr %d out of range [%d-%d]\n",
3168				a, low, high);
3169			a -= low;
3170			if (i == -1)	/* no previous in range */
3171			    i = a;
3172			else {		/* check that range is valid */
3173			    if (i > a)
3174				errx(EX_DATAERR, "invalid range %d-%d",
3175					i+low, a+low);
3176			    if (*s == '-')
3177				errx(EX_DATAERR, "double '-' in range");
3178			}
3179			for (; i <= a; i++)
3180			    map[i/32] |= 1<<(i & 31);
3181			i = -1;
3182			if (*s == '-')
3183			    i = a;
3184			else if (*s == '}')
3185			    break;
3186			av = s+1;
3187		}
3188		return;
3189	}
3190	av = p;
3191	if (av)			/* then *av must be a ',' */
3192		av++;
3193
3194	/* Check this entry */
3195	if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */
3196		/*
3197		 * 'any' turns the entire list into a NOP.
3198		 * 'not any' never matches, so it is removed from the
3199		 * list unless it is the only item, in which case we
3200		 * report an error.
3201		 */
3202		if (cmd->o.len & F_NOT) {	/* "not any" never matches */
3203			if (av == NULL && len == 0) /* only this entry */
3204				errx(EX_DATAERR, "not any never matches");
3205		}
3206		/* else do nothing and skip this entry */
3207		return;
3208	}
3209	/* A single IP can be stored in an optimized format */
3210	if (d[1] == (uint32_t)~0 && av == NULL && len == 0) {
3211		cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
3212		return;
3213	}
3214	len += 2;	/* two words... */
3215	d += 2;
3216    } /* end while */
3217    if (len + 1 > F_LEN_MASK)
3218	errx(EX_DATAERR, "address list too long");
3219    cmd->o.len |= len+1;
3220}
3221
3222
3223/* n2mask sets n bits of the mask */
3224void
3225n2mask(struct in6_addr *mask, int n)
3226{
3227	static int	minimask[9] =
3228	    { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
3229	u_char		*p;
3230
3231	memset(mask, 0, sizeof(struct in6_addr));
3232	p = (u_char *) mask;
3233	for (; n > 0; p++, n -= 8) {
3234		if (n >= 8)
3235			*p = 0xff;
3236		else
3237			*p = minimask[n];
3238	}
3239	return;
3240}
3241
3242static void
3243fill_flags_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode,
3244	struct _s_x *flags, char *p)
3245{
3246	char *e;
3247	uint32_t set = 0, clear = 0;
3248
3249	if (fill_flags(flags, p, &e, &set, &clear) != 0)
3250		errx(EX_DATAERR, "invalid flag %s", e);
3251
3252	cmd->opcode = opcode;
3253	cmd->len =  (cmd->len & (F_NOT | F_OR)) | 1;
3254	cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
3255}
3256
3257
3258void
3259ipfw_delete(char *av[])
3260{
3261	ipfw_range_tlv rt;
3262	char *sep;
3263	int i, j;
3264	int exitval = EX_OK;
3265	int do_set = 0;
3266
3267	av++;
3268	NEED1("missing rule specification");
3269	if ( *av && _substrcmp(*av, "set") == 0) {
3270		/* Do not allow using the following syntax:
3271		 *	ipfw set N delete set M
3272		 */
3273		if (co.use_set)
3274			errx(EX_DATAERR, "invalid syntax");
3275		do_set = 1;	/* delete set */
3276		av++;
3277	}
3278
3279	/* Rule number */
3280	while (*av && isdigit(**av)) {
3281		i = strtol(*av, &sep, 10);
3282		j = i;
3283		if (*sep== '-')
3284			j = strtol(sep + 1, NULL, 10);
3285		av++;
3286		if (co.do_nat) {
3287			exitval = do_cmd(IP_FW_NAT_DEL, &i, sizeof i);
3288			if (exitval) {
3289				exitval = EX_UNAVAILABLE;
3290				if (co.do_quiet)
3291					continue;
3292				warn("nat %u not available", i);
3293			}
3294		} else if (co.do_pipe) {
3295			exitval = ipfw_delete_pipe(co.do_pipe, i);
3296		} else {
3297			memset(&rt, 0, sizeof(rt));
3298			if (do_set != 0) {
3299				rt.set = i & 31;
3300				rt.flags = IPFW_RCFLAG_SET;
3301			} else {
3302				rt.start_rule = i & 0xffff;
3303				rt.end_rule = j & 0xffff;
3304				if (rt.start_rule == 0 && rt.end_rule == 0)
3305					rt.flags |= IPFW_RCFLAG_ALL;
3306				else
3307					rt.flags |= IPFW_RCFLAG_RANGE;
3308				if (co.use_set != 0) {
3309					rt.set = co.use_set - 1;
3310					rt.flags |= IPFW_RCFLAG_SET;
3311				}
3312			}
3313			i = do_range_cmd(IP_FW_XDEL, &rt);
3314			if (i != 0) {
3315				exitval = EX_UNAVAILABLE;
3316				if (co.do_quiet)
3317					continue;
3318				warn("rule %u: setsockopt(IP_FW_XDEL)",
3319				    rt.start_rule);
3320			} else if (rt.new_set == 0 && do_set == 0) {
3321				exitval = EX_UNAVAILABLE;
3322				if (co.do_quiet)
3323					continue;
3324				if (rt.start_rule != rt.end_rule)
3325					warnx("no rules rules in %u-%u range",
3326					    rt.start_rule, rt.end_rule);
3327				else
3328					warnx("rule %u not found",
3329					    rt.start_rule);
3330			}
3331		}
3332	}
3333	if (exitval != EX_OK && co.do_force == 0)
3334		exit(exitval);
3335}
3336
3337
3338/*
3339 * fill the interface structure. We do not check the name as we can
3340 * create interfaces dynamically, so checking them at insert time
3341 * makes relatively little sense.
3342 * Interface names containing '*', '?', or '[' are assumed to be shell
3343 * patterns which match interfaces.
3344 */
3345static void
3346fill_iface(ipfw_insn_if *cmd, char *arg, int cblen, struct tidx *tstate)
3347{
3348	char *p;
3349	uint16_t uidx;
3350
3351	cmd->name[0] = '\0';
3352	cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
3353
3354	CHECK_CMDLEN;
3355
3356	/* Parse the interface or address */
3357	if (strcmp(arg, "any") == 0)
3358		cmd->o.len = 0;		/* effectively ignore this command */
3359	else if (strncmp(arg, "table(", 6) == 0) {
3360		if ((p = strchr(arg + 6, ')')) == NULL)
3361			errx(EX_DATAERR, "forgotten parenthesis: '%s'", arg);
3362		*p = '\0';
3363		p = strchr(arg + 6, ',');
3364		if (p)
3365			*p++ = '\0';
3366		if ((uidx = pack_table(tstate, arg + 6)) == 0)
3367			errx(EX_DATAERR, "Invalid table name: %s", arg + 6);
3368
3369		cmd->name[0] = '\1'; /* Special value indicating table */
3370		cmd->p.kidx = uidx;
3371	} else if (!isdigit(*arg)) {
3372		strlcpy(cmd->name, arg, sizeof(cmd->name));
3373		cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
3374	} else if (!inet_aton(arg, &cmd->p.ip))
3375		errx(EX_DATAERR, "bad ip address ``%s''", arg);
3376}
3377
3378static void
3379get_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask)
3380{
3381	int i;
3382	size_t l;
3383	char *ap, *ptr, *optr;
3384	struct ether_addr *mac;
3385	const char *macset = "0123456789abcdefABCDEF:";
3386
3387	if (strcmp(p, "any") == 0) {
3388		for (i = 0; i < ETHER_ADDR_LEN; i++)
3389			addr[i] = mask[i] = 0;
3390		return;
3391	}
3392
3393	optr = ptr = strdup(p);
3394	if ((ap = strsep(&ptr, "&/")) != NULL && *ap != 0) {
3395		l = strlen(ap);
3396		if (strspn(ap, macset) != l || (mac = ether_aton(ap)) == NULL)
3397			errx(EX_DATAERR, "Incorrect MAC address");
3398		bcopy(mac, addr, ETHER_ADDR_LEN);
3399	} else
3400		errx(EX_DATAERR, "Incorrect MAC address");
3401
3402	if (ptr != NULL) { /* we have mask? */
3403		if (p[ptr - optr - 1] == '/') { /* mask len */
3404			long ml = strtol(ptr, &ap, 10);
3405			if (*ap != 0 || ml > ETHER_ADDR_LEN * 8 || ml < 0)
3406				errx(EX_DATAERR, "Incorrect mask length");
3407			for (i = 0; ml > 0 && i < ETHER_ADDR_LEN; ml -= 8, i++)
3408				mask[i] = (ml >= 8) ? 0xff: (~0) << (8 - ml);
3409		} else { /* mask */
3410			l = strlen(ptr);
3411			if (strspn(ptr, macset) != l ||
3412			    (mac = ether_aton(ptr)) == NULL)
3413				errx(EX_DATAERR, "Incorrect mask");
3414			bcopy(mac, mask, ETHER_ADDR_LEN);
3415		}
3416	} else { /* default mask: ff:ff:ff:ff:ff:ff */
3417		for (i = 0; i < ETHER_ADDR_LEN; i++)
3418			mask[i] = 0xff;
3419	}
3420	for (i = 0; i < ETHER_ADDR_LEN; i++)
3421		addr[i] &= mask[i];
3422
3423	free(optr);
3424}
3425
3426/*
3427 * helper function, updates the pointer to cmd with the length
3428 * of the current command, and also cleans up the first word of
3429 * the new command in case it has been clobbered before.
3430 */
3431static ipfw_insn *
3432next_cmd(ipfw_insn *cmd, int *len)
3433{
3434	*len -= F_LEN(cmd);
3435	CHECK_LENGTH(*len, 0);
3436	cmd += F_LEN(cmd);
3437	bzero(cmd, sizeof(*cmd));
3438	return cmd;
3439}
3440
3441/*
3442 * Takes arguments and copies them into a comment
3443 */
3444static void
3445fill_comment(ipfw_insn *cmd, char **av, int cblen)
3446{
3447	int i, l;
3448	char *p = (char *)(cmd + 1);
3449
3450	cmd->opcode = O_NOP;
3451	cmd->len =  (cmd->len & (F_NOT | F_OR));
3452
3453	/* Compute length of comment string. */
3454	for (i = 0, l = 0; av[i] != NULL; i++)
3455		l += strlen(av[i]) + 1;
3456	if (l == 0)
3457		return;
3458	if (l > 84)
3459		errx(EX_DATAERR,
3460		    "comment too long (max 80 chars)");
3461	l = 1 + (l+3)/4;
3462	cmd->len =  (cmd->len & (F_NOT | F_OR)) | l;
3463	CHECK_CMDLEN;
3464
3465	for (i = 0; av[i] != NULL; i++) {
3466		strcpy(p, av[i]);
3467		p += strlen(av[i]);
3468		*p++ = ' ';
3469	}
3470	*(--p) = '\0';
3471}
3472
3473/*
3474 * A function to fill simple commands of size 1.
3475 * Existing flags are preserved.
3476 */
3477static void
3478fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg)
3479{
3480	cmd->opcode = opcode;
3481	cmd->len =  ((cmd->len | flags) & (F_NOT | F_OR)) | 1;
3482	cmd->arg1 = arg;
3483}
3484
3485/*
3486 * Fetch and add the MAC address and type, with masks. This generates one or
3487 * two microinstructions, and returns the pointer to the last one.
3488 */
3489static ipfw_insn *
3490add_mac(ipfw_insn *cmd, char *av[], int cblen)
3491{
3492	ipfw_insn_mac *mac;
3493
3494	if ( ( av[0] == NULL ) || ( av[1] == NULL ) )
3495		errx(EX_DATAERR, "MAC dst src");
3496
3497	cmd->opcode = O_MACADDR2;
3498	cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
3499	CHECK_CMDLEN;
3500
3501	mac = (ipfw_insn_mac *)cmd;
3502	get_mac_addr_mask(av[0], mac->addr, mac->mask);	/* dst */
3503	get_mac_addr_mask(av[1], &(mac->addr[ETHER_ADDR_LEN]),
3504	    &(mac->mask[ETHER_ADDR_LEN])); /* src */
3505	return cmd;
3506}
3507
3508static ipfw_insn *
3509add_mactype(ipfw_insn *cmd, char *av, int cblen)
3510{
3511	if (!av)
3512		errx(EX_DATAERR, "missing MAC type");
3513	if (strcmp(av, "any") != 0) { /* we have a non-null type */
3514		fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE,
3515		    cblen);
3516		cmd->opcode = O_MAC_TYPE;
3517		return cmd;
3518	} else
3519		return NULL;
3520}
3521
3522static ipfw_insn *
3523add_proto0(ipfw_insn *cmd, char *av, u_char *protop)
3524{
3525	struct protoent *pe;
3526	char *ep;
3527	int proto;
3528
3529	proto = strtol(av, &ep, 10);
3530	if (*ep != '\0' || proto <= 0) {
3531		if ((pe = getprotobyname(av)) == NULL)
3532			return NULL;
3533		proto = pe->p_proto;
3534	}
3535
3536	fill_cmd(cmd, O_PROTO, 0, proto);
3537	*protop = proto;
3538	return cmd;
3539}
3540
3541static ipfw_insn *
3542add_proto(ipfw_insn *cmd, char *av, u_char *protop)
3543{
3544	u_char proto = IPPROTO_IP;
3545
3546	if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
3547		; /* do not set O_IP4 nor O_IP6 */
3548	else if (strcmp(av, "ip4") == 0)
3549		/* explicit "just IPv4" rule */
3550		fill_cmd(cmd, O_IP4, 0, 0);
3551	else if (strcmp(av, "ip6") == 0) {
3552		/* explicit "just IPv6" rule */
3553		proto = IPPROTO_IPV6;
3554		fill_cmd(cmd, O_IP6, 0, 0);
3555	} else
3556		return add_proto0(cmd, av, protop);
3557
3558	*protop = proto;
3559	return cmd;
3560}
3561
3562static ipfw_insn *
3563add_proto_compat(ipfw_insn *cmd, char *av, u_char *protop)
3564{
3565	u_char proto = IPPROTO_IP;
3566
3567	if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
3568		; /* do not set O_IP4 nor O_IP6 */
3569	else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0)
3570		/* explicit "just IPv4" rule */
3571		fill_cmd(cmd, O_IP4, 0, 0);
3572	else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) {
3573		/* explicit "just IPv6" rule */
3574		proto = IPPROTO_IPV6;
3575		fill_cmd(cmd, O_IP6, 0, 0);
3576	} else
3577		return add_proto0(cmd, av, protop);
3578
3579	*protop = proto;
3580	return cmd;
3581}
3582
3583static ipfw_insn *
3584add_srcip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate)
3585{
3586	fill_ip((ipfw_insn_ip *)cmd, av, cblen, tstate);
3587	if (cmd->opcode == O_IP_DST_SET)			/* set */
3588		cmd->opcode = O_IP_SRC_SET;
3589	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
3590		cmd->opcode = O_IP_SRC_LOOKUP;
3591	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
3592		cmd->opcode = O_IP_SRC_ME;
3593	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
3594		cmd->opcode = O_IP_SRC;
3595	else							/* addr/mask */
3596		cmd->opcode = O_IP_SRC_MASK;
3597	return cmd;
3598}
3599
3600static ipfw_insn *
3601add_dstip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate)
3602{
3603	fill_ip((ipfw_insn_ip *)cmd, av, cblen, tstate);
3604	if (cmd->opcode == O_IP_DST_SET)			/* set */
3605		;
3606	else if (cmd->opcode == O_IP_DST_LOOKUP)		/* table */
3607		;
3608	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn))		/* me */
3609		cmd->opcode = O_IP_DST_ME;
3610	else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))	/* one IP */
3611		cmd->opcode = O_IP_DST;
3612	else							/* addr/mask */
3613		cmd->opcode = O_IP_DST_MASK;
3614	return cmd;
3615}
3616
3617static struct _s_x f_reserved_keywords[] = {
3618	{ "altq",	TOK_OR },
3619	{ "//",		TOK_OR },
3620	{ "diverted",	TOK_OR },
3621	{ "dst-port",	TOK_OR },
3622	{ "src-port",	TOK_OR },
3623	{ "established",	TOK_OR },
3624	{ "keep-state",	TOK_OR },
3625	{ "frag",	TOK_OR },
3626	{ "icmptypes",	TOK_OR },
3627	{ "in",		TOK_OR },
3628	{ "out",	TOK_OR },
3629	{ "ip6",	TOK_OR },
3630	{ "any",	TOK_OR },
3631	{ "to",		TOK_OR },
3632	{ "via",	TOK_OR },
3633	{ "{",		TOK_OR },
3634	{ NULL, 0 }	/* terminator */
3635};
3636
3637static ipfw_insn *
3638add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode, int cblen)
3639{
3640
3641	if (match_token(f_reserved_keywords, av) != -1)
3642		return (NULL);
3643
3644	if (fill_newports((ipfw_insn_u16 *)cmd, av, proto, cblen)) {
3645		/* XXX todo: check that we have a protocol with ports */
3646		cmd->opcode = opcode;
3647		return cmd;
3648	}
3649	return NULL;
3650}
3651
3652static ipfw_insn *
3653add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate)
3654{
3655	struct in6_addr a;
3656	char *host, *ch, buf[INET6_ADDRSTRLEN];
3657	ipfw_insn *ret = NULL;
3658	int len;
3659
3660	/* Copy first address in set if needed */
3661	if ((ch = strpbrk(av, "/,")) != NULL) {
3662		len = ch - av;
3663		strlcpy(buf, av, sizeof(buf));
3664		if (len < sizeof(buf))
3665			buf[len] = '\0';
3666		host = buf;
3667	} else
3668		host = av;
3669
3670	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
3671	    inet_pton(AF_INET6, host, &a) == 1)
3672		ret = add_srcip6(cmd, av, cblen, tstate);
3673	/* XXX: should check for IPv4, not !IPv6 */
3674	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
3675	    inet_pton(AF_INET6, host, &a) != 1))
3676		ret = add_srcip(cmd, av, cblen, tstate);
3677	if (ret == NULL && strcmp(av, "any") != 0)
3678		ret = cmd;
3679
3680	return ret;
3681}
3682
3683static ipfw_insn *
3684add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate)
3685{
3686	struct in6_addr a;
3687	char *host, *ch, buf[INET6_ADDRSTRLEN];
3688	ipfw_insn *ret = NULL;
3689	int len;
3690
3691	/* Copy first address in set if needed */
3692	if ((ch = strpbrk(av, "/,")) != NULL) {
3693		len = ch - av;
3694		strlcpy(buf, av, sizeof(buf));
3695		if (len < sizeof(buf))
3696			buf[len] = '\0';
3697		host = buf;
3698	} else
3699		host = av;
3700
3701	if (proto == IPPROTO_IPV6  || strcmp(av, "me6") == 0 ||
3702	    inet_pton(AF_INET6, host, &a) == 1)
3703		ret = add_dstip6(cmd, av, cblen, tstate);
3704	/* XXX: should check for IPv4, not !IPv6 */
3705	if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
3706	    inet_pton(AF_INET6, host, &a) != 1))
3707		ret = add_dstip(cmd, av, cblen, tstate);
3708	if (ret == NULL && strcmp(av, "any") != 0)
3709		ret = cmd;
3710
3711	return ret;
3712}
3713
3714/*
3715 * Parse arguments and assemble the microinstructions which make up a rule.
3716 * Rules are added into the 'rulebuf' and then copied in the correct order
3717 * into the actual rule.
3718 *
3719 * The syntax for a rule starts with the action, followed by
3720 * optional action parameters, and the various match patterns.
3721 * In the assembled microcode, the first opcode must be an O_PROBE_STATE
3722 * (generated if the rule includes a keep-state option), then the
3723 * various match patterns, log/altq actions, and the actual action.
3724 *
3725 */
3726void
3727compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate)
3728{
3729	/*
3730	 * rules are added into the 'rulebuf' and then copied in
3731	 * the correct order into the actual rule.
3732	 * Some things that need to go out of order (prob, action etc.)
3733	 * go into actbuf[].
3734	 */
3735	static uint32_t actbuf[255], cmdbuf[255];
3736	int rblen, ablen, cblen;
3737
3738	ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
3739	ipfw_insn *first_cmd;	/* first match pattern */
3740
3741	struct ip_fw_rule *rule;
3742
3743	/*
3744	 * various flags used to record that we entered some fields.
3745	 */
3746	ipfw_insn *have_state = NULL;	/* any state-related option */
3747	int have_rstate = 0;
3748	ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL;
3749	ipfw_insn *have_skipcmd = NULL;
3750	size_t len;
3751
3752	int i;
3753
3754	int open_par = 0;	/* open parenthesis ( */
3755
3756	/* proto is here because it is used to fetch ports */
3757	u_char proto = IPPROTO_IP;	/* default protocol */
3758
3759	double match_prob = 1; /* match probability, default is always match */
3760
3761	bzero(actbuf, sizeof(actbuf));		/* actions go here */
3762	bzero(cmdbuf, sizeof(cmdbuf));
3763	bzero(rbuf, *rbufsize);
3764
3765	rule = (struct ip_fw_rule *)rbuf;
3766	cmd = (ipfw_insn *)cmdbuf;
3767	action = (ipfw_insn *)actbuf;
3768
3769	rblen = *rbufsize / sizeof(uint32_t);
3770	rblen -= sizeof(struct ip_fw_rule) / sizeof(uint32_t);
3771	ablen = sizeof(actbuf) / sizeof(actbuf[0]);
3772	cblen = sizeof(cmdbuf) / sizeof(cmdbuf[0]);
3773	cblen -= F_INSN_SIZE(ipfw_insn_u32) + 1;
3774
3775#define	CHECK_RBUFLEN(len)	{ CHECK_LENGTH(rblen, len); rblen -= len; }
3776#define	CHECK_ACTLEN		CHECK_LENGTH(ablen, action->len)
3777
3778	av++;
3779
3780	/* [rule N]	-- Rule number optional */
3781	if (av[0] && isdigit(**av)) {
3782		rule->rulenum = atoi(*av);
3783		av++;
3784	}
3785
3786	/* [set N]	-- set number (0..RESVD_SET), optional */
3787	if (av[0] && av[1] && _substrcmp(*av, "set") == 0) {
3788		int set = strtoul(av[1], NULL, 10);
3789		if (set < 0 || set > RESVD_SET)
3790			errx(EX_DATAERR, "illegal set %s", av[1]);
3791		rule->set = set;
3792		tstate->set = set;
3793		av += 2;
3794	}
3795
3796	/* [prob D]	-- match probability, optional */
3797	if (av[0] && av[1] && _substrcmp(*av, "prob") == 0) {
3798		match_prob = strtod(av[1], NULL);
3799
3800		if (match_prob <= 0 || match_prob > 1)
3801			errx(EX_DATAERR, "illegal match prob. %s", av[1]);
3802		av += 2;
3803	}
3804
3805	/* action	-- mandatory */
3806	NEED1("missing action");
3807	i = match_token(rule_actions, *av);
3808	av++;
3809	action->len = 1;	/* default */
3810	CHECK_ACTLEN;
3811	switch(i) {
3812	case TOK_CHECKSTATE:
3813		have_state = action;
3814		action->opcode = O_CHECK_STATE;
3815		if (*av == NULL ||
3816		    match_token(rule_options, *av) == TOK_COMMENT) {
3817			action->arg1 = pack_object(tstate,
3818			    default_state_name, IPFW_TLV_STATE_NAME);
3819			break;
3820		}
3821		if (*av[0] == ':') {
3822			if (strcmp(*av + 1, "any") == 0)
3823				action->arg1 = 0;
3824			else if (state_check_name(*av + 1) == 0)
3825				action->arg1 = pack_object(tstate, *av + 1,
3826				    IPFW_TLV_STATE_NAME);
3827			else
3828				errx(EX_DATAERR, "Invalid state name %s",
3829				    *av);
3830			av++;
3831			break;
3832		}
3833		errx(EX_DATAERR, "Invalid state name %s", *av);
3834		break;
3835
3836	case TOK_ABORT:
3837		action->opcode = O_REJECT;
3838		action->arg1 = ICMP_REJECT_ABORT;
3839		break;
3840
3841	case TOK_ABORT6:
3842		action->opcode = O_UNREACH6;
3843		action->arg1 = ICMP6_UNREACH_ABORT;
3844		break;
3845
3846	case TOK_ACCEPT:
3847		action->opcode = O_ACCEPT;
3848		break;
3849
3850	case TOK_DENY:
3851		action->opcode = O_DENY;
3852		action->arg1 = 0;
3853		break;
3854
3855	case TOK_REJECT:
3856		action->opcode = O_REJECT;
3857		action->arg1 = ICMP_UNREACH_HOST;
3858		break;
3859
3860	case TOK_RESET:
3861		action->opcode = O_REJECT;
3862		action->arg1 = ICMP_REJECT_RST;
3863		break;
3864
3865	case TOK_RESET6:
3866		action->opcode = O_UNREACH6;
3867		action->arg1 = ICMP6_UNREACH_RST;
3868		break;
3869
3870	case TOK_UNREACH:
3871		action->opcode = O_REJECT;
3872		NEED1("missing reject code");
3873		fill_reject_code(&action->arg1, *av);
3874		av++;
3875		break;
3876
3877	case TOK_UNREACH6:
3878		action->opcode = O_UNREACH6;
3879		NEED1("missing unreach code");
3880		fill_unreach6_code(&action->arg1, *av);
3881		av++;
3882		break;
3883
3884	case TOK_COUNT:
3885		action->opcode = O_COUNT;
3886		break;
3887
3888	case TOK_NAT:
3889		action->opcode = O_NAT;
3890		action->len = F_INSN_SIZE(ipfw_insn_nat);
3891		CHECK_ACTLEN;
3892		if (*av != NULL && _substrcmp(*av, "global") == 0) {
3893			action->arg1 = IP_FW_NAT44_GLOBAL;
3894			av++;
3895			break;
3896		} else
3897			goto chkarg;
3898	case TOK_QUEUE:
3899		action->opcode = O_QUEUE;
3900		goto chkarg;
3901	case TOK_PIPE:
3902		action->opcode = O_PIPE;
3903		goto chkarg;
3904	case TOK_SKIPTO:
3905		action->opcode = O_SKIPTO;
3906		goto chkarg;
3907	case TOK_NETGRAPH:
3908		action->opcode = O_NETGRAPH;
3909		goto chkarg;
3910	case TOK_NGTEE:
3911		action->opcode = O_NGTEE;
3912		goto chkarg;
3913	case TOK_DIVERT:
3914		action->opcode = O_DIVERT;
3915		goto chkarg;
3916	case TOK_TEE:
3917		action->opcode = O_TEE;
3918		goto chkarg;
3919	case TOK_CALL:
3920		action->opcode = O_CALLRETURN;
3921chkarg:
3922		if (!av[0])
3923			errx(EX_USAGE, "missing argument for %s", *(av - 1));
3924		if (isdigit(**av)) {
3925			action->arg1 = strtoul(*av, NULL, 10);
3926			if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG)
3927				errx(EX_DATAERR, "illegal argument for %s",
3928				    *(av - 1));
3929		} else if (_substrcmp(*av, "tablearg") == 0) {
3930			action->arg1 = IP_FW_TARG;
3931		} else if (i == TOK_DIVERT || i == TOK_TEE) {
3932			struct servent *s;
3933			setservent(1);
3934			s = getservbyname(av[0], "divert");
3935			if (s != NULL)
3936				action->arg1 = ntohs(s->s_port);
3937			else
3938				errx(EX_DATAERR, "illegal divert/tee port");
3939		} else
3940			errx(EX_DATAERR, "illegal argument for %s", *(av - 1));
3941		av++;
3942		break;
3943
3944	case TOK_FORWARD: {
3945		/*
3946		 * Locate the address-port separator (':' or ',').
3947		 * Could be one of the following:
3948		 *	hostname:port
3949		 *	IPv4 a.b.c.d,port
3950		 *	IPv4 a.b.c.d:port
3951		 *	IPv6 w:x:y::z,port
3952		 * The ':' can only be used with hostname and IPv4 address.
3953		 * XXX-BZ Should we also support [w:x:y::z]:port?
3954		 */
3955		struct sockaddr_storage result;
3956		struct addrinfo *res;
3957		char *s, *end;
3958		int family;
3959		u_short port_number;
3960
3961		NEED1("missing forward address[:port]");
3962
3963		/*
3964		 * locate the address-port separator (':' or ',')
3965		 */
3966		s = strchr(*av, ',');
3967		if (s == NULL) {
3968			/* Distinguish between IPv4:port and IPv6 cases. */
3969			s = strchr(*av, ':');
3970			if (s && strchr(s+1, ':'))
3971				s = NULL; /* no port */
3972		}
3973
3974		port_number = 0;
3975		if (s != NULL) {
3976			/* Terminate host portion and set s to start of port. */
3977			*(s++) = '\0';
3978			i = strtoport(s, &end, 0 /* base */, 0 /* proto */);
3979			if (s == end)
3980				errx(EX_DATAERR,
3981				    "illegal forwarding port ``%s''", s);
3982			port_number = (u_short)i;
3983		}
3984
3985		if (_substrcmp(*av, "tablearg") == 0) {
3986			family = PF_INET;
3987			((struct sockaddr_in*)&result)->sin_addr.s_addr =
3988			    INADDR_ANY;
3989		} else {
3990			/*
3991			 * Resolve the host name or address to a family and a
3992			 * network representation of the address.
3993			 */
3994			if (getaddrinfo(*av, NULL, NULL, &res))
3995				errx(EX_DATAERR, NULL);
3996			/* Just use the first host in the answer. */
3997			family = res->ai_family;
3998			memcpy(&result, res->ai_addr, res->ai_addrlen);
3999			freeaddrinfo(res);
4000		}
4001
4002 		if (family == PF_INET) {
4003			ipfw_insn_sa *p = (ipfw_insn_sa *)action;
4004
4005			action->opcode = O_FORWARD_IP;
4006			action->len = F_INSN_SIZE(ipfw_insn_sa);
4007			CHECK_ACTLEN;
4008
4009			/*
4010			 * In the kernel we assume AF_INET and use only
4011			 * sin_port and sin_addr. Remember to set sin_len as
4012			 * the routing code seems to use it too.
4013			 */
4014			p->sa.sin_len = sizeof(struct sockaddr_in);
4015			p->sa.sin_family = AF_INET;
4016			p->sa.sin_port = port_number;
4017			p->sa.sin_addr.s_addr =
4018			     ((struct sockaddr_in *)&result)->sin_addr.s_addr;
4019		} else if (family == PF_INET6) {
4020			ipfw_insn_sa6 *p = (ipfw_insn_sa6 *)action;
4021
4022			action->opcode = O_FORWARD_IP6;
4023			action->len = F_INSN_SIZE(ipfw_insn_sa6);
4024			CHECK_ACTLEN;
4025
4026			p->sa.sin6_len = sizeof(struct sockaddr_in6);
4027			p->sa.sin6_family = AF_INET6;
4028			p->sa.sin6_port = port_number;
4029			p->sa.sin6_flowinfo = 0;
4030			p->sa.sin6_scope_id =
4031			    ((struct sockaddr_in6 *)&result)->sin6_scope_id;
4032			bcopy(&((struct sockaddr_in6*)&result)->sin6_addr,
4033			    &p->sa.sin6_addr, sizeof(p->sa.sin6_addr));
4034		} else {
4035			errx(EX_DATAERR, "Invalid address family in forward action");
4036		}
4037		av++;
4038		break;
4039	    }
4040	case TOK_COMMENT:
4041		/* pretend it is a 'count' rule followed by the comment */
4042		action->opcode = O_COUNT;
4043		av--;		/* go back... */
4044		break;
4045
4046	case TOK_SETFIB:
4047	    {
4048		int numfibs;
4049		size_t intsize = sizeof(int);
4050
4051		action->opcode = O_SETFIB;
4052		NEED1("missing fib number");
4053		if (_substrcmp(*av, "tablearg") == 0) {
4054			action->arg1 = IP_FW_TARG;
4055		} else {
4056		        action->arg1 = strtoul(*av, NULL, 10);
4057			if (sysctlbyname("net.fibs", &numfibs, &intsize,
4058			    NULL, 0) == -1)
4059				errx(EX_DATAERR, "fibs not suported.\n");
4060			if (action->arg1 >= numfibs)  /* Temporary */
4061				errx(EX_DATAERR, "fib too large.\n");
4062			/* Add high-order bit to fib to make room for tablearg*/
4063			action->arg1 |= 0x8000;
4064		}
4065		av++;
4066		break;
4067	    }
4068
4069	case TOK_SETDSCP:
4070	    {
4071		int code;
4072
4073		action->opcode = O_SETDSCP;
4074		NEED1("missing DSCP code");
4075		if (_substrcmp(*av, "tablearg") == 0) {
4076			action->arg1 = IP_FW_TARG;
4077		} else {
4078			if (isalpha(*av[0])) {
4079				if ((code = match_token(f_ipdscp, *av)) == -1)
4080					errx(EX_DATAERR, "Unknown DSCP code");
4081				action->arg1 = code;
4082			} else
4083			        action->arg1 = strtoul(*av, NULL, 10);
4084			/*
4085			 * Add high-order bit to DSCP to make room
4086			 * for tablearg
4087			 */
4088			action->arg1 |= 0x8000;
4089		}
4090		av++;
4091		break;
4092	    }
4093
4094	case TOK_REASS:
4095		action->opcode = O_REASS;
4096		break;
4097
4098	case TOK_RETURN:
4099		fill_cmd(action, O_CALLRETURN, F_NOT, 0);
4100		break;
4101
4102	case TOK_TCPSETMSS: {
4103		u_long mss;
4104		uint16_t idx;
4105
4106		idx = pack_object(tstate, "tcp-setmss", IPFW_TLV_EACTION);
4107		if (idx == 0)
4108			errx(EX_DATAERR, "pack_object failed");
4109		fill_cmd(action, O_EXTERNAL_ACTION, 0, idx);
4110		NEED1("Missing MSS value");
4111		action = next_cmd(action, &ablen);
4112		action->len = 1;
4113		CHECK_ACTLEN;
4114		mss = strtoul(*av, NULL, 10);
4115		if (mss == 0 || mss > UINT16_MAX)
4116			errx(EX_USAGE, "invalid MSS value %s", *av);
4117		fill_cmd(action, O_EXTERNAL_DATA, 0, (uint16_t)mss);
4118		av++;
4119		break;
4120	}
4121
4122	default:
4123		av--;
4124		if (match_token(rule_eactions, *av) == -1)
4125			errx(EX_DATAERR, "invalid action %s\n", *av);
4126		/*
4127		 * External actions support.
4128		 * XXX: we support only syntax with instance name.
4129		 *	For known external actions (from rule_eactions list)
4130		 *	we can handle syntax directly. But with `eaction'
4131		 *	keyword we can use only `eaction <name> <instance>'
4132		 *	syntax.
4133		 */
4134	case TOK_EACTION: {
4135		uint16_t idx;
4136
4137		NEED1("Missing eaction name");
4138		if (eaction_check_name(*av) != 0)
4139			errx(EX_DATAERR, "Invalid eaction name %s", *av);
4140		idx = pack_object(tstate, *av, IPFW_TLV_EACTION);
4141		if (idx == 0)
4142			errx(EX_DATAERR, "pack_object failed");
4143		fill_cmd(action, O_EXTERNAL_ACTION, 0, idx);
4144		av++;
4145		NEED1("Missing eaction instance name");
4146		action = next_cmd(action, &ablen);
4147		action->len = 1;
4148		CHECK_ACTLEN;
4149		if (eaction_check_name(*av) != 0)
4150			errx(EX_DATAERR, "Invalid eaction instance name %s",
4151			    *av);
4152		/*
4153		 * External action instance object has TLV type depended
4154		 * from the external action name object index. Since we
4155		 * currently don't know this index, use zero as TLV type.
4156		 */
4157		idx = pack_object(tstate, *av, 0);
4158		if (idx == 0)
4159			errx(EX_DATAERR, "pack_object failed");
4160		fill_cmd(action, O_EXTERNAL_INSTANCE, 0, idx);
4161		av++;
4162		}
4163	}
4164	action = next_cmd(action, &ablen);
4165
4166	/*
4167	 * [altq queuename] -- altq tag, optional
4168	 * [log [logamount N]]	-- log, optional
4169	 *
4170	 * If they exist, it go first in the cmdbuf, but then it is
4171	 * skipped in the copy section to the end of the buffer.
4172	 */
4173	while (av[0] != NULL && (i = match_token(rule_action_params, *av)) != -1) {
4174		av++;
4175		switch (i) {
4176		case TOK_LOG:
4177		    {
4178			ipfw_insn_log *c = (ipfw_insn_log *)cmd;
4179			int l;
4180
4181			if (have_log)
4182				errx(EX_DATAERR,
4183				    "log cannot be specified more than once");
4184			have_log = (ipfw_insn *)c;
4185			cmd->len = F_INSN_SIZE(ipfw_insn_log);
4186			CHECK_CMDLEN;
4187			cmd->opcode = O_LOG;
4188			if (av[0] && _substrcmp(*av, "logamount") == 0) {
4189				av++;
4190				NEED1("logamount requires argument");
4191				l = atoi(*av);
4192				if (l < 0)
4193					errx(EX_DATAERR,
4194					    "logamount must be positive");
4195				c->max_log = l;
4196				av++;
4197			} else {
4198				len = sizeof(c->max_log);
4199				if (sysctlbyname("net.inet.ip.fw.verbose_limit",
4200				    &c->max_log, &len, NULL, 0) == -1) {
4201					if (co.test_only) {
4202						c->max_log = 0;
4203						break;
4204					}
4205					errx(1, "sysctlbyname(\"%s\")",
4206					    "net.inet.ip.fw.verbose_limit");
4207				}
4208			}
4209		    }
4210			break;
4211
4212#ifndef NO_ALTQ
4213		case TOK_ALTQ:
4214		    {
4215			ipfw_insn_altq *a = (ipfw_insn_altq *)cmd;
4216
4217			NEED1("missing altq queue name");
4218			if (have_altq)
4219				errx(EX_DATAERR,
4220				    "altq cannot be specified more than once");
4221			have_altq = (ipfw_insn *)a;
4222			cmd->len = F_INSN_SIZE(ipfw_insn_altq);
4223			CHECK_CMDLEN;
4224			cmd->opcode = O_ALTQ;
4225			a->qid = altq_name_to_qid(*av);
4226			av++;
4227		    }
4228			break;
4229#endif
4230
4231		case TOK_TAG:
4232		case TOK_UNTAG: {
4233			uint16_t tag;
4234
4235			if (have_tag)
4236				errx(EX_USAGE, "tag and untag cannot be "
4237				    "specified more than once");
4238			GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX, i,
4239			   rule_action_params);
4240			have_tag = cmd;
4241			fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag);
4242			av++;
4243			break;
4244		}
4245
4246		default:
4247			abort();
4248		}
4249		cmd = next_cmd(cmd, &cblen);
4250	}
4251
4252	if (have_state)	{ /* must be a check-state, we are done */
4253		if (*av != NULL &&
4254		    match_token(rule_options, *av) == TOK_COMMENT) {
4255			/* check-state has a comment */
4256			av++;
4257			fill_comment(cmd, av, cblen);
4258			cmd = next_cmd(cmd, &cblen);
4259			av[0] = NULL;
4260		}
4261		goto done;
4262	}
4263
4264#define OR_START(target)					\
4265	if (av[0] && (*av[0] == '(' || *av[0] == '{')) { 	\
4266		if (open_par)					\
4267			errx(EX_USAGE, "nested \"(\" not allowed\n"); \
4268		prev = NULL;					\
4269		open_par = 1;					\
4270		if ( (av[0])[1] == '\0') {			\
4271			av++;					\
4272		} else						\
4273			(*av)++;				\
4274	}							\
4275	target:							\
4276
4277
4278#define	CLOSE_PAR						\
4279	if (open_par) {						\
4280		if (av[0] && (					\
4281		    strcmp(*av, ")") == 0 ||			\
4282		    strcmp(*av, "}") == 0)) {			\
4283			prev = NULL;				\
4284			open_par = 0;				\
4285			av++;					\
4286		} else						\
4287			errx(EX_USAGE, "missing \")\"\n");	\
4288	}
4289
4290#define NOT_BLOCK						\
4291	if (av[0] && _substrcmp(*av, "not") == 0) {		\
4292		if (cmd->len & F_NOT)				\
4293			errx(EX_USAGE, "double \"not\" not allowed\n"); \
4294		cmd->len |= F_NOT;				\
4295		av++;						\
4296	}
4297
4298#define OR_BLOCK(target)					\
4299	if (av[0] && _substrcmp(*av, "or") == 0) {		\
4300		if (prev == NULL || open_par == 0)		\
4301			errx(EX_DATAERR, "invalid OR block");	\
4302		prev->len |= F_OR;				\
4303		av++;					\
4304		goto target;					\
4305	}							\
4306	CLOSE_PAR;
4307
4308	first_cmd = cmd;
4309
4310#if 0
4311	/*
4312	 * MAC addresses, optional.
4313	 * If we have this, we skip the part "proto from src to dst"
4314	 * and jump straight to the option parsing.
4315	 */
4316	NOT_BLOCK;
4317	NEED1("missing protocol");
4318	if (_substrcmp(*av, "MAC") == 0 ||
4319	    _substrcmp(*av, "mac") == 0) {
4320		av++;			/* the "MAC" keyword */
4321		add_mac(cmd, av);	/* exits in case of errors */
4322		cmd = next_cmd(cmd);
4323		av += 2;		/* dst-mac and src-mac */
4324		NOT_BLOCK;
4325		NEED1("missing mac type");
4326		if (add_mactype(cmd, av[0]))
4327			cmd = next_cmd(cmd);
4328		av++;			/* any or mac-type */
4329		goto read_options;
4330	}
4331#endif
4332
4333	/*
4334	 * protocol, mandatory
4335	 */
4336    OR_START(get_proto);
4337	NOT_BLOCK;
4338	NEED1("missing protocol");
4339	if (add_proto_compat(cmd, *av, &proto)) {
4340		av++;
4341		if (F_LEN(cmd) != 0) {
4342			prev = cmd;
4343			cmd = next_cmd(cmd, &cblen);
4344		}
4345	} else if (first_cmd != cmd) {
4346		errx(EX_DATAERR, "invalid protocol ``%s''", *av);
4347	} else {
4348		rule->flags |= IPFW_RULE_JUSTOPTS;
4349		goto read_options;
4350	}
4351    OR_BLOCK(get_proto);
4352
4353	/*
4354	 * "from", mandatory
4355	 */
4356	if ((av[0] == NULL) || _substrcmp(*av, "from") != 0)
4357		errx(EX_USAGE, "missing ``from''");
4358	av++;
4359
4360	/*
4361	 * source IP, mandatory
4362	 */
4363    OR_START(source_ip);
4364	NOT_BLOCK;	/* optional "not" */
4365	NEED1("missing source address");
4366	if (add_src(cmd, *av, proto, cblen, tstate)) {
4367		av++;
4368		if (F_LEN(cmd) != 0) {	/* ! any */
4369			prev = cmd;
4370			cmd = next_cmd(cmd, &cblen);
4371		}
4372	} else
4373		errx(EX_USAGE, "bad source address %s", *av);
4374    OR_BLOCK(source_ip);
4375
4376	/*
4377	 * source ports, optional
4378	 */
4379	NOT_BLOCK;	/* optional "not" */
4380	if ( av[0] != NULL ) {
4381		if (_substrcmp(*av, "any") == 0 ||
4382		    add_ports(cmd, *av, proto, O_IP_SRCPORT, cblen)) {
4383			av++;
4384			if (F_LEN(cmd) != 0)
4385				cmd = next_cmd(cmd, &cblen);
4386		}
4387	}
4388
4389	/*
4390	 * "to", mandatory
4391	 */
4392	if ( (av[0] == NULL) || _substrcmp(*av, "to") != 0 )
4393		errx(EX_USAGE, "missing ``to''");
4394	av++;
4395
4396	/*
4397	 * destination, mandatory
4398	 */
4399    OR_START(dest_ip);
4400	NOT_BLOCK;	/* optional "not" */
4401	NEED1("missing dst address");
4402	if (add_dst(cmd, *av, proto, cblen, tstate)) {
4403		av++;
4404		if (F_LEN(cmd) != 0) {	/* ! any */
4405			prev = cmd;
4406			cmd = next_cmd(cmd, &cblen);
4407		}
4408	} else
4409		errx( EX_USAGE, "bad destination address %s", *av);
4410    OR_BLOCK(dest_ip);
4411
4412	/*
4413	 * dest. ports, optional
4414	 */
4415	NOT_BLOCK;	/* optional "not" */
4416	if (av[0]) {
4417		if (_substrcmp(*av, "any") == 0 ||
4418		    add_ports(cmd, *av, proto, O_IP_DSTPORT, cblen)) {
4419			av++;
4420			if (F_LEN(cmd) != 0)
4421				cmd = next_cmd(cmd, &cblen);
4422		}
4423	}
4424
4425read_options:
4426	prev = NULL;
4427	while ( av[0] != NULL ) {
4428		char *s;
4429		ipfw_insn_u32 *cmd32;	/* alias for cmd */
4430
4431		s = *av;
4432		cmd32 = (ipfw_insn_u32 *)cmd;
4433
4434		if (*s == '!') {	/* alternate syntax for NOT */
4435			if (cmd->len & F_NOT)
4436				errx(EX_USAGE, "double \"not\" not allowed\n");
4437			cmd->len = F_NOT;
4438			s++;
4439		}
4440		i = match_token(rule_options, s);
4441		av++;
4442		switch(i) {
4443		case TOK_NOT:
4444			if (cmd->len & F_NOT)
4445				errx(EX_USAGE, "double \"not\" not allowed\n");
4446			cmd->len = F_NOT;
4447			break;
4448
4449		case TOK_OR:
4450			if (open_par == 0 || prev == NULL)
4451				errx(EX_USAGE, "invalid \"or\" block\n");
4452			prev->len |= F_OR;
4453			break;
4454
4455		case TOK_STARTBRACE:
4456			if (open_par)
4457				errx(EX_USAGE, "+nested \"(\" not allowed\n");
4458			open_par = 1;
4459			break;
4460
4461		case TOK_ENDBRACE:
4462			if (!open_par)
4463				errx(EX_USAGE, "+missing \")\"\n");
4464			open_par = 0;
4465			prev = NULL;
4466			break;
4467
4468		case TOK_IN:
4469			fill_cmd(cmd, O_IN, 0, 0);
4470			break;
4471
4472		case TOK_OUT:
4473			cmd->len ^= F_NOT; /* toggle F_NOT */
4474			fill_cmd(cmd, O_IN, 0, 0);
4475			break;
4476
4477		case TOK_DIVERTED:
4478			fill_cmd(cmd, O_DIVERTED, 0, 3);
4479			break;
4480
4481		case TOK_DIVERTEDLOOPBACK:
4482			fill_cmd(cmd, O_DIVERTED, 0, 1);
4483			break;
4484
4485		case TOK_DIVERTEDOUTPUT:
4486			fill_cmd(cmd, O_DIVERTED, 0, 2);
4487			break;
4488
4489		case TOK_FRAG:
4490			fill_cmd(cmd, O_FRAG, 0, 0);
4491			break;
4492
4493		case TOK_LAYER2:
4494			fill_cmd(cmd, O_LAYER2, 0, 0);
4495			break;
4496
4497		case TOK_XMIT:
4498		case TOK_RECV:
4499		case TOK_VIA:
4500			NEED1("recv, xmit, via require interface name"
4501				" or address");
4502			fill_iface((ipfw_insn_if *)cmd, av[0], cblen, tstate);
4503			av++;
4504			if (F_LEN(cmd) == 0)	/* not a valid address */
4505				break;
4506			if (i == TOK_XMIT)
4507				cmd->opcode = O_XMIT;
4508			else if (i == TOK_RECV)
4509				cmd->opcode = O_RECV;
4510			else if (i == TOK_VIA)
4511				cmd->opcode = O_VIA;
4512			break;
4513
4514		case TOK_ICMPTYPES:
4515			NEED1("icmptypes requires list of types");
4516			fill_icmptypes((ipfw_insn_u32 *)cmd, *av);
4517			av++;
4518			break;
4519
4520		case TOK_ICMP6TYPES:
4521			NEED1("icmptypes requires list of types");
4522			fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av, cblen);
4523			av++;
4524			break;
4525
4526		case TOK_IPTTL:
4527			NEED1("ipttl requires TTL");
4528			if (strpbrk(*av, "-,")) {
4529			    if (!add_ports(cmd, *av, 0, O_IPTTL, cblen))
4530				errx(EX_DATAERR, "invalid ipttl %s", *av);
4531			} else
4532			    fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
4533			av++;
4534			break;
4535
4536		case TOK_IPID:
4537			NEED1("ipid requires id");
4538			if (strpbrk(*av, "-,")) {
4539			    if (!add_ports(cmd, *av, 0, O_IPID, cblen))
4540				errx(EX_DATAERR, "invalid ipid %s", *av);
4541			} else
4542			    fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0));
4543			av++;
4544			break;
4545
4546		case TOK_IPLEN:
4547			NEED1("iplen requires length");
4548			if (strpbrk(*av, "-,")) {
4549			    if (!add_ports(cmd, *av, 0, O_IPLEN, cblen))
4550				errx(EX_DATAERR, "invalid ip len %s", *av);
4551			} else
4552			    fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0));
4553			av++;
4554			break;
4555
4556		case TOK_IPVER:
4557			NEED1("ipver requires version");
4558			fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0));
4559			av++;
4560			break;
4561
4562		case TOK_IPPRECEDENCE:
4563			NEED1("ipprecedence requires value");
4564			fill_cmd(cmd, O_IPPRECEDENCE, 0,
4565			    (strtoul(*av, NULL, 0) & 7) << 5);
4566			av++;
4567			break;
4568
4569		case TOK_DSCP:
4570			NEED1("missing DSCP code");
4571			fill_dscp(cmd, *av, cblen);
4572			av++;
4573			break;
4574
4575		case TOK_IPOPTS:
4576			NEED1("missing argument for ipoptions");
4577			fill_flags_cmd(cmd, O_IPOPT, f_ipopts, *av);
4578			av++;
4579			break;
4580
4581		case TOK_IPTOS:
4582			NEED1("missing argument for iptos");
4583			fill_flags_cmd(cmd, O_IPTOS, f_iptos, *av);
4584			av++;
4585			break;
4586
4587		case TOK_UID:
4588			NEED1("uid requires argument");
4589		    {
4590			char *end;
4591			uid_t uid;
4592			struct passwd *pwd;
4593
4594			cmd->opcode = O_UID;
4595			uid = strtoul(*av, &end, 0);
4596			pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av);
4597			if (pwd == NULL)
4598				errx(EX_DATAERR, "uid \"%s\" nonexistent", *av);
4599			cmd32->d[0] = pwd->pw_uid;
4600			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
4601			av++;
4602		    }
4603			break;
4604
4605		case TOK_GID:
4606			NEED1("gid requires argument");
4607		    {
4608			char *end;
4609			gid_t gid;
4610			struct group *grp;
4611
4612			cmd->opcode = O_GID;
4613			gid = strtoul(*av, &end, 0);
4614			grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av);
4615			if (grp == NULL)
4616				errx(EX_DATAERR, "gid \"%s\" nonexistent", *av);
4617			cmd32->d[0] = grp->gr_gid;
4618			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
4619			av++;
4620		    }
4621			break;
4622
4623		case TOK_JAIL:
4624			NEED1("jail requires argument");
4625		    {
4626			int jid;
4627
4628			cmd->opcode = O_JAIL;
4629			jid = jail_getid(*av);
4630			if (jid < 0)
4631				errx(EX_DATAERR, "%s", jail_errmsg);
4632			cmd32->d[0] = (uint32_t)jid;
4633			cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
4634			av++;
4635		    }
4636			break;
4637
4638		case TOK_ESTAB:
4639			fill_cmd(cmd, O_ESTAB, 0, 0);
4640			break;
4641
4642		case TOK_SETUP:
4643			fill_cmd(cmd, O_TCPFLAGS, 0,
4644				(TH_SYN) | ( (TH_ACK) & 0xff) <<8 );
4645			break;
4646
4647		case TOK_TCPDATALEN:
4648			NEED1("tcpdatalen requires length");
4649			if (strpbrk(*av, "-,")) {
4650			    if (!add_ports(cmd, *av, 0, O_TCPDATALEN, cblen))
4651				errx(EX_DATAERR, "invalid tcpdata len %s", *av);
4652			} else
4653			    fill_cmd(cmd, O_TCPDATALEN, 0,
4654				    strtoul(*av, NULL, 0));
4655			av++;
4656			break;
4657
4658		case TOK_TCPOPTS:
4659			NEED1("missing argument for tcpoptions");
4660			fill_flags_cmd(cmd, O_TCPOPTS, f_tcpopts, *av);
4661			av++;
4662			break;
4663
4664		case TOK_TCPSEQ:
4665		case TOK_TCPACK:
4666			NEED1("tcpseq/tcpack requires argument");
4667			cmd->len = F_INSN_SIZE(ipfw_insn_u32);
4668			cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK;
4669			cmd32->d[0] = htonl(strtoul(*av, NULL, 0));
4670			av++;
4671			break;
4672
4673		case TOK_TCPWIN:
4674			NEED1("tcpwin requires length");
4675			if (strpbrk(*av, "-,")) {
4676			    if (!add_ports(cmd, *av, 0, O_TCPWIN, cblen))
4677				errx(EX_DATAERR, "invalid tcpwin len %s", *av);
4678			} else
4679			    fill_cmd(cmd, O_TCPWIN, 0,
4680				    strtoul(*av, NULL, 0));
4681			av++;
4682			break;
4683
4684		case TOK_TCPFLAGS:
4685			NEED1("missing argument for tcpflags");
4686			cmd->opcode = O_TCPFLAGS;
4687			fill_flags_cmd(cmd, O_TCPFLAGS, f_tcpflags, *av);
4688			av++;
4689			break;
4690
4691		case TOK_KEEPSTATE:
4692		case TOK_RECORDSTATE: {
4693			uint16_t uidx;
4694
4695			if (open_par)
4696				errx(EX_USAGE, "keep-state or record-state cannot be part "
4697				    "of an or block");
4698			if (have_state)
4699				errx(EX_USAGE, "only one of keep-state, record-state, "
4700					" limit and set-limit is allowed");
4701			if (*av != NULL && *av[0] == ':') {
4702				if (state_check_name(*av + 1) != 0)
4703					errx(EX_DATAERR,
4704					    "Invalid state name %s", *av);
4705				uidx = pack_object(tstate, *av + 1,
4706				    IPFW_TLV_STATE_NAME);
4707				av++;
4708			} else
4709				uidx = pack_object(tstate, default_state_name,
4710				    IPFW_TLV_STATE_NAME);
4711			have_state = cmd;
4712			have_rstate = i == TOK_RECORDSTATE;
4713			fill_cmd(cmd, O_KEEP_STATE, 0, uidx);
4714			break;
4715		}
4716
4717		case TOK_LIMIT:
4718		case TOK_SETLIMIT: {
4719			ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
4720			int val;
4721
4722			if (open_par)
4723				errx(EX_USAGE,
4724				    "limit or set-limit cannot be part of an or block");
4725			if (have_state)
4726				errx(EX_USAGE, "only one of keep-state, record-state, "
4727					" limit and set-limit is allowed");
4728			have_state = cmd;
4729			have_rstate = i == TOK_SETLIMIT;
4730
4731			cmd->len = F_INSN_SIZE(ipfw_insn_limit);
4732			CHECK_CMDLEN;
4733			cmd->opcode = O_LIMIT;
4734			c->limit_mask = c->conn_limit = 0;
4735
4736			while ( av[0] != NULL ) {
4737				if ((val = match_token(limit_masks, *av)) <= 0)
4738					break;
4739				c->limit_mask |= val;
4740				av++;
4741			}
4742
4743			if (c->limit_mask == 0)
4744				errx(EX_USAGE, "limit: missing limit mask");
4745
4746			GET_UINT_ARG(c->conn_limit, IPFW_ARG_MIN, IPFW_ARG_MAX,
4747			    TOK_LIMIT, rule_options);
4748			av++;
4749
4750			if (*av != NULL && *av[0] == ':') {
4751				if (state_check_name(*av + 1) != 0)
4752					errx(EX_DATAERR,
4753					    "Invalid state name %s", *av);
4754				cmd->arg1 = pack_object(tstate, *av + 1,
4755				    IPFW_TLV_STATE_NAME);
4756				av++;
4757			} else
4758				cmd->arg1 = pack_object(tstate,
4759				    default_state_name, IPFW_TLV_STATE_NAME);
4760			break;
4761		}
4762
4763		case TOK_PROTO:
4764			NEED1("missing protocol");
4765			if (add_proto(cmd, *av, &proto)) {
4766				av++;
4767			} else
4768				errx(EX_DATAERR, "invalid protocol ``%s''",
4769				    *av);
4770			break;
4771
4772		case TOK_SRCIP:
4773			NEED1("missing source IP");
4774			if (add_srcip(cmd, *av, cblen, tstate)) {
4775				av++;
4776			}
4777			break;
4778
4779		case TOK_DSTIP:
4780			NEED1("missing destination IP");
4781			if (add_dstip(cmd, *av, cblen, tstate)) {
4782				av++;
4783			}
4784			break;
4785
4786		case TOK_SRCIP6:
4787			NEED1("missing source IP6");
4788			if (add_srcip6(cmd, *av, cblen, tstate)) {
4789				av++;
4790			}
4791			break;
4792
4793		case TOK_DSTIP6:
4794			NEED1("missing destination IP6");
4795			if (add_dstip6(cmd, *av, cblen, tstate)) {
4796				av++;
4797			}
4798			break;
4799
4800		case TOK_SRCPORT:
4801			NEED1("missing source port");
4802			if (_substrcmp(*av, "any") == 0 ||
4803			    add_ports(cmd, *av, proto, O_IP_SRCPORT, cblen)) {
4804				av++;
4805			} else
4806				errx(EX_DATAERR, "invalid source port %s", *av);
4807			break;
4808
4809		case TOK_DSTPORT:
4810			NEED1("missing destination port");
4811			if (_substrcmp(*av, "any") == 0 ||
4812			    add_ports(cmd, *av, proto, O_IP_DSTPORT, cblen)) {
4813				av++;
4814			} else
4815				errx(EX_DATAERR, "invalid destination port %s",
4816				    *av);
4817			break;
4818
4819		case TOK_MAC:
4820			if (add_mac(cmd, av, cblen))
4821				av += 2;
4822			break;
4823
4824		case TOK_MACTYPE:
4825			NEED1("missing mac type");
4826			if (!add_mactype(cmd, *av, cblen))
4827				errx(EX_DATAERR, "invalid mac type %s", *av);
4828			av++;
4829			break;
4830
4831		case TOK_VERREVPATH:
4832			fill_cmd(cmd, O_VERREVPATH, 0, 0);
4833			break;
4834
4835		case TOK_VERSRCREACH:
4836			fill_cmd(cmd, O_VERSRCREACH, 0, 0);
4837			break;
4838
4839		case TOK_ANTISPOOF:
4840			fill_cmd(cmd, O_ANTISPOOF, 0, 0);
4841			break;
4842
4843		case TOK_IPSEC:
4844			fill_cmd(cmd, O_IPSEC, 0, 0);
4845			break;
4846
4847		case TOK_IPV6:
4848			fill_cmd(cmd, O_IP6, 0, 0);
4849			break;
4850
4851		case TOK_IPV4:
4852			fill_cmd(cmd, O_IP4, 0, 0);
4853			break;
4854
4855		case TOK_EXT6HDR:
4856			fill_ext6hdr( cmd, *av );
4857			av++;
4858			break;
4859
4860		case TOK_FLOWID:
4861			if (proto != IPPROTO_IPV6 )
4862				errx( EX_USAGE, "flow-id filter is active "
4863				    "only for ipv6 protocol\n");
4864			fill_flow6( (ipfw_insn_u32 *) cmd, *av, cblen);
4865			av++;
4866			break;
4867
4868		case TOK_COMMENT:
4869			fill_comment(cmd, av, cblen);
4870			av[0]=NULL;
4871			break;
4872
4873		case TOK_TAGGED:
4874			if (av[0] && strpbrk(*av, "-,")) {
4875				if (!add_ports(cmd, *av, 0, O_TAGGED, cblen))
4876					errx(EX_DATAERR, "tagged: invalid tag"
4877					    " list: %s", *av);
4878			}
4879			else {
4880				uint16_t tag;
4881
4882				GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX,
4883				    TOK_TAGGED, rule_options);
4884				fill_cmd(cmd, O_TAGGED, 0, tag);
4885			}
4886			av++;
4887			break;
4888
4889		case TOK_FIB:
4890			NEED1("fib requires fib number");
4891			fill_cmd(cmd, O_FIB, 0, strtoul(*av, NULL, 0));
4892			av++;
4893			break;
4894		case TOK_SOCKARG:
4895			fill_cmd(cmd, O_SOCKARG, 0, 0);
4896			break;
4897
4898		case TOK_LOOKUP: {
4899			ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd;
4900			int j;
4901
4902			if (!av[0] || !av[1])
4903				errx(EX_USAGE, "format: lookup argument tablenum");
4904			cmd->opcode = O_IP_DST_LOOKUP;
4905			cmd->len |= F_INSN_SIZE(ipfw_insn) + 2;
4906			i = match_token(rule_options, *av);
4907			for (j = 0; lookup_key[j] >= 0 ; j++) {
4908				if (i == lookup_key[j])
4909					break;
4910			}
4911			if (lookup_key[j] <= 0)
4912				errx(EX_USAGE, "format: cannot lookup on %s", *av);
4913			__PAST_END(c->d, 1) = j; // i converted to option
4914			av++;
4915
4916			if ((j = pack_table(tstate, *av)) == 0)
4917				errx(EX_DATAERR, "Invalid table name: %s", *av);
4918
4919			cmd->arg1 = j;
4920			av++;
4921		    }
4922			break;
4923		case TOK_FLOW:
4924			NEED1("missing table name");
4925			if (strncmp(*av, "table(", 6) != 0)
4926				errx(EX_DATAERR,
4927				    "enclose table name into \"table()\"");
4928			fill_table(cmd, *av, O_IP_FLOW_LOOKUP, tstate);
4929			av++;
4930			break;
4931
4932		case TOK_SKIPACTION:
4933			if (have_skipcmd)
4934				errx(EX_USAGE, "only one defer-action "
4935					"is allowed");
4936			have_skipcmd = cmd;
4937			fill_cmd(cmd, O_SKIP_ACTION, 0, 0);
4938			break;
4939
4940		default:
4941			errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
4942		}
4943		if (F_LEN(cmd) > 0) {	/* prepare to advance */
4944			prev = cmd;
4945			cmd = next_cmd(cmd, &cblen);
4946		}
4947	}
4948
4949done:
4950
4951	if (!have_state && have_skipcmd)
4952		warnx("Rule contains \"defer-immediate-action\" "
4953			"and doesn't contain any state-related options.");
4954
4955	/*
4956	 * Now copy stuff into the rule.
4957	 * If we have a keep-state option, the first instruction
4958	 * must be a PROBE_STATE (which is generated here).
4959	 * If we have a LOG option, it was stored as the first command,
4960	 * and now must be moved to the top of the action part.
4961	 */
4962	dst = (ipfw_insn *)rule->cmd;
4963
4964	/*
4965	 * First thing to write into the command stream is the match probability.
4966	 */
4967	if (match_prob != 1) { /* 1 means always match */
4968		dst->opcode = O_PROB;
4969		dst->len = 2;
4970		*((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff);
4971		dst += dst->len;
4972	}
4973
4974	/*
4975	 * generate O_PROBE_STATE if necessary
4976	 */
4977	if (have_state && have_state->opcode != O_CHECK_STATE && !have_rstate) {
4978		fill_cmd(dst, O_PROBE_STATE, 0, have_state->arg1);
4979		dst = next_cmd(dst, &rblen);
4980	}
4981
4982	/*
4983	 * copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG,
4984	 * O_SKIP_ACTION
4985	 */
4986	for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
4987		i = F_LEN(src);
4988		CHECK_RBUFLEN(i);
4989
4990		switch (src->opcode) {
4991		case O_LOG:
4992		case O_KEEP_STATE:
4993		case O_LIMIT:
4994		case O_ALTQ:
4995		case O_TAG:
4996		case O_SKIP_ACTION:
4997			break;
4998		default:
4999			bcopy(src, dst, i * sizeof(uint32_t));
5000			dst += i;
5001		}
5002	}
5003
5004	/*
5005	 * put back the have_state command as last opcode
5006	 */
5007	if (have_state && have_state->opcode != O_CHECK_STATE) {
5008		i = F_LEN(have_state);
5009		CHECK_RBUFLEN(i);
5010		bcopy(have_state, dst, i * sizeof(uint32_t));
5011		dst += i;
5012	}
5013
5014	/*
5015	 * put back the have_skipcmd command as very last opcode
5016	 */
5017	if (have_skipcmd) {
5018		i = F_LEN(have_skipcmd);
5019		CHECK_RBUFLEN(i);
5020		bcopy(have_skipcmd, dst, i * sizeof(uint32_t));
5021		dst += i;
5022	}
5023
5024	/*
5025	 * start action section
5026	 */
5027	rule->act_ofs = dst - rule->cmd;
5028
5029	/* put back O_LOG, O_ALTQ, O_TAG if necessary */
5030	if (have_log) {
5031		i = F_LEN(have_log);
5032		CHECK_RBUFLEN(i);
5033		bcopy(have_log, dst, i * sizeof(uint32_t));
5034		dst += i;
5035	}
5036	if (have_altq) {
5037		i = F_LEN(have_altq);
5038		CHECK_RBUFLEN(i);
5039		bcopy(have_altq, dst, i * sizeof(uint32_t));
5040		dst += i;
5041	}
5042	if (have_tag) {
5043		i = F_LEN(have_tag);
5044		CHECK_RBUFLEN(i);
5045		bcopy(have_tag, dst, i * sizeof(uint32_t));
5046		dst += i;
5047	}
5048
5049	/*
5050	 * copy all other actions
5051	 */
5052	for (src = (ipfw_insn *)actbuf; src != action; src += i) {
5053		i = F_LEN(src);
5054		CHECK_RBUFLEN(i);
5055		bcopy(src, dst, i * sizeof(uint32_t));
5056		dst += i;
5057	}
5058
5059	rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd);
5060	*rbufsize = (char *)dst - (char *)rule;
5061}
5062
5063static int
5064compare_ntlv(const void *_a, const void *_b)
5065{
5066	ipfw_obj_ntlv *a, *b;
5067
5068	a = (ipfw_obj_ntlv *)_a;
5069	b = (ipfw_obj_ntlv *)_b;
5070
5071	if (a->set < b->set)
5072		return (-1);
5073	else if (a->set > b->set)
5074		return (1);
5075
5076	if (a->idx < b->idx)
5077		return (-1);
5078	else if (a->idx > b->idx)
5079		return (1);
5080
5081	if (a->head.type < b->head.type)
5082		return (-1);
5083	else if (a->head.type > b->head.type)
5084		return (1);
5085
5086	return (0);
5087}
5088
5089/*
5090 * Provide kernel with sorted list of referenced objects
5091 */
5092static void
5093object_sort_ctlv(ipfw_obj_ctlv *ctlv)
5094{
5095
5096	qsort(ctlv + 1, ctlv->count, ctlv->objsize, compare_ntlv);
5097}
5098
5099struct object_kt {
5100	uint16_t	uidx;
5101	uint16_t	type;
5102};
5103static int
5104compare_object_kntlv(const void *k, const void *v)
5105{
5106	ipfw_obj_ntlv *ntlv;
5107	struct object_kt key;
5108
5109	key = *((struct object_kt *)k);
5110	ntlv = (ipfw_obj_ntlv *)v;
5111
5112	if (key.uidx < ntlv->idx)
5113		return (-1);
5114	else if (key.uidx > ntlv->idx)
5115		return (1);
5116
5117	if (key.type < ntlv->head.type)
5118		return (-1);
5119	else if (key.type > ntlv->head.type)
5120		return (1);
5121
5122	return (0);
5123}
5124
5125/*
5126 * Finds object name in @ctlv by @idx and @type.
5127 * Uses the following facts:
5128 * 1) All TLVs are the same size
5129 * 2) Kernel implementation provides already sorted list.
5130 *
5131 * Returns table name or NULL.
5132 */
5133static char *
5134object_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx, uint16_t type)
5135{
5136	ipfw_obj_ntlv *ntlv;
5137	struct object_kt key;
5138
5139	key.uidx = idx;
5140	key.type = type;
5141
5142	ntlv = bsearch(&key, (ctlv + 1), ctlv->count, ctlv->objsize,
5143	    compare_object_kntlv);
5144
5145	if (ntlv != NULL)
5146		return (ntlv->name);
5147
5148	return (NULL);
5149}
5150
5151static char *
5152table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx)
5153{
5154
5155	return (object_search_ctlv(ctlv, idx, IPFW_TLV_TBL_NAME));
5156}
5157
5158/*
5159 * Adds one or more rules to ipfw chain.
5160 * Data layout:
5161 * Request:
5162 * [
5163 *   ip_fw3_opheader
5164 *   [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1)
5165 *   [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) [ ip_fw_rule ip_fw_insn ] x N ] (*2) (*3)
5166 * ]
5167 * Reply:
5168 * [
5169 *   ip_fw3_opheader
5170 *   [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional)
5171 *   [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) [ ip_fw_rule ip_fw_insn ] x N ]
5172 * ]
5173 *
5174 * Rules in reply are modified to store their actual ruleset number.
5175 *
5176 * (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending
5177 * according to their idx field and there has to be no duplicates.
5178 * (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending.
5179 * (*3) Each ip_fw structure needs to be aligned to u64 boundary.
5180 */
5181void
5182ipfw_add(char *av[])
5183{
5184	uint32_t rulebuf[1024];
5185	int rbufsize, default_off, tlen, rlen;
5186	size_t sz;
5187	struct tidx ts;
5188	struct ip_fw_rule *rule;
5189	caddr_t tbuf;
5190	ip_fw3_opheader *op3;
5191	ipfw_obj_ctlv *ctlv, *tstate;
5192
5193	rbufsize = sizeof(rulebuf);
5194	memset(rulebuf, 0, rbufsize);
5195	memset(&ts, 0, sizeof(ts));
5196
5197	/* Optimize case with no tables */
5198	default_off = sizeof(ipfw_obj_ctlv) + sizeof(ip_fw3_opheader);
5199	op3 = (ip_fw3_opheader *)rulebuf;
5200	ctlv = (ipfw_obj_ctlv *)(op3 + 1);
5201	rule = (struct ip_fw_rule *)(ctlv + 1);
5202	rbufsize -= default_off;
5203
5204	compile_rule(av, (uint32_t *)rule, &rbufsize, &ts);
5205	/* Align rule size to u64 boundary */
5206	rlen = roundup2(rbufsize, sizeof(uint64_t));
5207
5208	tbuf = NULL;
5209	sz = 0;
5210	tstate = NULL;
5211	if (ts.count != 0) {
5212		/* Some tables. We have to alloc more data */
5213		tlen = ts.count * sizeof(ipfw_obj_ntlv);
5214		sz = default_off + sizeof(ipfw_obj_ctlv) + tlen + rlen;
5215
5216		if ((tbuf = calloc(1, sz)) == NULL)
5217			err(EX_UNAVAILABLE, "malloc() failed for IP_FW_ADD");
5218		op3 = (ip_fw3_opheader *)tbuf;
5219		/* Tables first */
5220		ctlv = (ipfw_obj_ctlv *)(op3 + 1);
5221		ctlv->head.type = IPFW_TLV_TBLNAME_LIST;
5222		ctlv->head.length = sizeof(ipfw_obj_ctlv) + tlen;
5223		ctlv->count = ts.count;
5224		ctlv->objsize = sizeof(ipfw_obj_ntlv);
5225		memcpy(ctlv + 1, ts.idx, tlen);
5226		object_sort_ctlv(ctlv);
5227		tstate = ctlv;
5228		/* Rule next */
5229		ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
5230		ctlv->head.type = IPFW_TLV_RULE_LIST;
5231		ctlv->head.length = sizeof(ipfw_obj_ctlv) + rlen;
5232		ctlv->count = 1;
5233		memcpy(ctlv + 1, rule, rbufsize);
5234	} else {
5235		/* Simply add header */
5236		sz = rlen + default_off;
5237		memset(ctlv, 0, sizeof(*ctlv));
5238		ctlv->head.type = IPFW_TLV_RULE_LIST;
5239		ctlv->head.length = sizeof(ipfw_obj_ctlv) + rlen;
5240		ctlv->count = 1;
5241	}
5242
5243	if (do_get3(IP_FW_XADD, op3, &sz) != 0)
5244		err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_XADD");
5245
5246	if (!co.do_quiet) {
5247		struct format_opts sfo;
5248		struct buf_pr bp;
5249		memset(&sfo, 0, sizeof(sfo));
5250		sfo.tstate = tstate;
5251		sfo.set_mask = (uint32_t)(-1);
5252		bp_alloc(&bp, 4096);
5253		show_static_rule(&co, &sfo, &bp, rule, NULL);
5254		printf("%s", bp.buf);
5255		bp_free(&bp);
5256	}
5257
5258	if (tbuf != NULL)
5259		free(tbuf);
5260
5261	if (ts.idx != NULL)
5262		free(ts.idx);
5263}
5264
5265/*
5266 * clear the counters or the log counters.
5267 * optname has the following values:
5268 *  0 (zero both counters and logging)
5269 *  1 (zero logging only)
5270 */
5271void
5272ipfw_zero(int ac, char *av[], int optname)
5273{
5274	ipfw_range_tlv rt;
5275	char const *errstr;
5276	char const *name = optname ? "RESETLOG" : "ZERO";
5277	uint32_t arg;
5278	int failed = EX_OK;
5279
5280	optname = optname ? IP_FW_XRESETLOG : IP_FW_XZERO;
5281	av++; ac--;
5282
5283	if (ac == 0) {
5284		/* clear all entries */
5285		memset(&rt, 0, sizeof(rt));
5286		rt.flags = IPFW_RCFLAG_ALL;
5287		if (do_range_cmd(optname, &rt) < 0)
5288			err(EX_UNAVAILABLE, "setsockopt(IP_FW_X%s)", name);
5289		if (!co.do_quiet)
5290			printf("%s.\n", optname == IP_FW_XZERO ?
5291			    "Accounting cleared":"Logging counts reset");
5292
5293		return;
5294	}
5295
5296	while (ac) {
5297		/* Rule number */
5298		if (isdigit(**av)) {
5299			arg = strtonum(*av, 0, 0xffff, &errstr);
5300			if (errstr)
5301				errx(EX_DATAERR,
5302				    "invalid rule number %s\n", *av);
5303			memset(&rt, 0, sizeof(rt));
5304			rt.start_rule = arg;
5305			rt.end_rule = arg;
5306			rt.flags |= IPFW_RCFLAG_RANGE;
5307			if (co.use_set != 0) {
5308				rt.set = co.use_set - 1;
5309				rt.flags |= IPFW_RCFLAG_SET;
5310			}
5311			if (do_range_cmd(optname, &rt) != 0) {
5312				warn("rule %u: setsockopt(IP_FW_X%s)",
5313				    arg, name);
5314				failed = EX_UNAVAILABLE;
5315			} else if (rt.new_set == 0) {
5316				printf("Entry %d not found\n", arg);
5317				failed = EX_UNAVAILABLE;
5318			} else if (!co.do_quiet)
5319				printf("Entry %d %s.\n", arg,
5320				    optname == IP_FW_XZERO ?
5321					"cleared" : "logging count reset");
5322		} else {
5323			errx(EX_USAGE, "invalid rule number ``%s''", *av);
5324		}
5325		av++; ac--;
5326	}
5327	if (failed != EX_OK)
5328		exit(failed);
5329}
5330
5331void
5332ipfw_flush(int force)
5333{
5334	ipfw_range_tlv rt;
5335
5336	if (!force && !co.do_quiet) { /* need to ask user */
5337		int c;
5338
5339		printf("Are you sure? [yn] ");
5340		fflush(stdout);
5341		do {
5342			c = toupper(getc(stdin));
5343			while (c != '\n' && getc(stdin) != '\n')
5344				if (feof(stdin))
5345					return; /* and do not flush */
5346		} while (c != 'Y' && c != 'N');
5347		printf("\n");
5348		if (c == 'N')	/* user said no */
5349			return;
5350	}
5351	if (co.do_pipe) {
5352		dummynet_flush();
5353		return;
5354	}
5355	/* `ipfw set N flush` - is the same that `ipfw delete set N` */
5356	memset(&rt, 0, sizeof(rt));
5357	if (co.use_set != 0) {
5358		rt.set = co.use_set - 1;
5359		rt.flags = IPFW_RCFLAG_SET;
5360	} else
5361		rt.flags = IPFW_RCFLAG_ALL;
5362	if (do_range_cmd(IP_FW_XDEL, &rt) != 0)
5363			err(EX_UNAVAILABLE, "setsockopt(IP_FW_XDEL)");
5364	if (!co.do_quiet)
5365		printf("Flushed all %s.\n", co.do_pipe ? "pipes" : "rules");
5366}
5367
5368static struct _s_x intcmds[] = {
5369      { "talist",	TOK_TALIST },
5370      { "iflist",	TOK_IFLIST },
5371      { "olist",	TOK_OLIST },
5372      { "vlist",	TOK_VLIST },
5373      { NULL, 0 }
5374};
5375
5376static struct _s_x otypes[] = {
5377	{ "EACTION",	IPFW_TLV_EACTION },
5378	{ "DYNSTATE",	IPFW_TLV_STATE_NAME },
5379	{ NULL, 0 }
5380};
5381
5382static const char*
5383lookup_eaction_name(ipfw_obj_ntlv *ntlv, int cnt, uint16_t type)
5384{
5385	const char *name;
5386	int i;
5387
5388	name = NULL;
5389	for (i = 0; i < cnt; i++) {
5390		if (ntlv[i].head.type != IPFW_TLV_EACTION)
5391			continue;
5392		if (IPFW_TLV_EACTION_NAME(ntlv[i].idx) != type)
5393			continue;
5394		name = ntlv[i].name;
5395		break;
5396	}
5397	return (name);
5398}
5399
5400static void
5401ipfw_list_objects(int ac, char *av[])
5402{
5403	ipfw_obj_lheader req, *olh;
5404	ipfw_obj_ntlv *ntlv;
5405	const char *name;
5406	size_t sz;
5407	int i;
5408
5409	memset(&req, 0, sizeof(req));
5410	sz = sizeof(req);
5411	if (do_get3(IP_FW_DUMP_SRVOBJECTS, &req.opheader, &sz) != 0)
5412		if (errno != ENOMEM)
5413			return;
5414
5415	sz = req.size;
5416	if ((olh = calloc(1, sz)) == NULL)
5417		return;
5418
5419	olh->size = sz;
5420	if (do_get3(IP_FW_DUMP_SRVOBJECTS, &olh->opheader, &sz) != 0) {
5421		free(olh);
5422		return;
5423	}
5424
5425	if (olh->count > 0)
5426		printf("Objects list:\n");
5427	else
5428		printf("There are no objects\n");
5429	ntlv = (ipfw_obj_ntlv *)(olh + 1);
5430	for (i = 0; i < olh->count; i++) {
5431		name = match_value(otypes, ntlv->head.type);
5432		if (name == NULL)
5433			name = lookup_eaction_name(
5434			    (ipfw_obj_ntlv *)(olh + 1), olh->count,
5435			    ntlv->head.type);
5436		if (name == NULL)
5437			printf(" kidx: %4d\ttype: %10d\tname: %s\n",
5438			    ntlv->idx, ntlv->head.type, ntlv->name);
5439		else
5440			printf(" kidx: %4d\ttype: %10s\tname: %s\n",
5441			    ntlv->idx, name, ntlv->name);
5442		ntlv++;
5443	}
5444	free(olh);
5445}
5446
5447void
5448ipfw_internal_handler(int ac, char *av[])
5449{
5450	int tcmd;
5451
5452	ac--; av++;
5453	NEED1("internal cmd required");
5454
5455	if ((tcmd = match_token(intcmds, *av)) == -1)
5456		errx(EX_USAGE, "invalid internal sub-cmd: %s", *av);
5457
5458	switch (tcmd) {
5459	case TOK_IFLIST:
5460		ipfw_list_tifaces();
5461		break;
5462	case TOK_TALIST:
5463		ipfw_list_ta(ac, av);
5464		break;
5465	case TOK_OLIST:
5466		ipfw_list_objects(ac, av);
5467		break;
5468	case TOK_VLIST:
5469		ipfw_list_values(ac, av);
5470		break;
5471	}
5472}
5473
5474static int
5475ipfw_get_tracked_ifaces(ipfw_obj_lheader **polh)
5476{
5477	ipfw_obj_lheader req, *olh;
5478	size_t sz;
5479
5480	memset(&req, 0, sizeof(req));
5481	sz = sizeof(req);
5482
5483	if (do_get3(IP_FW_XIFLIST, &req.opheader, &sz) != 0) {
5484		if (errno != ENOMEM)
5485			return (errno);
5486	}
5487
5488	sz = req.size;
5489	if ((olh = calloc(1, sz)) == NULL)
5490		return (ENOMEM);
5491
5492	olh->size = sz;
5493	if (do_get3(IP_FW_XIFLIST, &olh->opheader, &sz) != 0) {
5494		free(olh);
5495		return (errno);
5496	}
5497
5498	*polh = olh;
5499	return (0);
5500}
5501
5502static int
5503ifinfo_cmp(const void *a, const void *b)
5504{
5505	ipfw_iface_info *ia, *ib;
5506
5507	ia = (ipfw_iface_info *)a;
5508	ib = (ipfw_iface_info *)b;
5509
5510	return (stringnum_cmp(ia->ifname, ib->ifname));
5511}
5512
5513/*
5514 * Retrieves table list from kernel,
5515 * optionally sorts it and calls requested function for each table.
5516 * Returns 0 on success.
5517 */
5518static void
5519ipfw_list_tifaces()
5520{
5521	ipfw_obj_lheader *olh;
5522	ipfw_iface_info *info;
5523	int i, error;
5524
5525	if ((error = ipfw_get_tracked_ifaces(&olh)) != 0)
5526		err(EX_OSERR, "Unable to request ipfw tracked interface list");
5527
5528
5529	qsort(olh + 1, olh->count, olh->objsize, ifinfo_cmp);
5530
5531	info = (ipfw_iface_info *)(olh + 1);
5532	for (i = 0; i < olh->count; i++) {
5533		if (info->flags & IPFW_IFFLAG_RESOLVED)
5534			printf("%s ifindex: %d refcount: %u changes: %u\n",
5535			    info->ifname, info->ifindex, info->refcnt,
5536			    info->gencnt);
5537		else
5538			printf("%s ifindex: unresolved refcount: %u changes: %u\n",
5539			    info->ifname, info->refcnt, info->gencnt);
5540		info = (ipfw_iface_info *)((caddr_t)info + olh->objsize);
5541	}
5542
5543	free(olh);
5544}
5545
5546
5547
5548
5549