122ce4affSfengbojiang /*-
2127dd473Swhl739 * Copyright (c) 2002-2003 Luigi Rizzo
3127dd473Swhl739 * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
4127dd473Swhl739 * Copyright (c) 1994 Ugen J.S.Antsilevich
5127dd473Swhl739 *
6127dd473Swhl739 * Idea and grammar partially left from:
7127dd473Swhl739 * Copyright (c) 1993 Daniel Boulet
8127dd473Swhl739 *
9127dd473Swhl739 * Redistribution and use in source forms, with and without modification,
10127dd473Swhl739 * are permitted provided that this entire comment appears intact.
11127dd473Swhl739 *
12127dd473Swhl739 * Redistribution in binary form may occur without any restrictions.
13127dd473Swhl739 * Obviously, it would be nice if you gave credit where credit is due
14127dd473Swhl739 * but requiring it would be too onerous.
15127dd473Swhl739 *
16127dd473Swhl739 * This software is provided ``AS IS'' without any warranties of any kind.
17127dd473Swhl739 *
18127dd473Swhl739 * NEW command line interface for IP firewall facility
19127dd473Swhl739 *
20127dd473Swhl739 * $FreeBSD$
21127dd473Swhl739 */
22127dd473Swhl739
23127dd473Swhl739 #include <sys/types.h>
24127dd473Swhl739 #include <sys/param.h>
25127dd473Swhl739 #include <sys/socket.h>
26127dd473Swhl739 #include <sys/sockio.h>
27127dd473Swhl739 #include <sys/sysctl.h>
28127dd473Swhl739
29127dd473Swhl739 #include "ipfw2.h"
30127dd473Swhl739
31127dd473Swhl739 #include <ctype.h>
32127dd473Swhl739 #include <err.h>
33127dd473Swhl739 #include <errno.h>
34127dd473Swhl739 #include <grp.h>
35*d4a07e70Sfengbojiang #ifndef FSTACK
3622ce4affSfengbojiang #include <jail.h>
37*d4a07e70Sfengbojiang #endif
38127dd473Swhl739 #include <netdb.h>
39127dd473Swhl739 #include <pwd.h>
40127dd473Swhl739 #include <stdio.h>
41127dd473Swhl739 #include <stdarg.h>
4222ce4affSfengbojiang #include <stdint.h>
43127dd473Swhl739 #include <stdlib.h>
44127dd473Swhl739 #include <string.h>
45127dd473Swhl739 #include <sysexits.h>
46127dd473Swhl739 #include <time.h> /* ctime */
47127dd473Swhl739 #include <timeconv.h> /* _long_to_time */
48127dd473Swhl739 #include <unistd.h>
49127dd473Swhl739 #include <fcntl.h>
50127dd473Swhl739 #include <stddef.h> /* offsetof */
51127dd473Swhl739
52127dd473Swhl739 #include <net/ethernet.h>
53127dd473Swhl739 #include <net/if.h> /* only IFNAMSIZ */
54127dd473Swhl739 #include <netinet/in.h>
55127dd473Swhl739 #include <netinet/in_systm.h> /* only n_short, n_long */
56127dd473Swhl739 #include <netinet/ip.h>
57127dd473Swhl739 #include <netinet/ip_icmp.h>
58127dd473Swhl739 #include <netinet/ip_fw.h>
59127dd473Swhl739 #include <netinet/tcp.h>
60127dd473Swhl739 #include <arpa/inet.h>
61127dd473Swhl739
62*d4a07e70Sfengbojiang #ifdef FSTACK
63*d4a07e70Sfengbojiang #ifndef __unused
64*d4a07e70Sfengbojiang #define __unused __attribute__((__unused__))
65*d4a07e70Sfengbojiang #endif
66*d4a07e70Sfengbojiang #endif
67*d4a07e70Sfengbojiang
6822ce4affSfengbojiang struct cmdline_opts g_co; /* global options */
69127dd473Swhl739
70127dd473Swhl739 struct format_opts {
71127dd473Swhl739 int bcwidth;
72127dd473Swhl739 int pcwidth;
73127dd473Swhl739 int show_counters;
74127dd473Swhl739 int show_time; /* show timestamp */
75127dd473Swhl739 uint32_t set_mask; /* enabled sets mask */
76127dd473Swhl739 uint32_t flags; /* request flags */
77127dd473Swhl739 uint32_t first; /* first rule to request */
78127dd473Swhl739 uint32_t last; /* last rule to request */
79127dd473Swhl739 uint32_t dcnt; /* number of dynamic states */
80127dd473Swhl739 ipfw_obj_ctlv *tstate; /* table state data */
81127dd473Swhl739 };
82127dd473Swhl739
83127dd473Swhl739 int resvd_set_number = RESVD_SET;
84127dd473Swhl739
8522ce4affSfengbojiang static int ipfw_socket = -1;
86127dd473Swhl739
87127dd473Swhl739 #define CHECK_LENGTH(v, len) do { \
88127dd473Swhl739 if ((v) < (len)) \
89127dd473Swhl739 errx(EX_DATAERR, "Rule too long"); \
90127dd473Swhl739 } while (0)
91127dd473Swhl739 /*
92127dd473Swhl739 * Check if we have enough space in cmd buffer. Note that since
93127dd473Swhl739 * first 8? u32 words are reserved by reserved header, full cmd
94127dd473Swhl739 * buffer can't be used, so we need to protect from buffer overrun
95127dd473Swhl739 * only. At the beginning, cblen is less than actual buffer size by
96127dd473Swhl739 * size of ipfw_insn_u32 instruction + 1 u32 work. This eliminates need
97127dd473Swhl739 * for checking small instructions fitting in given range.
98127dd473Swhl739 * We also (ab)use the fact that ipfw_insn is always the first field
99127dd473Swhl739 * for any custom instruction.
100127dd473Swhl739 */
101127dd473Swhl739 #define CHECK_CMDLEN CHECK_LENGTH(cblen, F_LEN((ipfw_insn *)cmd))
102127dd473Swhl739
103127dd473Swhl739 #define GET_UINT_ARG(arg, min, max, tok, s_x) do { \
104127dd473Swhl739 if (!av[0]) \
105127dd473Swhl739 errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \
106127dd473Swhl739 if (_substrcmp(*av, "tablearg") == 0) { \
107127dd473Swhl739 arg = IP_FW_TARG; \
108127dd473Swhl739 break; \
109127dd473Swhl739 } \
110127dd473Swhl739 \
111127dd473Swhl739 { \
112127dd473Swhl739 long _xval; \
113127dd473Swhl739 char *end; \
114127dd473Swhl739 \
115127dd473Swhl739 _xval = strtol(*av, &end, 10); \
116127dd473Swhl739 \
117127dd473Swhl739 if (!isdigit(**av) || *end != '\0' || (_xval == 0 && errno == EINVAL)) \
118127dd473Swhl739 errx(EX_DATAERR, "%s: invalid argument: %s", \
119127dd473Swhl739 match_value(s_x, tok), *av); \
120127dd473Swhl739 \
121127dd473Swhl739 if (errno == ERANGE || _xval < min || _xval > max) \
122127dd473Swhl739 errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \
123127dd473Swhl739 match_value(s_x, tok), min, max, *av); \
124127dd473Swhl739 \
125127dd473Swhl739 if (_xval == IP_FW_TARG) \
126127dd473Swhl739 errx(EX_DATAERR, "%s: illegal argument value: %s", \
127127dd473Swhl739 match_value(s_x, tok), *av); \
128127dd473Swhl739 arg = _xval; \
129127dd473Swhl739 } \
130127dd473Swhl739 } while (0)
131127dd473Swhl739
132127dd473Swhl739 static struct _s_x f_tcpflags[] = {
133127dd473Swhl739 { "syn", TH_SYN },
134127dd473Swhl739 { "fin", TH_FIN },
135127dd473Swhl739 { "ack", TH_ACK },
136127dd473Swhl739 { "psh", TH_PUSH },
137127dd473Swhl739 { "rst", TH_RST },
138127dd473Swhl739 { "urg", TH_URG },
139127dd473Swhl739 { "tcp flag", 0 },
140127dd473Swhl739 { NULL, 0 }
141127dd473Swhl739 };
142127dd473Swhl739
143127dd473Swhl739 static struct _s_x f_tcpopts[] = {
144127dd473Swhl739 { "mss", IP_FW_TCPOPT_MSS },
145127dd473Swhl739 { "maxseg", IP_FW_TCPOPT_MSS },
146127dd473Swhl739 { "window", IP_FW_TCPOPT_WINDOW },
147127dd473Swhl739 { "sack", IP_FW_TCPOPT_SACK },
148127dd473Swhl739 { "ts", IP_FW_TCPOPT_TS },
149127dd473Swhl739 { "timestamp", IP_FW_TCPOPT_TS },
150127dd473Swhl739 { "cc", IP_FW_TCPOPT_CC },
151127dd473Swhl739 { "tcp option", 0 },
152127dd473Swhl739 { NULL, 0 }
153127dd473Swhl739 };
154127dd473Swhl739
155127dd473Swhl739 /*
156127dd473Swhl739 * IP options span the range 0 to 255 so we need to remap them
157127dd473Swhl739 * (though in fact only the low 5 bits are significant).
158127dd473Swhl739 */
159127dd473Swhl739 static struct _s_x f_ipopts[] = {
160127dd473Swhl739 { "ssrr", IP_FW_IPOPT_SSRR},
161127dd473Swhl739 { "lsrr", IP_FW_IPOPT_LSRR},
162127dd473Swhl739 { "rr", IP_FW_IPOPT_RR},
163127dd473Swhl739 { "ts", IP_FW_IPOPT_TS},
164127dd473Swhl739 { "ip option", 0 },
165127dd473Swhl739 { NULL, 0 }
166127dd473Swhl739 };
167127dd473Swhl739
168127dd473Swhl739 static struct _s_x f_iptos[] = {
169127dd473Swhl739 { "lowdelay", IPTOS_LOWDELAY},
170127dd473Swhl739 { "throughput", IPTOS_THROUGHPUT},
171127dd473Swhl739 { "reliability", IPTOS_RELIABILITY},
172127dd473Swhl739 { "mincost", IPTOS_MINCOST},
173127dd473Swhl739 { "congestion", IPTOS_ECN_CE},
174127dd473Swhl739 { "ecntransport", IPTOS_ECN_ECT0},
175127dd473Swhl739 { "ip tos option", 0},
176127dd473Swhl739 { NULL, 0 }
177127dd473Swhl739 };
178127dd473Swhl739
17922ce4affSfengbojiang static struct _s_x f_ipoff[] = {
18022ce4affSfengbojiang { "rf", IP_RF >> 8 },
18122ce4affSfengbojiang { "df", IP_DF >> 8 },
18222ce4affSfengbojiang { "mf", IP_MF >> 8 },
18322ce4affSfengbojiang { "offset", 0x1 },
18422ce4affSfengbojiang { NULL, 0}
18522ce4affSfengbojiang };
18622ce4affSfengbojiang
187127dd473Swhl739 struct _s_x f_ipdscp[] = {
188127dd473Swhl739 { "af11", IPTOS_DSCP_AF11 >> 2 }, /* 001010 */
189127dd473Swhl739 { "af12", IPTOS_DSCP_AF12 >> 2 }, /* 001100 */
190127dd473Swhl739 { "af13", IPTOS_DSCP_AF13 >> 2 }, /* 001110 */
191127dd473Swhl739 { "af21", IPTOS_DSCP_AF21 >> 2 }, /* 010010 */
192127dd473Swhl739 { "af22", IPTOS_DSCP_AF22 >> 2 }, /* 010100 */
193127dd473Swhl739 { "af23", IPTOS_DSCP_AF23 >> 2 }, /* 010110 */
194127dd473Swhl739 { "af31", IPTOS_DSCP_AF31 >> 2 }, /* 011010 */
195127dd473Swhl739 { "af32", IPTOS_DSCP_AF32 >> 2 }, /* 011100 */
196127dd473Swhl739 { "af33", IPTOS_DSCP_AF33 >> 2 }, /* 011110 */
197127dd473Swhl739 { "af41", IPTOS_DSCP_AF41 >> 2 }, /* 100010 */
198127dd473Swhl739 { "af42", IPTOS_DSCP_AF42 >> 2 }, /* 100100 */
199127dd473Swhl739 { "af43", IPTOS_DSCP_AF43 >> 2 }, /* 100110 */
200127dd473Swhl739 { "be", IPTOS_DSCP_CS0 >> 2 }, /* 000000 */
201127dd473Swhl739 { "ef", IPTOS_DSCP_EF >> 2 }, /* 101110 */
202127dd473Swhl739 { "cs0", IPTOS_DSCP_CS0 >> 2 }, /* 000000 */
203127dd473Swhl739 { "cs1", IPTOS_DSCP_CS1 >> 2 }, /* 001000 */
204127dd473Swhl739 { "cs2", IPTOS_DSCP_CS2 >> 2 }, /* 010000 */
205127dd473Swhl739 { "cs3", IPTOS_DSCP_CS3 >> 2 }, /* 011000 */
206127dd473Swhl739 { "cs4", IPTOS_DSCP_CS4 >> 2 }, /* 100000 */
207127dd473Swhl739 { "cs5", IPTOS_DSCP_CS5 >> 2 }, /* 101000 */
208127dd473Swhl739 { "cs6", IPTOS_DSCP_CS6 >> 2 }, /* 110000 */
209127dd473Swhl739 { "cs7", IPTOS_DSCP_CS7 >> 2 }, /* 100000 */
210127dd473Swhl739 { NULL, 0 }
211127dd473Swhl739 };
212127dd473Swhl739
213127dd473Swhl739 static struct _s_x limit_masks[] = {
214127dd473Swhl739 {"all", DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
215127dd473Swhl739 {"src-addr", DYN_SRC_ADDR},
216127dd473Swhl739 {"src-port", DYN_SRC_PORT},
217127dd473Swhl739 {"dst-addr", DYN_DST_ADDR},
218127dd473Swhl739 {"dst-port", DYN_DST_PORT},
219127dd473Swhl739 {NULL, 0}
220127dd473Swhl739 };
221127dd473Swhl739
222127dd473Swhl739 /*
223127dd473Swhl739 * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines
224127dd473Swhl739 * This is only used in this code.
225127dd473Swhl739 */
226127dd473Swhl739 #define IPPROTO_ETHERTYPE 0x1000
227127dd473Swhl739 static struct _s_x ether_types[] = {
228127dd473Swhl739 /*
229127dd473Swhl739 * Note, we cannot use "-:&/" in the names because they are field
230127dd473Swhl739 * separators in the type specifications. Also, we use s = NULL as
231127dd473Swhl739 * end-delimiter, because a type of 0 can be legal.
232127dd473Swhl739 */
233127dd473Swhl739 { "ip", 0x0800 },
234127dd473Swhl739 { "ipv4", 0x0800 },
235127dd473Swhl739 { "ipv6", 0x86dd },
236127dd473Swhl739 { "arp", 0x0806 },
237127dd473Swhl739 { "rarp", 0x8035 },
238127dd473Swhl739 { "vlan", 0x8100 },
239127dd473Swhl739 { "loop", 0x9000 },
240127dd473Swhl739 { "trail", 0x1000 },
241127dd473Swhl739 { "at", 0x809b },
242127dd473Swhl739 { "atalk", 0x809b },
243127dd473Swhl739 { "aarp", 0x80f3 },
244127dd473Swhl739 { "pppoe_disc", 0x8863 },
245127dd473Swhl739 { "pppoe_sess", 0x8864 },
246127dd473Swhl739 { "ipx_8022", 0x00E0 },
247127dd473Swhl739 { "ipx_8023", 0x0000 },
248127dd473Swhl739 { "ipx_ii", 0x8137 },
249127dd473Swhl739 { "ipx_snap", 0x8137 },
250127dd473Swhl739 { "ipx", 0x8137 },
251127dd473Swhl739 { "ns", 0x0600 },
252127dd473Swhl739 { NULL, 0 }
253127dd473Swhl739 };
254127dd473Swhl739
255127dd473Swhl739 static struct _s_x rule_eactions[] = {
25622ce4affSfengbojiang { "nat64clat", TOK_NAT64CLAT },
25722ce4affSfengbojiang { "nat64lsn", TOK_NAT64LSN },
25822ce4affSfengbojiang { "nat64stl", TOK_NAT64STL },
25922ce4affSfengbojiang { "nptv6", TOK_NPTV6 },
26022ce4affSfengbojiang { "tcp-setmss", TOK_TCPSETMSS },
261127dd473Swhl739 { NULL, 0 } /* terminator */
262127dd473Swhl739 };
263127dd473Swhl739
264127dd473Swhl739 static struct _s_x rule_actions[] = {
26522ce4affSfengbojiang { "abort6", TOK_ABORT6 },
26622ce4affSfengbojiang { "abort", TOK_ABORT },
267127dd473Swhl739 { "accept", TOK_ACCEPT },
268127dd473Swhl739 { "pass", TOK_ACCEPT },
269127dd473Swhl739 { "allow", TOK_ACCEPT },
270127dd473Swhl739 { "permit", TOK_ACCEPT },
271127dd473Swhl739 { "count", TOK_COUNT },
272127dd473Swhl739 { "pipe", TOK_PIPE },
273127dd473Swhl739 { "queue", TOK_QUEUE },
274127dd473Swhl739 { "divert", TOK_DIVERT },
275127dd473Swhl739 { "tee", TOK_TEE },
276127dd473Swhl739 { "netgraph", TOK_NETGRAPH },
277127dd473Swhl739 { "ngtee", TOK_NGTEE },
278127dd473Swhl739 { "fwd", TOK_FORWARD },
279127dd473Swhl739 { "forward", TOK_FORWARD },
280127dd473Swhl739 { "skipto", TOK_SKIPTO },
281127dd473Swhl739 { "deny", TOK_DENY },
282127dd473Swhl739 { "drop", TOK_DENY },
283127dd473Swhl739 { "reject", TOK_REJECT },
284127dd473Swhl739 { "reset6", TOK_RESET6 },
285127dd473Swhl739 { "reset", TOK_RESET },
286127dd473Swhl739 { "unreach6", TOK_UNREACH6 },
287127dd473Swhl739 { "unreach", TOK_UNREACH },
288127dd473Swhl739 { "check-state", TOK_CHECKSTATE },
289127dd473Swhl739 { "//", TOK_COMMENT },
290127dd473Swhl739 { "nat", TOK_NAT },
291127dd473Swhl739 { "reass", TOK_REASS },
292127dd473Swhl739 { "setfib", TOK_SETFIB },
293127dd473Swhl739 { "setdscp", TOK_SETDSCP },
294127dd473Swhl739 { "call", TOK_CALL },
295127dd473Swhl739 { "return", TOK_RETURN },
296127dd473Swhl739 { "eaction", TOK_EACTION },
29722ce4affSfengbojiang { "tcp-setmss", TOK_TCPSETMSS },
298127dd473Swhl739 { NULL, 0 } /* terminator */
299127dd473Swhl739 };
300127dd473Swhl739
301127dd473Swhl739 static struct _s_x rule_action_params[] = {
302127dd473Swhl739 { "altq", TOK_ALTQ },
303127dd473Swhl739 { "log", TOK_LOG },
304127dd473Swhl739 { "tag", TOK_TAG },
305127dd473Swhl739 { "untag", TOK_UNTAG },
306127dd473Swhl739 { NULL, 0 } /* terminator */
307127dd473Swhl739 };
308127dd473Swhl739
309127dd473Swhl739 /*
310127dd473Swhl739 * The 'lookup' instruction accepts one of the following arguments.
311127dd473Swhl739 * -1 is a terminator for the list.
312127dd473Swhl739 * Arguments are passed as v[1] in O_DST_LOOKUP options.
313127dd473Swhl739 */
314127dd473Swhl739 static int lookup_key[] = {
315127dd473Swhl739 TOK_DSTIP, TOK_SRCIP, TOK_DSTPORT, TOK_SRCPORT,
316127dd473Swhl739 TOK_UID, TOK_JAIL, TOK_DSCP, -1 };
317127dd473Swhl739
318127dd473Swhl739 static struct _s_x rule_options[] = {
319127dd473Swhl739 { "tagged", TOK_TAGGED },
320127dd473Swhl739 { "uid", TOK_UID },
321127dd473Swhl739 { "gid", TOK_GID },
322127dd473Swhl739 { "jail", TOK_JAIL },
323127dd473Swhl739 { "in", TOK_IN },
324127dd473Swhl739 { "limit", TOK_LIMIT },
32522ce4affSfengbojiang { "set-limit", TOK_SETLIMIT },
326127dd473Swhl739 { "keep-state", TOK_KEEPSTATE },
32722ce4affSfengbojiang { "record-state", TOK_RECORDSTATE },
328127dd473Swhl739 { "bridged", TOK_LAYER2 },
329127dd473Swhl739 { "layer2", TOK_LAYER2 },
330127dd473Swhl739 { "out", TOK_OUT },
331127dd473Swhl739 { "diverted", TOK_DIVERTED },
332127dd473Swhl739 { "diverted-loopback", TOK_DIVERTEDLOOPBACK },
333127dd473Swhl739 { "diverted-output", TOK_DIVERTEDOUTPUT },
334127dd473Swhl739 { "xmit", TOK_XMIT },
335127dd473Swhl739 { "recv", TOK_RECV },
336127dd473Swhl739 { "via", TOK_VIA },
337127dd473Swhl739 { "fragment", TOK_FRAG },
338127dd473Swhl739 { "frag", TOK_FRAG },
339127dd473Swhl739 { "fib", TOK_FIB },
340127dd473Swhl739 { "ipoptions", TOK_IPOPTS },
341127dd473Swhl739 { "ipopts", TOK_IPOPTS },
342127dd473Swhl739 { "iplen", TOK_IPLEN },
343127dd473Swhl739 { "ipid", TOK_IPID },
344127dd473Swhl739 { "ipprecedence", TOK_IPPRECEDENCE },
345127dd473Swhl739 { "dscp", TOK_DSCP },
346127dd473Swhl739 { "iptos", TOK_IPTOS },
347127dd473Swhl739 { "ipttl", TOK_IPTTL },
348127dd473Swhl739 { "ipversion", TOK_IPVER },
349127dd473Swhl739 { "ipver", TOK_IPVER },
350127dd473Swhl739 { "estab", TOK_ESTAB },
351127dd473Swhl739 { "established", TOK_ESTAB },
352127dd473Swhl739 { "setup", TOK_SETUP },
353127dd473Swhl739 { "sockarg", TOK_SOCKARG },
354127dd473Swhl739 { "tcpdatalen", TOK_TCPDATALEN },
355127dd473Swhl739 { "tcpflags", TOK_TCPFLAGS },
356127dd473Swhl739 { "tcpflgs", TOK_TCPFLAGS },
35722ce4affSfengbojiang { "tcpmss", TOK_TCPMSS },
358127dd473Swhl739 { "tcpoptions", TOK_TCPOPTS },
359127dd473Swhl739 { "tcpopts", TOK_TCPOPTS },
360127dd473Swhl739 { "tcpseq", TOK_TCPSEQ },
361127dd473Swhl739 { "tcpack", TOK_TCPACK },
362127dd473Swhl739 { "tcpwin", TOK_TCPWIN },
363127dd473Swhl739 { "icmptype", TOK_ICMPTYPES },
364127dd473Swhl739 { "icmptypes", TOK_ICMPTYPES },
365127dd473Swhl739 { "dst-ip", TOK_DSTIP },
366127dd473Swhl739 { "src-ip", TOK_SRCIP },
367127dd473Swhl739 { "dst-port", TOK_DSTPORT },
368127dd473Swhl739 { "src-port", TOK_SRCPORT },
369127dd473Swhl739 { "proto", TOK_PROTO },
370127dd473Swhl739 { "MAC", TOK_MAC },
371127dd473Swhl739 { "mac", TOK_MAC },
372127dd473Swhl739 { "mac-type", TOK_MACTYPE },
373127dd473Swhl739 { "verrevpath", TOK_VERREVPATH },
374127dd473Swhl739 { "versrcreach", TOK_VERSRCREACH },
375127dd473Swhl739 { "antispoof", TOK_ANTISPOOF },
376127dd473Swhl739 { "ipsec", TOK_IPSEC },
377127dd473Swhl739 { "icmp6type", TOK_ICMP6TYPES },
378127dd473Swhl739 { "icmp6types", TOK_ICMP6TYPES },
379127dd473Swhl739 { "ext6hdr", TOK_EXT6HDR},
380127dd473Swhl739 { "flow-id", TOK_FLOWID},
381127dd473Swhl739 { "ipv6", TOK_IPV6},
382127dd473Swhl739 { "ip6", TOK_IPV6},
383127dd473Swhl739 { "ipv4", TOK_IPV4},
384127dd473Swhl739 { "ip4", TOK_IPV4},
385127dd473Swhl739 { "dst-ipv6", TOK_DSTIP6},
386127dd473Swhl739 { "dst-ip6", TOK_DSTIP6},
387127dd473Swhl739 { "src-ipv6", TOK_SRCIP6},
388127dd473Swhl739 { "src-ip6", TOK_SRCIP6},
389127dd473Swhl739 { "lookup", TOK_LOOKUP},
390127dd473Swhl739 { "flow", TOK_FLOW},
39122ce4affSfengbojiang { "defer-action", TOK_SKIPACTION },
39222ce4affSfengbojiang { "defer-immediate-action", TOK_SKIPACTION },
393127dd473Swhl739 { "//", TOK_COMMENT },
394127dd473Swhl739
395127dd473Swhl739 { "not", TOK_NOT }, /* pseudo option */
396127dd473Swhl739 { "!", /* escape ? */ TOK_NOT }, /* pseudo option */
397127dd473Swhl739 { "or", TOK_OR }, /* pseudo option */
398127dd473Swhl739 { "|", /* escape */ TOK_OR }, /* pseudo option */
399127dd473Swhl739 { "{", TOK_STARTBRACE }, /* pseudo option */
400127dd473Swhl739 { "(", TOK_STARTBRACE }, /* pseudo option */
401127dd473Swhl739 { "}", TOK_ENDBRACE }, /* pseudo option */
402127dd473Swhl739 { ")", TOK_ENDBRACE }, /* pseudo option */
403127dd473Swhl739 { NULL, 0 } /* terminator */
404127dd473Swhl739 };
405127dd473Swhl739
406127dd473Swhl739 void bprint_uint_arg(struct buf_pr *bp, const char *str, uint32_t arg);
407127dd473Swhl739 static int ipfw_get_config(struct cmdline_opts *co, struct format_opts *fo,
408127dd473Swhl739 ipfw_cfg_lheader **pcfg, size_t *psize);
409127dd473Swhl739 static int ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo,
410127dd473Swhl739 ipfw_cfg_lheader *cfg, size_t sz, int ac, char **av);
411127dd473Swhl739 static void ipfw_list_tifaces(void);
412127dd473Swhl739
413127dd473Swhl739 struct tidx;
41422ce4affSfengbojiang static uint16_t pack_object(struct tidx *tstate, const char *name, int otype);
41522ce4affSfengbojiang static uint16_t pack_table(struct tidx *tstate, const char *name);
416127dd473Swhl739
417127dd473Swhl739 static char *table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx);
418127dd473Swhl739 static void object_sort_ctlv(ipfw_obj_ctlv *ctlv);
419127dd473Swhl739 static char *object_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx,
420127dd473Swhl739 uint16_t type);
421127dd473Swhl739
422127dd473Swhl739 /*
423127dd473Swhl739 * Simple string buffer API.
424127dd473Swhl739 * Used to simplify buffer passing between function and for
425127dd473Swhl739 * transparent overrun handling.
426127dd473Swhl739 */
427127dd473Swhl739
428127dd473Swhl739 /*
429127dd473Swhl739 * Allocates new buffer of given size @sz.
430127dd473Swhl739 *
431127dd473Swhl739 * Returns 0 on success.
432127dd473Swhl739 */
433127dd473Swhl739 int
bp_alloc(struct buf_pr * b,size_t size)434127dd473Swhl739 bp_alloc(struct buf_pr *b, size_t size)
435127dd473Swhl739 {
436127dd473Swhl739 memset(b, 0, sizeof(struct buf_pr));
437127dd473Swhl739
438127dd473Swhl739 if ((b->buf = calloc(1, size)) == NULL)
439127dd473Swhl739 return (ENOMEM);
440127dd473Swhl739
441127dd473Swhl739 b->ptr = b->buf;
442127dd473Swhl739 b->size = size;
443127dd473Swhl739 b->avail = b->size;
444127dd473Swhl739
445127dd473Swhl739 return (0);
446127dd473Swhl739 }
447127dd473Swhl739
448127dd473Swhl739 void
bp_free(struct buf_pr * b)449127dd473Swhl739 bp_free(struct buf_pr *b)
450127dd473Swhl739 {
451127dd473Swhl739
452127dd473Swhl739 free(b->buf);
453127dd473Swhl739 }
454127dd473Swhl739
455127dd473Swhl739 /*
456127dd473Swhl739 * Flushes buffer so new writer start from beginning.
457127dd473Swhl739 */
458127dd473Swhl739 void
bp_flush(struct buf_pr * b)459127dd473Swhl739 bp_flush(struct buf_pr *b)
460127dd473Swhl739 {
461127dd473Swhl739
462127dd473Swhl739 b->ptr = b->buf;
463127dd473Swhl739 b->avail = b->size;
464127dd473Swhl739 b->buf[0] = '\0';
465127dd473Swhl739 }
466127dd473Swhl739
467127dd473Swhl739 /*
468127dd473Swhl739 * Print message specified by @format and args.
469127dd473Swhl739 * Automatically manage buffer space and transparently handle
470127dd473Swhl739 * buffer overruns.
471127dd473Swhl739 *
472127dd473Swhl739 * Returns number of bytes that should have been printed.
473127dd473Swhl739 */
474127dd473Swhl739 int
bprintf(struct buf_pr * b,const char * format,...)47522ce4affSfengbojiang bprintf(struct buf_pr *b, const char *format, ...)
476127dd473Swhl739 {
477127dd473Swhl739 va_list args;
478127dd473Swhl739 int i;
479127dd473Swhl739
480127dd473Swhl739 va_start(args, format);
481127dd473Swhl739
482127dd473Swhl739 i = vsnprintf(b->ptr, b->avail, format, args);
483127dd473Swhl739 va_end(args);
484127dd473Swhl739
48522ce4affSfengbojiang if (i < 0 || (size_t)i > b->avail) {
486127dd473Swhl739 /* Overflow or print error */
487127dd473Swhl739 b->avail = 0;
488127dd473Swhl739 } else {
489127dd473Swhl739 b->ptr += i;
490127dd473Swhl739 b->avail -= i;
491127dd473Swhl739 }
492127dd473Swhl739
493127dd473Swhl739 b->needed += i;
494127dd473Swhl739
495127dd473Swhl739 return (i);
496127dd473Swhl739 }
497127dd473Swhl739
498127dd473Swhl739 /*
499127dd473Swhl739 * Special values printer for tablearg-aware opcodes.
500127dd473Swhl739 */
501127dd473Swhl739 void
bprint_uint_arg(struct buf_pr * bp,const char * str,uint32_t arg)502127dd473Swhl739 bprint_uint_arg(struct buf_pr *bp, const char *str, uint32_t arg)
503127dd473Swhl739 {
504127dd473Swhl739
505127dd473Swhl739 if (str != NULL)
506127dd473Swhl739 bprintf(bp, "%s", str);
507127dd473Swhl739 if (arg == IP_FW_TARG)
508127dd473Swhl739 bprintf(bp, "tablearg");
509127dd473Swhl739 else
510127dd473Swhl739 bprintf(bp, "%u", arg);
511127dd473Swhl739 }
512127dd473Swhl739
513127dd473Swhl739 /*
514127dd473Swhl739 * Helper routine to print a possibly unaligned uint64_t on
515127dd473Swhl739 * various platform. If width > 0, print the value with
516127dd473Swhl739 * the desired width, followed by a space;
517127dd473Swhl739 * otherwise, return the required width.
518127dd473Swhl739 */
519127dd473Swhl739 int
pr_u64(struct buf_pr * b,void * pd,int width)52022ce4affSfengbojiang pr_u64(struct buf_pr *b, void *pd, int width)
521127dd473Swhl739 {
522127dd473Swhl739 #ifdef TCC
523127dd473Swhl739 #define U64_FMT "I64"
524127dd473Swhl739 #else
525127dd473Swhl739 #define U64_FMT "llu"
526127dd473Swhl739 #endif
527127dd473Swhl739 uint64_t u;
528127dd473Swhl739 unsigned long long d;
529127dd473Swhl739
530127dd473Swhl739 bcopy (pd, &u, sizeof(u));
531127dd473Swhl739 d = u;
532127dd473Swhl739 return (width > 0) ?
533127dd473Swhl739 bprintf(b, "%*" U64_FMT " ", width, d) :
534127dd473Swhl739 snprintf(NULL, 0, "%" U64_FMT, d) ;
535127dd473Swhl739 #undef U64_FMT
536127dd473Swhl739 }
537127dd473Swhl739
538127dd473Swhl739
539127dd473Swhl739 void *
safe_calloc(size_t number,size_t size)540127dd473Swhl739 safe_calloc(size_t number, size_t size)
541127dd473Swhl739 {
542127dd473Swhl739 void *ret = calloc(number, size);
543127dd473Swhl739
544127dd473Swhl739 if (ret == NULL)
545127dd473Swhl739 err(EX_OSERR, "calloc");
546127dd473Swhl739 return ret;
547127dd473Swhl739 }
548127dd473Swhl739
549127dd473Swhl739 void *
safe_realloc(void * ptr,size_t size)550127dd473Swhl739 safe_realloc(void *ptr, size_t size)
551127dd473Swhl739 {
552127dd473Swhl739 void *ret = realloc(ptr, size);
553127dd473Swhl739
554127dd473Swhl739 if (ret == NULL)
555127dd473Swhl739 err(EX_OSERR, "realloc");
556127dd473Swhl739 return ret;
557127dd473Swhl739 }
558127dd473Swhl739
559127dd473Swhl739 /*
560127dd473Swhl739 * Compare things like interface or table names.
561127dd473Swhl739 */
562127dd473Swhl739 int
stringnum_cmp(const char * a,const char * b)563127dd473Swhl739 stringnum_cmp(const char *a, const char *b)
564127dd473Swhl739 {
565127dd473Swhl739 int la, lb;
566127dd473Swhl739
567127dd473Swhl739 la = strlen(a);
568127dd473Swhl739 lb = strlen(b);
569127dd473Swhl739
570127dd473Swhl739 if (la > lb)
571127dd473Swhl739 return (1);
572127dd473Swhl739 else if (la < lb)
573127dd473Swhl739 return (-01);
574127dd473Swhl739
575127dd473Swhl739 return (strcmp(a, b));
576127dd473Swhl739 }
577127dd473Swhl739
578127dd473Swhl739
579127dd473Swhl739 /*
580127dd473Swhl739 * conditionally runs the command.
581127dd473Swhl739 * Selected options or negative -> getsockopt
582127dd473Swhl739 */
583127dd473Swhl739 int
do_cmd(int optname,void * optval,uintptr_t optlen)584127dd473Swhl739 do_cmd(int optname, void *optval, uintptr_t optlen)
585127dd473Swhl739 {
586127dd473Swhl739 int i;
587127dd473Swhl739
58822ce4affSfengbojiang if (g_co.test_only)
589127dd473Swhl739 return 0;
590127dd473Swhl739
591127dd473Swhl739 if (ipfw_socket == -1)
592127dd473Swhl739 ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
593127dd473Swhl739 if (ipfw_socket < 0)
594127dd473Swhl739 err(EX_UNAVAILABLE, "socket");
595127dd473Swhl739
596127dd473Swhl739 if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
597127dd473Swhl739 optname == IP_FW_ADD || optname == IP_FW3 ||
598127dd473Swhl739 optname == IP_FW_NAT_GET_CONFIG ||
599127dd473Swhl739 optname < 0 ||
600127dd473Swhl739 optname == IP_FW_NAT_GET_LOG) {
601127dd473Swhl739 if (optname < 0)
602127dd473Swhl739 optname = -optname;
603127dd473Swhl739 i = getsockopt(ipfw_socket, IPPROTO_IP, optname, optval,
604127dd473Swhl739 (socklen_t *)optlen);
605127dd473Swhl739 } else {
606127dd473Swhl739 i = setsockopt(ipfw_socket, IPPROTO_IP, optname, optval, optlen);
607127dd473Swhl739 }
608127dd473Swhl739 return i;
609127dd473Swhl739 }
610127dd473Swhl739
611127dd473Swhl739 /*
612127dd473Swhl739 * do_set3 - pass ipfw control cmd to kernel
613127dd473Swhl739 * @optname: option name
614127dd473Swhl739 * @optval: pointer to option data
615127dd473Swhl739 * @optlen: option length
616127dd473Swhl739 *
617127dd473Swhl739 * Assumes op3 header is already embedded.
618127dd473Swhl739 * Calls setsockopt() with IP_FW3 as kernel-visible opcode.
619127dd473Swhl739 * Returns 0 on success or errno otherwise.
620127dd473Swhl739 */
621127dd473Swhl739 int
do_set3(int optname,ip_fw3_opheader * op3,size_t optlen)62222ce4affSfengbojiang do_set3(int optname, ip_fw3_opheader *op3, size_t optlen)
623127dd473Swhl739 {
624127dd473Swhl739
62522ce4affSfengbojiang if (g_co.test_only)
626127dd473Swhl739 return (0);
627127dd473Swhl739
628127dd473Swhl739 if (ipfw_socket == -1)
629127dd473Swhl739 ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
630127dd473Swhl739 if (ipfw_socket < 0)
631127dd473Swhl739 err(EX_UNAVAILABLE, "socket");
632127dd473Swhl739
633127dd473Swhl739 op3->opcode = optname;
634127dd473Swhl739
635127dd473Swhl739 return (setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen));
636127dd473Swhl739 }
637127dd473Swhl739
638127dd473Swhl739 /*
639127dd473Swhl739 * do_get3 - pass ipfw control cmd to kernel
640127dd473Swhl739 * @optname: option name
641127dd473Swhl739 * @optval: pointer to option data
642127dd473Swhl739 * @optlen: pointer to option length
643127dd473Swhl739 *
644127dd473Swhl739 * Assumes op3 header is already embedded.
645127dd473Swhl739 * Calls getsockopt() with IP_FW3 as kernel-visible opcode.
646127dd473Swhl739 * Returns 0 on success or errno otherwise.
647127dd473Swhl739 */
648127dd473Swhl739 int
do_get3(int optname,ip_fw3_opheader * op3,size_t * optlen)649127dd473Swhl739 do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen)
650127dd473Swhl739 {
651127dd473Swhl739 int error;
65222ce4affSfengbojiang socklen_t len;
653127dd473Swhl739
65422ce4affSfengbojiang if (g_co.test_only)
655127dd473Swhl739 return (0);
656127dd473Swhl739
657127dd473Swhl739 if (ipfw_socket == -1)
658127dd473Swhl739 ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
659127dd473Swhl739 if (ipfw_socket < 0)
660127dd473Swhl739 err(EX_UNAVAILABLE, "socket");
661127dd473Swhl739
662127dd473Swhl739 op3->opcode = optname;
663127dd473Swhl739
66422ce4affSfengbojiang len = *optlen;
66522ce4affSfengbojiang error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, &len);
66622ce4affSfengbojiang *optlen = len;
667127dd473Swhl739
668127dd473Swhl739 return (error);
669127dd473Swhl739 }
670127dd473Swhl739
671127dd473Swhl739 /**
672127dd473Swhl739 * match_token takes a table and a string, returns the value associated
673127dd473Swhl739 * with the string (-1 in case of failure).
674127dd473Swhl739 */
675127dd473Swhl739 int
match_token(struct _s_x * table,const char * string)676127dd473Swhl739 match_token(struct _s_x *table, const char *string)
677127dd473Swhl739 {
678127dd473Swhl739 struct _s_x *pt;
679127dd473Swhl739 uint i = strlen(string);
680127dd473Swhl739
681127dd473Swhl739 for (pt = table ; i && pt->s != NULL ; pt++)
682127dd473Swhl739 if (strlen(pt->s) == i && !bcmp(string, pt->s, i))
683127dd473Swhl739 return pt->x;
684127dd473Swhl739 return (-1);
685127dd473Swhl739 }
686127dd473Swhl739
687127dd473Swhl739 /**
688127dd473Swhl739 * match_token_relaxed takes a table and a string, returns the value associated
689127dd473Swhl739 * with the string for the best match.
690127dd473Swhl739 *
691127dd473Swhl739 * Returns:
692127dd473Swhl739 * value from @table for matched records
693127dd473Swhl739 * -1 for non-matched records
694127dd473Swhl739 * -2 if more than one records match @string.
695127dd473Swhl739 */
696127dd473Swhl739 int
match_token_relaxed(struct _s_x * table,const char * string)697127dd473Swhl739 match_token_relaxed(struct _s_x *table, const char *string)
698127dd473Swhl739 {
699127dd473Swhl739 struct _s_x *pt, *m;
700127dd473Swhl739 int i, c;
701127dd473Swhl739
702127dd473Swhl739 i = strlen(string);
703127dd473Swhl739 c = 0;
704127dd473Swhl739
705127dd473Swhl739 for (pt = table ; i != 0 && pt->s != NULL ; pt++) {
706127dd473Swhl739 if (strncmp(pt->s, string, i) != 0)
707127dd473Swhl739 continue;
708127dd473Swhl739 m = pt;
709127dd473Swhl739 c++;
710127dd473Swhl739 }
711127dd473Swhl739
712127dd473Swhl739 if (c == 1)
713127dd473Swhl739 return (m->x);
714127dd473Swhl739
715127dd473Swhl739 return (c > 0 ? -2: -1);
716127dd473Swhl739 }
717127dd473Swhl739
718127dd473Swhl739 int
get_token(struct _s_x * table,const char * string,const char * errbase)719127dd473Swhl739 get_token(struct _s_x *table, const char *string, const char *errbase)
720127dd473Swhl739 {
721127dd473Swhl739 int tcmd;
722127dd473Swhl739
723127dd473Swhl739 if ((tcmd = match_token_relaxed(table, string)) < 0)
724127dd473Swhl739 errx(EX_USAGE, "%s %s %s",
725127dd473Swhl739 (tcmd == 0) ? "invalid" : "ambiguous", errbase, string);
726127dd473Swhl739
727127dd473Swhl739 return (tcmd);
728127dd473Swhl739 }
729127dd473Swhl739
730127dd473Swhl739 /**
731127dd473Swhl739 * match_value takes a table and a value, returns the string associated
732127dd473Swhl739 * with the value (NULL in case of failure).
733127dd473Swhl739 */
734127dd473Swhl739 char const *
match_value(struct _s_x * p,int value)735127dd473Swhl739 match_value(struct _s_x *p, int value)
736127dd473Swhl739 {
737127dd473Swhl739 for (; p->s != NULL; p++)
738127dd473Swhl739 if (p->x == value)
739127dd473Swhl739 return p->s;
740127dd473Swhl739 return NULL;
741127dd473Swhl739 }
742127dd473Swhl739
743127dd473Swhl739 size_t
concat_tokens(char * buf,size_t bufsize,struct _s_x * table,const char * delimiter)74422ce4affSfengbojiang concat_tokens(char *buf, size_t bufsize, struct _s_x *table,
74522ce4affSfengbojiang const char *delimiter)
746127dd473Swhl739 {
747127dd473Swhl739 struct _s_x *pt;
748127dd473Swhl739 int l;
749127dd473Swhl739 size_t sz;
750127dd473Swhl739
751127dd473Swhl739 for (sz = 0, pt = table ; pt->s != NULL; pt++) {
752127dd473Swhl739 l = snprintf(buf + sz, bufsize - sz, "%s%s",
753127dd473Swhl739 (sz == 0) ? "" : delimiter, pt->s);
754127dd473Swhl739 sz += l;
755127dd473Swhl739 bufsize += l;
756127dd473Swhl739 if (sz > bufsize)
757127dd473Swhl739 return (bufsize);
758127dd473Swhl739 }
759127dd473Swhl739
760127dd473Swhl739 return (sz);
761127dd473Swhl739 }
762127dd473Swhl739
763127dd473Swhl739 /*
764127dd473Swhl739 * helper function to process a set of flags and set bits in the
765127dd473Swhl739 * appropriate masks.
766127dd473Swhl739 */
767127dd473Swhl739 int
fill_flags(struct _s_x * flags,char * p,char ** e,uint32_t * set,uint32_t * clear)768127dd473Swhl739 fill_flags(struct _s_x *flags, char *p, char **e, uint32_t *set,
769127dd473Swhl739 uint32_t *clear)
770127dd473Swhl739 {
771127dd473Swhl739 char *q; /* points to the separator */
772127dd473Swhl739 int val;
773127dd473Swhl739 uint32_t *which; /* mask we are working on */
774127dd473Swhl739
775127dd473Swhl739 while (p && *p) {
776127dd473Swhl739 if (*p == '!') {
777127dd473Swhl739 p++;
778127dd473Swhl739 which = clear;
779127dd473Swhl739 } else
780127dd473Swhl739 which = set;
781127dd473Swhl739 q = strchr(p, ',');
782127dd473Swhl739 if (q)
783127dd473Swhl739 *q++ = '\0';
784127dd473Swhl739 val = match_token(flags, p);
785127dd473Swhl739 if (val <= 0) {
786127dd473Swhl739 if (e != NULL)
787127dd473Swhl739 *e = p;
788127dd473Swhl739 return (-1);
789127dd473Swhl739 }
790127dd473Swhl739 *which |= (uint32_t)val;
791127dd473Swhl739 p = q;
792127dd473Swhl739 }
793127dd473Swhl739 return (0);
794127dd473Swhl739 }
795127dd473Swhl739
796127dd473Swhl739 void
print_flags_buffer(char * buf,size_t sz,struct _s_x * list,uint32_t set)797127dd473Swhl739 print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint32_t set)
798127dd473Swhl739 {
799127dd473Swhl739 char const *comma = "";
800127dd473Swhl739 int i, l;
801127dd473Swhl739
802127dd473Swhl739 for (i = 0; list[i].x != 0; i++) {
803127dd473Swhl739 if ((set & list[i].x) == 0)
804127dd473Swhl739 continue;
805127dd473Swhl739
806127dd473Swhl739 set &= ~list[i].x;
807127dd473Swhl739 l = snprintf(buf, sz, "%s%s", comma, list[i].s);
80822ce4affSfengbojiang if (l < 0 || (size_t)l >= sz)
809127dd473Swhl739 return;
810127dd473Swhl739 comma = ",";
811127dd473Swhl739 buf += l;
812127dd473Swhl739 sz -=l;
813127dd473Swhl739 }
814127dd473Swhl739 }
815127dd473Swhl739
816127dd473Swhl739 /*
817127dd473Swhl739 * _substrcmp takes two strings and returns 1 if they do not match,
818127dd473Swhl739 * and 0 if they match exactly or the first string is a sub-string
819127dd473Swhl739 * of the second. A warning is printed to stderr in the case that the
820127dd473Swhl739 * first string is a sub-string of the second.
821127dd473Swhl739 *
822127dd473Swhl739 * This function will be removed in the future through the usual
823127dd473Swhl739 * deprecation process.
824127dd473Swhl739 */
825127dd473Swhl739 int
_substrcmp(const char * str1,const char * str2)826127dd473Swhl739 _substrcmp(const char *str1, const char* str2)
827127dd473Swhl739 {
828127dd473Swhl739
829127dd473Swhl739 if (strncmp(str1, str2, strlen(str1)) != 0)
830127dd473Swhl739 return 1;
831127dd473Swhl739
832127dd473Swhl739 if (strlen(str1) != strlen(str2))
833127dd473Swhl739 warnx("DEPRECATED: '%s' matched '%s' as a sub-string",
834127dd473Swhl739 str1, str2);
835127dd473Swhl739 return 0;
836127dd473Swhl739 }
837127dd473Swhl739
838127dd473Swhl739 /*
839127dd473Swhl739 * _substrcmp2 takes three strings and returns 1 if the first two do not match,
840127dd473Swhl739 * and 0 if they match exactly or the second string is a sub-string
841127dd473Swhl739 * of the first. A warning is printed to stderr in the case that the
842127dd473Swhl739 * first string does not match the third.
843127dd473Swhl739 *
844127dd473Swhl739 * This function exists to warn about the bizarre construction
845127dd473Swhl739 * strncmp(str, "by", 2) which is used to allow people to use a shortcut
846127dd473Swhl739 * for "bytes". The problem is that in addition to accepting "by",
847127dd473Swhl739 * "byt", "byte", and "bytes", it also excepts "by_rabid_dogs" and any
848127dd473Swhl739 * other string beginning with "by".
849127dd473Swhl739 *
850127dd473Swhl739 * This function will be removed in the future through the usual
851127dd473Swhl739 * deprecation process.
852127dd473Swhl739 */
853127dd473Swhl739 int
_substrcmp2(const char * str1,const char * str2,const char * str3)854127dd473Swhl739 _substrcmp2(const char *str1, const char* str2, const char* str3)
855127dd473Swhl739 {
856127dd473Swhl739
857127dd473Swhl739 if (strncmp(str1, str2, strlen(str2)) != 0)
858127dd473Swhl739 return 1;
859127dd473Swhl739
860127dd473Swhl739 if (strcmp(str1, str3) != 0)
861127dd473Swhl739 warnx("DEPRECATED: '%s' matched '%s'",
862127dd473Swhl739 str1, str3);
863127dd473Swhl739 return 0;
864127dd473Swhl739 }
865127dd473Swhl739
866127dd473Swhl739 /*
867127dd473Swhl739 * prints one port, symbolic or numeric
868127dd473Swhl739 */
869127dd473Swhl739 static void
print_port(struct buf_pr * bp,int proto,uint16_t port)870127dd473Swhl739 print_port(struct buf_pr *bp, int proto, uint16_t port)
871127dd473Swhl739 {
872127dd473Swhl739
873127dd473Swhl739 if (proto == IPPROTO_ETHERTYPE) {
874127dd473Swhl739 char const *s;
875127dd473Swhl739
87622ce4affSfengbojiang if (g_co.do_resolv && (s = match_value(ether_types, port)) )
877127dd473Swhl739 bprintf(bp, "%s", s);
878127dd473Swhl739 else
879127dd473Swhl739 bprintf(bp, "0x%04x", port);
880127dd473Swhl739 } else {
881127dd473Swhl739 struct servent *se = NULL;
88222ce4affSfengbojiang if (g_co.do_resolv) {
883127dd473Swhl739 struct protoent *pe = getprotobynumber(proto);
884127dd473Swhl739
885127dd473Swhl739 se = getservbyport(htons(port), pe ? pe->p_name : NULL);
886127dd473Swhl739 }
887127dd473Swhl739 if (se)
888127dd473Swhl739 bprintf(bp, "%s", se->s_name);
889127dd473Swhl739 else
890127dd473Swhl739 bprintf(bp, "%d", port);
891127dd473Swhl739 }
892127dd473Swhl739 }
893127dd473Swhl739
894127dd473Swhl739 static struct _s_x _port_name[] = {
895127dd473Swhl739 {"dst-port", O_IP_DSTPORT},
896127dd473Swhl739 {"src-port", O_IP_SRCPORT},
897127dd473Swhl739 {"ipid", O_IPID},
898127dd473Swhl739 {"iplen", O_IPLEN},
899127dd473Swhl739 {"ipttl", O_IPTTL},
900127dd473Swhl739 {"mac-type", O_MAC_TYPE},
901127dd473Swhl739 {"tcpdatalen", O_TCPDATALEN},
90222ce4affSfengbojiang {"tcpmss", O_TCPMSS},
903127dd473Swhl739 {"tcpwin", O_TCPWIN},
904127dd473Swhl739 {"tagged", O_TAGGED},
905127dd473Swhl739 {NULL, 0}
906127dd473Swhl739 };
907127dd473Swhl739
908127dd473Swhl739 /*
909127dd473Swhl739 * Print the values in a list 16-bit items of the types above.
910127dd473Swhl739 * XXX todo: add support for mask.
911127dd473Swhl739 */
912127dd473Swhl739 static void
print_newports(struct buf_pr * bp,const ipfw_insn_u16 * cmd,int proto,int opcode)91322ce4affSfengbojiang print_newports(struct buf_pr *bp, const ipfw_insn_u16 *cmd, int proto, int opcode)
914127dd473Swhl739 {
91522ce4affSfengbojiang const uint16_t *p = cmd->ports;
916127dd473Swhl739 int i;
917127dd473Swhl739 char const *sep;
918127dd473Swhl739
919127dd473Swhl739 if (opcode != 0) {
920127dd473Swhl739 sep = match_value(_port_name, opcode);
921127dd473Swhl739 if (sep == NULL)
922127dd473Swhl739 sep = "???";
923127dd473Swhl739 bprintf(bp, " %s", sep);
924127dd473Swhl739 }
925127dd473Swhl739 sep = " ";
92622ce4affSfengbojiang for (i = F_LEN((const ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
927127dd473Swhl739 bprintf(bp, "%s", sep);
928127dd473Swhl739 print_port(bp, proto, p[0]);
929127dd473Swhl739 if (p[0] != p[1]) {
930127dd473Swhl739 bprintf(bp, "-");
931127dd473Swhl739 print_port(bp, proto, p[1]);
932127dd473Swhl739 }
933127dd473Swhl739 sep = ",";
934127dd473Swhl739 }
935127dd473Swhl739 }
936127dd473Swhl739
937127dd473Swhl739 /*
938127dd473Swhl739 * Like strtol, but also translates service names into port numbers
939127dd473Swhl739 * for some protocols.
940127dd473Swhl739 * In particular:
941127dd473Swhl739 * proto == -1 disables the protocol check;
942127dd473Swhl739 * proto == IPPROTO_ETHERTYPE looks up an internal table
943127dd473Swhl739 * proto == <some value in /etc/protocols> matches the values there.
944127dd473Swhl739 * Returns *end == s in case the parameter is not found.
945127dd473Swhl739 */
946127dd473Swhl739 static int
strtoport(char * s,char ** end,int base,int proto)947127dd473Swhl739 strtoport(char *s, char **end, int base, int proto)
948127dd473Swhl739 {
949127dd473Swhl739 char *p, *buf;
950127dd473Swhl739 char *s1;
951127dd473Swhl739 int i;
952127dd473Swhl739
953127dd473Swhl739 *end = s; /* default - not found */
954127dd473Swhl739 if (*s == '\0')
955127dd473Swhl739 return 0; /* not found */
956127dd473Swhl739
957127dd473Swhl739 if (isdigit(*s))
958127dd473Swhl739 return strtol(s, end, base);
959127dd473Swhl739
960127dd473Swhl739 /*
961127dd473Swhl739 * find separator. '\\' escapes the next char.
962127dd473Swhl739 */
96322ce4affSfengbojiang for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\' ||
96422ce4affSfengbojiang *s1 == '_' || *s1 == '.') ; s1++)
965127dd473Swhl739 if (*s1 == '\\' && s1[1] != '\0')
966127dd473Swhl739 s1++;
967127dd473Swhl739
968127dd473Swhl739 buf = safe_calloc(s1 - s + 1, 1);
969127dd473Swhl739
970127dd473Swhl739 /*
971127dd473Swhl739 * copy into a buffer skipping backslashes
972127dd473Swhl739 */
973127dd473Swhl739 for (p = s, i = 0; p != s1 ; p++)
974127dd473Swhl739 if (*p != '\\')
975127dd473Swhl739 buf[i++] = *p;
976127dd473Swhl739 buf[i++] = '\0';
977127dd473Swhl739
978127dd473Swhl739 if (proto == IPPROTO_ETHERTYPE) {
979127dd473Swhl739 i = match_token(ether_types, buf);
980127dd473Swhl739 free(buf);
981127dd473Swhl739 if (i != -1) { /* found */
982127dd473Swhl739 *end = s1;
983127dd473Swhl739 return i;
984127dd473Swhl739 }
985127dd473Swhl739 } else {
986127dd473Swhl739 struct protoent *pe = NULL;
987127dd473Swhl739 struct servent *se;
988127dd473Swhl739
989127dd473Swhl739 if (proto != 0)
990127dd473Swhl739 pe = getprotobynumber(proto);
991127dd473Swhl739 setservent(1);
992127dd473Swhl739 se = getservbyname(buf, pe ? pe->p_name : NULL);
993127dd473Swhl739 free(buf);
994127dd473Swhl739 if (se != NULL) {
995127dd473Swhl739 *end = s1;
996127dd473Swhl739 return ntohs(se->s_port);
997127dd473Swhl739 }
998127dd473Swhl739 }
999127dd473Swhl739 return 0; /* not found */
1000127dd473Swhl739 }
1001127dd473Swhl739
1002127dd473Swhl739 /*
1003127dd473Swhl739 * Fill the body of the command with the list of port ranges.
1004127dd473Swhl739 */
1005127dd473Swhl739 static int
fill_newports(ipfw_insn_u16 * cmd,char * av,int proto,int cblen)1006127dd473Swhl739 fill_newports(ipfw_insn_u16 *cmd, char *av, int proto, int cblen)
1007127dd473Swhl739 {
1008127dd473Swhl739 uint16_t a, b, *p = cmd->ports;
1009127dd473Swhl739 int i = 0;
1010127dd473Swhl739 char *s = av;
1011127dd473Swhl739
1012127dd473Swhl739 while (*s) {
1013127dd473Swhl739 a = strtoport(av, &s, 0, proto);
1014127dd473Swhl739 if (s == av) /* empty or invalid argument */
1015127dd473Swhl739 return (0);
1016127dd473Swhl739
1017127dd473Swhl739 CHECK_LENGTH(cblen, i + 2);
1018127dd473Swhl739
1019127dd473Swhl739 switch (*s) {
1020127dd473Swhl739 case '-': /* a range */
1021127dd473Swhl739 av = s + 1;
1022127dd473Swhl739 b = strtoport(av, &s, 0, proto);
1023127dd473Swhl739 /* Reject expressions like '1-abc' or '1-2-3'. */
1024127dd473Swhl739 if (s == av || (*s != ',' && *s != '\0'))
1025127dd473Swhl739 return (0);
1026127dd473Swhl739 p[0] = a;
1027127dd473Swhl739 p[1] = b;
1028127dd473Swhl739 break;
1029127dd473Swhl739 case ',': /* comma separated list */
1030127dd473Swhl739 case '\0':
1031127dd473Swhl739 p[0] = p[1] = a;
1032127dd473Swhl739 break;
1033127dd473Swhl739 default:
1034127dd473Swhl739 warnx("port list: invalid separator <%c> in <%s>",
1035127dd473Swhl739 *s, av);
1036127dd473Swhl739 return (0);
1037127dd473Swhl739 }
1038127dd473Swhl739
1039127dd473Swhl739 i++;
1040127dd473Swhl739 p += 2;
1041127dd473Swhl739 av = s + 1;
1042127dd473Swhl739 }
1043127dd473Swhl739 if (i > 0) {
1044127dd473Swhl739 if (i + 1 > F_LEN_MASK)
1045127dd473Swhl739 errx(EX_DATAERR, "too many ports/ranges\n");
1046127dd473Swhl739 cmd->o.len |= i + 1; /* leave F_NOT and F_OR untouched */
1047127dd473Swhl739 }
1048127dd473Swhl739 return (i);
1049127dd473Swhl739 }
1050127dd473Swhl739
1051127dd473Swhl739 /*
1052127dd473Swhl739 * Fill the body of the command with the list of DiffServ codepoints.
1053127dd473Swhl739 */
1054127dd473Swhl739 static void
fill_dscp(ipfw_insn * cmd,char * av,int cblen)1055127dd473Swhl739 fill_dscp(ipfw_insn *cmd, char *av, int cblen)
1056127dd473Swhl739 {
1057127dd473Swhl739 uint32_t *low, *high;
1058127dd473Swhl739 char *s = av, *a;
1059127dd473Swhl739 int code;
1060127dd473Swhl739
1061127dd473Swhl739 cmd->opcode = O_DSCP;
1062127dd473Swhl739 cmd->len |= F_INSN_SIZE(ipfw_insn_u32) + 1;
1063127dd473Swhl739
1064127dd473Swhl739 CHECK_CMDLEN;
1065127dd473Swhl739
1066127dd473Swhl739 low = (uint32_t *)(cmd + 1);
1067127dd473Swhl739 high = low + 1;
1068127dd473Swhl739
1069127dd473Swhl739 *low = 0;
1070127dd473Swhl739 *high = 0;
1071127dd473Swhl739
1072127dd473Swhl739 while (s != NULL) {
1073127dd473Swhl739 a = strchr(s, ',');
1074127dd473Swhl739
1075127dd473Swhl739 if (a != NULL)
1076127dd473Swhl739 *a++ = '\0';
1077127dd473Swhl739
1078127dd473Swhl739 if (isalpha(*s)) {
1079127dd473Swhl739 if ((code = match_token(f_ipdscp, s)) == -1)
1080127dd473Swhl739 errx(EX_DATAERR, "Unknown DSCP code");
1081127dd473Swhl739 } else {
1082127dd473Swhl739 code = strtoul(s, NULL, 10);
1083127dd473Swhl739 if (code < 0 || code > 63)
1084127dd473Swhl739 errx(EX_DATAERR, "Invalid DSCP value");
1085127dd473Swhl739 }
1086127dd473Swhl739
1087127dd473Swhl739 if (code >= 32)
1088127dd473Swhl739 *high |= 1 << (code - 32);
1089127dd473Swhl739 else
1090127dd473Swhl739 *low |= 1 << code;
1091127dd473Swhl739
1092127dd473Swhl739 s = a;
1093127dd473Swhl739 }
1094127dd473Swhl739 }
1095127dd473Swhl739
1096127dd473Swhl739 static struct _s_x icmpcodes[] = {
1097127dd473Swhl739 { "net", ICMP_UNREACH_NET },
1098127dd473Swhl739 { "host", ICMP_UNREACH_HOST },
1099127dd473Swhl739 { "protocol", ICMP_UNREACH_PROTOCOL },
1100127dd473Swhl739 { "port", ICMP_UNREACH_PORT },
1101127dd473Swhl739 { "needfrag", ICMP_UNREACH_NEEDFRAG },
1102127dd473Swhl739 { "srcfail", ICMP_UNREACH_SRCFAIL },
1103127dd473Swhl739 { "net-unknown", ICMP_UNREACH_NET_UNKNOWN },
1104127dd473Swhl739 { "host-unknown", ICMP_UNREACH_HOST_UNKNOWN },
1105127dd473Swhl739 { "isolated", ICMP_UNREACH_ISOLATED },
1106127dd473Swhl739 { "net-prohib", ICMP_UNREACH_NET_PROHIB },
1107127dd473Swhl739 { "host-prohib", ICMP_UNREACH_HOST_PROHIB },
1108127dd473Swhl739 { "tosnet", ICMP_UNREACH_TOSNET },
1109127dd473Swhl739 { "toshost", ICMP_UNREACH_TOSHOST },
1110127dd473Swhl739 { "filter-prohib", ICMP_UNREACH_FILTER_PROHIB },
1111127dd473Swhl739 { "host-precedence", ICMP_UNREACH_HOST_PRECEDENCE },
1112127dd473Swhl739 { "precedence-cutoff", ICMP_UNREACH_PRECEDENCE_CUTOFF },
1113127dd473Swhl739 { NULL, 0 }
1114127dd473Swhl739 };
1115127dd473Swhl739
1116127dd473Swhl739 static void
fill_reject_code(u_short * codep,char * str)1117127dd473Swhl739 fill_reject_code(u_short *codep, char *str)
1118127dd473Swhl739 {
1119127dd473Swhl739 int val;
1120127dd473Swhl739 char *s;
1121127dd473Swhl739
1122127dd473Swhl739 val = strtoul(str, &s, 0);
1123127dd473Swhl739 if (s == str || *s != '\0' || val >= 0x100)
1124127dd473Swhl739 val = match_token(icmpcodes, str);
1125127dd473Swhl739 if (val < 0)
1126127dd473Swhl739 errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
1127127dd473Swhl739 *codep = val;
1128127dd473Swhl739 return;
1129127dd473Swhl739 }
1130127dd473Swhl739
1131127dd473Swhl739 static void
print_reject_code(struct buf_pr * bp,uint16_t code)1132127dd473Swhl739 print_reject_code(struct buf_pr *bp, uint16_t code)
1133127dd473Swhl739 {
1134127dd473Swhl739 char const *s;
1135127dd473Swhl739
1136127dd473Swhl739 if ((s = match_value(icmpcodes, code)) != NULL)
1137127dd473Swhl739 bprintf(bp, "unreach %s", s);
1138127dd473Swhl739 else
1139127dd473Swhl739 bprintf(bp, "unreach %u", code);
1140127dd473Swhl739 }
1141127dd473Swhl739
1142127dd473Swhl739 /*
1143127dd473Swhl739 * Returns the number of bits set (from left) in a contiguous bitmask,
1144127dd473Swhl739 * or -1 if the mask is not contiguous.
1145127dd473Swhl739 * XXX this needs a proper fix.
1146127dd473Swhl739 * This effectively works on masks in big-endian (network) format.
1147127dd473Swhl739 * when compiled on little endian architectures.
1148127dd473Swhl739 *
1149127dd473Swhl739 * First bit is bit 7 of the first byte -- note, for MAC addresses,
1150127dd473Swhl739 * the first bit on the wire is bit 0 of the first byte.
1151127dd473Swhl739 * len is the max length in bits.
1152127dd473Swhl739 */
1153127dd473Swhl739 int
contigmask(const uint8_t * p,int len)115422ce4affSfengbojiang contigmask(const uint8_t *p, int len)
1155127dd473Swhl739 {
1156127dd473Swhl739 int i, n;
1157127dd473Swhl739
1158127dd473Swhl739 for (i=0; i<len ; i++)
1159127dd473Swhl739 if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
1160127dd473Swhl739 break;
1161127dd473Swhl739 for (n=i+1; n < len; n++)
1162127dd473Swhl739 if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
1163127dd473Swhl739 return -1; /* mask not contiguous */
1164127dd473Swhl739 return i;
1165127dd473Swhl739 }
1166127dd473Swhl739
1167127dd473Swhl739 /*
1168127dd473Swhl739 * print flags set/clear in the two bitmasks passed as parameters.
1169127dd473Swhl739 * There is a specialized check for f_tcpflags.
1170127dd473Swhl739 */
1171127dd473Swhl739 static void
print_flags(struct buf_pr * bp,char const * name,const ipfw_insn * cmd,struct _s_x * list)117222ce4affSfengbojiang print_flags(struct buf_pr *bp, char const *name, const ipfw_insn *cmd,
1173127dd473Swhl739 struct _s_x *list)
1174127dd473Swhl739 {
1175127dd473Swhl739 char const *comma = "";
1176127dd473Swhl739 int i;
1177127dd473Swhl739 uint8_t set = cmd->arg1 & 0xff;
1178127dd473Swhl739 uint8_t clear = (cmd->arg1 >> 8) & 0xff;
1179127dd473Swhl739
1180127dd473Swhl739 if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) {
1181127dd473Swhl739 bprintf(bp, " setup");
1182127dd473Swhl739 return;
1183127dd473Swhl739 }
1184127dd473Swhl739
1185127dd473Swhl739 bprintf(bp, " %s ", name);
1186127dd473Swhl739 for (i=0; list[i].x != 0; i++) {
1187127dd473Swhl739 if (set & list[i].x) {
1188127dd473Swhl739 set &= ~list[i].x;
1189127dd473Swhl739 bprintf(bp, "%s%s", comma, list[i].s);
1190127dd473Swhl739 comma = ",";
1191127dd473Swhl739 }
1192127dd473Swhl739 if (clear & list[i].x) {
1193127dd473Swhl739 clear &= ~list[i].x;
1194127dd473Swhl739 bprintf(bp, "%s!%s", comma, list[i].s);
1195127dd473Swhl739 comma = ",";
1196127dd473Swhl739 }
1197127dd473Swhl739 }
1198127dd473Swhl739 }
1199127dd473Swhl739
1200127dd473Swhl739
1201127dd473Swhl739 /*
1202127dd473Swhl739 * Print the ip address contained in a command.
1203127dd473Swhl739 */
1204127dd473Swhl739 static void
print_ip(struct buf_pr * bp,const struct format_opts * fo,const ipfw_insn_ip * cmd)120522ce4affSfengbojiang print_ip(struct buf_pr *bp, const struct format_opts *fo,
120622ce4affSfengbojiang const ipfw_insn_ip *cmd)
1207127dd473Swhl739 {
1208127dd473Swhl739 struct hostent *he = NULL;
120922ce4affSfengbojiang const struct in_addr *ia;
121022ce4affSfengbojiang const uint32_t *a = ((const ipfw_insn_u32 *)cmd)->d;
121122ce4affSfengbojiang uint32_t len = F_LEN((const ipfw_insn *)cmd);
1212127dd473Swhl739 char *t;
1213127dd473Swhl739
121422ce4affSfengbojiang bprintf(bp, " ");
1215127dd473Swhl739 if (cmd->o.opcode == O_IP_DST_LOOKUP && len > F_INSN_SIZE(ipfw_insn_u32)) {
1216127dd473Swhl739 uint32_t d = a[1];
1217127dd473Swhl739 const char *arg = "<invalid>";
1218127dd473Swhl739
1219127dd473Swhl739 if (d < sizeof(lookup_key)/sizeof(lookup_key[0]))
1220127dd473Swhl739 arg = match_value(rule_options, lookup_key[d]);
122122ce4affSfengbojiang t = table_search_ctlv(fo->tstate,
122222ce4affSfengbojiang ((const ipfw_insn *)cmd)->arg1);
122322ce4affSfengbojiang bprintf(bp, "lookup %s %s", arg, t);
1224127dd473Swhl739 return;
1225127dd473Swhl739 }
1226127dd473Swhl739 if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) {
1227127dd473Swhl739 bprintf(bp, "me");
1228127dd473Swhl739 return;
1229127dd473Swhl739 }
1230127dd473Swhl739 if (cmd->o.opcode == O_IP_SRC_LOOKUP ||
1231127dd473Swhl739 cmd->o.opcode == O_IP_DST_LOOKUP) {
123222ce4affSfengbojiang t = table_search_ctlv(fo->tstate,
123322ce4affSfengbojiang ((const ipfw_insn *)cmd)->arg1);
1234127dd473Swhl739 bprintf(bp, "table(%s", t);
1235127dd473Swhl739 if (len == F_INSN_SIZE(ipfw_insn_u32))
1236127dd473Swhl739 bprintf(bp, ",%u", *a);
1237127dd473Swhl739 bprintf(bp, ")");
1238127dd473Swhl739 return;
1239127dd473Swhl739 }
1240127dd473Swhl739 if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) {
124122ce4affSfengbojiang const uint32_t *map = (const uint32_t *)&cmd->mask;
124222ce4affSfengbojiang struct in_addr addr;
124322ce4affSfengbojiang uint32_t x;
1244127dd473Swhl739 int i, j;
1245127dd473Swhl739 char comma = '{';
1246127dd473Swhl739
1247127dd473Swhl739 x = cmd->o.arg1 - 1;
1248127dd473Swhl739 x = htonl(~x);
124922ce4affSfengbojiang addr.s_addr = htonl(cmd->addr.s_addr);
125022ce4affSfengbojiang bprintf(bp, "%s/%d", inet_ntoa(addr),
1251127dd473Swhl739 contigmask((uint8_t *)&x, 32));
125222ce4affSfengbojiang x = cmd->addr.s_addr;
1253127dd473Swhl739 x &= 0xff; /* base */
1254127dd473Swhl739 /*
1255127dd473Swhl739 * Print bits and ranges.
1256127dd473Swhl739 * Locate first bit set (i), then locate first bit unset (j).
1257127dd473Swhl739 * If we have 3+ consecutive bits set, then print them as a
1258127dd473Swhl739 * range, otherwise only print the initial bit and rescan.
1259127dd473Swhl739 */
1260127dd473Swhl739 for (i=0; i < cmd->o.arg1; i++)
1261127dd473Swhl739 if (map[i/32] & (1<<(i & 31))) {
1262127dd473Swhl739 for (j=i+1; j < cmd->o.arg1; j++)
1263127dd473Swhl739 if (!(map[ j/32] & (1<<(j & 31))))
1264127dd473Swhl739 break;
1265127dd473Swhl739 bprintf(bp, "%c%d", comma, i+x);
1266127dd473Swhl739 if (j>i+2) { /* range has at least 3 elements */
1267127dd473Swhl739 bprintf(bp, "-%d", j-1+x);
1268127dd473Swhl739 i = j-1;
1269127dd473Swhl739 }
1270127dd473Swhl739 comma = ',';
1271127dd473Swhl739 }
1272127dd473Swhl739 bprintf(bp, "}");
1273127dd473Swhl739 return;
1274127dd473Swhl739 }
1275127dd473Swhl739 /*
1276127dd473Swhl739 * len == 2 indicates a single IP, whereas lists of 1 or more
1277127dd473Swhl739 * addr/mask pairs have len = (2n+1). We convert len to n so we
1278127dd473Swhl739 * use that to count the number of entries.
1279127dd473Swhl739 */
1280127dd473Swhl739 for (len = len / 2; len > 0; len--, a += 2) {
1281127dd473Swhl739 int mb = /* mask length */
1282127dd473Swhl739 (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ?
128322ce4affSfengbojiang 32 : contigmask((const uint8_t *)&(a[1]), 32);
128422ce4affSfengbojiang if (mb == 32 && g_co.do_resolv)
128522ce4affSfengbojiang he = gethostbyaddr((const char *)&(a[0]), sizeof(in_addr_t),
128622ce4affSfengbojiang AF_INET);
1287127dd473Swhl739 if (he != NULL) /* resolved to name */
1288127dd473Swhl739 bprintf(bp, "%s", he->h_name);
1289127dd473Swhl739 else if (mb == 0) /* any */
1290127dd473Swhl739 bprintf(bp, "any");
1291127dd473Swhl739 else { /* numeric IP followed by some kind of mask */
129222ce4affSfengbojiang ia = (const struct in_addr *)&a[0];
1293127dd473Swhl739 bprintf(bp, "%s", inet_ntoa(*ia));
1294127dd473Swhl739 if (mb < 0) {
129522ce4affSfengbojiang ia = (const struct in_addr *)&a[1];
1296127dd473Swhl739 bprintf(bp, ":%s", inet_ntoa(*ia));
1297127dd473Swhl739 } else if (mb < 32)
1298127dd473Swhl739 bprintf(bp, "/%d", mb);
1299127dd473Swhl739 }
1300127dd473Swhl739 if (len > 1)
1301127dd473Swhl739 bprintf(bp, ",");
1302127dd473Swhl739 }
1303127dd473Swhl739 }
1304127dd473Swhl739
1305127dd473Swhl739 /*
1306127dd473Swhl739 * prints a MAC address/mask pair
1307127dd473Swhl739 */
1308127dd473Swhl739 static void
format_mac(struct buf_pr * bp,const uint8_t * addr,const uint8_t * mask)130922ce4affSfengbojiang format_mac(struct buf_pr *bp, const uint8_t *addr, const uint8_t *mask)
1310127dd473Swhl739 {
1311127dd473Swhl739 int l = contigmask(mask, 48);
1312127dd473Swhl739
1313127dd473Swhl739 if (l == 0)
1314127dd473Swhl739 bprintf(bp, " any");
1315127dd473Swhl739 else {
1316127dd473Swhl739 bprintf(bp, " %02x:%02x:%02x:%02x:%02x:%02x",
1317127dd473Swhl739 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
1318127dd473Swhl739 if (l == -1)
1319127dd473Swhl739 bprintf(bp, "&%02x:%02x:%02x:%02x:%02x:%02x",
1320127dd473Swhl739 mask[0], mask[1], mask[2],
1321127dd473Swhl739 mask[3], mask[4], mask[5]);
1322127dd473Swhl739 else if (l < 48)
1323127dd473Swhl739 bprintf(bp, "/%d", l);
1324127dd473Swhl739 }
1325127dd473Swhl739 }
1326127dd473Swhl739
1327127dd473Swhl739 static void
print_mac(struct buf_pr * bp,const ipfw_insn_mac * mac)132822ce4affSfengbojiang print_mac(struct buf_pr *bp, const ipfw_insn_mac *mac)
132922ce4affSfengbojiang {
133022ce4affSfengbojiang
133122ce4affSfengbojiang bprintf(bp, " MAC");
133222ce4affSfengbojiang format_mac(bp, mac->addr, mac->mask);
133322ce4affSfengbojiang format_mac(bp, mac->addr + 6, mac->mask + 6);
133422ce4affSfengbojiang }
133522ce4affSfengbojiang
133622ce4affSfengbojiang static void
fill_icmptypes(ipfw_insn_u32 * cmd,char * av)1337127dd473Swhl739 fill_icmptypes(ipfw_insn_u32 *cmd, char *av)
1338127dd473Swhl739 {
1339127dd473Swhl739 uint8_t type;
1340127dd473Swhl739
1341127dd473Swhl739 cmd->d[0] = 0;
1342127dd473Swhl739 while (*av) {
1343127dd473Swhl739 if (*av == ',')
1344127dd473Swhl739 av++;
1345127dd473Swhl739
1346127dd473Swhl739 type = strtoul(av, &av, 0);
1347127dd473Swhl739
1348127dd473Swhl739 if (*av != ',' && *av != '\0')
1349127dd473Swhl739 errx(EX_DATAERR, "invalid ICMP type");
1350127dd473Swhl739
1351127dd473Swhl739 if (type > 31)
1352127dd473Swhl739 errx(EX_DATAERR, "ICMP type out of range");
1353127dd473Swhl739
1354127dd473Swhl739 cmd->d[0] |= 1 << type;
1355127dd473Swhl739 }
1356127dd473Swhl739 cmd->o.opcode = O_ICMPTYPE;
1357127dd473Swhl739 cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
1358127dd473Swhl739 }
1359127dd473Swhl739
1360127dd473Swhl739 static void
print_icmptypes(struct buf_pr * bp,const ipfw_insn_u32 * cmd)136122ce4affSfengbojiang print_icmptypes(struct buf_pr *bp, const ipfw_insn_u32 *cmd)
1362127dd473Swhl739 {
1363127dd473Swhl739 int i;
1364127dd473Swhl739 char sep= ' ';
1365127dd473Swhl739
1366127dd473Swhl739 bprintf(bp, " icmptypes");
1367127dd473Swhl739 for (i = 0; i < 32; i++) {
1368127dd473Swhl739 if ( (cmd->d[0] & (1 << (i))) == 0)
1369127dd473Swhl739 continue;
1370127dd473Swhl739 bprintf(bp, "%c%d", sep, i);
1371127dd473Swhl739 sep = ',';
1372127dd473Swhl739 }
1373127dd473Swhl739 }
1374127dd473Swhl739
1375127dd473Swhl739 static void
print_dscp(struct buf_pr * bp,const ipfw_insn_u32 * cmd)137622ce4affSfengbojiang print_dscp(struct buf_pr *bp, const ipfw_insn_u32 *cmd)
1377127dd473Swhl739 {
137822ce4affSfengbojiang const uint32_t *v;
1379127dd473Swhl739 const char *code;
138022ce4affSfengbojiang int i = 0;
138122ce4affSfengbojiang char sep= ' ';
1382127dd473Swhl739
1383127dd473Swhl739 bprintf(bp, " dscp");
1384127dd473Swhl739 v = cmd->d;
1385127dd473Swhl739 while (i < 64) {
1386127dd473Swhl739 if (*v & (1 << i)) {
1387127dd473Swhl739 if ((code = match_value(f_ipdscp, i)) != NULL)
1388127dd473Swhl739 bprintf(bp, "%c%s", sep, code);
1389127dd473Swhl739 else
1390127dd473Swhl739 bprintf(bp, "%c%d", sep, i);
1391127dd473Swhl739 sep = ',';
1392127dd473Swhl739 }
1393127dd473Swhl739
1394127dd473Swhl739 if ((++i % 32) == 0)
1395127dd473Swhl739 v++;
1396127dd473Swhl739 }
1397127dd473Swhl739 }
1398127dd473Swhl739
139922ce4affSfengbojiang #define insntod(cmd, type) ((const ipfw_insn_ ## type *)(cmd))
140022ce4affSfengbojiang struct show_state {
140122ce4affSfengbojiang struct ip_fw_rule *rule;
140222ce4affSfengbojiang const ipfw_insn *eaction;
140322ce4affSfengbojiang uint8_t *printed;
140422ce4affSfengbojiang int flags;
1405127dd473Swhl739 #define HAVE_PROTO 0x0001
1406127dd473Swhl739 #define HAVE_SRCIP 0x0002
1407127dd473Swhl739 #define HAVE_DSTIP 0x0004
140822ce4affSfengbojiang #define HAVE_PROBE_STATE 0x0008
140922ce4affSfengbojiang int proto;
141022ce4affSfengbojiang int or_block;
141122ce4affSfengbojiang };
141222ce4affSfengbojiang
141322ce4affSfengbojiang static int
init_show_state(struct show_state * state,struct ip_fw_rule * rule)141422ce4affSfengbojiang init_show_state(struct show_state *state, struct ip_fw_rule *rule)
141522ce4affSfengbojiang {
141622ce4affSfengbojiang
141722ce4affSfengbojiang state->printed = calloc(rule->cmd_len, sizeof(uint8_t));
141822ce4affSfengbojiang if (state->printed == NULL)
141922ce4affSfengbojiang return (ENOMEM);
142022ce4affSfengbojiang state->rule = rule;
142122ce4affSfengbojiang state->eaction = NULL;
142222ce4affSfengbojiang state->flags = 0;
142322ce4affSfengbojiang state->proto = 0;
142422ce4affSfengbojiang state->or_block = 0;
142522ce4affSfengbojiang return (0);
142622ce4affSfengbojiang }
1427127dd473Swhl739
1428127dd473Swhl739 static void
free_show_state(struct show_state * state)142922ce4affSfengbojiang free_show_state(struct show_state *state)
1430127dd473Swhl739 {
1431127dd473Swhl739
143222ce4affSfengbojiang free(state->printed);
143322ce4affSfengbojiang }
143422ce4affSfengbojiang
143522ce4affSfengbojiang static uint8_t
is_printed_opcode(struct show_state * state,const ipfw_insn * cmd)143622ce4affSfengbojiang is_printed_opcode(struct show_state *state, const ipfw_insn *cmd)
143722ce4affSfengbojiang {
143822ce4affSfengbojiang
143922ce4affSfengbojiang return (state->printed[cmd - state->rule->cmd]);
144022ce4affSfengbojiang }
144122ce4affSfengbojiang
144222ce4affSfengbojiang static void
mark_printed(struct show_state * state,const ipfw_insn * cmd)144322ce4affSfengbojiang mark_printed(struct show_state *state, const ipfw_insn *cmd)
144422ce4affSfengbojiang {
144522ce4affSfengbojiang
144622ce4affSfengbojiang state->printed[cmd - state->rule->cmd] = 1;
144722ce4affSfengbojiang }
144822ce4affSfengbojiang
144922ce4affSfengbojiang static void
print_limit_mask(struct buf_pr * bp,const ipfw_insn_limit * limit)145022ce4affSfengbojiang print_limit_mask(struct buf_pr *bp, const ipfw_insn_limit *limit)
145122ce4affSfengbojiang {
145222ce4affSfengbojiang struct _s_x *p = limit_masks;
145322ce4affSfengbojiang char const *comma = " ";
145422ce4affSfengbojiang uint8_t x;
145522ce4affSfengbojiang
145622ce4affSfengbojiang for (x = limit->limit_mask; p->x != 0; p++) {
145722ce4affSfengbojiang if ((x & p->x) == p->x) {
145822ce4affSfengbojiang x &= ~p->x;
145922ce4affSfengbojiang bprintf(bp, "%s%s", comma, p->s);
146022ce4affSfengbojiang comma = ",";
146122ce4affSfengbojiang }
146222ce4affSfengbojiang }
146322ce4affSfengbojiang bprint_uint_arg(bp, " ", limit->conn_limit);
146422ce4affSfengbojiang }
146522ce4affSfengbojiang
146622ce4affSfengbojiang static int
print_instruction(struct buf_pr * bp,const struct format_opts * fo,struct show_state * state,const ipfw_insn * cmd)146722ce4affSfengbojiang print_instruction(struct buf_pr *bp, const struct format_opts *fo,
146822ce4affSfengbojiang struct show_state *state, const ipfw_insn *cmd)
146922ce4affSfengbojiang {
147022ce4affSfengbojiang struct protoent *pe;
147122ce4affSfengbojiang struct passwd *pwd;
147222ce4affSfengbojiang struct group *grp;
147322ce4affSfengbojiang const char *s;
147422ce4affSfengbojiang double d;
147522ce4affSfengbojiang
147622ce4affSfengbojiang if (is_printed_opcode(state, cmd))
147722ce4affSfengbojiang return (0);
147822ce4affSfengbojiang if ((cmd->len & F_OR) != 0 && state->or_block == 0)
147922ce4affSfengbojiang bprintf(bp, " {");
148022ce4affSfengbojiang if (cmd->opcode != O_IN && (cmd->len & F_NOT) != 0)
148122ce4affSfengbojiang bprintf(bp, " not");
148222ce4affSfengbojiang
148322ce4affSfengbojiang switch (cmd->opcode) {
148422ce4affSfengbojiang case O_PROB:
148522ce4affSfengbojiang d = 1.0 * insntod(cmd, u32)->d[0] / 0x7fffffff;
148622ce4affSfengbojiang bprintf(bp, "prob %f ", d);
148722ce4affSfengbojiang break;
148822ce4affSfengbojiang case O_PROBE_STATE: /* no need to print anything here */
148922ce4affSfengbojiang state->flags |= HAVE_PROBE_STATE;
149022ce4affSfengbojiang break;
149122ce4affSfengbojiang case O_IP_SRC:
149222ce4affSfengbojiang case O_IP_SRC_LOOKUP:
149322ce4affSfengbojiang case O_IP_SRC_MASK:
149422ce4affSfengbojiang case O_IP_SRC_ME:
149522ce4affSfengbojiang case O_IP_SRC_SET:
149622ce4affSfengbojiang if (state->flags & HAVE_SRCIP)
149722ce4affSfengbojiang bprintf(bp, " src-ip");
149822ce4affSfengbojiang print_ip(bp, fo, insntod(cmd, ip));
149922ce4affSfengbojiang break;
150022ce4affSfengbojiang case O_IP_DST:
150122ce4affSfengbojiang case O_IP_DST_LOOKUP:
150222ce4affSfengbojiang case O_IP_DST_MASK:
150322ce4affSfengbojiang case O_IP_DST_ME:
150422ce4affSfengbojiang case O_IP_DST_SET:
150522ce4affSfengbojiang if (state->flags & HAVE_DSTIP)
150622ce4affSfengbojiang bprintf(bp, " dst-ip");
150722ce4affSfengbojiang print_ip(bp, fo, insntod(cmd, ip));
150822ce4affSfengbojiang break;
150922ce4affSfengbojiang case O_IP6_SRC:
151022ce4affSfengbojiang case O_IP6_SRC_MASK:
151122ce4affSfengbojiang case O_IP6_SRC_ME:
151222ce4affSfengbojiang if (state->flags & HAVE_SRCIP)
151322ce4affSfengbojiang bprintf(bp, " src-ip6");
151422ce4affSfengbojiang print_ip6(bp, insntod(cmd, ip6));
151522ce4affSfengbojiang break;
151622ce4affSfengbojiang case O_IP6_DST:
151722ce4affSfengbojiang case O_IP6_DST_MASK:
151822ce4affSfengbojiang case O_IP6_DST_ME:
151922ce4affSfengbojiang if (state->flags & HAVE_DSTIP)
152022ce4affSfengbojiang bprintf(bp, " dst-ip6");
152122ce4affSfengbojiang print_ip6(bp, insntod(cmd, ip6));
152222ce4affSfengbojiang break;
152322ce4affSfengbojiang case O_FLOW6ID:
152422ce4affSfengbojiang print_flow6id(bp, insntod(cmd, u32));
152522ce4affSfengbojiang break;
152622ce4affSfengbojiang case O_IP_DSTPORT:
152722ce4affSfengbojiang case O_IP_SRCPORT:
152822ce4affSfengbojiang print_newports(bp, insntod(cmd, u16), state->proto,
152922ce4affSfengbojiang (state->flags & (HAVE_SRCIP | HAVE_DSTIP)) ==
153022ce4affSfengbojiang (HAVE_SRCIP | HAVE_DSTIP) ? cmd->opcode: 0);
153122ce4affSfengbojiang break;
153222ce4affSfengbojiang case O_PROTO:
153322ce4affSfengbojiang pe = getprotobynumber(cmd->arg1);
153422ce4affSfengbojiang if (state->flags & HAVE_PROTO)
153522ce4affSfengbojiang bprintf(bp, " proto");
153622ce4affSfengbojiang if (pe != NULL)
153722ce4affSfengbojiang bprintf(bp, " %s", pe->p_name);
1538127dd473Swhl739 else
153922ce4affSfengbojiang bprintf(bp, " %u", cmd->arg1);
154022ce4affSfengbojiang state->proto = cmd->arg1;
154122ce4affSfengbojiang break;
154222ce4affSfengbojiang case O_MACADDR2:
154322ce4affSfengbojiang print_mac(bp, insntod(cmd, mac));
154422ce4affSfengbojiang break;
154522ce4affSfengbojiang case O_MAC_TYPE:
154622ce4affSfengbojiang print_newports(bp, insntod(cmd, u16),
154722ce4affSfengbojiang IPPROTO_ETHERTYPE, cmd->opcode);
154822ce4affSfengbojiang break;
154922ce4affSfengbojiang case O_FRAG:
155022ce4affSfengbojiang print_flags(bp, "frag", cmd, f_ipoff);
155122ce4affSfengbojiang break;
155222ce4affSfengbojiang case O_FIB:
155322ce4affSfengbojiang bprintf(bp, " fib %u", cmd->arg1);
155422ce4affSfengbojiang break;
155522ce4affSfengbojiang case O_SOCKARG:
155622ce4affSfengbojiang bprintf(bp, " sockarg");
155722ce4affSfengbojiang break;
155822ce4affSfengbojiang case O_IN:
155922ce4affSfengbojiang bprintf(bp, cmd->len & F_NOT ? " out" : " in");
156022ce4affSfengbojiang break;
156122ce4affSfengbojiang case O_DIVERTED:
156222ce4affSfengbojiang switch (cmd->arg1) {
156322ce4affSfengbojiang case 3:
156422ce4affSfengbojiang bprintf(bp, " diverted");
156522ce4affSfengbojiang break;
156622ce4affSfengbojiang case 2:
156722ce4affSfengbojiang bprintf(bp, " diverted-output");
156822ce4affSfengbojiang break;
156922ce4affSfengbojiang case 1:
157022ce4affSfengbojiang bprintf(bp, " diverted-loopback");
157122ce4affSfengbojiang break;
157222ce4affSfengbojiang default:
157322ce4affSfengbojiang bprintf(bp, " diverted-?<%u>", cmd->arg1);
157422ce4affSfengbojiang break;
157522ce4affSfengbojiang }
157622ce4affSfengbojiang break;
157722ce4affSfengbojiang case O_LAYER2:
157822ce4affSfengbojiang bprintf(bp, " layer2");
157922ce4affSfengbojiang break;
158022ce4affSfengbojiang case O_XMIT:
158122ce4affSfengbojiang case O_RECV:
158222ce4affSfengbojiang case O_VIA:
158322ce4affSfengbojiang if (cmd->opcode == O_XMIT)
158422ce4affSfengbojiang s = "xmit";
158522ce4affSfengbojiang else if (cmd->opcode == O_RECV)
158622ce4affSfengbojiang s = "recv";
158722ce4affSfengbojiang else /* if (cmd->opcode == O_VIA) */
158822ce4affSfengbojiang s = "via";
158922ce4affSfengbojiang switch (insntod(cmd, if)->name[0]) {
159022ce4affSfengbojiang case '\0':
159122ce4affSfengbojiang bprintf(bp, " %s %s", s,
159222ce4affSfengbojiang inet_ntoa(insntod(cmd, if)->p.ip));
159322ce4affSfengbojiang break;
159422ce4affSfengbojiang case '\1':
159522ce4affSfengbojiang bprintf(bp, " %s table(%s)", s,
159622ce4affSfengbojiang table_search_ctlv(fo->tstate,
159722ce4affSfengbojiang insntod(cmd, if)->p.kidx));
159822ce4affSfengbojiang break;
159922ce4affSfengbojiang default:
160022ce4affSfengbojiang bprintf(bp, " %s %s", s,
160122ce4affSfengbojiang insntod(cmd, if)->name);
160222ce4affSfengbojiang }
160322ce4affSfengbojiang break;
160422ce4affSfengbojiang case O_IP_FLOW_LOOKUP:
160522ce4affSfengbojiang s = table_search_ctlv(fo->tstate, cmd->arg1);
160622ce4affSfengbojiang bprintf(bp, " flow table(%s", s);
160722ce4affSfengbojiang if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))
160822ce4affSfengbojiang bprintf(bp, ",%u", insntod(cmd, u32)->d[0]);
160922ce4affSfengbojiang bprintf(bp, ")");
161022ce4affSfengbojiang break;
161122ce4affSfengbojiang case O_IPID:
161222ce4affSfengbojiang case O_IPTTL:
161322ce4affSfengbojiang case O_IPLEN:
161422ce4affSfengbojiang case O_TCPDATALEN:
161522ce4affSfengbojiang case O_TCPMSS:
161622ce4affSfengbojiang case O_TCPWIN:
161722ce4affSfengbojiang if (F_LEN(cmd) == 1) {
161822ce4affSfengbojiang switch (cmd->opcode) {
161922ce4affSfengbojiang case O_IPID:
162022ce4affSfengbojiang s = "ipid";
162122ce4affSfengbojiang break;
162222ce4affSfengbojiang case O_IPTTL:
162322ce4affSfengbojiang s = "ipttl";
162422ce4affSfengbojiang break;
162522ce4affSfengbojiang case O_IPLEN:
162622ce4affSfengbojiang s = "iplen";
162722ce4affSfengbojiang break;
162822ce4affSfengbojiang case O_TCPDATALEN:
162922ce4affSfengbojiang s = "tcpdatalen";
163022ce4affSfengbojiang break;
163122ce4affSfengbojiang case O_TCPMSS:
163222ce4affSfengbojiang s = "tcpmss";
163322ce4affSfengbojiang break;
163422ce4affSfengbojiang case O_TCPWIN:
163522ce4affSfengbojiang s = "tcpwin";
163622ce4affSfengbojiang break;
163722ce4affSfengbojiang default:
163822ce4affSfengbojiang s = "<unknown>";
163922ce4affSfengbojiang break;
164022ce4affSfengbojiang }
164122ce4affSfengbojiang bprintf(bp, " %s %u", s, cmd->arg1);
164222ce4affSfengbojiang } else
164322ce4affSfengbojiang print_newports(bp, insntod(cmd, u16), 0,
164422ce4affSfengbojiang cmd->opcode);
164522ce4affSfengbojiang break;
164622ce4affSfengbojiang case O_IPVER:
164722ce4affSfengbojiang bprintf(bp, " ipver %u", cmd->arg1);
164822ce4affSfengbojiang break;
164922ce4affSfengbojiang case O_IPPRECEDENCE:
165022ce4affSfengbojiang bprintf(bp, " ipprecedence %u", cmd->arg1 >> 5);
165122ce4affSfengbojiang break;
165222ce4affSfengbojiang case O_DSCP:
165322ce4affSfengbojiang print_dscp(bp, insntod(cmd, u32));
165422ce4affSfengbojiang break;
165522ce4affSfengbojiang case O_IPOPT:
165622ce4affSfengbojiang print_flags(bp, "ipoptions", cmd, f_ipopts);
165722ce4affSfengbojiang break;
165822ce4affSfengbojiang case O_IPTOS:
165922ce4affSfengbojiang print_flags(bp, "iptos", cmd, f_iptos);
166022ce4affSfengbojiang break;
166122ce4affSfengbojiang case O_ICMPTYPE:
166222ce4affSfengbojiang print_icmptypes(bp, insntod(cmd, u32));
166322ce4affSfengbojiang break;
166422ce4affSfengbojiang case O_ESTAB:
166522ce4affSfengbojiang bprintf(bp, " established");
166622ce4affSfengbojiang break;
166722ce4affSfengbojiang case O_TCPFLAGS:
166822ce4affSfengbojiang print_flags(bp, "tcpflags", cmd, f_tcpflags);
166922ce4affSfengbojiang break;
167022ce4affSfengbojiang case O_TCPOPTS:
167122ce4affSfengbojiang print_flags(bp, "tcpoptions", cmd, f_tcpopts);
167222ce4affSfengbojiang break;
167322ce4affSfengbojiang case O_TCPACK:
167422ce4affSfengbojiang bprintf(bp, " tcpack %d",
167522ce4affSfengbojiang ntohl(insntod(cmd, u32)->d[0]));
167622ce4affSfengbojiang break;
167722ce4affSfengbojiang case O_TCPSEQ:
167822ce4affSfengbojiang bprintf(bp, " tcpseq %d",
167922ce4affSfengbojiang ntohl(insntod(cmd, u32)->d[0]));
168022ce4affSfengbojiang break;
168122ce4affSfengbojiang case O_UID:
168222ce4affSfengbojiang pwd = getpwuid(insntod(cmd, u32)->d[0]);
168322ce4affSfengbojiang if (pwd != NULL)
168422ce4affSfengbojiang bprintf(bp, " uid %s", pwd->pw_name);
168522ce4affSfengbojiang else
168622ce4affSfengbojiang bprintf(bp, " uid %u",
168722ce4affSfengbojiang insntod(cmd, u32)->d[0]);
168822ce4affSfengbojiang break;
168922ce4affSfengbojiang case O_GID:
169022ce4affSfengbojiang grp = getgrgid(insntod(cmd, u32)->d[0]);
169122ce4affSfengbojiang if (grp != NULL)
169222ce4affSfengbojiang bprintf(bp, " gid %s", grp->gr_name);
169322ce4affSfengbojiang else
169422ce4affSfengbojiang bprintf(bp, " gid %u",
169522ce4affSfengbojiang insntod(cmd, u32)->d[0]);
169622ce4affSfengbojiang break;
169722ce4affSfengbojiang case O_JAIL:
169822ce4affSfengbojiang bprintf(bp, " jail %d", insntod(cmd, u32)->d[0]);
169922ce4affSfengbojiang break;
170022ce4affSfengbojiang case O_VERREVPATH:
170122ce4affSfengbojiang bprintf(bp, " verrevpath");
170222ce4affSfengbojiang break;
170322ce4affSfengbojiang case O_VERSRCREACH:
170422ce4affSfengbojiang bprintf(bp, " versrcreach");
170522ce4affSfengbojiang break;
170622ce4affSfengbojiang case O_ANTISPOOF:
170722ce4affSfengbojiang bprintf(bp, " antispoof");
170822ce4affSfengbojiang break;
170922ce4affSfengbojiang case O_IPSEC:
171022ce4affSfengbojiang bprintf(bp, " ipsec");
171122ce4affSfengbojiang break;
171222ce4affSfengbojiang case O_NOP:
171322ce4affSfengbojiang bprintf(bp, " // %s", (const char *)(cmd + 1));
171422ce4affSfengbojiang break;
171522ce4affSfengbojiang case O_KEEP_STATE:
171622ce4affSfengbojiang if (state->flags & HAVE_PROBE_STATE)
171722ce4affSfengbojiang bprintf(bp, " keep-state");
171822ce4affSfengbojiang else
171922ce4affSfengbojiang bprintf(bp, " record-state");
172022ce4affSfengbojiang bprintf(bp, " :%s",
172122ce4affSfengbojiang object_search_ctlv(fo->tstate, cmd->arg1,
172222ce4affSfengbojiang IPFW_TLV_STATE_NAME));
172322ce4affSfengbojiang break;
172422ce4affSfengbojiang case O_LIMIT:
172522ce4affSfengbojiang if (state->flags & HAVE_PROBE_STATE)
172622ce4affSfengbojiang bprintf(bp, " limit");
172722ce4affSfengbojiang else
172822ce4affSfengbojiang bprintf(bp, " set-limit");
172922ce4affSfengbojiang print_limit_mask(bp, insntod(cmd, limit));
173022ce4affSfengbojiang bprintf(bp, " :%s",
173122ce4affSfengbojiang object_search_ctlv(fo->tstate, cmd->arg1,
173222ce4affSfengbojiang IPFW_TLV_STATE_NAME));
173322ce4affSfengbojiang break;
173422ce4affSfengbojiang case O_IP6:
173522ce4affSfengbojiang if (state->flags & HAVE_PROTO)
173622ce4affSfengbojiang bprintf(bp, " proto");
173722ce4affSfengbojiang bprintf(bp, " ip6");
173822ce4affSfengbojiang break;
173922ce4affSfengbojiang case O_IP4:
174022ce4affSfengbojiang if (state->flags & HAVE_PROTO)
174122ce4affSfengbojiang bprintf(bp, " proto");
174222ce4affSfengbojiang bprintf(bp, " ip4");
174322ce4affSfengbojiang break;
174422ce4affSfengbojiang case O_ICMP6TYPE:
174522ce4affSfengbojiang print_icmp6types(bp, insntod(cmd, u32));
174622ce4affSfengbojiang break;
174722ce4affSfengbojiang case O_EXT_HDR:
174822ce4affSfengbojiang print_ext6hdr(bp, cmd);
174922ce4affSfengbojiang break;
175022ce4affSfengbojiang case O_TAGGED:
175122ce4affSfengbojiang if (F_LEN(cmd) == 1)
175222ce4affSfengbojiang bprint_uint_arg(bp, " tagged ", cmd->arg1);
175322ce4affSfengbojiang else
175422ce4affSfengbojiang print_newports(bp, insntod(cmd, u16),
175522ce4affSfengbojiang 0, O_TAGGED);
175622ce4affSfengbojiang break;
175722ce4affSfengbojiang case O_SKIP_ACTION:
175822ce4affSfengbojiang bprintf(bp, " defer-immediate-action");
175922ce4affSfengbojiang break;
176022ce4affSfengbojiang default:
176122ce4affSfengbojiang bprintf(bp, " [opcode %d len %d]", cmd->opcode,
176222ce4affSfengbojiang cmd->len);
176322ce4affSfengbojiang }
176422ce4affSfengbojiang if (cmd->len & F_OR) {
176522ce4affSfengbojiang bprintf(bp, " or");
176622ce4affSfengbojiang state->or_block = 1;
176722ce4affSfengbojiang } else if (state->or_block != 0) {
176822ce4affSfengbojiang bprintf(bp, " }");
176922ce4affSfengbojiang state->or_block = 0;
177022ce4affSfengbojiang }
177122ce4affSfengbojiang mark_printed(state, cmd);
177222ce4affSfengbojiang
177322ce4affSfengbojiang return (1);
177422ce4affSfengbojiang }
177522ce4affSfengbojiang
177622ce4affSfengbojiang static ipfw_insn *
print_opcode(struct buf_pr * bp,struct format_opts * fo,struct show_state * state,int opcode)177722ce4affSfengbojiang print_opcode(struct buf_pr *bp, struct format_opts *fo,
177822ce4affSfengbojiang struct show_state *state, int opcode)
177922ce4affSfengbojiang {
178022ce4affSfengbojiang ipfw_insn *cmd;
178122ce4affSfengbojiang int l;
178222ce4affSfengbojiang
178322ce4affSfengbojiang for (l = state->rule->act_ofs, cmd = state->rule->cmd;
178422ce4affSfengbojiang l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
178522ce4affSfengbojiang /* We use zero opcode to print the rest of options */
178622ce4affSfengbojiang if (opcode >= 0 && cmd->opcode != opcode)
178722ce4affSfengbojiang continue;
178822ce4affSfengbojiang /*
178922ce4affSfengbojiang * Skip O_NOP, when we printing the rest
179022ce4affSfengbojiang * of options, it will be handled separately.
179122ce4affSfengbojiang */
179222ce4affSfengbojiang if (cmd->opcode == O_NOP && opcode != O_NOP)
179322ce4affSfengbojiang continue;
179422ce4affSfengbojiang if (!print_instruction(bp, fo, state, cmd))
179522ce4affSfengbojiang continue;
179622ce4affSfengbojiang return (cmd);
179722ce4affSfengbojiang }
179822ce4affSfengbojiang return (NULL);
179922ce4affSfengbojiang }
180022ce4affSfengbojiang
180122ce4affSfengbojiang static void
print_fwd(struct buf_pr * bp,const ipfw_insn * cmd)180222ce4affSfengbojiang print_fwd(struct buf_pr *bp, const ipfw_insn *cmd)
180322ce4affSfengbojiang {
180422ce4affSfengbojiang char buf[INET6_ADDRSTRLEN + IF_NAMESIZE + 2];
180522ce4affSfengbojiang const ipfw_insn_sa6 *sa6;
180622ce4affSfengbojiang const ipfw_insn_sa *sa;
180722ce4affSfengbojiang uint16_t port;
180822ce4affSfengbojiang
180922ce4affSfengbojiang if (cmd->opcode == O_FORWARD_IP) {
181022ce4affSfengbojiang sa = insntod(cmd, sa);
181122ce4affSfengbojiang port = sa->sa.sin_port;
181222ce4affSfengbojiang if (sa->sa.sin_addr.s_addr == INADDR_ANY)
181322ce4affSfengbojiang bprintf(bp, "fwd tablearg");
181422ce4affSfengbojiang else
181522ce4affSfengbojiang bprintf(bp, "fwd %s", inet_ntoa(sa->sa.sin_addr));
181622ce4affSfengbojiang } else {
181722ce4affSfengbojiang sa6 = insntod(cmd, sa6);
181822ce4affSfengbojiang port = sa6->sa.sin6_port;
181922ce4affSfengbojiang bprintf(bp, "fwd ");
182022ce4affSfengbojiang if (getnameinfo((const struct sockaddr *)&sa6->sa,
182122ce4affSfengbojiang sizeof(struct sockaddr_in6), buf, sizeof(buf), NULL, 0,
182222ce4affSfengbojiang NI_NUMERICHOST) == 0)
182322ce4affSfengbojiang bprintf(bp, "%s", buf);
182422ce4affSfengbojiang }
182522ce4affSfengbojiang if (port != 0)
182622ce4affSfengbojiang bprintf(bp, ",%u", port);
182722ce4affSfengbojiang }
182822ce4affSfengbojiang
182922ce4affSfengbojiang static int
print_action_instruction(struct buf_pr * bp,const struct format_opts * fo,struct show_state * state,const ipfw_insn * cmd)183022ce4affSfengbojiang print_action_instruction(struct buf_pr *bp, const struct format_opts *fo,
183122ce4affSfengbojiang struct show_state *state, const ipfw_insn *cmd)
183222ce4affSfengbojiang {
183322ce4affSfengbojiang const char *s;
183422ce4affSfengbojiang
183522ce4affSfengbojiang if (is_printed_opcode(state, cmd))
183622ce4affSfengbojiang return (0);
183722ce4affSfengbojiang switch (cmd->opcode) {
183822ce4affSfengbojiang case O_CHECK_STATE:
183922ce4affSfengbojiang bprintf(bp, "check-state");
184022ce4affSfengbojiang if (cmd->arg1 != 0)
184122ce4affSfengbojiang s = object_search_ctlv(fo->tstate, cmd->arg1,
184222ce4affSfengbojiang IPFW_TLV_STATE_NAME);
184322ce4affSfengbojiang else
184422ce4affSfengbojiang s = NULL;
184522ce4affSfengbojiang bprintf(bp, " :%s", s ? s: "any");
184622ce4affSfengbojiang break;
184722ce4affSfengbojiang case O_ACCEPT:
184822ce4affSfengbojiang bprintf(bp, "allow");
184922ce4affSfengbojiang break;
185022ce4affSfengbojiang case O_COUNT:
185122ce4affSfengbojiang bprintf(bp, "count");
185222ce4affSfengbojiang break;
185322ce4affSfengbojiang case O_DENY:
185422ce4affSfengbojiang bprintf(bp, "deny");
185522ce4affSfengbojiang break;
185622ce4affSfengbojiang case O_REJECT:
185722ce4affSfengbojiang if (cmd->arg1 == ICMP_REJECT_RST)
185822ce4affSfengbojiang bprintf(bp, "reset");
185922ce4affSfengbojiang else if (cmd->arg1 == ICMP_REJECT_ABORT)
186022ce4affSfengbojiang bprintf(bp, "abort");
186122ce4affSfengbojiang else if (cmd->arg1 == ICMP_UNREACH_HOST)
186222ce4affSfengbojiang bprintf(bp, "reject");
186322ce4affSfengbojiang else
186422ce4affSfengbojiang print_reject_code(bp, cmd->arg1);
186522ce4affSfengbojiang break;
186622ce4affSfengbojiang case O_UNREACH6:
186722ce4affSfengbojiang if (cmd->arg1 == ICMP6_UNREACH_RST)
186822ce4affSfengbojiang bprintf(bp, "reset6");
186922ce4affSfengbojiang else if (cmd->arg1 == ICMP6_UNREACH_ABORT)
187022ce4affSfengbojiang bprintf(bp, "abort6");
187122ce4affSfengbojiang else
187222ce4affSfengbojiang print_unreach6_code(bp, cmd->arg1);
187322ce4affSfengbojiang break;
187422ce4affSfengbojiang case O_SKIPTO:
187522ce4affSfengbojiang bprint_uint_arg(bp, "skipto ", cmd->arg1);
187622ce4affSfengbojiang break;
187722ce4affSfengbojiang case O_PIPE:
187822ce4affSfengbojiang bprint_uint_arg(bp, "pipe ", cmd->arg1);
187922ce4affSfengbojiang break;
188022ce4affSfengbojiang case O_QUEUE:
188122ce4affSfengbojiang bprint_uint_arg(bp, "queue ", cmd->arg1);
188222ce4affSfengbojiang break;
188322ce4affSfengbojiang case O_DIVERT:
188422ce4affSfengbojiang bprint_uint_arg(bp, "divert ", cmd->arg1);
188522ce4affSfengbojiang break;
188622ce4affSfengbojiang case O_TEE:
188722ce4affSfengbojiang bprint_uint_arg(bp, "tee ", cmd->arg1);
188822ce4affSfengbojiang break;
188922ce4affSfengbojiang case O_NETGRAPH:
189022ce4affSfengbojiang bprint_uint_arg(bp, "netgraph ", cmd->arg1);
189122ce4affSfengbojiang break;
189222ce4affSfengbojiang case O_NGTEE:
189322ce4affSfengbojiang bprint_uint_arg(bp, "ngtee ", cmd->arg1);
189422ce4affSfengbojiang break;
189522ce4affSfengbojiang case O_FORWARD_IP:
189622ce4affSfengbojiang case O_FORWARD_IP6:
189722ce4affSfengbojiang print_fwd(bp, cmd);
189822ce4affSfengbojiang break;
189922ce4affSfengbojiang case O_LOG:
190022ce4affSfengbojiang if (insntod(cmd, log)->max_log > 0)
190122ce4affSfengbojiang bprintf(bp, " log logamount %d",
190222ce4affSfengbojiang insntod(cmd, log)->max_log);
190322ce4affSfengbojiang else
190422ce4affSfengbojiang bprintf(bp, " log");
190522ce4affSfengbojiang break;
190622ce4affSfengbojiang case O_ALTQ:
190722ce4affSfengbojiang #ifndef NO_ALTQ
190822ce4affSfengbojiang print_altq_cmd(bp, insntod(cmd, altq));
190922ce4affSfengbojiang #endif
191022ce4affSfengbojiang break;
191122ce4affSfengbojiang case O_TAG:
191222ce4affSfengbojiang bprint_uint_arg(bp, cmd->len & F_NOT ? " untag ":
191322ce4affSfengbojiang " tag ", cmd->arg1);
191422ce4affSfengbojiang break;
191522ce4affSfengbojiang case O_NAT:
191622ce4affSfengbojiang if (cmd->arg1 != IP_FW_NAT44_GLOBAL)
191722ce4affSfengbojiang bprint_uint_arg(bp, "nat ", cmd->arg1);
191822ce4affSfengbojiang else
191922ce4affSfengbojiang bprintf(bp, "nat global");
192022ce4affSfengbojiang break;
192122ce4affSfengbojiang case O_SETFIB:
192222ce4affSfengbojiang if (cmd->arg1 == IP_FW_TARG)
192322ce4affSfengbojiang bprint_uint_arg(bp, "setfib ", cmd->arg1);
192422ce4affSfengbojiang else
192522ce4affSfengbojiang bprintf(bp, "setfib %u", cmd->arg1 & 0x7FFF);
192622ce4affSfengbojiang break;
192722ce4affSfengbojiang case O_EXTERNAL_ACTION:
192822ce4affSfengbojiang /*
192922ce4affSfengbojiang * The external action can consists of two following
193022ce4affSfengbojiang * each other opcodes - O_EXTERNAL_ACTION and
193122ce4affSfengbojiang * O_EXTERNAL_INSTANCE. The first contains the ID of
193222ce4affSfengbojiang * name of external action. The second contains the ID
193322ce4affSfengbojiang * of name of external action instance.
193422ce4affSfengbojiang * NOTE: in case when external action has no named
193522ce4affSfengbojiang * instances support, the second opcode isn't needed.
193622ce4affSfengbojiang */
193722ce4affSfengbojiang state->eaction = cmd;
193822ce4affSfengbojiang s = object_search_ctlv(fo->tstate, cmd->arg1,
193922ce4affSfengbojiang IPFW_TLV_EACTION);
194022ce4affSfengbojiang if (match_token(rule_eactions, s) != -1)
194122ce4affSfengbojiang bprintf(bp, "%s", s);
194222ce4affSfengbojiang else
194322ce4affSfengbojiang bprintf(bp, "eaction %s", s);
194422ce4affSfengbojiang break;
194522ce4affSfengbojiang case O_EXTERNAL_INSTANCE:
194622ce4affSfengbojiang if (state->eaction == NULL)
194722ce4affSfengbojiang break;
194822ce4affSfengbojiang /*
194922ce4affSfengbojiang * XXX: we need to teach ipfw(9) to rewrite opcodes
195022ce4affSfengbojiang * in the user buffer on rule addition. When we add
195122ce4affSfengbojiang * the rule, we specify zero TLV type for
195222ce4affSfengbojiang * O_EXTERNAL_INSTANCE object. To show correct
195322ce4affSfengbojiang * rule after `ipfw add` we need to search instance
195422ce4affSfengbojiang * name with zero type. But when we do `ipfw show`
195522ce4affSfengbojiang * we calculate TLV type using IPFW_TLV_EACTION_NAME()
195622ce4affSfengbojiang * macro.
195722ce4affSfengbojiang */
195822ce4affSfengbojiang s = object_search_ctlv(fo->tstate, cmd->arg1, 0);
195922ce4affSfengbojiang if (s == NULL)
196022ce4affSfengbojiang s = object_search_ctlv(fo->tstate,
196122ce4affSfengbojiang cmd->arg1, IPFW_TLV_EACTION_NAME(
196222ce4affSfengbojiang state->eaction->arg1));
196322ce4affSfengbojiang bprintf(bp, " %s", s);
196422ce4affSfengbojiang break;
196522ce4affSfengbojiang case O_EXTERNAL_DATA:
196622ce4affSfengbojiang if (state->eaction == NULL)
196722ce4affSfengbojiang break;
196822ce4affSfengbojiang /*
196922ce4affSfengbojiang * Currently we support data formatting only for
197022ce4affSfengbojiang * external data with datalen u16. For unknown data
197122ce4affSfengbojiang * print its size in bytes.
197222ce4affSfengbojiang */
197322ce4affSfengbojiang if (cmd->len == F_INSN_SIZE(ipfw_insn))
197422ce4affSfengbojiang bprintf(bp, " %u", cmd->arg1);
197522ce4affSfengbojiang else
197622ce4affSfengbojiang bprintf(bp, " %ubytes",
197722ce4affSfengbojiang cmd->len * sizeof(uint32_t));
197822ce4affSfengbojiang break;
197922ce4affSfengbojiang case O_SETDSCP:
198022ce4affSfengbojiang if (cmd->arg1 == IP_FW_TARG) {
198122ce4affSfengbojiang bprintf(bp, "setdscp tablearg");
198222ce4affSfengbojiang break;
198322ce4affSfengbojiang }
198422ce4affSfengbojiang s = match_value(f_ipdscp, cmd->arg1 & 0x3F);
198522ce4affSfengbojiang if (s != NULL)
198622ce4affSfengbojiang bprintf(bp, "setdscp %s", s);
198722ce4affSfengbojiang else
198822ce4affSfengbojiang bprintf(bp, "setdscp %u", cmd->arg1 & 0x3F);
198922ce4affSfengbojiang break;
199022ce4affSfengbojiang case O_REASS:
199122ce4affSfengbojiang bprintf(bp, "reass");
199222ce4affSfengbojiang break;
199322ce4affSfengbojiang case O_CALLRETURN:
199422ce4affSfengbojiang if (cmd->len & F_NOT)
199522ce4affSfengbojiang bprintf(bp, "return");
199622ce4affSfengbojiang else
199722ce4affSfengbojiang bprint_uint_arg(bp, "call ", cmd->arg1);
199822ce4affSfengbojiang break;
199922ce4affSfengbojiang default:
200022ce4affSfengbojiang bprintf(bp, "** unrecognized action %d len %d ",
200122ce4affSfengbojiang cmd->opcode, cmd->len);
200222ce4affSfengbojiang }
200322ce4affSfengbojiang mark_printed(state, cmd);
200422ce4affSfengbojiang
200522ce4affSfengbojiang return (1);
200622ce4affSfengbojiang }
200722ce4affSfengbojiang
200822ce4affSfengbojiang
200922ce4affSfengbojiang static ipfw_insn *
print_action(struct buf_pr * bp,struct format_opts * fo,struct show_state * state,uint8_t opcode)201022ce4affSfengbojiang print_action(struct buf_pr *bp, struct format_opts *fo,
201122ce4affSfengbojiang struct show_state *state, uint8_t opcode)
201222ce4affSfengbojiang {
201322ce4affSfengbojiang ipfw_insn *cmd;
201422ce4affSfengbojiang int l;
201522ce4affSfengbojiang
201622ce4affSfengbojiang for (l = state->rule->cmd_len - state->rule->act_ofs,
201722ce4affSfengbojiang cmd = ACTION_PTR(state->rule); l > 0;
201822ce4affSfengbojiang l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
201922ce4affSfengbojiang if (cmd->opcode != opcode)
202022ce4affSfengbojiang continue;
202122ce4affSfengbojiang if (!print_action_instruction(bp, fo, state, cmd))
202222ce4affSfengbojiang continue;
202322ce4affSfengbojiang return (cmd);
202422ce4affSfengbojiang }
202522ce4affSfengbojiang return (NULL);
202622ce4affSfengbojiang }
202722ce4affSfengbojiang
202822ce4affSfengbojiang static void
print_proto(struct buf_pr * bp,struct format_opts * fo,struct show_state * state)202922ce4affSfengbojiang print_proto(struct buf_pr *bp, struct format_opts *fo,
203022ce4affSfengbojiang struct show_state *state)
203122ce4affSfengbojiang {
203222ce4affSfengbojiang ipfw_insn *cmd;
203322ce4affSfengbojiang int l, proto, ip4, ip6;
203422ce4affSfengbojiang
203522ce4affSfengbojiang /* Count all O_PROTO, O_IP4, O_IP6 instructions. */
203622ce4affSfengbojiang proto = ip4 = ip6 = 0;
203722ce4affSfengbojiang for (l = state->rule->act_ofs, cmd = state->rule->cmd;
203822ce4affSfengbojiang l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
203922ce4affSfengbojiang switch (cmd->opcode) {
204022ce4affSfengbojiang case O_PROTO:
204122ce4affSfengbojiang proto++;
204222ce4affSfengbojiang break;
204322ce4affSfengbojiang case O_IP4:
204422ce4affSfengbojiang ip4 = 1;
204522ce4affSfengbojiang if (cmd->len & F_OR)
204622ce4affSfengbojiang ip4++;
204722ce4affSfengbojiang break;
204822ce4affSfengbojiang case O_IP6:
204922ce4affSfengbojiang ip6 = 1;
205022ce4affSfengbojiang if (cmd->len & F_OR)
205122ce4affSfengbojiang ip6++;
205222ce4affSfengbojiang break;
205322ce4affSfengbojiang default:
205422ce4affSfengbojiang continue;
205522ce4affSfengbojiang }
205622ce4affSfengbojiang }
205722ce4affSfengbojiang if (proto == 0 && ip4 == 0 && ip6 == 0) {
205822ce4affSfengbojiang state->proto = IPPROTO_IP;
205922ce4affSfengbojiang state->flags |= HAVE_PROTO;
2060127dd473Swhl739 bprintf(bp, " ip");
206122ce4affSfengbojiang return;
2062127dd473Swhl739 }
206322ce4affSfengbojiang /* To handle the case { ip4 or ip6 }, print opcode with F_OR first */
206422ce4affSfengbojiang cmd = NULL;
206522ce4affSfengbojiang if (ip4 || ip6)
206622ce4affSfengbojiang cmd = print_opcode(bp, fo, state, ip4 > ip6 ? O_IP4: O_IP6);
206722ce4affSfengbojiang if (cmd != NULL && (cmd->len & F_OR))
206822ce4affSfengbojiang cmd = print_opcode(bp, fo, state, ip4 > ip6 ? O_IP6: O_IP4);
206922ce4affSfengbojiang if (cmd == NULL || (cmd->len & F_OR))
207022ce4affSfengbojiang for (l = proto; l > 0; l--) {
207122ce4affSfengbojiang cmd = print_opcode(bp, fo, state, O_PROTO);
207222ce4affSfengbojiang if (cmd == NULL || (cmd->len & F_OR) == 0)
207322ce4affSfengbojiang break;
2074127dd473Swhl739 }
207522ce4affSfengbojiang /* Initialize proto, it is used by print_newports() */
207622ce4affSfengbojiang state->flags |= HAVE_PROTO;
207722ce4affSfengbojiang if (state->proto == 0 && ip6 != 0)
207822ce4affSfengbojiang state->proto = IPPROTO_IPV6;
2079127dd473Swhl739 }
2080127dd473Swhl739
208122ce4affSfengbojiang static int
match_opcode(int opcode,const int opcodes[],size_t nops)208222ce4affSfengbojiang match_opcode(int opcode, const int opcodes[], size_t nops)
208322ce4affSfengbojiang {
208422ce4affSfengbojiang size_t i;
208522ce4affSfengbojiang
208622ce4affSfengbojiang for (i = 0; i < nops; i++)
208722ce4affSfengbojiang if (opcode == opcodes[i])
208822ce4affSfengbojiang return (1);
208922ce4affSfengbojiang return (0);
209022ce4affSfengbojiang }
209122ce4affSfengbojiang
209222ce4affSfengbojiang static void
print_address(struct buf_pr * bp,struct format_opts * fo,struct show_state * state,const int opcodes[],size_t nops,int portop,int flag)209322ce4affSfengbojiang print_address(struct buf_pr *bp, struct format_opts *fo,
209422ce4affSfengbojiang struct show_state *state, const int opcodes[], size_t nops, int portop,
209522ce4affSfengbojiang int flag)
209622ce4affSfengbojiang {
209722ce4affSfengbojiang ipfw_insn *cmd;
209822ce4affSfengbojiang int count, l, portcnt, pf;
209922ce4affSfengbojiang
210022ce4affSfengbojiang count = portcnt = 0;
210122ce4affSfengbojiang for (l = state->rule->act_ofs, cmd = state->rule->cmd;
210222ce4affSfengbojiang l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
210322ce4affSfengbojiang if (match_opcode(cmd->opcode, opcodes, nops))
210422ce4affSfengbojiang count++;
210522ce4affSfengbojiang else if (cmd->opcode == portop)
210622ce4affSfengbojiang portcnt++;
210722ce4affSfengbojiang }
210822ce4affSfengbojiang if (count == 0)
210922ce4affSfengbojiang bprintf(bp, " any");
211022ce4affSfengbojiang for (l = state->rule->act_ofs, cmd = state->rule->cmd;
211122ce4affSfengbojiang l > 0 && count > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
211222ce4affSfengbojiang if (!match_opcode(cmd->opcode, opcodes, nops))
211322ce4affSfengbojiang continue;
211422ce4affSfengbojiang print_instruction(bp, fo, state, cmd);
211522ce4affSfengbojiang if ((cmd->len & F_OR) == 0)
211622ce4affSfengbojiang break;
211722ce4affSfengbojiang count--;
211822ce4affSfengbojiang }
211922ce4affSfengbojiang /*
212022ce4affSfengbojiang * If several O_IP_?PORT opcodes specified, leave them to the
212122ce4affSfengbojiang * options section.
212222ce4affSfengbojiang */
212322ce4affSfengbojiang if (portcnt == 1) {
212422ce4affSfengbojiang for (l = state->rule->act_ofs, cmd = state->rule->cmd, pf = 0;
212522ce4affSfengbojiang l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
212622ce4affSfengbojiang if (cmd->opcode != portop) {
212722ce4affSfengbojiang pf = (cmd->len & F_OR);
212822ce4affSfengbojiang continue;
212922ce4affSfengbojiang }
213022ce4affSfengbojiang /* Print opcode iff it is not in OR block. */
213122ce4affSfengbojiang if (pf == 0 && (cmd->len & F_OR) == 0)
213222ce4affSfengbojiang print_instruction(bp, fo, state, cmd);
213322ce4affSfengbojiang break;
213422ce4affSfengbojiang }
213522ce4affSfengbojiang }
213622ce4affSfengbojiang state->flags |= flag;
213722ce4affSfengbojiang }
213822ce4affSfengbojiang
213922ce4affSfengbojiang static const int action_opcodes[] = {
214022ce4affSfengbojiang O_CHECK_STATE, O_ACCEPT, O_COUNT, O_DENY, O_REJECT,
214122ce4affSfengbojiang O_UNREACH6, O_SKIPTO, O_PIPE, O_QUEUE, O_DIVERT, O_TEE,
214222ce4affSfengbojiang O_NETGRAPH, O_NGTEE, O_FORWARD_IP, O_FORWARD_IP6, O_NAT,
214322ce4affSfengbojiang O_SETFIB, O_SETDSCP, O_REASS, O_CALLRETURN,
214422ce4affSfengbojiang /* keep the following opcodes at the end of the list */
214522ce4affSfengbojiang O_EXTERNAL_ACTION, O_EXTERNAL_INSTANCE, O_EXTERNAL_DATA
214622ce4affSfengbojiang };
214722ce4affSfengbojiang
214822ce4affSfengbojiang static const int modifier_opcodes[] = {
214922ce4affSfengbojiang O_LOG, O_ALTQ, O_TAG
215022ce4affSfengbojiang };
215122ce4affSfengbojiang
215222ce4affSfengbojiang static const int src_opcodes[] = {
215322ce4affSfengbojiang O_IP_SRC, O_IP_SRC_LOOKUP, O_IP_SRC_MASK, O_IP_SRC_ME,
215422ce4affSfengbojiang O_IP_SRC_SET, O_IP6_SRC, O_IP6_SRC_MASK, O_IP6_SRC_ME
215522ce4affSfengbojiang };
215622ce4affSfengbojiang
215722ce4affSfengbojiang static const int dst_opcodes[] = {
215822ce4affSfengbojiang O_IP_DST, O_IP_DST_LOOKUP, O_IP_DST_MASK, O_IP_DST_ME,
215922ce4affSfengbojiang O_IP_DST_SET, O_IP6_DST, O_IP6_DST_MASK, O_IP6_DST_ME
216022ce4affSfengbojiang };
216122ce4affSfengbojiang
2162127dd473Swhl739 static void
show_static_rule(struct cmdline_opts * co,struct format_opts * fo,struct buf_pr * bp,struct ip_fw_rule * rule,struct ip_fw_bcounter * cntr)2163127dd473Swhl739 show_static_rule(struct cmdline_opts *co, struct format_opts *fo,
2164127dd473Swhl739 struct buf_pr *bp, struct ip_fw_rule *rule, struct ip_fw_bcounter *cntr)
2165127dd473Swhl739 {
2166127dd473Swhl739 static int twidth = 0;
216722ce4affSfengbojiang struct show_state state;
216822ce4affSfengbojiang ipfw_insn *cmd;
216922ce4affSfengbojiang size_t i;
2170127dd473Swhl739
217122ce4affSfengbojiang /* Print # DISABLED or skip the rule */
2172127dd473Swhl739 if ((fo->set_mask & (1 << rule->set)) == 0) {
2173127dd473Swhl739 /* disabled mask */
2174127dd473Swhl739 if (!co->show_sets)
2175127dd473Swhl739 return;
2176127dd473Swhl739 else
2177127dd473Swhl739 bprintf(bp, "# DISABLED ");
2178127dd473Swhl739 }
217922ce4affSfengbojiang if (init_show_state(&state, rule) != 0) {
218022ce4affSfengbojiang warn("init_show_state() failed");
218122ce4affSfengbojiang return;
218222ce4affSfengbojiang }
2183127dd473Swhl739 bprintf(bp, "%05u ", rule->rulenum);
2184127dd473Swhl739
2185127dd473Swhl739 /* Print counters if enabled */
2186127dd473Swhl739 if (fo->pcwidth > 0 || fo->bcwidth > 0) {
2187127dd473Swhl739 pr_u64(bp, &cntr->pcnt, fo->pcwidth);
2188127dd473Swhl739 pr_u64(bp, &cntr->bcnt, fo->bcwidth);
2189127dd473Swhl739 }
2190127dd473Swhl739
219122ce4affSfengbojiang /* Print timestamp */
219222ce4affSfengbojiang if (co->do_time == TIMESTAMP_NUMERIC)
2193127dd473Swhl739 bprintf(bp, "%10u ", cntr->timestamp);
219422ce4affSfengbojiang else if (co->do_time == TIMESTAMP_STRING) {
2195127dd473Swhl739 char timestr[30];
2196127dd473Swhl739 time_t t = (time_t)0;
2197127dd473Swhl739
2198127dd473Swhl739 if (twidth == 0) {
2199127dd473Swhl739 strcpy(timestr, ctime(&t));
2200127dd473Swhl739 *strchr(timestr, '\n') = '\0';
2201127dd473Swhl739 twidth = strlen(timestr);
2202127dd473Swhl739 }
2203127dd473Swhl739 if (cntr->timestamp > 0) {
2204127dd473Swhl739 t = _long_to_time(cntr->timestamp);
2205127dd473Swhl739
2206127dd473Swhl739 strcpy(timestr, ctime(&t));
2207127dd473Swhl739 *strchr(timestr, '\n') = '\0';
2208127dd473Swhl739 bprintf(bp, "%s ", timestr);
2209127dd473Swhl739 } else {
2210127dd473Swhl739 bprintf(bp, "%*s ", twidth, "");
2211127dd473Swhl739 }
2212127dd473Swhl739 }
2213127dd473Swhl739
221422ce4affSfengbojiang /* Print set number */
2215127dd473Swhl739 if (co->show_sets)
2216127dd473Swhl739 bprintf(bp, "set %d ", rule->set);
2217127dd473Swhl739
221822ce4affSfengbojiang /* Print the optional "match probability" */
221922ce4affSfengbojiang cmd = print_opcode(bp, fo, &state, O_PROB);
222022ce4affSfengbojiang /* Print rule action */
222122ce4affSfengbojiang for (i = 0; i < nitems(action_opcodes); i++) {
222222ce4affSfengbojiang cmd = print_action(bp, fo, &state, action_opcodes[i]);
222322ce4affSfengbojiang if (cmd == NULL)
222422ce4affSfengbojiang continue;
222522ce4affSfengbojiang /* Handle special cases */
2226127dd473Swhl739 switch (cmd->opcode) {
2227127dd473Swhl739 case O_CHECK_STATE:
222822ce4affSfengbojiang goto end;
222922ce4affSfengbojiang case O_EXTERNAL_ACTION:
223022ce4affSfengbojiang case O_EXTERNAL_INSTANCE:
223122ce4affSfengbojiang /* External action can have several instructions */
2232127dd473Swhl739 continue;
223322ce4affSfengbojiang }
2234127dd473Swhl739 break;
2235127dd473Swhl739 }
223622ce4affSfengbojiang /* Print rule modifiers */
223722ce4affSfengbojiang for (i = 0; i < nitems(modifier_opcodes); i++)
223822ce4affSfengbojiang print_action(bp, fo, &state, modifier_opcodes[i]);
223922ce4affSfengbojiang /*
224022ce4affSfengbojiang * Print rule body
224122ce4affSfengbojiang */
224222ce4affSfengbojiang if (co->comment_only != 0)
224322ce4affSfengbojiang goto end;
224422ce4affSfengbojiang
224522ce4affSfengbojiang if (rule->flags & IPFW_RULE_JUSTOPTS) {
224622ce4affSfengbojiang state.flags |= HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP;
224722ce4affSfengbojiang goto justopts;
2248127dd473Swhl739 }
2249127dd473Swhl739
225022ce4affSfengbojiang print_proto(bp, fo, &state);
225122ce4affSfengbojiang if (co->do_compact != 0 && (rule->flags & IPFW_RULE_NOOPT))
225222ce4affSfengbojiang goto justopts;
2253127dd473Swhl739
225422ce4affSfengbojiang /* Print source */
2255127dd473Swhl739 bprintf(bp, " from");
225622ce4affSfengbojiang print_address(bp, fo, &state, src_opcodes, nitems(src_opcodes),
225722ce4affSfengbojiang O_IP_SRCPORT, HAVE_SRCIP);
2258127dd473Swhl739
225922ce4affSfengbojiang /* Print destination */
2260127dd473Swhl739 bprintf(bp, " to");
226122ce4affSfengbojiang print_address(bp, fo, &state, dst_opcodes, nitems(dst_opcodes),
226222ce4affSfengbojiang O_IP_DSTPORT, HAVE_DSTIP);
2263127dd473Swhl739
226422ce4affSfengbojiang justopts:
226522ce4affSfengbojiang /* Print the rest of options */
226622ce4affSfengbojiang while (print_opcode(bp, fo, &state, -1))
226722ce4affSfengbojiang ;
226822ce4affSfengbojiang end:
226922ce4affSfengbojiang /* Print comment at the end */
227022ce4affSfengbojiang cmd = print_opcode(bp, fo, &state, O_NOP);
227122ce4affSfengbojiang if (co->comment_only != 0 && cmd == NULL)
227222ce4affSfengbojiang bprintf(bp, " // ...");
2273127dd473Swhl739 bprintf(bp, "\n");
227422ce4affSfengbojiang free_show_state(&state);
2275127dd473Swhl739 }
2276127dd473Swhl739
2277127dd473Swhl739 static void
show_dyn_state(struct cmdline_opts * co,struct format_opts * fo,struct buf_pr * bp,ipfw_dyn_rule * d)2278127dd473Swhl739 show_dyn_state(struct cmdline_opts *co, struct format_opts *fo,
2279127dd473Swhl739 struct buf_pr *bp, ipfw_dyn_rule *d)
2280127dd473Swhl739 {
2281127dd473Swhl739 struct protoent *pe;
2282127dd473Swhl739 struct in_addr a;
2283127dd473Swhl739 uint16_t rulenum;
2284127dd473Swhl739 char buf[INET6_ADDRSTRLEN];
2285127dd473Swhl739
228622ce4affSfengbojiang if (d->expire == 0 && d->dyn_type != O_LIMIT_PARENT)
2287127dd473Swhl739 return;
228822ce4affSfengbojiang
2289127dd473Swhl739 bcopy(&d->rule, &rulenum, sizeof(rulenum));
2290127dd473Swhl739 bprintf(bp, "%05d", rulenum);
2291127dd473Swhl739 if (fo->pcwidth > 0 || fo->bcwidth > 0) {
2292127dd473Swhl739 bprintf(bp, " ");
2293127dd473Swhl739 pr_u64(bp, &d->pcnt, fo->pcwidth);
2294127dd473Swhl739 pr_u64(bp, &d->bcnt, fo->bcwidth);
2295127dd473Swhl739 bprintf(bp, "(%ds)", d->expire);
2296127dd473Swhl739 }
2297127dd473Swhl739 switch (d->dyn_type) {
2298127dd473Swhl739 case O_LIMIT_PARENT:
2299127dd473Swhl739 bprintf(bp, " PARENT %d", d->count);
2300127dd473Swhl739 break;
2301127dd473Swhl739 case O_LIMIT:
2302127dd473Swhl739 bprintf(bp, " LIMIT");
2303127dd473Swhl739 break;
2304127dd473Swhl739 case O_KEEP_STATE: /* bidir, no mask */
2305127dd473Swhl739 bprintf(bp, " STATE");
2306127dd473Swhl739 break;
2307127dd473Swhl739 }
2308127dd473Swhl739
2309127dd473Swhl739 if ((pe = getprotobynumber(d->id.proto)) != NULL)
2310127dd473Swhl739 bprintf(bp, " %s", pe->p_name);
2311127dd473Swhl739 else
2312127dd473Swhl739 bprintf(bp, " proto %u", d->id.proto);
2313127dd473Swhl739
2314127dd473Swhl739 if (d->id.addr_type == 4) {
2315127dd473Swhl739 a.s_addr = htonl(d->id.src_ip);
2316127dd473Swhl739 bprintf(bp, " %s %d", inet_ntoa(a), d->id.src_port);
2317127dd473Swhl739
2318127dd473Swhl739 a.s_addr = htonl(d->id.dst_ip);
2319127dd473Swhl739 bprintf(bp, " <-> %s %d", inet_ntoa(a), d->id.dst_port);
2320127dd473Swhl739 } else if (d->id.addr_type == 6) {
2321*d4a07e70Sfengbojiang bprintf(bp, " %s %d", inet_ntop(AF_INET6_LINUX, &d->id.src_ip6, buf,
2322127dd473Swhl739 sizeof(buf)), d->id.src_port);
2323*d4a07e70Sfengbojiang bprintf(bp, " <-> %s %d", inet_ntop(AF_INET6_LINUX, &d->id.dst_ip6,
2324127dd473Swhl739 buf, sizeof(buf)), d->id.dst_port);
2325127dd473Swhl739 } else
232622ce4affSfengbojiang bprintf(bp, " UNKNOWN <-> UNKNOWN");
232722ce4affSfengbojiang if (d->kidx != 0)
232822ce4affSfengbojiang bprintf(bp, " :%s", object_search_ctlv(fo->tstate,
232922ce4affSfengbojiang d->kidx, IPFW_TLV_STATE_NAME));
233022ce4affSfengbojiang
233122ce4affSfengbojiang #define BOTH_SYN (TH_SYN | (TH_SYN << 8))
233222ce4affSfengbojiang #define BOTH_FIN (TH_FIN | (TH_FIN << 8))
233322ce4affSfengbojiang if (co->verbose) {
233422ce4affSfengbojiang bprintf(bp, " state 0x%08x%s", d->state,
233522ce4affSfengbojiang d->state ? " ": ",");
233622ce4affSfengbojiang if (d->state & IPFW_DYN_ORPHANED)
233722ce4affSfengbojiang bprintf(bp, "ORPHANED,");
233822ce4affSfengbojiang if ((d->state & BOTH_SYN) == BOTH_SYN)
233922ce4affSfengbojiang bprintf(bp, "BOTH_SYN,");
234022ce4affSfengbojiang else {
234122ce4affSfengbojiang if (d->state & TH_SYN)
234222ce4affSfengbojiang bprintf(bp, "F_SYN,");
234322ce4affSfengbojiang if (d->state & (TH_SYN << 8))
234422ce4affSfengbojiang bprintf(bp, "R_SYN,");
234522ce4affSfengbojiang }
234622ce4affSfengbojiang if ((d->state & BOTH_FIN) == BOTH_FIN)
234722ce4affSfengbojiang bprintf(bp, "BOTH_FIN,");
234822ce4affSfengbojiang else {
234922ce4affSfengbojiang if (d->state & TH_FIN)
235022ce4affSfengbojiang bprintf(bp, "F_FIN,");
235122ce4affSfengbojiang if (d->state & (TH_FIN << 8))
235222ce4affSfengbojiang bprintf(bp, "R_FIN,");
235322ce4affSfengbojiang }
235422ce4affSfengbojiang bprintf(bp, " f_ack 0x%x, r_ack 0x%x", d->ack_fwd,
235522ce4affSfengbojiang d->ack_rev);
235622ce4affSfengbojiang }
2357127dd473Swhl739 }
2358127dd473Swhl739
2359127dd473Swhl739 static int
do_range_cmd(int cmd,ipfw_range_tlv * rt)2360127dd473Swhl739 do_range_cmd(int cmd, ipfw_range_tlv *rt)
2361127dd473Swhl739 {
2362127dd473Swhl739 ipfw_range_header rh;
2363127dd473Swhl739 size_t sz;
2364127dd473Swhl739
2365127dd473Swhl739 memset(&rh, 0, sizeof(rh));
2366127dd473Swhl739 memcpy(&rh.range, rt, sizeof(*rt));
2367127dd473Swhl739 rh.range.head.length = sizeof(*rt);
2368127dd473Swhl739 rh.range.head.type = IPFW_TLV_RANGE;
2369127dd473Swhl739 sz = sizeof(rh);
2370127dd473Swhl739
2371127dd473Swhl739 if (do_get3(cmd, &rh.opheader, &sz) != 0)
2372127dd473Swhl739 return (-1);
2373127dd473Swhl739 /* Save number of matched objects */
2374127dd473Swhl739 rt->new_set = rh.range.new_set;
2375127dd473Swhl739 return (0);
2376127dd473Swhl739 }
2377127dd473Swhl739
2378127dd473Swhl739 /*
2379127dd473Swhl739 * This one handles all set-related commands
2380127dd473Swhl739 * ipfw set { show | enable | disable }
2381127dd473Swhl739 * ipfw set swap X Y
2382127dd473Swhl739 * ipfw set move X to Y
2383127dd473Swhl739 * ipfw set move rule X to Y
2384127dd473Swhl739 */
2385127dd473Swhl739 void
ipfw_sets_handler(char * av[])2386127dd473Swhl739 ipfw_sets_handler(char *av[])
2387127dd473Swhl739 {
238822ce4affSfengbojiang ipfw_range_tlv rt;
238922ce4affSfengbojiang const char *msg;
239022ce4affSfengbojiang size_t size;
2391127dd473Swhl739 uint32_t masks[2];
2392127dd473Swhl739 int i;
239322ce4affSfengbojiang uint16_t rulenum;
239422ce4affSfengbojiang uint8_t cmd;
2395127dd473Swhl739
2396127dd473Swhl739 av++;
2397127dd473Swhl739 memset(&rt, 0, sizeof(rt));
2398127dd473Swhl739
2399127dd473Swhl739 if (av[0] == NULL)
2400127dd473Swhl739 errx(EX_USAGE, "set needs command");
2401127dd473Swhl739 if (_substrcmp(*av, "show") == 0) {
2402127dd473Swhl739 struct format_opts fo;
2403127dd473Swhl739 ipfw_cfg_lheader *cfg;
2404127dd473Swhl739
2405127dd473Swhl739 memset(&fo, 0, sizeof(fo));
240622ce4affSfengbojiang if (ipfw_get_config(&g_co, &fo, &cfg, &size) != 0)
2407127dd473Swhl739 err(EX_OSERR, "requesting config failed");
2408127dd473Swhl739
2409127dd473Swhl739 for (i = 0, msg = "disable"; i < RESVD_SET; i++)
2410127dd473Swhl739 if ((cfg->set_mask & (1<<i)) == 0) {
2411127dd473Swhl739 printf("%s %d", msg, i);
2412127dd473Swhl739 msg = "";
2413127dd473Swhl739 }
2414127dd473Swhl739 msg = (cfg->set_mask != (uint32_t)-1) ? " enable" : "enable";
2415127dd473Swhl739 for (i = 0; i < RESVD_SET; i++)
2416127dd473Swhl739 if ((cfg->set_mask & (1<<i)) != 0) {
2417127dd473Swhl739 printf("%s %d", msg, i);
2418127dd473Swhl739 msg = "";
2419127dd473Swhl739 }
2420127dd473Swhl739 printf("\n");
2421127dd473Swhl739 free(cfg);
2422127dd473Swhl739 } else if (_substrcmp(*av, "swap") == 0) {
2423127dd473Swhl739 av++;
2424127dd473Swhl739 if ( av[0] == NULL || av[1] == NULL )
2425127dd473Swhl739 errx(EX_USAGE, "set swap needs 2 set numbers\n");
2426127dd473Swhl739 rt.set = atoi(av[0]);
2427127dd473Swhl739 rt.new_set = atoi(av[1]);
2428127dd473Swhl739 if (!isdigit(*(av[0])) || rt.set > RESVD_SET)
2429127dd473Swhl739 errx(EX_DATAERR, "invalid set number %s\n", av[0]);
2430127dd473Swhl739 if (!isdigit(*(av[1])) || rt.new_set > RESVD_SET)
2431127dd473Swhl739 errx(EX_DATAERR, "invalid set number %s\n", av[1]);
2432127dd473Swhl739 i = do_range_cmd(IP_FW_SET_SWAP, &rt);
2433127dd473Swhl739 } else if (_substrcmp(*av, "move") == 0) {
2434127dd473Swhl739 av++;
2435127dd473Swhl739 if (av[0] && _substrcmp(*av, "rule") == 0) {
2436127dd473Swhl739 rt.flags = IPFW_RCFLAG_RANGE; /* move rules to new set */
2437127dd473Swhl739 cmd = IP_FW_XMOVE;
2438127dd473Swhl739 av++;
2439127dd473Swhl739 } else
2440127dd473Swhl739 cmd = IP_FW_SET_MOVE; /* Move set to new one */
2441127dd473Swhl739 if (av[0] == NULL || av[1] == NULL || av[2] == NULL ||
2442127dd473Swhl739 av[3] != NULL || _substrcmp(av[1], "to") != 0)
2443127dd473Swhl739 errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
2444127dd473Swhl739 rulenum = atoi(av[0]);
2445127dd473Swhl739 rt.new_set = atoi(av[2]);
2446127dd473Swhl739 if (cmd == IP_FW_XMOVE) {
2447127dd473Swhl739 rt.start_rule = rulenum;
2448127dd473Swhl739 rt.end_rule = rulenum;
2449127dd473Swhl739 } else
2450127dd473Swhl739 rt.set = rulenum;
2451127dd473Swhl739 rt.new_set = atoi(av[2]);
2452127dd473Swhl739 if (!isdigit(*(av[0])) || (cmd == 3 && rt.set > RESVD_SET) ||
2453127dd473Swhl739 (cmd == 2 && rt.start_rule == IPFW_DEFAULT_RULE) )
2454127dd473Swhl739 errx(EX_DATAERR, "invalid source number %s\n", av[0]);
2455127dd473Swhl739 if (!isdigit(*(av[2])) || rt.new_set > RESVD_SET)
2456127dd473Swhl739 errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
2457127dd473Swhl739 i = do_range_cmd(cmd, &rt);
2458127dd473Swhl739 if (i < 0)
2459127dd473Swhl739 err(EX_OSERR, "failed to move %s",
2460127dd473Swhl739 cmd == IP_FW_SET_MOVE ? "set": "rule");
2461127dd473Swhl739 } else if (_substrcmp(*av, "disable") == 0 ||
2462127dd473Swhl739 _substrcmp(*av, "enable") == 0 ) {
2463127dd473Swhl739 int which = _substrcmp(*av, "enable") == 0 ? 1 : 0;
2464127dd473Swhl739
2465127dd473Swhl739 av++;
2466127dd473Swhl739 masks[0] = masks[1] = 0;
2467127dd473Swhl739
2468127dd473Swhl739 while (av[0]) {
2469127dd473Swhl739 if (isdigit(**av)) {
2470127dd473Swhl739 i = atoi(*av);
2471127dd473Swhl739 if (i < 0 || i > RESVD_SET)
2472127dd473Swhl739 errx(EX_DATAERR,
2473127dd473Swhl739 "invalid set number %d\n", i);
2474127dd473Swhl739 masks[which] |= (1<<i);
2475127dd473Swhl739 } else if (_substrcmp(*av, "disable") == 0)
2476127dd473Swhl739 which = 0;
2477127dd473Swhl739 else if (_substrcmp(*av, "enable") == 0)
2478127dd473Swhl739 which = 1;
2479127dd473Swhl739 else
2480127dd473Swhl739 errx(EX_DATAERR,
2481127dd473Swhl739 "invalid set command %s\n", *av);
2482127dd473Swhl739 av++;
2483127dd473Swhl739 }
2484127dd473Swhl739 if ( (masks[0] & masks[1]) != 0 )
2485127dd473Swhl739 errx(EX_DATAERR,
2486127dd473Swhl739 "cannot enable and disable the same set\n");
2487127dd473Swhl739
2488127dd473Swhl739 rt.set = masks[0];
2489127dd473Swhl739 rt.new_set = masks[1];
2490127dd473Swhl739 i = do_range_cmd(IP_FW_SET_ENABLE, &rt);
2491127dd473Swhl739 if (i)
2492127dd473Swhl739 warn("set enable/disable: setsockopt(IP_FW_SET_ENABLE)");
2493127dd473Swhl739 } else
2494127dd473Swhl739 errx(EX_USAGE, "invalid set command %s\n", *av);
2495127dd473Swhl739 }
2496127dd473Swhl739
2497127dd473Swhl739 void
ipfw_sysctl_handler(char * av[],int which)2498127dd473Swhl739 ipfw_sysctl_handler(char *av[], int which)
2499127dd473Swhl739 {
2500127dd473Swhl739 av++;
2501127dd473Swhl739
2502127dd473Swhl739 if (av[0] == NULL) {
2503127dd473Swhl739 warnx("missing keyword to enable/disable\n");
2504127dd473Swhl739 } else if (_substrcmp(*av, "firewall") == 0) {
2505127dd473Swhl739 sysctlbyname("net.inet.ip.fw.enable", NULL, 0,
2506127dd473Swhl739 &which, sizeof(which));
2507127dd473Swhl739 sysctlbyname("net.inet6.ip6.fw.enable", NULL, 0,
2508127dd473Swhl739 &which, sizeof(which));
2509127dd473Swhl739 } else if (_substrcmp(*av, "one_pass") == 0) {
2510127dd473Swhl739 sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0,
2511127dd473Swhl739 &which, sizeof(which));
2512127dd473Swhl739 } else if (_substrcmp(*av, "debug") == 0) {
2513127dd473Swhl739 sysctlbyname("net.inet.ip.fw.debug", NULL, 0,
2514127dd473Swhl739 &which, sizeof(which));
2515127dd473Swhl739 } else if (_substrcmp(*av, "verbose") == 0) {
2516127dd473Swhl739 sysctlbyname("net.inet.ip.fw.verbose", NULL, 0,
2517127dd473Swhl739 &which, sizeof(which));
2518127dd473Swhl739 } else if (_substrcmp(*av, "dyn_keepalive") == 0) {
2519127dd473Swhl739 sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0,
2520127dd473Swhl739 &which, sizeof(which));
2521127dd473Swhl739 #ifndef NO_ALTQ
2522127dd473Swhl739 } else if (_substrcmp(*av, "altq") == 0) {
2523127dd473Swhl739 altq_set_enabled(which);
2524127dd473Swhl739 #endif
2525127dd473Swhl739 } else {
2526127dd473Swhl739 warnx("unrecognize enable/disable keyword: %s\n", *av);
2527127dd473Swhl739 }
2528127dd473Swhl739 }
2529127dd473Swhl739
2530127dd473Swhl739 typedef void state_cb(struct cmdline_opts *co, struct format_opts *fo,
2531127dd473Swhl739 void *arg, void *state);
2532127dd473Swhl739
2533127dd473Swhl739 static void
prepare_format_dyn(struct cmdline_opts * co,struct format_opts * fo,void * arg __unused,void * _state)2534127dd473Swhl739 prepare_format_dyn(struct cmdline_opts *co, struct format_opts *fo,
253522ce4affSfengbojiang void *arg __unused, void *_state)
2536127dd473Swhl739 {
2537127dd473Swhl739 ipfw_dyn_rule *d;
2538127dd473Swhl739 int width;
2539127dd473Swhl739 uint8_t set;
2540127dd473Swhl739
2541127dd473Swhl739 d = (ipfw_dyn_rule *)_state;
2542127dd473Swhl739 /* Count _ALL_ states */
2543127dd473Swhl739 fo->dcnt++;
2544127dd473Swhl739
2545127dd473Swhl739 if (fo->show_counters == 0)
2546127dd473Swhl739 return;
2547127dd473Swhl739
2548127dd473Swhl739 if (co->use_set) {
2549127dd473Swhl739 /* skip states from another set */
2550127dd473Swhl739 bcopy((char *)&d->rule + sizeof(uint16_t), &set,
2551127dd473Swhl739 sizeof(uint8_t));
2552127dd473Swhl739 if (set != co->use_set - 1)
2553127dd473Swhl739 return;
2554127dd473Swhl739 }
2555127dd473Swhl739
2556127dd473Swhl739 width = pr_u64(NULL, &d->pcnt, 0);
2557127dd473Swhl739 if (width > fo->pcwidth)
2558127dd473Swhl739 fo->pcwidth = width;
2559127dd473Swhl739
2560127dd473Swhl739 width = pr_u64(NULL, &d->bcnt, 0);
2561127dd473Swhl739 if (width > fo->bcwidth)
2562127dd473Swhl739 fo->bcwidth = width;
2563127dd473Swhl739 }
2564127dd473Swhl739
2565127dd473Swhl739 static int
foreach_state(struct cmdline_opts * co,struct format_opts * fo,caddr_t base,size_t sz,state_cb dyn_bc,void * dyn_arg)2566127dd473Swhl739 foreach_state(struct cmdline_opts *co, struct format_opts *fo,
2567127dd473Swhl739 caddr_t base, size_t sz, state_cb dyn_bc, void *dyn_arg)
2568127dd473Swhl739 {
2569127dd473Swhl739 int ttype;
2570127dd473Swhl739 state_cb *fptr;
2571127dd473Swhl739 void *farg;
2572127dd473Swhl739 ipfw_obj_tlv *tlv;
2573127dd473Swhl739 ipfw_obj_ctlv *ctlv;
2574127dd473Swhl739
2575127dd473Swhl739 fptr = NULL;
2576127dd473Swhl739 ttype = 0;
2577127dd473Swhl739
2578127dd473Swhl739 while (sz > 0) {
2579127dd473Swhl739 ctlv = (ipfw_obj_ctlv *)base;
2580127dd473Swhl739 switch (ctlv->head.type) {
2581127dd473Swhl739 case IPFW_TLV_DYNSTATE_LIST:
2582127dd473Swhl739 base += sizeof(*ctlv);
2583127dd473Swhl739 sz -= sizeof(*ctlv);
2584127dd473Swhl739 ttype = IPFW_TLV_DYN_ENT;
2585127dd473Swhl739 fptr = dyn_bc;
2586127dd473Swhl739 farg = dyn_arg;
2587127dd473Swhl739 break;
2588127dd473Swhl739 default:
2589127dd473Swhl739 return (sz);
2590127dd473Swhl739 }
2591127dd473Swhl739
2592127dd473Swhl739 while (sz > 0) {
2593127dd473Swhl739 tlv = (ipfw_obj_tlv *)base;
2594127dd473Swhl739 if (tlv->type != ttype)
2595127dd473Swhl739 break;
2596127dd473Swhl739
2597127dd473Swhl739 fptr(co, fo, farg, tlv + 1);
2598127dd473Swhl739 sz -= tlv->length;
2599127dd473Swhl739 base += tlv->length;
2600127dd473Swhl739 }
2601127dd473Swhl739 }
2602127dd473Swhl739
2603127dd473Swhl739 return (sz);
2604127dd473Swhl739 }
2605127dd473Swhl739
2606127dd473Swhl739 static void
prepare_format_opts(struct cmdline_opts * co,struct format_opts * fo,ipfw_obj_tlv * rtlv,int rcnt,caddr_t dynbase,size_t dynsz)2607127dd473Swhl739 prepare_format_opts(struct cmdline_opts *co, struct format_opts *fo,
2608127dd473Swhl739 ipfw_obj_tlv *rtlv, int rcnt, caddr_t dynbase, size_t dynsz)
2609127dd473Swhl739 {
2610127dd473Swhl739 int bcwidth, pcwidth, width;
2611127dd473Swhl739 int n;
2612127dd473Swhl739 struct ip_fw_bcounter *cntr;
2613127dd473Swhl739 struct ip_fw_rule *r;
2614127dd473Swhl739
2615127dd473Swhl739 bcwidth = 0;
2616127dd473Swhl739 pcwidth = 0;
2617127dd473Swhl739 if (fo->show_counters != 0) {
2618127dd473Swhl739 for (n = 0; n < rcnt; n++,
2619127dd473Swhl739 rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) {
2620127dd473Swhl739 cntr = (struct ip_fw_bcounter *)(rtlv + 1);
2621127dd473Swhl739 r = (struct ip_fw_rule *)((caddr_t)cntr + cntr->size);
2622127dd473Swhl739 /* skip rules from another set */
2623127dd473Swhl739 if (co->use_set && r->set != co->use_set - 1)
2624127dd473Swhl739 continue;
2625127dd473Swhl739
2626127dd473Swhl739 /* packet counter */
2627127dd473Swhl739 width = pr_u64(NULL, &cntr->pcnt, 0);
2628127dd473Swhl739 if (width > pcwidth)
2629127dd473Swhl739 pcwidth = width;
2630127dd473Swhl739
2631127dd473Swhl739 /* byte counter */
2632127dd473Swhl739 width = pr_u64(NULL, &cntr->bcnt, 0);
2633127dd473Swhl739 if (width > bcwidth)
2634127dd473Swhl739 bcwidth = width;
2635127dd473Swhl739 }
2636127dd473Swhl739 }
2637127dd473Swhl739 fo->bcwidth = bcwidth;
2638127dd473Swhl739 fo->pcwidth = pcwidth;
2639127dd473Swhl739
2640127dd473Swhl739 fo->dcnt = 0;
2641127dd473Swhl739 if (co->do_dynamic && dynsz > 0)
2642127dd473Swhl739 foreach_state(co, fo, dynbase, dynsz, prepare_format_dyn, NULL);
2643127dd473Swhl739 }
2644127dd473Swhl739
2645127dd473Swhl739 static int
list_static_range(struct cmdline_opts * co,struct format_opts * fo,struct buf_pr * bp,ipfw_obj_tlv * rtlv,int rcnt)2646127dd473Swhl739 list_static_range(struct cmdline_opts *co, struct format_opts *fo,
2647127dd473Swhl739 struct buf_pr *bp, ipfw_obj_tlv *rtlv, int rcnt)
2648127dd473Swhl739 {
2649127dd473Swhl739 int n, seen;
2650127dd473Swhl739 struct ip_fw_rule *r;
2651127dd473Swhl739 struct ip_fw_bcounter *cntr;
2652127dd473Swhl739 int c = 0;
2653127dd473Swhl739
2654127dd473Swhl739 for (n = seen = 0; n < rcnt; n++,
2655127dd473Swhl739 rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) {
2656127dd473Swhl739
2657127dd473Swhl739 if ((fo->show_counters | fo->show_time) != 0) {
2658127dd473Swhl739 cntr = (struct ip_fw_bcounter *)(rtlv + 1);
2659127dd473Swhl739 r = (struct ip_fw_rule *)((caddr_t)cntr + cntr->size);
2660127dd473Swhl739 } else {
2661127dd473Swhl739 cntr = NULL;
2662127dd473Swhl739 r = (struct ip_fw_rule *)(rtlv + 1);
2663127dd473Swhl739 }
2664127dd473Swhl739 if (r->rulenum > fo->last)
2665127dd473Swhl739 break;
2666127dd473Swhl739 if (co->use_set && r->set != co->use_set - 1)
2667127dd473Swhl739 continue;
2668127dd473Swhl739 if (r->rulenum >= fo->first && r->rulenum <= fo->last) {
2669127dd473Swhl739 show_static_rule(co, fo, bp, r, cntr);
2670127dd473Swhl739 printf("%s", bp->buf);
2671127dd473Swhl739 c += rtlv->length;
2672127dd473Swhl739 bp_flush(bp);
2673127dd473Swhl739 seen++;
2674127dd473Swhl739 }
2675127dd473Swhl739 }
2676127dd473Swhl739
2677127dd473Swhl739 return (seen);
2678127dd473Swhl739 }
2679127dd473Swhl739
2680127dd473Swhl739 static void
list_dyn_state(struct cmdline_opts * co,struct format_opts * fo,void * _arg,void * _state)2681127dd473Swhl739 list_dyn_state(struct cmdline_opts *co, struct format_opts *fo,
2682127dd473Swhl739 void *_arg, void *_state)
2683127dd473Swhl739 {
2684127dd473Swhl739 uint16_t rulenum;
2685127dd473Swhl739 uint8_t set;
2686127dd473Swhl739 ipfw_dyn_rule *d;
2687127dd473Swhl739 struct buf_pr *bp;
2688127dd473Swhl739
2689127dd473Swhl739 d = (ipfw_dyn_rule *)_state;
2690127dd473Swhl739 bp = (struct buf_pr *)_arg;
2691127dd473Swhl739
2692127dd473Swhl739 bcopy(&d->rule, &rulenum, sizeof(rulenum));
2693127dd473Swhl739 if (rulenum > fo->last)
2694127dd473Swhl739 return;
2695127dd473Swhl739 if (co->use_set) {
2696127dd473Swhl739 bcopy((char *)&d->rule + sizeof(uint16_t),
2697127dd473Swhl739 &set, sizeof(uint8_t));
2698127dd473Swhl739 if (set != co->use_set - 1)
2699127dd473Swhl739 return;
2700127dd473Swhl739 }
2701127dd473Swhl739 if (rulenum >= fo->first) {
2702127dd473Swhl739 show_dyn_state(co, fo, bp, d);
2703127dd473Swhl739 printf("%s\n", bp->buf);
2704127dd473Swhl739 bp_flush(bp);
2705127dd473Swhl739 }
2706127dd473Swhl739 }
2707127dd473Swhl739
2708127dd473Swhl739 static int
list_dyn_range(struct cmdline_opts * co,struct format_opts * fo,struct buf_pr * bp,caddr_t base,size_t sz)2709127dd473Swhl739 list_dyn_range(struct cmdline_opts *co, struct format_opts *fo,
2710127dd473Swhl739 struct buf_pr *bp, caddr_t base, size_t sz)
2711127dd473Swhl739 {
2712127dd473Swhl739
2713127dd473Swhl739 sz = foreach_state(co, fo, base, sz, list_dyn_state, bp);
2714127dd473Swhl739 return (sz);
2715127dd473Swhl739 }
2716127dd473Swhl739
2717127dd473Swhl739 void
ipfw_list(int ac,char * av[],int show_counters)2718127dd473Swhl739 ipfw_list(int ac, char *av[], int show_counters)
2719127dd473Swhl739 {
2720127dd473Swhl739 ipfw_cfg_lheader *cfg;
2721127dd473Swhl739 struct format_opts sfo;
2722127dd473Swhl739 size_t sz;
2723127dd473Swhl739 int error;
2724127dd473Swhl739 int lac;
2725127dd473Swhl739 char **lav;
2726127dd473Swhl739 uint32_t rnum;
2727127dd473Swhl739 char *endptr;
2728127dd473Swhl739
272922ce4affSfengbojiang if (g_co.test_only) {
2730127dd473Swhl739 fprintf(stderr, "Testing only, list disabled\n");
2731127dd473Swhl739 return;
2732127dd473Swhl739 }
273322ce4affSfengbojiang if (g_co.do_pipe) {
2734*d4a07e70Sfengbojiang #ifdef DUMMYNET
2735127dd473Swhl739 dummynet_list(ac, av, show_counters);
2736*d4a07e70Sfengbojiang #else
2737*d4a07e70Sfengbojiang fprintf(stderr, "dummynet_list not supported\n");
2738*d4a07e70Sfengbojiang #endif
2739127dd473Swhl739 return;
2740127dd473Swhl739 }
2741127dd473Swhl739
2742127dd473Swhl739 ac--;
2743127dd473Swhl739 av++;
2744127dd473Swhl739 memset(&sfo, 0, sizeof(sfo));
2745127dd473Swhl739
2746127dd473Swhl739 /* Determine rule range to request */
2747127dd473Swhl739 if (ac > 0) {
2748127dd473Swhl739 for (lac = ac, lav = av; lac != 0; lac--) {
2749127dd473Swhl739 rnum = strtoul(*lav++, &endptr, 10);
2750127dd473Swhl739 if (sfo.first == 0 || rnum < sfo.first)
2751127dd473Swhl739 sfo.first = rnum;
2752127dd473Swhl739
2753127dd473Swhl739 if (*endptr == '-')
2754127dd473Swhl739 rnum = strtoul(endptr + 1, &endptr, 10);
2755127dd473Swhl739 if (sfo.last == 0 || rnum > sfo.last)
2756127dd473Swhl739 sfo.last = rnum;
2757127dd473Swhl739 }
2758127dd473Swhl739 }
2759127dd473Swhl739
2760127dd473Swhl739 /* get configuraion from kernel */
2761127dd473Swhl739 cfg = NULL;
2762127dd473Swhl739 sfo.show_counters = show_counters;
276322ce4affSfengbojiang sfo.show_time = g_co.do_time;
276422ce4affSfengbojiang if (g_co.do_dynamic != 2)
276522ce4affSfengbojiang sfo.flags |= IPFW_CFG_GET_STATIC;
276622ce4affSfengbojiang if (g_co.do_dynamic != 0)
2767127dd473Swhl739 sfo.flags |= IPFW_CFG_GET_STATES;
2768127dd473Swhl739 if ((sfo.show_counters | sfo.show_time) != 0)
2769127dd473Swhl739 sfo.flags |= IPFW_CFG_GET_COUNTERS;
277022ce4affSfengbojiang if (ipfw_get_config(&g_co, &sfo, &cfg, &sz) != 0)
2771127dd473Swhl739 err(EX_OSERR, "retrieving config failed");
2772127dd473Swhl739
277322ce4affSfengbojiang error = ipfw_show_config(&g_co, &sfo, cfg, sz, ac, av);
2774127dd473Swhl739
2775127dd473Swhl739 free(cfg);
2776127dd473Swhl739
2777127dd473Swhl739 if (error != EX_OK)
2778127dd473Swhl739 exit(error);
2779127dd473Swhl739 }
2780127dd473Swhl739
2781127dd473Swhl739 static int
ipfw_show_config(struct cmdline_opts * co,struct format_opts * fo,ipfw_cfg_lheader * cfg,size_t sz,int ac,char * av[])2782127dd473Swhl739 ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo,
2783127dd473Swhl739 ipfw_cfg_lheader *cfg, size_t sz, int ac, char *av[])
2784127dd473Swhl739 {
2785127dd473Swhl739 caddr_t dynbase;
2786127dd473Swhl739 size_t dynsz;
2787127dd473Swhl739 int rcnt;
2788127dd473Swhl739 int exitval = EX_OK;
2789127dd473Swhl739 int lac;
2790127dd473Swhl739 char **lav;
2791127dd473Swhl739 char *endptr;
2792127dd473Swhl739 size_t readsz;
2793127dd473Swhl739 struct buf_pr bp;
2794127dd473Swhl739 ipfw_obj_ctlv *ctlv, *tstate;
2795127dd473Swhl739 ipfw_obj_tlv *rbase;
2796127dd473Swhl739
2797127dd473Swhl739 /*
2798127dd473Swhl739 * Handle tablenames TLV first, if any
2799127dd473Swhl739 */
2800127dd473Swhl739 tstate = NULL;
2801127dd473Swhl739 rbase = NULL;
2802127dd473Swhl739 dynbase = NULL;
2803127dd473Swhl739 dynsz = 0;
2804127dd473Swhl739 readsz = sizeof(*cfg);
2805127dd473Swhl739 rcnt = 0;
2806127dd473Swhl739
2807127dd473Swhl739 fo->set_mask = cfg->set_mask;
2808127dd473Swhl739
2809127dd473Swhl739 ctlv = (ipfw_obj_ctlv *)(cfg + 1);
2810127dd473Swhl739 if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) {
2811127dd473Swhl739 object_sort_ctlv(ctlv);
2812127dd473Swhl739 fo->tstate = ctlv;
2813127dd473Swhl739 readsz += ctlv->head.length;
281422ce4affSfengbojiang ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
2815127dd473Swhl739 }
2816127dd473Swhl739
281722ce4affSfengbojiang if (cfg->flags & IPFW_CFG_GET_STATIC) {
281822ce4affSfengbojiang /* We've requested static rules */
2819127dd473Swhl739 if (ctlv->head.type == IPFW_TLV_RULE_LIST) {
2820127dd473Swhl739 rbase = (ipfw_obj_tlv *)(ctlv + 1);
2821127dd473Swhl739 rcnt = ctlv->count;
2822127dd473Swhl739 readsz += ctlv->head.length;
2823127dd473Swhl739 ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv +
2824127dd473Swhl739 ctlv->head.length);
2825127dd473Swhl739 }
2826127dd473Swhl739 }
2827127dd473Swhl739
2828127dd473Swhl739 if ((cfg->flags & IPFW_CFG_GET_STATES) && (readsz != sz)) {
2829127dd473Swhl739 /* We may have some dynamic states */
2830127dd473Swhl739 dynsz = sz - readsz;
2831127dd473Swhl739 /* Skip empty header */
2832127dd473Swhl739 if (dynsz != sizeof(ipfw_obj_ctlv))
2833127dd473Swhl739 dynbase = (caddr_t)ctlv;
2834127dd473Swhl739 else
2835127dd473Swhl739 dynsz = 0;
2836127dd473Swhl739 }
2837127dd473Swhl739
2838127dd473Swhl739 prepare_format_opts(co, fo, rbase, rcnt, dynbase, dynsz);
2839127dd473Swhl739 bp_alloc(&bp, 4096);
2840127dd473Swhl739
2841127dd473Swhl739 /* if no rule numbers were specified, list all rules */
2842127dd473Swhl739 if (ac == 0) {
2843127dd473Swhl739 fo->first = 0;
2844127dd473Swhl739 fo->last = IPFW_DEFAULT_RULE;
284522ce4affSfengbojiang if (cfg->flags & IPFW_CFG_GET_STATIC)
2846127dd473Swhl739 list_static_range(co, fo, &bp, rbase, rcnt);
2847127dd473Swhl739
2848127dd473Swhl739 if (co->do_dynamic && dynsz > 0) {
284922ce4affSfengbojiang printf("## Dynamic rules (%d %zu):\n", fo->dcnt,
285022ce4affSfengbojiang dynsz);
2851127dd473Swhl739 list_dyn_range(co, fo, &bp, dynbase, dynsz);
2852127dd473Swhl739 }
2853127dd473Swhl739
2854127dd473Swhl739 bp_free(&bp);
2855127dd473Swhl739 return (EX_OK);
2856127dd473Swhl739 }
2857127dd473Swhl739
2858127dd473Swhl739 /* display specific rules requested on command line */
2859127dd473Swhl739 for (lac = ac, lav = av; lac != 0; lac--) {
2860127dd473Swhl739 /* convert command line rule # */
2861127dd473Swhl739 fo->last = fo->first = strtoul(*lav++, &endptr, 10);
2862127dd473Swhl739 if (*endptr == '-')
2863127dd473Swhl739 fo->last = strtoul(endptr + 1, &endptr, 10);
2864127dd473Swhl739 if (*endptr) {
2865127dd473Swhl739 exitval = EX_USAGE;
2866127dd473Swhl739 warnx("invalid rule number: %s", *(lav - 1));
2867127dd473Swhl739 continue;
2868127dd473Swhl739 }
2869127dd473Swhl739
287022ce4affSfengbojiang if ((cfg->flags & IPFW_CFG_GET_STATIC) == 0)
287122ce4affSfengbojiang continue;
287222ce4affSfengbojiang
2873127dd473Swhl739 if (list_static_range(co, fo, &bp, rbase, rcnt) == 0) {
2874127dd473Swhl739 /* give precedence to other error(s) */
2875127dd473Swhl739 if (exitval == EX_OK)
2876127dd473Swhl739 exitval = EX_UNAVAILABLE;
2877127dd473Swhl739 if (fo->first == fo->last)
2878127dd473Swhl739 warnx("rule %u does not exist", fo->first);
2879127dd473Swhl739 else
2880127dd473Swhl739 warnx("no rules in range %u-%u",
2881127dd473Swhl739 fo->first, fo->last);
2882127dd473Swhl739 }
2883127dd473Swhl739 }
2884127dd473Swhl739
2885127dd473Swhl739 if (co->do_dynamic && dynsz > 0) {
2886127dd473Swhl739 printf("## Dynamic rules:\n");
2887127dd473Swhl739 for (lac = ac, lav = av; lac != 0; lac--) {
2888127dd473Swhl739 fo->last = fo->first = strtoul(*lav++, &endptr, 10);
2889127dd473Swhl739 if (*endptr == '-')
2890127dd473Swhl739 fo->last = strtoul(endptr+1, &endptr, 10);
2891127dd473Swhl739 if (*endptr)
2892127dd473Swhl739 /* already warned */
2893127dd473Swhl739 continue;
2894127dd473Swhl739 list_dyn_range(co, fo, &bp, dynbase, dynsz);
2895127dd473Swhl739 }
2896127dd473Swhl739 }
2897127dd473Swhl739
2898127dd473Swhl739 bp_free(&bp);
2899127dd473Swhl739 return (exitval);
2900127dd473Swhl739 }
2901127dd473Swhl739
2902127dd473Swhl739
2903127dd473Swhl739 /*
2904127dd473Swhl739 * Retrieves current ipfw configuration of given type
2905127dd473Swhl739 * and stores its pointer to @pcfg.
2906127dd473Swhl739 *
2907127dd473Swhl739 * Caller is responsible for freeing @pcfg.
2908127dd473Swhl739 *
2909127dd473Swhl739 * Returns 0 on success.
2910127dd473Swhl739 */
2911127dd473Swhl739
2912127dd473Swhl739 static int
ipfw_get_config(struct cmdline_opts * co,struct format_opts * fo,ipfw_cfg_lheader ** pcfg,size_t * psize)2913127dd473Swhl739 ipfw_get_config(struct cmdline_opts *co, struct format_opts *fo,
2914127dd473Swhl739 ipfw_cfg_lheader **pcfg, size_t *psize)
2915127dd473Swhl739 {
2916127dd473Swhl739 ipfw_cfg_lheader *cfg;
2917127dd473Swhl739 size_t sz;
2918127dd473Swhl739 int i;
2919127dd473Swhl739
2920127dd473Swhl739
2921127dd473Swhl739 if (co->test_only != 0) {
2922127dd473Swhl739 fprintf(stderr, "Testing only, list disabled\n");
2923127dd473Swhl739 return (0);
2924127dd473Swhl739 }
2925127dd473Swhl739
2926127dd473Swhl739 /* Start with some data size */
2927127dd473Swhl739 sz = 4096;
2928127dd473Swhl739 cfg = NULL;
2929127dd473Swhl739
2930127dd473Swhl739 for (i = 0; i < 16; i++) {
2931127dd473Swhl739 if (cfg != NULL)
2932127dd473Swhl739 free(cfg);
2933127dd473Swhl739 if ((cfg = calloc(1, sz)) == NULL)
2934127dd473Swhl739 return (ENOMEM);
2935127dd473Swhl739
2936127dd473Swhl739 cfg->flags = fo->flags;
2937127dd473Swhl739 cfg->start_rule = fo->first;
2938127dd473Swhl739 cfg->end_rule = fo->last;
2939127dd473Swhl739
2940127dd473Swhl739 if (do_get3(IP_FW_XGET, &cfg->opheader, &sz) != 0) {
2941127dd473Swhl739 if (errno != ENOMEM) {
2942127dd473Swhl739 free(cfg);
2943127dd473Swhl739 return (errno);
2944127dd473Swhl739 }
2945127dd473Swhl739
2946127dd473Swhl739 /* Buffer size is not enough. Try to increase */
2947127dd473Swhl739 sz = sz * 2;
2948127dd473Swhl739 if (sz < cfg->size)
2949127dd473Swhl739 sz = cfg->size;
2950127dd473Swhl739 continue;
2951127dd473Swhl739 }
2952127dd473Swhl739
2953127dd473Swhl739 *pcfg = cfg;
2954127dd473Swhl739 *psize = sz;
2955127dd473Swhl739 return (0);
2956127dd473Swhl739 }
2957127dd473Swhl739
2958127dd473Swhl739 free(cfg);
2959127dd473Swhl739 return (ENOMEM);
2960127dd473Swhl739 }
2961127dd473Swhl739
2962127dd473Swhl739 static int
lookup_host(char * host,struct in_addr * ipaddr)2963127dd473Swhl739 lookup_host (char *host, struct in_addr *ipaddr)
2964127dd473Swhl739 {
2965127dd473Swhl739 struct hostent *he;
2966127dd473Swhl739
2967127dd473Swhl739 if (!inet_aton(host, ipaddr)) {
2968*d4a07e70Sfengbojiang #ifndef FSTACK
2969127dd473Swhl739 if ((he = gethostbyname(host)) == NULL)
2970127dd473Swhl739 return(-1);
2971127dd473Swhl739 *ipaddr = *(struct in_addr *)he->h_addr_list[0];
2972*d4a07e70Sfengbojiang #else
2973*d4a07e70Sfengbojiang return (-1);
2974*d4a07e70Sfengbojiang #endif
2975127dd473Swhl739 }
2976127dd473Swhl739 return(0);
2977127dd473Swhl739 }
2978127dd473Swhl739
2979127dd473Swhl739 struct tidx {
2980127dd473Swhl739 ipfw_obj_ntlv *idx;
2981127dd473Swhl739 uint32_t count;
2982127dd473Swhl739 uint32_t size;
2983127dd473Swhl739 uint16_t counter;
2984127dd473Swhl739 uint8_t set;
2985127dd473Swhl739 };
2986127dd473Swhl739
2987127dd473Swhl739 int
ipfw_check_object_name(const char * name)2988127dd473Swhl739 ipfw_check_object_name(const char *name)
2989127dd473Swhl739 {
2990127dd473Swhl739 int c, i, l;
2991127dd473Swhl739
2992127dd473Swhl739 /*
2993127dd473Swhl739 * Check that name is null-terminated and contains
2994127dd473Swhl739 * valid symbols only. Valid mask is:
2995127dd473Swhl739 * [a-zA-Z0-9\-_\.]{1,63}
2996127dd473Swhl739 */
2997127dd473Swhl739 l = strlen(name);
2998127dd473Swhl739 if (l == 0 || l >= 64)
2999127dd473Swhl739 return (EINVAL);
3000127dd473Swhl739 for (i = 0; i < l; i++) {
3001127dd473Swhl739 c = name[i];
3002127dd473Swhl739 if (isalpha(c) || isdigit(c) || c == '_' ||
3003127dd473Swhl739 c == '-' || c == '.')
3004127dd473Swhl739 continue;
3005127dd473Swhl739 return (EINVAL);
3006127dd473Swhl739 }
3007127dd473Swhl739 return (0);
3008127dd473Swhl739 }
3009127dd473Swhl739
301022ce4affSfengbojiang static const char *default_state_name = "default";
301122ce4affSfengbojiang
301222ce4affSfengbojiang static int
state_check_name(const char * name)301322ce4affSfengbojiang state_check_name(const char *name)
301422ce4affSfengbojiang {
301522ce4affSfengbojiang
301622ce4affSfengbojiang if (ipfw_check_object_name(name) != 0)
301722ce4affSfengbojiang return (EINVAL);
301822ce4affSfengbojiang if (strcmp(name, "any") == 0)
301922ce4affSfengbojiang return (EINVAL);
302022ce4affSfengbojiang return (0);
302122ce4affSfengbojiang }
302222ce4affSfengbojiang
3023127dd473Swhl739 static int
eaction_check_name(const char * name)3024127dd473Swhl739 eaction_check_name(const char *name)
3025127dd473Swhl739 {
3026127dd473Swhl739
3027127dd473Swhl739 if (ipfw_check_object_name(name) != 0)
3028127dd473Swhl739 return (EINVAL);
3029127dd473Swhl739 /* Restrict some 'special' names */
3030127dd473Swhl739 if (match_token(rule_actions, name) != -1 &&
3031127dd473Swhl739 match_token(rule_action_params, name) != -1)
3032127dd473Swhl739 return (EINVAL);
3033127dd473Swhl739 return (0);
3034127dd473Swhl739 }
3035127dd473Swhl739
3036127dd473Swhl739 static uint16_t
pack_object(struct tidx * tstate,const char * name,int otype)303722ce4affSfengbojiang pack_object(struct tidx *tstate, const char *name, int otype)
3038127dd473Swhl739 {
3039127dd473Swhl739 ipfw_obj_ntlv *ntlv;
304022ce4affSfengbojiang uint32_t i;
3041127dd473Swhl739
3042127dd473Swhl739 for (i = 0; i < tstate->count; i++) {
3043127dd473Swhl739 if (strcmp(tstate->idx[i].name, name) != 0)
3044127dd473Swhl739 continue;
3045127dd473Swhl739 if (tstate->idx[i].set != tstate->set)
3046127dd473Swhl739 continue;
3047127dd473Swhl739 if (tstate->idx[i].head.type != otype)
3048127dd473Swhl739 continue;
3049127dd473Swhl739
3050127dd473Swhl739 return (tstate->idx[i].idx);
3051127dd473Swhl739 }
3052127dd473Swhl739
3053127dd473Swhl739 if (tstate->count + 1 > tstate->size) {
3054127dd473Swhl739 tstate->size += 4;
3055127dd473Swhl739 tstate->idx = realloc(tstate->idx, tstate->size *
3056127dd473Swhl739 sizeof(ipfw_obj_ntlv));
3057127dd473Swhl739 if (tstate->idx == NULL)
3058127dd473Swhl739 return (0);
3059127dd473Swhl739 }
3060127dd473Swhl739
3061127dd473Swhl739 ntlv = &tstate->idx[i];
3062127dd473Swhl739 memset(ntlv, 0, sizeof(ipfw_obj_ntlv));
3063127dd473Swhl739 strlcpy(ntlv->name, name, sizeof(ntlv->name));
3064127dd473Swhl739 ntlv->head.type = otype;
3065127dd473Swhl739 ntlv->head.length = sizeof(ipfw_obj_ntlv);
3066127dd473Swhl739 ntlv->set = tstate->set;
3067127dd473Swhl739 ntlv->idx = ++tstate->counter;
3068127dd473Swhl739 tstate->count++;
3069127dd473Swhl739
3070127dd473Swhl739 return (ntlv->idx);
3071127dd473Swhl739 }
3072127dd473Swhl739
3073127dd473Swhl739 static uint16_t
pack_table(struct tidx * tstate,const char * name)307422ce4affSfengbojiang pack_table(struct tidx *tstate, const char *name)
3075127dd473Swhl739 {
3076127dd473Swhl739
3077127dd473Swhl739 if (table_check_name(name) != 0)
3078127dd473Swhl739 return (0);
3079127dd473Swhl739
3080127dd473Swhl739 return (pack_object(tstate, name, IPFW_TLV_TBL_NAME));
3081127dd473Swhl739 }
3082127dd473Swhl739
308322ce4affSfengbojiang void
fill_table(struct _ipfw_insn * cmd,char * av,uint8_t opcode,struct tidx * tstate)308422ce4affSfengbojiang fill_table(struct _ipfw_insn *cmd, char *av, uint8_t opcode,
308522ce4affSfengbojiang struct tidx *tstate)
3086127dd473Swhl739 {
3087127dd473Swhl739 uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
3088127dd473Swhl739 uint16_t uidx;
3089127dd473Swhl739 char *p;
3090127dd473Swhl739
3091127dd473Swhl739 if ((p = strchr(av + 6, ')')) == NULL)
3092127dd473Swhl739 errx(EX_DATAERR, "forgotten parenthesis: '%s'", av);
3093127dd473Swhl739 *p = '\0';
3094127dd473Swhl739 p = strchr(av + 6, ',');
3095127dd473Swhl739 if (p)
3096127dd473Swhl739 *p++ = '\0';
3097127dd473Swhl739
3098127dd473Swhl739 if ((uidx = pack_table(tstate, av + 6)) == 0)
3099127dd473Swhl739 errx(EX_DATAERR, "Invalid table name: %s", av + 6);
3100127dd473Swhl739
3101127dd473Swhl739 cmd->opcode = opcode;
3102127dd473Swhl739 cmd->arg1 = uidx;
3103127dd473Swhl739 if (p) {
3104127dd473Swhl739 cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
3105127dd473Swhl739 d[0] = strtoul(p, NULL, 0);
3106127dd473Swhl739 } else
3107127dd473Swhl739 cmd->len |= F_INSN_SIZE(ipfw_insn);
3108127dd473Swhl739 }
3109127dd473Swhl739
3110127dd473Swhl739
3111127dd473Swhl739 /*
3112127dd473Swhl739 * fills the addr and mask fields in the instruction as appropriate from av.
3113127dd473Swhl739 * Update length as appropriate.
3114127dd473Swhl739 * The following formats are allowed:
3115127dd473Swhl739 * me returns O_IP_*_ME
3116127dd473Swhl739 * 1.2.3.4 single IP address
3117127dd473Swhl739 * 1.2.3.4:5.6.7.8 address:mask
3118127dd473Swhl739 * 1.2.3.4/24 address/mask
3119127dd473Swhl739 * 1.2.3.4/26{1,6,5,4,23} set of addresses in a subnet
3120127dd473Swhl739 * We can have multiple comma-separated address/mask entries.
3121127dd473Swhl739 */
3122127dd473Swhl739 static void
fill_ip(ipfw_insn_ip * cmd,char * av,int cblen,struct tidx * tstate)3123127dd473Swhl739 fill_ip(ipfw_insn_ip *cmd, char *av, int cblen, struct tidx *tstate)
3124127dd473Swhl739 {
3125127dd473Swhl739 int len = 0;
3126127dd473Swhl739 uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
3127127dd473Swhl739
3128127dd473Swhl739 cmd->o.len &= ~F_LEN_MASK; /* zero len */
3129127dd473Swhl739
3130127dd473Swhl739 if (_substrcmp(av, "any") == 0)
3131127dd473Swhl739 return;
3132127dd473Swhl739
3133127dd473Swhl739 if (_substrcmp(av, "me") == 0) {
3134127dd473Swhl739 cmd->o.len |= F_INSN_SIZE(ipfw_insn);
3135127dd473Swhl739 return;
3136127dd473Swhl739 }
3137127dd473Swhl739
3138127dd473Swhl739 if (strncmp(av, "table(", 6) == 0) {
3139127dd473Swhl739 fill_table(&cmd->o, av, O_IP_DST_LOOKUP, tstate);
3140127dd473Swhl739 return;
3141127dd473Swhl739 }
3142127dd473Swhl739
3143127dd473Swhl739 while (av) {
3144127dd473Swhl739 /*
3145127dd473Swhl739 * After the address we can have '/' or ':' indicating a mask,
3146127dd473Swhl739 * ',' indicating another address follows, '{' indicating a
3147127dd473Swhl739 * set of addresses of unspecified size.
3148127dd473Swhl739 */
3149127dd473Swhl739 char *t = NULL, *p = strpbrk(av, "/:,{");
3150127dd473Swhl739 int masklen;
3151127dd473Swhl739 char md, nd = '\0';
3152127dd473Swhl739
315322ce4affSfengbojiang CHECK_LENGTH(cblen, (int)F_INSN_SIZE(ipfw_insn) + 2 + len);
3154127dd473Swhl739
3155127dd473Swhl739 if (p) {
3156127dd473Swhl739 md = *p;
3157127dd473Swhl739 *p++ = '\0';
3158127dd473Swhl739 if ((t = strpbrk(p, ",{")) != NULL) {
3159127dd473Swhl739 nd = *t;
3160127dd473Swhl739 *t = '\0';
3161127dd473Swhl739 }
3162127dd473Swhl739 } else
3163127dd473Swhl739 md = '\0';
3164127dd473Swhl739
3165127dd473Swhl739 if (lookup_host(av, (struct in_addr *)&d[0]) != 0)
3166127dd473Swhl739 errx(EX_NOHOST, "hostname ``%s'' unknown", av);
3167127dd473Swhl739 switch (md) {
3168127dd473Swhl739 case ':':
3169127dd473Swhl739 if (!inet_aton(p, (struct in_addr *)&d[1]))
3170127dd473Swhl739 errx(EX_DATAERR, "bad netmask ``%s''", p);
3171127dd473Swhl739 break;
3172127dd473Swhl739 case '/':
3173127dd473Swhl739 masklen = atoi(p);
3174127dd473Swhl739 if (masklen == 0)
3175127dd473Swhl739 d[1] = htonl(0U); /* mask */
3176127dd473Swhl739 else if (masklen > 32)
3177127dd473Swhl739 errx(EX_DATAERR, "bad width ``%s''", p);
3178127dd473Swhl739 else
3179127dd473Swhl739 d[1] = htonl(~0U << (32 - masklen));
3180127dd473Swhl739 break;
3181127dd473Swhl739 case '{': /* no mask, assume /24 and put back the '{' */
3182127dd473Swhl739 d[1] = htonl(~0U << (32 - 24));
3183127dd473Swhl739 *(--p) = md;
3184127dd473Swhl739 break;
3185127dd473Swhl739
3186127dd473Swhl739 case ',': /* single address plus continuation */
3187127dd473Swhl739 *(--p) = md;
3188127dd473Swhl739 /* FALLTHROUGH */
3189127dd473Swhl739 case 0: /* initialization value */
3190127dd473Swhl739 default:
3191127dd473Swhl739 d[1] = htonl(~0U); /* force /32 */
3192127dd473Swhl739 break;
3193127dd473Swhl739 }
3194127dd473Swhl739 d[0] &= d[1]; /* mask base address with mask */
3195127dd473Swhl739 if (t)
3196127dd473Swhl739 *t = nd;
3197127dd473Swhl739 /* find next separator */
3198127dd473Swhl739 if (p)
3199127dd473Swhl739 p = strpbrk(p, ",{");
3200127dd473Swhl739 if (p && *p == '{') {
3201127dd473Swhl739 /*
3202127dd473Swhl739 * We have a set of addresses. They are stored as follows:
3203127dd473Swhl739 * arg1 is the set size (powers of 2, 2..256)
3204127dd473Swhl739 * addr is the base address IN HOST FORMAT
3205127dd473Swhl739 * mask.. is an array of arg1 bits (rounded up to
3206127dd473Swhl739 * the next multiple of 32) with bits set
3207127dd473Swhl739 * for each host in the map.
3208127dd473Swhl739 */
3209127dd473Swhl739 uint32_t *map = (uint32_t *)&cmd->mask;
3210127dd473Swhl739 int low, high;
3211127dd473Swhl739 int i = contigmask((uint8_t *)&(d[1]), 32);
3212127dd473Swhl739
3213127dd473Swhl739 if (len > 0)
3214127dd473Swhl739 errx(EX_DATAERR, "address set cannot be in a list");
3215127dd473Swhl739 if (i < 24 || i > 31)
3216127dd473Swhl739 errx(EX_DATAERR, "invalid set with mask %d\n", i);
3217127dd473Swhl739 cmd->o.arg1 = 1<<(32-i); /* map length */
3218127dd473Swhl739 d[0] = ntohl(d[0]); /* base addr in host format */
3219127dd473Swhl739 cmd->o.opcode = O_IP_DST_SET; /* default */
3220127dd473Swhl739 cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32;
3221127dd473Swhl739 for (i = 0; i < (cmd->o.arg1+31)/32 ; i++)
3222127dd473Swhl739 map[i] = 0; /* clear map */
3223127dd473Swhl739
3224127dd473Swhl739 av = p + 1;
3225127dd473Swhl739 low = d[0] & 0xff;
3226127dd473Swhl739 high = low + cmd->o.arg1 - 1;
3227127dd473Swhl739 /*
3228127dd473Swhl739 * Here, i stores the previous value when we specify a range
3229127dd473Swhl739 * of addresses within a mask, e.g. 45-63. i = -1 means we
3230127dd473Swhl739 * have no previous value.
3231127dd473Swhl739 */
3232127dd473Swhl739 i = -1; /* previous value in a range */
3233127dd473Swhl739 while (isdigit(*av)) {
3234127dd473Swhl739 char *s;
3235127dd473Swhl739 int a = strtol(av, &s, 0);
3236127dd473Swhl739
3237127dd473Swhl739 if (s == av) { /* no parameter */
3238127dd473Swhl739 if (*av != '}')
3239127dd473Swhl739 errx(EX_DATAERR, "set not closed\n");
3240127dd473Swhl739 if (i != -1)
3241127dd473Swhl739 errx(EX_DATAERR, "incomplete range %d-", i);
3242127dd473Swhl739 break;
3243127dd473Swhl739 }
3244127dd473Swhl739 if (a < low || a > high)
3245127dd473Swhl739 errx(EX_DATAERR, "addr %d out of range [%d-%d]\n",
3246127dd473Swhl739 a, low, high);
3247127dd473Swhl739 a -= low;
3248127dd473Swhl739 if (i == -1) /* no previous in range */
3249127dd473Swhl739 i = a;
3250127dd473Swhl739 else { /* check that range is valid */
3251127dd473Swhl739 if (i > a)
3252127dd473Swhl739 errx(EX_DATAERR, "invalid range %d-%d",
3253127dd473Swhl739 i+low, a+low);
3254127dd473Swhl739 if (*s == '-')
3255127dd473Swhl739 errx(EX_DATAERR, "double '-' in range");
3256127dd473Swhl739 }
3257127dd473Swhl739 for (; i <= a; i++)
3258127dd473Swhl739 map[i/32] |= 1<<(i & 31);
3259127dd473Swhl739 i = -1;
3260127dd473Swhl739 if (*s == '-')
3261127dd473Swhl739 i = a;
3262127dd473Swhl739 else if (*s == '}')
3263127dd473Swhl739 break;
3264127dd473Swhl739 av = s+1;
3265127dd473Swhl739 }
3266127dd473Swhl739 return;
3267127dd473Swhl739 }
3268127dd473Swhl739 av = p;
3269127dd473Swhl739 if (av) /* then *av must be a ',' */
3270127dd473Swhl739 av++;
3271127dd473Swhl739
3272127dd473Swhl739 /* Check this entry */
3273127dd473Swhl739 if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */
3274127dd473Swhl739 /*
3275127dd473Swhl739 * 'any' turns the entire list into a NOP.
3276127dd473Swhl739 * 'not any' never matches, so it is removed from the
3277127dd473Swhl739 * list unless it is the only item, in which case we
3278127dd473Swhl739 * report an error.
3279127dd473Swhl739 */
3280127dd473Swhl739 if (cmd->o.len & F_NOT) { /* "not any" never matches */
3281127dd473Swhl739 if (av == NULL && len == 0) /* only this entry */
3282127dd473Swhl739 errx(EX_DATAERR, "not any never matches");
3283127dd473Swhl739 }
3284127dd473Swhl739 /* else do nothing and skip this entry */
3285127dd473Swhl739 return;
3286127dd473Swhl739 }
3287127dd473Swhl739 /* A single IP can be stored in an optimized format */
3288127dd473Swhl739 if (d[1] == (uint32_t)~0 && av == NULL && len == 0) {
3289127dd473Swhl739 cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
3290127dd473Swhl739 return;
3291127dd473Swhl739 }
3292127dd473Swhl739 len += 2; /* two words... */
3293127dd473Swhl739 d += 2;
3294127dd473Swhl739 } /* end while */
3295127dd473Swhl739 if (len + 1 > F_LEN_MASK)
3296127dd473Swhl739 errx(EX_DATAERR, "address list too long");
3297127dd473Swhl739 cmd->o.len |= len+1;
3298127dd473Swhl739 }
3299127dd473Swhl739
3300127dd473Swhl739
3301127dd473Swhl739 /* n2mask sets n bits of the mask */
3302127dd473Swhl739 void
n2mask(struct in6_addr * mask,int n)3303127dd473Swhl739 n2mask(struct in6_addr *mask, int n)
3304127dd473Swhl739 {
3305127dd473Swhl739 static int minimask[9] =
3306127dd473Swhl739 { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
3307127dd473Swhl739 u_char *p;
3308127dd473Swhl739
3309127dd473Swhl739 memset(mask, 0, sizeof(struct in6_addr));
3310127dd473Swhl739 p = (u_char *) mask;
3311127dd473Swhl739 for (; n > 0; p++, n -= 8) {
3312127dd473Swhl739 if (n >= 8)
3313127dd473Swhl739 *p = 0xff;
3314127dd473Swhl739 else
3315127dd473Swhl739 *p = minimask[n];
3316127dd473Swhl739 }
3317127dd473Swhl739 return;
3318127dd473Swhl739 }
3319127dd473Swhl739
3320127dd473Swhl739 static void
fill_flags_cmd(ipfw_insn * cmd,enum ipfw_opcodes opcode,struct _s_x * flags,char * p)3321127dd473Swhl739 fill_flags_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode,
3322127dd473Swhl739 struct _s_x *flags, char *p)
3323127dd473Swhl739 {
3324127dd473Swhl739 char *e;
3325127dd473Swhl739 uint32_t set = 0, clear = 0;
3326127dd473Swhl739
3327127dd473Swhl739 if (fill_flags(flags, p, &e, &set, &clear) != 0)
3328127dd473Swhl739 errx(EX_DATAERR, "invalid flag %s", e);
3329127dd473Swhl739
3330127dd473Swhl739 cmd->opcode = opcode;
3331127dd473Swhl739 cmd->len = (cmd->len & (F_NOT | F_OR)) | 1;
3332127dd473Swhl739 cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
3333127dd473Swhl739 }
3334127dd473Swhl739
3335127dd473Swhl739
3336127dd473Swhl739 void
ipfw_delete(char * av[])3337127dd473Swhl739 ipfw_delete(char *av[])
3338127dd473Swhl739 {
333922ce4affSfengbojiang ipfw_range_tlv rt;
334022ce4affSfengbojiang char *sep;
3341127dd473Swhl739 int i, j;
3342127dd473Swhl739 int exitval = EX_OK;
3343127dd473Swhl739 int do_set = 0;
3344127dd473Swhl739
3345127dd473Swhl739 av++;
3346127dd473Swhl739 NEED1("missing rule specification");
3347127dd473Swhl739 if ( *av && _substrcmp(*av, "set") == 0) {
3348127dd473Swhl739 /* Do not allow using the following syntax:
3349127dd473Swhl739 * ipfw set N delete set M
3350127dd473Swhl739 */
335122ce4affSfengbojiang if (g_co.use_set)
3352127dd473Swhl739 errx(EX_DATAERR, "invalid syntax");
3353127dd473Swhl739 do_set = 1; /* delete set */
3354127dd473Swhl739 av++;
3355127dd473Swhl739 }
3356127dd473Swhl739
3357127dd473Swhl739 /* Rule number */
3358127dd473Swhl739 while (*av && isdigit(**av)) {
3359127dd473Swhl739 i = strtol(*av, &sep, 10);
3360127dd473Swhl739 j = i;
3361127dd473Swhl739 if (*sep== '-')
3362127dd473Swhl739 j = strtol(sep + 1, NULL, 10);
3363127dd473Swhl739 av++;
336422ce4affSfengbojiang if (g_co.do_nat) {
336522ce4affSfengbojiang exitval = ipfw_delete_nat(i);
336622ce4affSfengbojiang } else if (g_co.do_pipe) {
3367*d4a07e70Sfengbojiang #ifdef DUMMYNET
336822ce4affSfengbojiang exitval = ipfw_delete_pipe(g_co.do_pipe, i);
3369*d4a07e70Sfengbojiang #else
3370*d4a07e70Sfengbojiang exitval = EX_UNAVAILABLE;
3371*d4a07e70Sfengbojiang #endif
3372127dd473Swhl739 } else {
337322ce4affSfengbojiang memset(&rt, 0, sizeof(rt));
3374127dd473Swhl739 if (do_set != 0) {
3375127dd473Swhl739 rt.set = i & 31;
3376127dd473Swhl739 rt.flags = IPFW_RCFLAG_SET;
3377127dd473Swhl739 } else {
3378127dd473Swhl739 rt.start_rule = i & 0xffff;
3379127dd473Swhl739 rt.end_rule = j & 0xffff;
3380127dd473Swhl739 if (rt.start_rule == 0 && rt.end_rule == 0)
3381127dd473Swhl739 rt.flags |= IPFW_RCFLAG_ALL;
3382127dd473Swhl739 else
3383127dd473Swhl739 rt.flags |= IPFW_RCFLAG_RANGE;
338422ce4affSfengbojiang if (g_co.use_set != 0) {
338522ce4affSfengbojiang rt.set = g_co.use_set - 1;
3386127dd473Swhl739 rt.flags |= IPFW_RCFLAG_SET;
3387127dd473Swhl739 }
3388127dd473Swhl739 }
338922ce4affSfengbojiang if (g_co.do_dynamic == 2)
339022ce4affSfengbojiang rt.flags |= IPFW_RCFLAG_DYNAMIC;
3391127dd473Swhl739 i = do_range_cmd(IP_FW_XDEL, &rt);
3392127dd473Swhl739 if (i != 0) {
3393127dd473Swhl739 exitval = EX_UNAVAILABLE;
339422ce4affSfengbojiang if (g_co.do_quiet)
339522ce4affSfengbojiang continue;
3396127dd473Swhl739 warn("rule %u: setsockopt(IP_FW_XDEL)",
3397127dd473Swhl739 rt.start_rule);
339822ce4affSfengbojiang } else if (rt.new_set == 0 && do_set == 0 &&
339922ce4affSfengbojiang g_co.do_dynamic != 2) {
3400127dd473Swhl739 exitval = EX_UNAVAILABLE;
340122ce4affSfengbojiang if (g_co.do_quiet)
340222ce4affSfengbojiang continue;
3403127dd473Swhl739 if (rt.start_rule != rt.end_rule)
3404127dd473Swhl739 warnx("no rules rules in %u-%u range",
3405127dd473Swhl739 rt.start_rule, rt.end_rule);
3406127dd473Swhl739 else
3407127dd473Swhl739 warnx("rule %u not found",
3408127dd473Swhl739 rt.start_rule);
3409127dd473Swhl739 }
3410127dd473Swhl739 }
3411127dd473Swhl739 }
341222ce4affSfengbojiang if (exitval != EX_OK && g_co.do_force == 0)
3413127dd473Swhl739 exit(exitval);
3414127dd473Swhl739 }
3415127dd473Swhl739
3416127dd473Swhl739
3417127dd473Swhl739 /*
3418127dd473Swhl739 * fill the interface structure. We do not check the name as we can
3419127dd473Swhl739 * create interfaces dynamically, so checking them at insert time
3420127dd473Swhl739 * makes relatively little sense.
3421127dd473Swhl739 * Interface names containing '*', '?', or '[' are assumed to be shell
3422127dd473Swhl739 * patterns which match interfaces.
3423127dd473Swhl739 */
3424127dd473Swhl739 static void
fill_iface(ipfw_insn_if * cmd,char * arg,int cblen,struct tidx * tstate)3425127dd473Swhl739 fill_iface(ipfw_insn_if *cmd, char *arg, int cblen, struct tidx *tstate)
3426127dd473Swhl739 {
3427127dd473Swhl739 char *p;
3428127dd473Swhl739 uint16_t uidx;
3429127dd473Swhl739
3430127dd473Swhl739 cmd->name[0] = '\0';
3431127dd473Swhl739 cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
3432127dd473Swhl739
3433127dd473Swhl739 CHECK_CMDLEN;
3434127dd473Swhl739
3435127dd473Swhl739 /* Parse the interface or address */
3436127dd473Swhl739 if (strcmp(arg, "any") == 0)
3437127dd473Swhl739 cmd->o.len = 0; /* effectively ignore this command */
3438127dd473Swhl739 else if (strncmp(arg, "table(", 6) == 0) {
3439127dd473Swhl739 if ((p = strchr(arg + 6, ')')) == NULL)
3440127dd473Swhl739 errx(EX_DATAERR, "forgotten parenthesis: '%s'", arg);
3441127dd473Swhl739 *p = '\0';
3442127dd473Swhl739 p = strchr(arg + 6, ',');
3443127dd473Swhl739 if (p)
3444127dd473Swhl739 *p++ = '\0';
3445127dd473Swhl739 if ((uidx = pack_table(tstate, arg + 6)) == 0)
3446127dd473Swhl739 errx(EX_DATAERR, "Invalid table name: %s", arg + 6);
3447127dd473Swhl739
3448127dd473Swhl739 cmd->name[0] = '\1'; /* Special value indicating table */
3449127dd473Swhl739 cmd->p.kidx = uidx;
3450127dd473Swhl739 } else if (!isdigit(*arg)) {
3451127dd473Swhl739 strlcpy(cmd->name, arg, sizeof(cmd->name));
3452127dd473Swhl739 cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
3453127dd473Swhl739 } else if (!inet_aton(arg, &cmd->p.ip))
3454127dd473Swhl739 errx(EX_DATAERR, "bad ip address ``%s''", arg);
3455127dd473Swhl739 }
3456127dd473Swhl739
3457127dd473Swhl739 static void
get_mac_addr_mask(const char * p,uint8_t * addr,uint8_t * mask)3458127dd473Swhl739 get_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask)
3459127dd473Swhl739 {
3460127dd473Swhl739 int i;
3461127dd473Swhl739 size_t l;
3462127dd473Swhl739 char *ap, *ptr, *optr;
3463127dd473Swhl739 struct ether_addr *mac;
3464127dd473Swhl739 const char *macset = "0123456789abcdefABCDEF:";
3465127dd473Swhl739
3466127dd473Swhl739 if (strcmp(p, "any") == 0) {
3467127dd473Swhl739 for (i = 0; i < ETHER_ADDR_LEN; i++)
3468127dd473Swhl739 addr[i] = mask[i] = 0;
3469127dd473Swhl739 return;
3470127dd473Swhl739 }
3471127dd473Swhl739
3472127dd473Swhl739 optr = ptr = strdup(p);
3473127dd473Swhl739 if ((ap = strsep(&ptr, "&/")) != NULL && *ap != 0) {
3474127dd473Swhl739 l = strlen(ap);
3475127dd473Swhl739 if (strspn(ap, macset) != l || (mac = ether_aton(ap)) == NULL)
3476127dd473Swhl739 errx(EX_DATAERR, "Incorrect MAC address");
3477127dd473Swhl739 bcopy(mac, addr, ETHER_ADDR_LEN);
3478127dd473Swhl739 } else
3479127dd473Swhl739 errx(EX_DATAERR, "Incorrect MAC address");
3480127dd473Swhl739
3481127dd473Swhl739 if (ptr != NULL) { /* we have mask? */
3482127dd473Swhl739 if (p[ptr - optr - 1] == '/') { /* mask len */
3483127dd473Swhl739 long ml = strtol(ptr, &ap, 10);
3484127dd473Swhl739 if (*ap != 0 || ml > ETHER_ADDR_LEN * 8 || ml < 0)
3485127dd473Swhl739 errx(EX_DATAERR, "Incorrect mask length");
3486127dd473Swhl739 for (i = 0; ml > 0 && i < ETHER_ADDR_LEN; ml -= 8, i++)
3487127dd473Swhl739 mask[i] = (ml >= 8) ? 0xff: (~0) << (8 - ml);
3488127dd473Swhl739 } else { /* mask */
3489127dd473Swhl739 l = strlen(ptr);
3490127dd473Swhl739 if (strspn(ptr, macset) != l ||
3491127dd473Swhl739 (mac = ether_aton(ptr)) == NULL)
3492127dd473Swhl739 errx(EX_DATAERR, "Incorrect mask");
3493127dd473Swhl739 bcopy(mac, mask, ETHER_ADDR_LEN);
3494127dd473Swhl739 }
3495127dd473Swhl739 } else { /* default mask: ff:ff:ff:ff:ff:ff */
3496127dd473Swhl739 for (i = 0; i < ETHER_ADDR_LEN; i++)
3497127dd473Swhl739 mask[i] = 0xff;
3498127dd473Swhl739 }
3499127dd473Swhl739 for (i = 0; i < ETHER_ADDR_LEN; i++)
3500127dd473Swhl739 addr[i] &= mask[i];
3501127dd473Swhl739
3502127dd473Swhl739 free(optr);
3503127dd473Swhl739 }
3504127dd473Swhl739
3505127dd473Swhl739 /*
3506127dd473Swhl739 * helper function, updates the pointer to cmd with the length
3507127dd473Swhl739 * of the current command, and also cleans up the first word of
3508127dd473Swhl739 * the new command in case it has been clobbered before.
3509127dd473Swhl739 */
3510127dd473Swhl739 static ipfw_insn *
next_cmd(ipfw_insn * cmd,int * len)3511127dd473Swhl739 next_cmd(ipfw_insn *cmd, int *len)
3512127dd473Swhl739 {
3513127dd473Swhl739 *len -= F_LEN(cmd);
3514127dd473Swhl739 CHECK_LENGTH(*len, 0);
3515127dd473Swhl739 cmd += F_LEN(cmd);
3516127dd473Swhl739 bzero(cmd, sizeof(*cmd));
3517127dd473Swhl739 return cmd;
3518127dd473Swhl739 }
3519127dd473Swhl739
3520127dd473Swhl739 /*
3521127dd473Swhl739 * Takes arguments and copies them into a comment
3522127dd473Swhl739 */
3523127dd473Swhl739 static void
fill_comment(ipfw_insn * cmd,char ** av,int cblen)3524127dd473Swhl739 fill_comment(ipfw_insn *cmd, char **av, int cblen)
3525127dd473Swhl739 {
3526127dd473Swhl739 int i, l;
3527127dd473Swhl739 char *p = (char *)(cmd + 1);
3528127dd473Swhl739
3529127dd473Swhl739 cmd->opcode = O_NOP;
3530127dd473Swhl739 cmd->len = (cmd->len & (F_NOT | F_OR));
3531127dd473Swhl739
3532127dd473Swhl739 /* Compute length of comment string. */
3533127dd473Swhl739 for (i = 0, l = 0; av[i] != NULL; i++)
3534127dd473Swhl739 l += strlen(av[i]) + 1;
3535127dd473Swhl739 if (l == 0)
3536127dd473Swhl739 return;
3537127dd473Swhl739 if (l > 84)
3538127dd473Swhl739 errx(EX_DATAERR,
3539127dd473Swhl739 "comment too long (max 80 chars)");
3540127dd473Swhl739 l = 1 + (l+3)/4;
3541127dd473Swhl739 cmd->len = (cmd->len & (F_NOT | F_OR)) | l;
3542127dd473Swhl739 CHECK_CMDLEN;
3543127dd473Swhl739
3544127dd473Swhl739 for (i = 0; av[i] != NULL; i++) {
3545127dd473Swhl739 strcpy(p, av[i]);
3546127dd473Swhl739 p += strlen(av[i]);
3547127dd473Swhl739 *p++ = ' ';
3548127dd473Swhl739 }
3549127dd473Swhl739 *(--p) = '\0';
3550127dd473Swhl739 }
3551127dd473Swhl739
3552127dd473Swhl739 /*
3553127dd473Swhl739 * A function to fill simple commands of size 1.
3554127dd473Swhl739 * Existing flags are preserved.
3555127dd473Swhl739 */
3556127dd473Swhl739 static void
fill_cmd(ipfw_insn * cmd,enum ipfw_opcodes opcode,int flags,uint16_t arg)3557127dd473Swhl739 fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg)
3558127dd473Swhl739 {
3559127dd473Swhl739 cmd->opcode = opcode;
3560127dd473Swhl739 cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | 1;
3561127dd473Swhl739 cmd->arg1 = arg;
3562127dd473Swhl739 }
3563127dd473Swhl739
3564127dd473Swhl739 /*
3565127dd473Swhl739 * Fetch and add the MAC address and type, with masks. This generates one or
3566127dd473Swhl739 * two microinstructions, and returns the pointer to the last one.
3567127dd473Swhl739 */
3568127dd473Swhl739 static ipfw_insn *
add_mac(ipfw_insn * cmd,char * av[],int cblen)3569127dd473Swhl739 add_mac(ipfw_insn *cmd, char *av[], int cblen)
3570127dd473Swhl739 {
3571127dd473Swhl739 ipfw_insn_mac *mac;
3572127dd473Swhl739
3573127dd473Swhl739 if ( ( av[0] == NULL ) || ( av[1] == NULL ) )
3574127dd473Swhl739 errx(EX_DATAERR, "MAC dst src");
3575127dd473Swhl739
3576127dd473Swhl739 cmd->opcode = O_MACADDR2;
3577127dd473Swhl739 cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
3578127dd473Swhl739 CHECK_CMDLEN;
3579127dd473Swhl739
3580127dd473Swhl739 mac = (ipfw_insn_mac *)cmd;
3581127dd473Swhl739 get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */
3582127dd473Swhl739 get_mac_addr_mask(av[1], &(mac->addr[ETHER_ADDR_LEN]),
3583127dd473Swhl739 &(mac->mask[ETHER_ADDR_LEN])); /* src */
3584127dd473Swhl739 return cmd;
3585127dd473Swhl739 }
3586127dd473Swhl739
3587127dd473Swhl739 static ipfw_insn *
add_mactype(ipfw_insn * cmd,char * av,int cblen)3588127dd473Swhl739 add_mactype(ipfw_insn *cmd, char *av, int cblen)
3589127dd473Swhl739 {
3590127dd473Swhl739 if (!av)
3591127dd473Swhl739 errx(EX_DATAERR, "missing MAC type");
3592127dd473Swhl739 if (strcmp(av, "any") != 0) { /* we have a non-null type */
3593127dd473Swhl739 fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE,
3594127dd473Swhl739 cblen);
3595127dd473Swhl739 cmd->opcode = O_MAC_TYPE;
3596127dd473Swhl739 return cmd;
3597127dd473Swhl739 } else
3598127dd473Swhl739 return NULL;
3599127dd473Swhl739 }
3600127dd473Swhl739
3601127dd473Swhl739 static ipfw_insn *
add_proto0(ipfw_insn * cmd,char * av,u_char * protop)3602127dd473Swhl739 add_proto0(ipfw_insn *cmd, char *av, u_char *protop)
3603127dd473Swhl739 {
3604127dd473Swhl739 struct protoent *pe;
3605127dd473Swhl739 char *ep;
3606127dd473Swhl739 int proto;
3607127dd473Swhl739
3608127dd473Swhl739 proto = strtol(av, &ep, 10);
3609127dd473Swhl739 if (*ep != '\0' || proto <= 0) {
3610127dd473Swhl739 if ((pe = getprotobyname(av)) == NULL)
3611127dd473Swhl739 return NULL;
3612127dd473Swhl739 proto = pe->p_proto;
3613127dd473Swhl739 }
3614127dd473Swhl739
3615127dd473Swhl739 fill_cmd(cmd, O_PROTO, 0, proto);
3616127dd473Swhl739 *protop = proto;
3617127dd473Swhl739 return cmd;
3618127dd473Swhl739 }
3619127dd473Swhl739
3620127dd473Swhl739 static ipfw_insn *
add_proto(ipfw_insn * cmd,char * av,u_char * protop)3621127dd473Swhl739 add_proto(ipfw_insn *cmd, char *av, u_char *protop)
3622127dd473Swhl739 {
3623127dd473Swhl739 u_char proto = IPPROTO_IP;
3624127dd473Swhl739
3625127dd473Swhl739 if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
3626127dd473Swhl739 ; /* do not set O_IP4 nor O_IP6 */
3627127dd473Swhl739 else if (strcmp(av, "ip4") == 0)
3628127dd473Swhl739 /* explicit "just IPv4" rule */
3629127dd473Swhl739 fill_cmd(cmd, O_IP4, 0, 0);
3630127dd473Swhl739 else if (strcmp(av, "ip6") == 0) {
3631127dd473Swhl739 /* explicit "just IPv6" rule */
3632127dd473Swhl739 proto = IPPROTO_IPV6;
3633127dd473Swhl739 fill_cmd(cmd, O_IP6, 0, 0);
3634127dd473Swhl739 } else
3635127dd473Swhl739 return add_proto0(cmd, av, protop);
3636127dd473Swhl739
3637127dd473Swhl739 *protop = proto;
3638127dd473Swhl739 return cmd;
3639127dd473Swhl739 }
3640127dd473Swhl739
3641127dd473Swhl739 static ipfw_insn *
add_proto_compat(ipfw_insn * cmd,char * av,u_char * protop)3642127dd473Swhl739 add_proto_compat(ipfw_insn *cmd, char *av, u_char *protop)
3643127dd473Swhl739 {
3644127dd473Swhl739 u_char proto = IPPROTO_IP;
3645127dd473Swhl739
3646127dd473Swhl739 if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0)
3647127dd473Swhl739 ; /* do not set O_IP4 nor O_IP6 */
3648127dd473Swhl739 else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0)
3649127dd473Swhl739 /* explicit "just IPv4" rule */
3650127dd473Swhl739 fill_cmd(cmd, O_IP4, 0, 0);
3651127dd473Swhl739 else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) {
3652127dd473Swhl739 /* explicit "just IPv6" rule */
3653127dd473Swhl739 proto = IPPROTO_IPV6;
3654127dd473Swhl739 fill_cmd(cmd, O_IP6, 0, 0);
3655127dd473Swhl739 } else
3656127dd473Swhl739 return add_proto0(cmd, av, protop);
3657127dd473Swhl739
3658127dd473Swhl739 *protop = proto;
3659127dd473Swhl739 return cmd;
3660127dd473Swhl739 }
3661127dd473Swhl739
3662127dd473Swhl739 static ipfw_insn *
add_srcip(ipfw_insn * cmd,char * av,int cblen,struct tidx * tstate)3663127dd473Swhl739 add_srcip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate)
3664127dd473Swhl739 {
3665127dd473Swhl739 fill_ip((ipfw_insn_ip *)cmd, av, cblen, tstate);
3666127dd473Swhl739 if (cmd->opcode == O_IP_DST_SET) /* set */
3667127dd473Swhl739 cmd->opcode = O_IP_SRC_SET;
3668127dd473Swhl739 else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
3669127dd473Swhl739 cmd->opcode = O_IP_SRC_LOOKUP;
3670127dd473Swhl739 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */
3671127dd473Swhl739 cmd->opcode = O_IP_SRC_ME;
3672127dd473Swhl739 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */
3673127dd473Swhl739 cmd->opcode = O_IP_SRC;
3674127dd473Swhl739 else /* addr/mask */
3675127dd473Swhl739 cmd->opcode = O_IP_SRC_MASK;
3676127dd473Swhl739 return cmd;
3677127dd473Swhl739 }
3678127dd473Swhl739
3679127dd473Swhl739 static ipfw_insn *
add_dstip(ipfw_insn * cmd,char * av,int cblen,struct tidx * tstate)3680127dd473Swhl739 add_dstip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate)
3681127dd473Swhl739 {
3682127dd473Swhl739 fill_ip((ipfw_insn_ip *)cmd, av, cblen, tstate);
3683127dd473Swhl739 if (cmd->opcode == O_IP_DST_SET) /* set */
3684127dd473Swhl739 ;
3685127dd473Swhl739 else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
3686127dd473Swhl739 ;
3687127dd473Swhl739 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */
3688127dd473Swhl739 cmd->opcode = O_IP_DST_ME;
3689127dd473Swhl739 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */
3690127dd473Swhl739 cmd->opcode = O_IP_DST;
3691127dd473Swhl739 else /* addr/mask */
3692127dd473Swhl739 cmd->opcode = O_IP_DST_MASK;
3693127dd473Swhl739 return cmd;
3694127dd473Swhl739 }
3695127dd473Swhl739
3696127dd473Swhl739 static struct _s_x f_reserved_keywords[] = {
3697127dd473Swhl739 { "altq", TOK_OR },
3698127dd473Swhl739 { "//", TOK_OR },
3699127dd473Swhl739 { "diverted", TOK_OR },
3700127dd473Swhl739 { "dst-port", TOK_OR },
3701127dd473Swhl739 { "src-port", TOK_OR },
3702127dd473Swhl739 { "established", TOK_OR },
3703127dd473Swhl739 { "keep-state", TOK_OR },
3704127dd473Swhl739 { "frag", TOK_OR },
3705127dd473Swhl739 { "icmptypes", TOK_OR },
3706127dd473Swhl739 { "in", TOK_OR },
3707127dd473Swhl739 { "out", TOK_OR },
3708127dd473Swhl739 { "ip6", TOK_OR },
3709127dd473Swhl739 { "any", TOK_OR },
3710127dd473Swhl739 { "to", TOK_OR },
3711127dd473Swhl739 { "via", TOK_OR },
3712127dd473Swhl739 { "{", TOK_OR },
3713127dd473Swhl739 { NULL, 0 } /* terminator */
3714127dd473Swhl739 };
3715127dd473Swhl739
3716127dd473Swhl739 static ipfw_insn *
add_ports(ipfw_insn * cmd,char * av,u_char proto,int opcode,int cblen)3717127dd473Swhl739 add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode, int cblen)
3718127dd473Swhl739 {
3719127dd473Swhl739
3720127dd473Swhl739 if (match_token(f_reserved_keywords, av) != -1)
3721127dd473Swhl739 return (NULL);
3722127dd473Swhl739
3723127dd473Swhl739 if (fill_newports((ipfw_insn_u16 *)cmd, av, proto, cblen)) {
3724127dd473Swhl739 /* XXX todo: check that we have a protocol with ports */
3725127dd473Swhl739 cmd->opcode = opcode;
3726127dd473Swhl739 return cmd;
3727127dd473Swhl739 }
3728127dd473Swhl739 return NULL;
3729127dd473Swhl739 }
3730127dd473Swhl739
3731127dd473Swhl739 static ipfw_insn *
add_src(ipfw_insn * cmd,char * av,u_char proto,int cblen,struct tidx * tstate)3732127dd473Swhl739 add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate)
3733127dd473Swhl739 {
3734127dd473Swhl739 struct in6_addr a;
3735127dd473Swhl739 char *host, *ch, buf[INET6_ADDRSTRLEN];
3736127dd473Swhl739 ipfw_insn *ret = NULL;
373722ce4affSfengbojiang size_t len;
3738127dd473Swhl739
3739127dd473Swhl739 /* Copy first address in set if needed */
3740127dd473Swhl739 if ((ch = strpbrk(av, "/,")) != NULL) {
3741127dd473Swhl739 len = ch - av;
3742127dd473Swhl739 strlcpy(buf, av, sizeof(buf));
3743127dd473Swhl739 if (len < sizeof(buf))
3744127dd473Swhl739 buf[len] = '\0';
3745127dd473Swhl739 host = buf;
3746127dd473Swhl739 } else
3747127dd473Swhl739 host = av;
3748127dd473Swhl739
3749127dd473Swhl739 if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 ||
3750*d4a07e70Sfengbojiang inet_pton(AF_INET6_LINUX, host, &a) == 1)
375122ce4affSfengbojiang ret = add_srcip6(cmd, av, cblen, tstate);
3752127dd473Swhl739 /* XXX: should check for IPv4, not !IPv6 */
3753127dd473Swhl739 if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
3754*d4a07e70Sfengbojiang inet_pton(AF_INET6_LINUX, host, &a) != 1))
3755127dd473Swhl739 ret = add_srcip(cmd, av, cblen, tstate);
3756127dd473Swhl739 if (ret == NULL && strcmp(av, "any") != 0)
3757127dd473Swhl739 ret = cmd;
3758127dd473Swhl739
3759127dd473Swhl739 return ret;
3760127dd473Swhl739 }
3761127dd473Swhl739
3762127dd473Swhl739 static ipfw_insn *
add_dst(ipfw_insn * cmd,char * av,u_char proto,int cblen,struct tidx * tstate)3763127dd473Swhl739 add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate)
3764127dd473Swhl739 {
3765127dd473Swhl739 struct in6_addr a;
3766127dd473Swhl739 char *host, *ch, buf[INET6_ADDRSTRLEN];
3767127dd473Swhl739 ipfw_insn *ret = NULL;
376822ce4affSfengbojiang size_t len;
3769127dd473Swhl739
3770127dd473Swhl739 /* Copy first address in set if needed */
3771127dd473Swhl739 if ((ch = strpbrk(av, "/,")) != NULL) {
3772127dd473Swhl739 len = ch - av;
3773127dd473Swhl739 strlcpy(buf, av, sizeof(buf));
3774127dd473Swhl739 if (len < sizeof(buf))
3775127dd473Swhl739 buf[len] = '\0';
3776127dd473Swhl739 host = buf;
3777127dd473Swhl739 } else
3778127dd473Swhl739 host = av;
3779127dd473Swhl739
3780127dd473Swhl739 if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 ||
3781*d4a07e70Sfengbojiang inet_pton(AF_INET6_LINUX, host, &a) == 1)
378222ce4affSfengbojiang ret = add_dstip6(cmd, av, cblen, tstate);
3783127dd473Swhl739 /* XXX: should check for IPv4, not !IPv6 */
3784127dd473Swhl739 if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
3785*d4a07e70Sfengbojiang inet_pton(AF_INET6_LINUX, host, &a) != 1))
3786127dd473Swhl739 ret = add_dstip(cmd, av, cblen, tstate);
3787127dd473Swhl739 if (ret == NULL && strcmp(av, "any") != 0)
3788127dd473Swhl739 ret = cmd;
3789127dd473Swhl739
3790127dd473Swhl739 return ret;
3791127dd473Swhl739 }
3792127dd473Swhl739
3793127dd473Swhl739 /*
3794127dd473Swhl739 * Parse arguments and assemble the microinstructions which make up a rule.
3795127dd473Swhl739 * Rules are added into the 'rulebuf' and then copied in the correct order
3796127dd473Swhl739 * into the actual rule.
3797127dd473Swhl739 *
3798127dd473Swhl739 * The syntax for a rule starts with the action, followed by
3799127dd473Swhl739 * optional action parameters, and the various match patterns.
3800127dd473Swhl739 * In the assembled microcode, the first opcode must be an O_PROBE_STATE
3801127dd473Swhl739 * (generated if the rule includes a keep-state option), then the
3802127dd473Swhl739 * various match patterns, log/altq actions, and the actual action.
3803127dd473Swhl739 *
3804127dd473Swhl739 */
380522ce4affSfengbojiang static void
compile_rule(char * av[],uint32_t * rbuf,int * rbufsize,struct tidx * tstate)3806127dd473Swhl739 compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate)
3807127dd473Swhl739 {
3808127dd473Swhl739 /*
3809127dd473Swhl739 * rules are added into the 'rulebuf' and then copied in
3810127dd473Swhl739 * the correct order into the actual rule.
3811127dd473Swhl739 * Some things that need to go out of order (prob, action etc.)
3812127dd473Swhl739 * go into actbuf[].
3813127dd473Swhl739 */
3814127dd473Swhl739 static uint32_t actbuf[255], cmdbuf[255];
3815127dd473Swhl739 int rblen, ablen, cblen;
3816127dd473Swhl739
3817127dd473Swhl739 ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
3818127dd473Swhl739 ipfw_insn *first_cmd; /* first match pattern */
3819127dd473Swhl739
3820127dd473Swhl739 struct ip_fw_rule *rule;
3821127dd473Swhl739
3822127dd473Swhl739 /*
3823127dd473Swhl739 * various flags used to record that we entered some fields.
3824127dd473Swhl739 */
382522ce4affSfengbojiang ipfw_insn *have_state = NULL; /* any state-related option */
382622ce4affSfengbojiang int have_rstate = 0;
3827127dd473Swhl739 ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL;
382822ce4affSfengbojiang ipfw_insn *have_skipcmd = NULL;
3829127dd473Swhl739 size_t len;
3830127dd473Swhl739
3831127dd473Swhl739 int i;
3832127dd473Swhl739
3833127dd473Swhl739 int open_par = 0; /* open parenthesis ( */
3834127dd473Swhl739
3835127dd473Swhl739 /* proto is here because it is used to fetch ports */
3836127dd473Swhl739 u_char proto = IPPROTO_IP; /* default protocol */
3837127dd473Swhl739
3838127dd473Swhl739 double match_prob = 1; /* match probability, default is always match */
3839127dd473Swhl739
3840127dd473Swhl739 bzero(actbuf, sizeof(actbuf)); /* actions go here */
3841127dd473Swhl739 bzero(cmdbuf, sizeof(cmdbuf));
3842127dd473Swhl739 bzero(rbuf, *rbufsize);
3843127dd473Swhl739
3844127dd473Swhl739 rule = (struct ip_fw_rule *)rbuf;
3845127dd473Swhl739 cmd = (ipfw_insn *)cmdbuf;
3846127dd473Swhl739 action = (ipfw_insn *)actbuf;
3847127dd473Swhl739
3848127dd473Swhl739 rblen = *rbufsize / sizeof(uint32_t);
3849127dd473Swhl739 rblen -= sizeof(struct ip_fw_rule) / sizeof(uint32_t);
3850127dd473Swhl739 ablen = sizeof(actbuf) / sizeof(actbuf[0]);
3851127dd473Swhl739 cblen = sizeof(cmdbuf) / sizeof(cmdbuf[0]);
3852127dd473Swhl739 cblen -= F_INSN_SIZE(ipfw_insn_u32) + 1;
3853127dd473Swhl739
3854127dd473Swhl739 #define CHECK_RBUFLEN(len) { CHECK_LENGTH(rblen, len); rblen -= len; }
3855127dd473Swhl739 #define CHECK_ACTLEN CHECK_LENGTH(ablen, action->len)
3856127dd473Swhl739
3857127dd473Swhl739 av++;
3858127dd473Swhl739
3859127dd473Swhl739 /* [rule N] -- Rule number optional */
3860127dd473Swhl739 if (av[0] && isdigit(**av)) {
3861127dd473Swhl739 rule->rulenum = atoi(*av);
3862127dd473Swhl739 av++;
3863127dd473Swhl739 }
3864127dd473Swhl739
3865127dd473Swhl739 /* [set N] -- set number (0..RESVD_SET), optional */
3866127dd473Swhl739 if (av[0] && av[1] && _substrcmp(*av, "set") == 0) {
3867127dd473Swhl739 int set = strtoul(av[1], NULL, 10);
3868127dd473Swhl739 if (set < 0 || set > RESVD_SET)
3869127dd473Swhl739 errx(EX_DATAERR, "illegal set %s", av[1]);
3870127dd473Swhl739 rule->set = set;
3871127dd473Swhl739 tstate->set = set;
3872127dd473Swhl739 av += 2;
3873127dd473Swhl739 }
3874127dd473Swhl739
3875127dd473Swhl739 /* [prob D] -- match probability, optional */
3876127dd473Swhl739 if (av[0] && av[1] && _substrcmp(*av, "prob") == 0) {
3877127dd473Swhl739 match_prob = strtod(av[1], NULL);
3878127dd473Swhl739
3879127dd473Swhl739 if (match_prob <= 0 || match_prob > 1)
3880127dd473Swhl739 errx(EX_DATAERR, "illegal match prob. %s", av[1]);
3881127dd473Swhl739 av += 2;
3882127dd473Swhl739 }
3883127dd473Swhl739
3884127dd473Swhl739 /* action -- mandatory */
3885127dd473Swhl739 NEED1("missing action");
3886127dd473Swhl739 i = match_token(rule_actions, *av);
3887127dd473Swhl739 av++;
3888127dd473Swhl739 action->len = 1; /* default */
3889127dd473Swhl739 CHECK_ACTLEN;
3890127dd473Swhl739 switch(i) {
3891127dd473Swhl739 case TOK_CHECKSTATE:
3892127dd473Swhl739 have_state = action;
3893127dd473Swhl739 action->opcode = O_CHECK_STATE;
389422ce4affSfengbojiang if (*av == NULL ||
389522ce4affSfengbojiang match_token(rule_options, *av) == TOK_COMMENT) {
389622ce4affSfengbojiang action->arg1 = pack_object(tstate,
389722ce4affSfengbojiang default_state_name, IPFW_TLV_STATE_NAME);
389822ce4affSfengbojiang break;
389922ce4affSfengbojiang }
390022ce4affSfengbojiang if (*av[0] == ':') {
390122ce4affSfengbojiang if (strcmp(*av + 1, "any") == 0)
390222ce4affSfengbojiang action->arg1 = 0;
390322ce4affSfengbojiang else if (state_check_name(*av + 1) == 0)
390422ce4affSfengbojiang action->arg1 = pack_object(tstate, *av + 1,
390522ce4affSfengbojiang IPFW_TLV_STATE_NAME);
390622ce4affSfengbojiang else
390722ce4affSfengbojiang errx(EX_DATAERR, "Invalid state name %s",
390822ce4affSfengbojiang *av);
390922ce4affSfengbojiang av++;
391022ce4affSfengbojiang break;
391122ce4affSfengbojiang }
391222ce4affSfengbojiang errx(EX_DATAERR, "Invalid state name %s", *av);
391322ce4affSfengbojiang break;
391422ce4affSfengbojiang
391522ce4affSfengbojiang case TOK_ABORT:
391622ce4affSfengbojiang action->opcode = O_REJECT;
391722ce4affSfengbojiang action->arg1 = ICMP_REJECT_ABORT;
391822ce4affSfengbojiang break;
391922ce4affSfengbojiang
392022ce4affSfengbojiang case TOK_ABORT6:
392122ce4affSfengbojiang action->opcode = O_UNREACH6;
392222ce4affSfengbojiang action->arg1 = ICMP6_UNREACH_ABORT;
3923127dd473Swhl739 break;
3924127dd473Swhl739
3925127dd473Swhl739 case TOK_ACCEPT:
3926127dd473Swhl739 action->opcode = O_ACCEPT;
3927127dd473Swhl739 break;
3928127dd473Swhl739
3929127dd473Swhl739 case TOK_DENY:
3930127dd473Swhl739 action->opcode = O_DENY;
3931127dd473Swhl739 action->arg1 = 0;
3932127dd473Swhl739 break;
3933127dd473Swhl739
3934127dd473Swhl739 case TOK_REJECT:
3935127dd473Swhl739 action->opcode = O_REJECT;
3936127dd473Swhl739 action->arg1 = ICMP_UNREACH_HOST;
3937127dd473Swhl739 break;
3938127dd473Swhl739
3939127dd473Swhl739 case TOK_RESET:
3940127dd473Swhl739 action->opcode = O_REJECT;
3941127dd473Swhl739 action->arg1 = ICMP_REJECT_RST;
3942127dd473Swhl739 break;
3943127dd473Swhl739
3944127dd473Swhl739 case TOK_RESET6:
3945127dd473Swhl739 action->opcode = O_UNREACH6;
3946127dd473Swhl739 action->arg1 = ICMP6_UNREACH_RST;
3947127dd473Swhl739 break;
3948127dd473Swhl739
3949127dd473Swhl739 case TOK_UNREACH:
3950127dd473Swhl739 action->opcode = O_REJECT;
3951127dd473Swhl739 NEED1("missing reject code");
3952127dd473Swhl739 fill_reject_code(&action->arg1, *av);
3953127dd473Swhl739 av++;
3954127dd473Swhl739 break;
3955127dd473Swhl739
3956127dd473Swhl739 case TOK_UNREACH6:
3957127dd473Swhl739 action->opcode = O_UNREACH6;
3958127dd473Swhl739 NEED1("missing unreach code");
3959127dd473Swhl739 fill_unreach6_code(&action->arg1, *av);
3960127dd473Swhl739 av++;
3961127dd473Swhl739 break;
3962127dd473Swhl739
3963127dd473Swhl739 case TOK_COUNT:
3964127dd473Swhl739 action->opcode = O_COUNT;
3965127dd473Swhl739 break;
3966127dd473Swhl739
3967127dd473Swhl739 case TOK_NAT:
3968127dd473Swhl739 action->opcode = O_NAT;
3969127dd473Swhl739 action->len = F_INSN_SIZE(ipfw_insn_nat);
3970127dd473Swhl739 CHECK_ACTLEN;
3971127dd473Swhl739 if (*av != NULL && _substrcmp(*av, "global") == 0) {
3972127dd473Swhl739 action->arg1 = IP_FW_NAT44_GLOBAL;
3973127dd473Swhl739 av++;
3974127dd473Swhl739 break;
3975127dd473Swhl739 } else
3976127dd473Swhl739 goto chkarg;
3977127dd473Swhl739 case TOK_QUEUE:
3978127dd473Swhl739 action->opcode = O_QUEUE;
3979127dd473Swhl739 goto chkarg;
3980127dd473Swhl739 case TOK_PIPE:
3981127dd473Swhl739 action->opcode = O_PIPE;
3982127dd473Swhl739 goto chkarg;
3983127dd473Swhl739 case TOK_SKIPTO:
3984127dd473Swhl739 action->opcode = O_SKIPTO;
3985127dd473Swhl739 goto chkarg;
3986127dd473Swhl739 case TOK_NETGRAPH:
3987127dd473Swhl739 action->opcode = O_NETGRAPH;
3988127dd473Swhl739 goto chkarg;
3989127dd473Swhl739 case TOK_NGTEE:
3990127dd473Swhl739 action->opcode = O_NGTEE;
3991127dd473Swhl739 goto chkarg;
3992127dd473Swhl739 case TOK_DIVERT:
3993127dd473Swhl739 action->opcode = O_DIVERT;
3994127dd473Swhl739 goto chkarg;
3995127dd473Swhl739 case TOK_TEE:
3996127dd473Swhl739 action->opcode = O_TEE;
3997127dd473Swhl739 goto chkarg;
3998127dd473Swhl739 case TOK_CALL:
3999127dd473Swhl739 action->opcode = O_CALLRETURN;
4000127dd473Swhl739 chkarg:
4001127dd473Swhl739 if (!av[0])
4002127dd473Swhl739 errx(EX_USAGE, "missing argument for %s", *(av - 1));
4003127dd473Swhl739 if (isdigit(**av)) {
4004127dd473Swhl739 action->arg1 = strtoul(*av, NULL, 10);
4005127dd473Swhl739 if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG)
4006127dd473Swhl739 errx(EX_DATAERR, "illegal argument for %s",
4007127dd473Swhl739 *(av - 1));
4008127dd473Swhl739 } else if (_substrcmp(*av, "tablearg") == 0) {
4009127dd473Swhl739 action->arg1 = IP_FW_TARG;
4010127dd473Swhl739 } else if (i == TOK_DIVERT || i == TOK_TEE) {
4011127dd473Swhl739 struct servent *s;
4012127dd473Swhl739 setservent(1);
4013127dd473Swhl739 s = getservbyname(av[0], "divert");
4014127dd473Swhl739 if (s != NULL)
4015127dd473Swhl739 action->arg1 = ntohs(s->s_port);
4016127dd473Swhl739 else
4017127dd473Swhl739 errx(EX_DATAERR, "illegal divert/tee port");
4018127dd473Swhl739 } else
4019127dd473Swhl739 errx(EX_DATAERR, "illegal argument for %s", *(av - 1));
4020127dd473Swhl739 av++;
4021127dd473Swhl739 break;
4022127dd473Swhl739
4023127dd473Swhl739 case TOK_FORWARD: {
4024127dd473Swhl739 /*
4025127dd473Swhl739 * Locate the address-port separator (':' or ',').
4026127dd473Swhl739 * Could be one of the following:
4027127dd473Swhl739 * hostname:port
4028127dd473Swhl739 * IPv4 a.b.c.d,port
4029127dd473Swhl739 * IPv4 a.b.c.d:port
4030127dd473Swhl739 * IPv6 w:x:y::z,port
403122ce4affSfengbojiang * IPv6 [w:x:y::z]:port
4032127dd473Swhl739 */
4033127dd473Swhl739 struct sockaddr_storage result;
4034127dd473Swhl739 struct addrinfo *res;
4035127dd473Swhl739 char *s, *end;
4036127dd473Swhl739 int family;
403722ce4affSfengbojiang u_short port_number = 0;
4038127dd473Swhl739
4039127dd473Swhl739 NEED1("missing forward address[:port]");
4040127dd473Swhl739
404122ce4affSfengbojiang if (_substrcmp(*av, "tablearg") == 0) {
404222ce4affSfengbojiang family = PF_INET;
404322ce4affSfengbojiang ((struct sockaddr_in*)&result)->sin_addr.s_addr =
404422ce4affSfengbojiang INADDR_ANY;
404522ce4affSfengbojiang } else {
404622ce4affSfengbojiang /*
404722ce4affSfengbojiang * Are we an bracket-enclosed IPv6 address?
404822ce4affSfengbojiang */
404922ce4affSfengbojiang if (strchr(*av, '['))
405022ce4affSfengbojiang (*av)++;
405122ce4affSfengbojiang
4052127dd473Swhl739 /*
4053127dd473Swhl739 * locate the address-port separator (':' or ',')
4054127dd473Swhl739 */
4055127dd473Swhl739 s = strchr(*av, ',');
4056127dd473Swhl739 if (s == NULL) {
405722ce4affSfengbojiang s = strchr(*av, ']');
405822ce4affSfengbojiang /* Prevent erroneous parsing on brackets. */
405922ce4affSfengbojiang if (s != NULL)
406022ce4affSfengbojiang *(s++) = '\0';
406122ce4affSfengbojiang else
406222ce4affSfengbojiang s = *av;
406322ce4affSfengbojiang
4064127dd473Swhl739 /* Distinguish between IPv4:port and IPv6 cases. */
406522ce4affSfengbojiang s = strchr(s, ':');
4066127dd473Swhl739 if (s && strchr(s+1, ':'))
4067127dd473Swhl739 s = NULL; /* no port */
4068127dd473Swhl739 }
4069127dd473Swhl739
4070127dd473Swhl739 if (s != NULL) {
4071127dd473Swhl739 /* Terminate host portion and set s to start of port. */
4072127dd473Swhl739 *(s++) = '\0';
4073127dd473Swhl739 i = strtoport(s, &end, 0 /* base */, 0 /* proto */);
4074127dd473Swhl739 if (s == end)
4075127dd473Swhl739 errx(EX_DATAERR,
4076127dd473Swhl739 "illegal forwarding port ``%s''", s);
4077127dd473Swhl739 port_number = (u_short)i;
4078127dd473Swhl739 }
4079127dd473Swhl739
4080127dd473Swhl739 /*
4081127dd473Swhl739 * Resolve the host name or address to a family and a
4082127dd473Swhl739 * network representation of the address.
4083127dd473Swhl739 */
4084127dd473Swhl739 if (getaddrinfo(*av, NULL, NULL, &res))
4085127dd473Swhl739 errx(EX_DATAERR, NULL);
4086127dd473Swhl739 /* Just use the first host in the answer. */
4087127dd473Swhl739 family = res->ai_family;
4088127dd473Swhl739 memcpy(&result, res->ai_addr, res->ai_addrlen);
4089127dd473Swhl739 freeaddrinfo(res);
4090127dd473Swhl739 }
4091127dd473Swhl739
4092127dd473Swhl739 if (family == PF_INET) {
4093127dd473Swhl739 ipfw_insn_sa *p = (ipfw_insn_sa *)action;
4094127dd473Swhl739
4095127dd473Swhl739 action->opcode = O_FORWARD_IP;
4096127dd473Swhl739 action->len = F_INSN_SIZE(ipfw_insn_sa);
4097127dd473Swhl739 CHECK_ACTLEN;
4098127dd473Swhl739
4099127dd473Swhl739 /*
4100127dd473Swhl739 * In the kernel we assume AF_INET and use only
4101127dd473Swhl739 * sin_port and sin_addr. Remember to set sin_len as
4102127dd473Swhl739 * the routing code seems to use it too.
4103127dd473Swhl739 */
4104127dd473Swhl739 p->sa.sin_len = sizeof(struct sockaddr_in);
4105127dd473Swhl739 p->sa.sin_family = AF_INET;
4106127dd473Swhl739 p->sa.sin_port = port_number;
4107127dd473Swhl739 p->sa.sin_addr.s_addr =
4108127dd473Swhl739 ((struct sockaddr_in *)&result)->sin_addr.s_addr;
4109127dd473Swhl739 } else if (family == PF_INET6) {
4110127dd473Swhl739 ipfw_insn_sa6 *p = (ipfw_insn_sa6 *)action;
4111127dd473Swhl739
4112127dd473Swhl739 action->opcode = O_FORWARD_IP6;
4113127dd473Swhl739 action->len = F_INSN_SIZE(ipfw_insn_sa6);
4114127dd473Swhl739 CHECK_ACTLEN;
4115127dd473Swhl739
4116127dd473Swhl739 p->sa.sin6_len = sizeof(struct sockaddr_in6);
411722ce4affSfengbojiang p->sa.sin6_family = AF_INET6;
4118127dd473Swhl739 p->sa.sin6_port = port_number;
4119127dd473Swhl739 p->sa.sin6_flowinfo = 0;
4120127dd473Swhl739 p->sa.sin6_scope_id =
4121127dd473Swhl739 ((struct sockaddr_in6 *)&result)->sin6_scope_id;
4122127dd473Swhl739 bcopy(&((struct sockaddr_in6*)&result)->sin6_addr,
4123127dd473Swhl739 &p->sa.sin6_addr, sizeof(p->sa.sin6_addr));
4124127dd473Swhl739 } else {
4125127dd473Swhl739 errx(EX_DATAERR, "Invalid address family in forward action");
4126127dd473Swhl739 }
4127127dd473Swhl739 av++;
4128127dd473Swhl739 break;
4129127dd473Swhl739 }
4130127dd473Swhl739 case TOK_COMMENT:
4131127dd473Swhl739 /* pretend it is a 'count' rule followed by the comment */
4132127dd473Swhl739 action->opcode = O_COUNT;
4133127dd473Swhl739 av--; /* go back... */
4134127dd473Swhl739 break;
4135127dd473Swhl739
4136127dd473Swhl739 case TOK_SETFIB:
4137127dd473Swhl739 {
4138127dd473Swhl739 int numfibs;
4139127dd473Swhl739 size_t intsize = sizeof(int);
4140127dd473Swhl739
4141127dd473Swhl739 action->opcode = O_SETFIB;
4142127dd473Swhl739 NEED1("missing fib number");
4143127dd473Swhl739 if (_substrcmp(*av, "tablearg") == 0) {
4144127dd473Swhl739 action->arg1 = IP_FW_TARG;
4145127dd473Swhl739 } else {
4146127dd473Swhl739 action->arg1 = strtoul(*av, NULL, 10);
4147127dd473Swhl739 if (sysctlbyname("net.fibs", &numfibs, &intsize,
4148127dd473Swhl739 NULL, 0) == -1)
4149127dd473Swhl739 errx(EX_DATAERR, "fibs not suported.\n");
4150127dd473Swhl739 if (action->arg1 >= numfibs) /* Temporary */
4151127dd473Swhl739 errx(EX_DATAERR, "fib too large.\n");
4152127dd473Swhl739 /* Add high-order bit to fib to make room for tablearg*/
4153127dd473Swhl739 action->arg1 |= 0x8000;
4154127dd473Swhl739 }
4155127dd473Swhl739 av++;
4156127dd473Swhl739 break;
4157127dd473Swhl739 }
4158127dd473Swhl739
4159127dd473Swhl739 case TOK_SETDSCP:
4160127dd473Swhl739 {
4161127dd473Swhl739 int code;
4162127dd473Swhl739
4163127dd473Swhl739 action->opcode = O_SETDSCP;
4164127dd473Swhl739 NEED1("missing DSCP code");
4165127dd473Swhl739 if (_substrcmp(*av, "tablearg") == 0) {
4166127dd473Swhl739 action->arg1 = IP_FW_TARG;
4167127dd473Swhl739 } else {
4168127dd473Swhl739 if (isalpha(*av[0])) {
4169127dd473Swhl739 if ((code = match_token(f_ipdscp, *av)) == -1)
4170127dd473Swhl739 errx(EX_DATAERR, "Unknown DSCP code");
4171127dd473Swhl739 action->arg1 = code;
4172127dd473Swhl739 } else
4173127dd473Swhl739 action->arg1 = strtoul(*av, NULL, 10);
4174127dd473Swhl739 /*
4175127dd473Swhl739 * Add high-order bit to DSCP to make room
4176127dd473Swhl739 * for tablearg
4177127dd473Swhl739 */
4178127dd473Swhl739 action->arg1 |= 0x8000;
4179127dd473Swhl739 }
4180127dd473Swhl739 av++;
4181127dd473Swhl739 break;
4182127dd473Swhl739 }
4183127dd473Swhl739
4184127dd473Swhl739 case TOK_REASS:
4185127dd473Swhl739 action->opcode = O_REASS;
4186127dd473Swhl739 break;
4187127dd473Swhl739
4188127dd473Swhl739 case TOK_RETURN:
4189127dd473Swhl739 fill_cmd(action, O_CALLRETURN, F_NOT, 0);
4190127dd473Swhl739 break;
4191127dd473Swhl739
419222ce4affSfengbojiang case TOK_TCPSETMSS: {
419322ce4affSfengbojiang u_long mss;
419422ce4affSfengbojiang uint16_t idx;
419522ce4affSfengbojiang
419622ce4affSfengbojiang idx = pack_object(tstate, "tcp-setmss", IPFW_TLV_EACTION);
419722ce4affSfengbojiang if (idx == 0)
419822ce4affSfengbojiang errx(EX_DATAERR, "pack_object failed");
419922ce4affSfengbojiang fill_cmd(action, O_EXTERNAL_ACTION, 0, idx);
420022ce4affSfengbojiang NEED1("Missing MSS value");
420122ce4affSfengbojiang action = next_cmd(action, &ablen);
420222ce4affSfengbojiang action->len = 1;
420322ce4affSfengbojiang CHECK_ACTLEN;
420422ce4affSfengbojiang mss = strtoul(*av, NULL, 10);
420522ce4affSfengbojiang if (mss == 0 || mss > UINT16_MAX)
420622ce4affSfengbojiang errx(EX_USAGE, "invalid MSS value %s", *av);
420722ce4affSfengbojiang fill_cmd(action, O_EXTERNAL_DATA, 0, (uint16_t)mss);
420822ce4affSfengbojiang av++;
420922ce4affSfengbojiang break;
421022ce4affSfengbojiang }
421122ce4affSfengbojiang
4212127dd473Swhl739 default:
4213127dd473Swhl739 av--;
4214127dd473Swhl739 if (match_token(rule_eactions, *av) == -1)
4215127dd473Swhl739 errx(EX_DATAERR, "invalid action %s\n", *av);
4216127dd473Swhl739 /*
4217127dd473Swhl739 * External actions support.
4218127dd473Swhl739 * XXX: we support only syntax with instance name.
4219127dd473Swhl739 * For known external actions (from rule_eactions list)
4220127dd473Swhl739 * we can handle syntax directly. But with `eaction'
4221127dd473Swhl739 * keyword we can use only `eaction <name> <instance>'
4222127dd473Swhl739 * syntax.
4223127dd473Swhl739 */
4224127dd473Swhl739 case TOK_EACTION: {
4225127dd473Swhl739 uint16_t idx;
4226127dd473Swhl739
4227127dd473Swhl739 NEED1("Missing eaction name");
4228127dd473Swhl739 if (eaction_check_name(*av) != 0)
4229127dd473Swhl739 errx(EX_DATAERR, "Invalid eaction name %s", *av);
4230127dd473Swhl739 idx = pack_object(tstate, *av, IPFW_TLV_EACTION);
4231127dd473Swhl739 if (idx == 0)
4232127dd473Swhl739 errx(EX_DATAERR, "pack_object failed");
4233127dd473Swhl739 fill_cmd(action, O_EXTERNAL_ACTION, 0, idx);
4234127dd473Swhl739 av++;
4235127dd473Swhl739 NEED1("Missing eaction instance name");
4236127dd473Swhl739 action = next_cmd(action, &ablen);
4237127dd473Swhl739 action->len = 1;
4238127dd473Swhl739 CHECK_ACTLEN;
4239127dd473Swhl739 if (eaction_check_name(*av) != 0)
4240127dd473Swhl739 errx(EX_DATAERR, "Invalid eaction instance name %s",
4241127dd473Swhl739 *av);
4242127dd473Swhl739 /*
4243127dd473Swhl739 * External action instance object has TLV type depended
4244127dd473Swhl739 * from the external action name object index. Since we
4245127dd473Swhl739 * currently don't know this index, use zero as TLV type.
4246127dd473Swhl739 */
4247127dd473Swhl739 idx = pack_object(tstate, *av, 0);
4248127dd473Swhl739 if (idx == 0)
4249127dd473Swhl739 errx(EX_DATAERR, "pack_object failed");
4250127dd473Swhl739 fill_cmd(action, O_EXTERNAL_INSTANCE, 0, idx);
4251127dd473Swhl739 av++;
4252127dd473Swhl739 }
4253127dd473Swhl739 }
4254127dd473Swhl739 action = next_cmd(action, &ablen);
4255127dd473Swhl739
4256127dd473Swhl739 /*
4257127dd473Swhl739 * [altq queuename] -- altq tag, optional
4258127dd473Swhl739 * [log [logamount N]] -- log, optional
4259127dd473Swhl739 *
4260127dd473Swhl739 * If they exist, it go first in the cmdbuf, but then it is
4261127dd473Swhl739 * skipped in the copy section to the end of the buffer.
4262127dd473Swhl739 */
4263127dd473Swhl739 while (av[0] != NULL && (i = match_token(rule_action_params, *av)) != -1) {
4264127dd473Swhl739 av++;
4265127dd473Swhl739 switch (i) {
4266127dd473Swhl739 case TOK_LOG:
4267127dd473Swhl739 {
4268127dd473Swhl739 ipfw_insn_log *c = (ipfw_insn_log *)cmd;
4269127dd473Swhl739 int l;
4270127dd473Swhl739
4271127dd473Swhl739 if (have_log)
4272127dd473Swhl739 errx(EX_DATAERR,
4273127dd473Swhl739 "log cannot be specified more than once");
4274127dd473Swhl739 have_log = (ipfw_insn *)c;
4275127dd473Swhl739 cmd->len = F_INSN_SIZE(ipfw_insn_log);
4276127dd473Swhl739 CHECK_CMDLEN;
4277127dd473Swhl739 cmd->opcode = O_LOG;
4278127dd473Swhl739 if (av[0] && _substrcmp(*av, "logamount") == 0) {
4279127dd473Swhl739 av++;
4280127dd473Swhl739 NEED1("logamount requires argument");
4281127dd473Swhl739 l = atoi(*av);
4282127dd473Swhl739 if (l < 0)
4283127dd473Swhl739 errx(EX_DATAERR,
4284127dd473Swhl739 "logamount must be positive");
4285127dd473Swhl739 c->max_log = l;
4286127dd473Swhl739 av++;
4287127dd473Swhl739 } else {
4288127dd473Swhl739 len = sizeof(c->max_log);
4289127dd473Swhl739 if (sysctlbyname("net.inet.ip.fw.verbose_limit",
4290127dd473Swhl739 &c->max_log, &len, NULL, 0) == -1) {
429122ce4affSfengbojiang if (g_co.test_only) {
4292127dd473Swhl739 c->max_log = 0;
4293127dd473Swhl739 break;
4294127dd473Swhl739 }
4295127dd473Swhl739 errx(1, "sysctlbyname(\"%s\")",
4296127dd473Swhl739 "net.inet.ip.fw.verbose_limit");
4297127dd473Swhl739 }
4298127dd473Swhl739 }
4299127dd473Swhl739 }
4300127dd473Swhl739 break;
4301127dd473Swhl739
4302127dd473Swhl739 #ifndef NO_ALTQ
4303127dd473Swhl739 case TOK_ALTQ:
4304127dd473Swhl739 {
4305127dd473Swhl739 ipfw_insn_altq *a = (ipfw_insn_altq *)cmd;
4306127dd473Swhl739
4307127dd473Swhl739 NEED1("missing altq queue name");
4308127dd473Swhl739 if (have_altq)
4309127dd473Swhl739 errx(EX_DATAERR,
4310127dd473Swhl739 "altq cannot be specified more than once");
4311127dd473Swhl739 have_altq = (ipfw_insn *)a;
4312127dd473Swhl739 cmd->len = F_INSN_SIZE(ipfw_insn_altq);
4313127dd473Swhl739 CHECK_CMDLEN;
4314127dd473Swhl739 cmd->opcode = O_ALTQ;
4315127dd473Swhl739 a->qid = altq_name_to_qid(*av);
4316127dd473Swhl739 av++;
4317127dd473Swhl739 }
4318127dd473Swhl739 break;
4319127dd473Swhl739 #endif
4320127dd473Swhl739
4321127dd473Swhl739 case TOK_TAG:
4322127dd473Swhl739 case TOK_UNTAG: {
4323127dd473Swhl739 uint16_t tag;
4324127dd473Swhl739
4325127dd473Swhl739 if (have_tag)
4326127dd473Swhl739 errx(EX_USAGE, "tag and untag cannot be "
4327127dd473Swhl739 "specified more than once");
4328127dd473Swhl739 GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX, i,
4329127dd473Swhl739 rule_action_params);
4330127dd473Swhl739 have_tag = cmd;
4331127dd473Swhl739 fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag);
4332127dd473Swhl739 av++;
4333127dd473Swhl739 break;
4334127dd473Swhl739 }
4335127dd473Swhl739
4336127dd473Swhl739 default:
4337127dd473Swhl739 abort();
4338127dd473Swhl739 }
4339127dd473Swhl739 cmd = next_cmd(cmd, &cblen);
4340127dd473Swhl739 }
4341127dd473Swhl739
434222ce4affSfengbojiang if (have_state) { /* must be a check-state, we are done */
434322ce4affSfengbojiang if (*av != NULL &&
434422ce4affSfengbojiang match_token(rule_options, *av) == TOK_COMMENT) {
434522ce4affSfengbojiang /* check-state has a comment */
434622ce4affSfengbojiang av++;
434722ce4affSfengbojiang fill_comment(cmd, av, cblen);
434822ce4affSfengbojiang cmd = next_cmd(cmd, &cblen);
434922ce4affSfengbojiang av[0] = NULL;
435022ce4affSfengbojiang }
4351127dd473Swhl739 goto done;
435222ce4affSfengbojiang }
4353127dd473Swhl739
4354127dd473Swhl739 #define OR_START(target) \
4355127dd473Swhl739 if (av[0] && (*av[0] == '(' || *av[0] == '{')) { \
4356127dd473Swhl739 if (open_par) \
4357127dd473Swhl739 errx(EX_USAGE, "nested \"(\" not allowed\n"); \
4358127dd473Swhl739 prev = NULL; \
4359127dd473Swhl739 open_par = 1; \
4360127dd473Swhl739 if ( (av[0])[1] == '\0') { \
4361127dd473Swhl739 av++; \
4362127dd473Swhl739 } else \
4363127dd473Swhl739 (*av)++; \
4364127dd473Swhl739 } \
4365127dd473Swhl739 target: \
4366127dd473Swhl739
4367127dd473Swhl739
4368127dd473Swhl739 #define CLOSE_PAR \
4369127dd473Swhl739 if (open_par) { \
4370127dd473Swhl739 if (av[0] && ( \
4371127dd473Swhl739 strcmp(*av, ")") == 0 || \
4372127dd473Swhl739 strcmp(*av, "}") == 0)) { \
4373127dd473Swhl739 prev = NULL; \
4374127dd473Swhl739 open_par = 0; \
4375127dd473Swhl739 av++; \
4376127dd473Swhl739 } else \
4377127dd473Swhl739 errx(EX_USAGE, "missing \")\"\n"); \
4378127dd473Swhl739 }
4379127dd473Swhl739
4380127dd473Swhl739 #define NOT_BLOCK \
4381127dd473Swhl739 if (av[0] && _substrcmp(*av, "not") == 0) { \
4382127dd473Swhl739 if (cmd->len & F_NOT) \
4383127dd473Swhl739 errx(EX_USAGE, "double \"not\" not allowed\n"); \
4384127dd473Swhl739 cmd->len |= F_NOT; \
4385127dd473Swhl739 av++; \
4386127dd473Swhl739 }
4387127dd473Swhl739
4388127dd473Swhl739 #define OR_BLOCK(target) \
4389127dd473Swhl739 if (av[0] && _substrcmp(*av, "or") == 0) { \
4390127dd473Swhl739 if (prev == NULL || open_par == 0) \
4391127dd473Swhl739 errx(EX_DATAERR, "invalid OR block"); \
4392127dd473Swhl739 prev->len |= F_OR; \
4393127dd473Swhl739 av++; \
4394127dd473Swhl739 goto target; \
4395127dd473Swhl739 } \
4396127dd473Swhl739 CLOSE_PAR;
4397127dd473Swhl739
4398127dd473Swhl739 first_cmd = cmd;
4399127dd473Swhl739
4400127dd473Swhl739 #if 0
4401127dd473Swhl739 /*
4402127dd473Swhl739 * MAC addresses, optional.
4403127dd473Swhl739 * If we have this, we skip the part "proto from src to dst"
4404127dd473Swhl739 * and jump straight to the option parsing.
4405127dd473Swhl739 */
4406127dd473Swhl739 NOT_BLOCK;
4407127dd473Swhl739 NEED1("missing protocol");
4408127dd473Swhl739 if (_substrcmp(*av, "MAC") == 0 ||
4409127dd473Swhl739 _substrcmp(*av, "mac") == 0) {
4410127dd473Swhl739 av++; /* the "MAC" keyword */
4411127dd473Swhl739 add_mac(cmd, av); /* exits in case of errors */
4412127dd473Swhl739 cmd = next_cmd(cmd);
4413127dd473Swhl739 av += 2; /* dst-mac and src-mac */
4414127dd473Swhl739 NOT_BLOCK;
4415127dd473Swhl739 NEED1("missing mac type");
4416127dd473Swhl739 if (add_mactype(cmd, av[0]))
4417127dd473Swhl739 cmd = next_cmd(cmd);
4418127dd473Swhl739 av++; /* any or mac-type */
4419127dd473Swhl739 goto read_options;
4420127dd473Swhl739 }
4421127dd473Swhl739 #endif
4422127dd473Swhl739
4423127dd473Swhl739 /*
4424127dd473Swhl739 * protocol, mandatory
4425127dd473Swhl739 */
4426127dd473Swhl739 OR_START(get_proto);
4427127dd473Swhl739 NOT_BLOCK;
4428127dd473Swhl739 NEED1("missing protocol");
4429127dd473Swhl739 if (add_proto_compat(cmd, *av, &proto)) {
4430127dd473Swhl739 av++;
4431127dd473Swhl739 if (F_LEN(cmd) != 0) {
4432127dd473Swhl739 prev = cmd;
4433127dd473Swhl739 cmd = next_cmd(cmd, &cblen);
4434127dd473Swhl739 }
4435127dd473Swhl739 } else if (first_cmd != cmd) {
4436127dd473Swhl739 errx(EX_DATAERR, "invalid protocol ``%s''", *av);
443722ce4affSfengbojiang } else {
443822ce4affSfengbojiang rule->flags |= IPFW_RULE_JUSTOPTS;
4439127dd473Swhl739 goto read_options;
444022ce4affSfengbojiang }
4441127dd473Swhl739 OR_BLOCK(get_proto);
4442127dd473Swhl739
444322ce4affSfengbojiang first_cmd = cmd; /* update pointer to use in compact form */
444422ce4affSfengbojiang
4445127dd473Swhl739 /*
4446127dd473Swhl739 * "from", mandatory
4447127dd473Swhl739 */
4448127dd473Swhl739 if ((av[0] == NULL) || _substrcmp(*av, "from") != 0)
4449127dd473Swhl739 errx(EX_USAGE, "missing ``from''");
4450127dd473Swhl739 av++;
4451127dd473Swhl739
4452127dd473Swhl739 /*
4453127dd473Swhl739 * source IP, mandatory
4454127dd473Swhl739 */
4455127dd473Swhl739 OR_START(source_ip);
4456127dd473Swhl739 NOT_BLOCK; /* optional "not" */
4457127dd473Swhl739 NEED1("missing source address");
4458127dd473Swhl739 if (add_src(cmd, *av, proto, cblen, tstate)) {
4459127dd473Swhl739 av++;
4460127dd473Swhl739 if (F_LEN(cmd) != 0) { /* ! any */
4461127dd473Swhl739 prev = cmd;
4462127dd473Swhl739 cmd = next_cmd(cmd, &cblen);
4463127dd473Swhl739 }
4464127dd473Swhl739 } else
4465127dd473Swhl739 errx(EX_USAGE, "bad source address %s", *av);
4466127dd473Swhl739 OR_BLOCK(source_ip);
4467127dd473Swhl739
4468127dd473Swhl739 /*
4469127dd473Swhl739 * source ports, optional
4470127dd473Swhl739 */
4471127dd473Swhl739 NOT_BLOCK; /* optional "not" */
4472127dd473Swhl739 if ( av[0] != NULL ) {
4473127dd473Swhl739 if (_substrcmp(*av, "any") == 0 ||
4474127dd473Swhl739 add_ports(cmd, *av, proto, O_IP_SRCPORT, cblen)) {
4475127dd473Swhl739 av++;
4476127dd473Swhl739 if (F_LEN(cmd) != 0)
4477127dd473Swhl739 cmd = next_cmd(cmd, &cblen);
4478127dd473Swhl739 }
4479127dd473Swhl739 }
4480127dd473Swhl739
4481127dd473Swhl739 /*
4482127dd473Swhl739 * "to", mandatory
4483127dd473Swhl739 */
4484127dd473Swhl739 if ( (av[0] == NULL) || _substrcmp(*av, "to") != 0 )
4485127dd473Swhl739 errx(EX_USAGE, "missing ``to''");
4486127dd473Swhl739 av++;
4487127dd473Swhl739
4488127dd473Swhl739 /*
4489127dd473Swhl739 * destination, mandatory
4490127dd473Swhl739 */
4491127dd473Swhl739 OR_START(dest_ip);
4492127dd473Swhl739 NOT_BLOCK; /* optional "not" */
4493127dd473Swhl739 NEED1("missing dst address");
4494127dd473Swhl739 if (add_dst(cmd, *av, proto, cblen, tstate)) {
4495127dd473Swhl739 av++;
4496127dd473Swhl739 if (F_LEN(cmd) != 0) { /* ! any */
4497127dd473Swhl739 prev = cmd;
4498127dd473Swhl739 cmd = next_cmd(cmd, &cblen);
4499127dd473Swhl739 }
4500127dd473Swhl739 } else
4501127dd473Swhl739 errx( EX_USAGE, "bad destination address %s", *av);
4502127dd473Swhl739 OR_BLOCK(dest_ip);
4503127dd473Swhl739
4504127dd473Swhl739 /*
4505127dd473Swhl739 * dest. ports, optional
4506127dd473Swhl739 */
4507127dd473Swhl739 NOT_BLOCK; /* optional "not" */
4508127dd473Swhl739 if (av[0]) {
4509127dd473Swhl739 if (_substrcmp(*av, "any") == 0 ||
4510127dd473Swhl739 add_ports(cmd, *av, proto, O_IP_DSTPORT, cblen)) {
4511127dd473Swhl739 av++;
4512127dd473Swhl739 if (F_LEN(cmd) != 0)
4513127dd473Swhl739 cmd = next_cmd(cmd, &cblen);
4514127dd473Swhl739 }
4515127dd473Swhl739 }
451622ce4affSfengbojiang if (first_cmd == cmd)
451722ce4affSfengbojiang rule->flags |= IPFW_RULE_NOOPT;
4518127dd473Swhl739
4519127dd473Swhl739 read_options:
4520127dd473Swhl739 prev = NULL;
4521127dd473Swhl739 while ( av[0] != NULL ) {
4522127dd473Swhl739 char *s;
4523127dd473Swhl739 ipfw_insn_u32 *cmd32; /* alias for cmd */
4524127dd473Swhl739
4525127dd473Swhl739 s = *av;
4526127dd473Swhl739 cmd32 = (ipfw_insn_u32 *)cmd;
4527127dd473Swhl739
4528127dd473Swhl739 if (*s == '!') { /* alternate syntax for NOT */
4529127dd473Swhl739 if (cmd->len & F_NOT)
4530127dd473Swhl739 errx(EX_USAGE, "double \"not\" not allowed\n");
4531127dd473Swhl739 cmd->len = F_NOT;
4532127dd473Swhl739 s++;
4533127dd473Swhl739 }
4534127dd473Swhl739 i = match_token(rule_options, s);
4535127dd473Swhl739 av++;
4536127dd473Swhl739 switch(i) {
4537127dd473Swhl739 case TOK_NOT:
4538127dd473Swhl739 if (cmd->len & F_NOT)
4539127dd473Swhl739 errx(EX_USAGE, "double \"not\" not allowed\n");
4540127dd473Swhl739 cmd->len = F_NOT;
4541127dd473Swhl739 break;
4542127dd473Swhl739
4543127dd473Swhl739 case TOK_OR:
4544127dd473Swhl739 if (open_par == 0 || prev == NULL)
4545127dd473Swhl739 errx(EX_USAGE, "invalid \"or\" block\n");
4546127dd473Swhl739 prev->len |= F_OR;
4547127dd473Swhl739 break;
4548127dd473Swhl739
4549127dd473Swhl739 case TOK_STARTBRACE:
4550127dd473Swhl739 if (open_par)
4551127dd473Swhl739 errx(EX_USAGE, "+nested \"(\" not allowed\n");
4552127dd473Swhl739 open_par = 1;
4553127dd473Swhl739 break;
4554127dd473Swhl739
4555127dd473Swhl739 case TOK_ENDBRACE:
4556127dd473Swhl739 if (!open_par)
4557127dd473Swhl739 errx(EX_USAGE, "+missing \")\"\n");
4558127dd473Swhl739 open_par = 0;
4559127dd473Swhl739 prev = NULL;
4560127dd473Swhl739 break;
4561127dd473Swhl739
4562127dd473Swhl739 case TOK_IN:
4563127dd473Swhl739 fill_cmd(cmd, O_IN, 0, 0);
4564127dd473Swhl739 break;
4565127dd473Swhl739
4566127dd473Swhl739 case TOK_OUT:
4567127dd473Swhl739 cmd->len ^= F_NOT; /* toggle F_NOT */
4568127dd473Swhl739 fill_cmd(cmd, O_IN, 0, 0);
4569127dd473Swhl739 break;
4570127dd473Swhl739
4571127dd473Swhl739 case TOK_DIVERTED:
4572127dd473Swhl739 fill_cmd(cmd, O_DIVERTED, 0, 3);
4573127dd473Swhl739 break;
4574127dd473Swhl739
4575127dd473Swhl739 case TOK_DIVERTEDLOOPBACK:
4576127dd473Swhl739 fill_cmd(cmd, O_DIVERTED, 0, 1);
4577127dd473Swhl739 break;
4578127dd473Swhl739
4579127dd473Swhl739 case TOK_DIVERTEDOUTPUT:
4580127dd473Swhl739 fill_cmd(cmd, O_DIVERTED, 0, 2);
4581127dd473Swhl739 break;
4582127dd473Swhl739
458322ce4affSfengbojiang case TOK_FRAG: {
458422ce4affSfengbojiang uint32_t set = 0, clear = 0;
458522ce4affSfengbojiang
458622ce4affSfengbojiang if (*av != NULL && fill_flags(f_ipoff, *av, NULL,
458722ce4affSfengbojiang &set, &clear) == 0)
458822ce4affSfengbojiang av++;
458922ce4affSfengbojiang else {
459022ce4affSfengbojiang /*
459122ce4affSfengbojiang * Compatibility: no argument after "frag"
459222ce4affSfengbojiang * keyword equals to "frag offset".
459322ce4affSfengbojiang */
459422ce4affSfengbojiang set = 0x01;
459522ce4affSfengbojiang clear = 0;
459622ce4affSfengbojiang }
459722ce4affSfengbojiang fill_cmd(cmd, O_FRAG, 0,
459822ce4affSfengbojiang (set & 0xff) | ( (clear & 0xff) << 8));
4599127dd473Swhl739 break;
460022ce4affSfengbojiang }
4601127dd473Swhl739
4602127dd473Swhl739 case TOK_LAYER2:
4603127dd473Swhl739 fill_cmd(cmd, O_LAYER2, 0, 0);
4604127dd473Swhl739 break;
4605127dd473Swhl739
4606127dd473Swhl739 case TOK_XMIT:
4607127dd473Swhl739 case TOK_RECV:
4608127dd473Swhl739 case TOK_VIA:
4609127dd473Swhl739 NEED1("recv, xmit, via require interface name"
4610127dd473Swhl739 " or address");
4611127dd473Swhl739 fill_iface((ipfw_insn_if *)cmd, av[0], cblen, tstate);
4612127dd473Swhl739 av++;
4613127dd473Swhl739 if (F_LEN(cmd) == 0) /* not a valid address */
4614127dd473Swhl739 break;
4615127dd473Swhl739 if (i == TOK_XMIT)
4616127dd473Swhl739 cmd->opcode = O_XMIT;
4617127dd473Swhl739 else if (i == TOK_RECV)
4618127dd473Swhl739 cmd->opcode = O_RECV;
4619127dd473Swhl739 else if (i == TOK_VIA)
4620127dd473Swhl739 cmd->opcode = O_VIA;
4621127dd473Swhl739 break;
4622127dd473Swhl739
4623127dd473Swhl739 case TOK_ICMPTYPES:
4624127dd473Swhl739 NEED1("icmptypes requires list of types");
4625127dd473Swhl739 fill_icmptypes((ipfw_insn_u32 *)cmd, *av);
4626127dd473Swhl739 av++;
4627127dd473Swhl739 break;
4628127dd473Swhl739
4629127dd473Swhl739 case TOK_ICMP6TYPES:
4630127dd473Swhl739 NEED1("icmptypes requires list of types");
4631127dd473Swhl739 fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av, cblen);
4632127dd473Swhl739 av++;
4633127dd473Swhl739 break;
4634127dd473Swhl739
4635127dd473Swhl739 case TOK_IPTTL:
4636127dd473Swhl739 NEED1("ipttl requires TTL");
4637127dd473Swhl739 if (strpbrk(*av, "-,")) {
4638127dd473Swhl739 if (!add_ports(cmd, *av, 0, O_IPTTL, cblen))
4639127dd473Swhl739 errx(EX_DATAERR, "invalid ipttl %s", *av);
4640127dd473Swhl739 } else
4641127dd473Swhl739 fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
4642127dd473Swhl739 av++;
4643127dd473Swhl739 break;
4644127dd473Swhl739
4645127dd473Swhl739 case TOK_IPID:
4646127dd473Swhl739 NEED1("ipid requires id");
4647127dd473Swhl739 if (strpbrk(*av, "-,")) {
4648127dd473Swhl739 if (!add_ports(cmd, *av, 0, O_IPID, cblen))
4649127dd473Swhl739 errx(EX_DATAERR, "invalid ipid %s", *av);
4650127dd473Swhl739 } else
4651127dd473Swhl739 fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0));
4652127dd473Swhl739 av++;
4653127dd473Swhl739 break;
4654127dd473Swhl739
4655127dd473Swhl739 case TOK_IPLEN:
4656127dd473Swhl739 NEED1("iplen requires length");
4657127dd473Swhl739 if (strpbrk(*av, "-,")) {
4658127dd473Swhl739 if (!add_ports(cmd, *av, 0, O_IPLEN, cblen))
4659127dd473Swhl739 errx(EX_DATAERR, "invalid ip len %s", *av);
4660127dd473Swhl739 } else
4661127dd473Swhl739 fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0));
4662127dd473Swhl739 av++;
4663127dd473Swhl739 break;
4664127dd473Swhl739
4665127dd473Swhl739 case TOK_IPVER:
4666127dd473Swhl739 NEED1("ipver requires version");
4667127dd473Swhl739 fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0));
4668127dd473Swhl739 av++;
4669127dd473Swhl739 break;
4670127dd473Swhl739
4671127dd473Swhl739 case TOK_IPPRECEDENCE:
4672127dd473Swhl739 NEED1("ipprecedence requires value");
4673127dd473Swhl739 fill_cmd(cmd, O_IPPRECEDENCE, 0,
4674127dd473Swhl739 (strtoul(*av, NULL, 0) & 7) << 5);
4675127dd473Swhl739 av++;
4676127dd473Swhl739 break;
4677127dd473Swhl739
4678127dd473Swhl739 case TOK_DSCP:
4679127dd473Swhl739 NEED1("missing DSCP code");
4680127dd473Swhl739 fill_dscp(cmd, *av, cblen);
4681127dd473Swhl739 av++;
4682127dd473Swhl739 break;
4683127dd473Swhl739
4684127dd473Swhl739 case TOK_IPOPTS:
4685127dd473Swhl739 NEED1("missing argument for ipoptions");
4686127dd473Swhl739 fill_flags_cmd(cmd, O_IPOPT, f_ipopts, *av);
4687127dd473Swhl739 av++;
4688127dd473Swhl739 break;
4689127dd473Swhl739
4690127dd473Swhl739 case TOK_IPTOS:
4691127dd473Swhl739 NEED1("missing argument for iptos");
4692127dd473Swhl739 fill_flags_cmd(cmd, O_IPTOS, f_iptos, *av);
4693127dd473Swhl739 av++;
4694127dd473Swhl739 break;
4695127dd473Swhl739
4696127dd473Swhl739 case TOK_UID:
4697127dd473Swhl739 NEED1("uid requires argument");
4698127dd473Swhl739 {
4699127dd473Swhl739 char *end;
4700127dd473Swhl739 uid_t uid;
4701127dd473Swhl739 struct passwd *pwd;
4702127dd473Swhl739
4703127dd473Swhl739 cmd->opcode = O_UID;
4704127dd473Swhl739 uid = strtoul(*av, &end, 0);
4705127dd473Swhl739 pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av);
4706127dd473Swhl739 if (pwd == NULL)
4707127dd473Swhl739 errx(EX_DATAERR, "uid \"%s\" nonexistent", *av);
4708127dd473Swhl739 cmd32->d[0] = pwd->pw_uid;
4709127dd473Swhl739 cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
4710127dd473Swhl739 av++;
4711127dd473Swhl739 }
4712127dd473Swhl739 break;
4713127dd473Swhl739
4714127dd473Swhl739 case TOK_GID:
4715127dd473Swhl739 NEED1("gid requires argument");
4716127dd473Swhl739 {
4717127dd473Swhl739 char *end;
4718127dd473Swhl739 gid_t gid;
4719127dd473Swhl739 struct group *grp;
4720127dd473Swhl739
4721127dd473Swhl739 cmd->opcode = O_GID;
4722127dd473Swhl739 gid = strtoul(*av, &end, 0);
4723127dd473Swhl739 grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av);
4724127dd473Swhl739 if (grp == NULL)
4725127dd473Swhl739 errx(EX_DATAERR, "gid \"%s\" nonexistent", *av);
4726127dd473Swhl739 cmd32->d[0] = grp->gr_gid;
4727127dd473Swhl739 cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
4728127dd473Swhl739 av++;
4729127dd473Swhl739 }
4730127dd473Swhl739 break;
4731127dd473Swhl739
4732127dd473Swhl739 case TOK_JAIL:
4733*d4a07e70Sfengbojiang #ifndef FSTACK
4734127dd473Swhl739 NEED1("jail requires argument");
4735127dd473Swhl739 {
4736127dd473Swhl739 char *end;
4737127dd473Swhl739 int jid;
4738127dd473Swhl739
4739127dd473Swhl739 cmd->opcode = O_JAIL;
474022ce4affSfengbojiang /*
474122ce4affSfengbojiang * If av is a number, then we'll just pass it as-is. If
474222ce4affSfengbojiang * it's a name, try to resolve that to a jid.
474322ce4affSfengbojiang *
474422ce4affSfengbojiang * We save the jail_getid(3) call for a fallback because
474522ce4affSfengbojiang * it entails an unconditional trip to the kernel to
474622ce4affSfengbojiang * either validate a jid or resolve a name to a jid.
474722ce4affSfengbojiang * This specific token doesn't currently require a
474822ce4affSfengbojiang * jid to be an active jail, so we save a transition
474922ce4affSfengbojiang * by simply using a number that we're given.
475022ce4affSfengbojiang */
475122ce4affSfengbojiang jid = strtoul(*av, &end, 10);
475222ce4affSfengbojiang if (*end != '\0') {
475322ce4affSfengbojiang jid = jail_getid(*av);
475422ce4affSfengbojiang if (jid < 0)
475522ce4affSfengbojiang errx(EX_DATAERR, "%s", jail_errmsg);
475622ce4affSfengbojiang }
4757127dd473Swhl739 cmd32->d[0] = (uint32_t)jid;
4758127dd473Swhl739 cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
4759127dd473Swhl739 av++;
4760127dd473Swhl739 }
4761*d4a07e70Sfengbojiang #else
4762*d4a07e70Sfengbojiang errx(EX_USAGE, "F-Stack not support JAIL");
4763*d4a07e70Sfengbojiang #endif
4764127dd473Swhl739 break;
4765127dd473Swhl739
4766127dd473Swhl739 case TOK_ESTAB:
4767127dd473Swhl739 fill_cmd(cmd, O_ESTAB, 0, 0);
4768127dd473Swhl739 break;
4769127dd473Swhl739
4770127dd473Swhl739 case TOK_SETUP:
4771127dd473Swhl739 fill_cmd(cmd, O_TCPFLAGS, 0,
4772127dd473Swhl739 (TH_SYN) | ( (TH_ACK) & 0xff) <<8 );
4773127dd473Swhl739 break;
4774127dd473Swhl739
4775127dd473Swhl739 case TOK_TCPDATALEN:
4776127dd473Swhl739 NEED1("tcpdatalen requires length");
4777127dd473Swhl739 if (strpbrk(*av, "-,")) {
4778127dd473Swhl739 if (!add_ports(cmd, *av, 0, O_TCPDATALEN, cblen))
4779127dd473Swhl739 errx(EX_DATAERR, "invalid tcpdata len %s", *av);
4780127dd473Swhl739 } else
4781127dd473Swhl739 fill_cmd(cmd, O_TCPDATALEN, 0,
4782127dd473Swhl739 strtoul(*av, NULL, 0));
4783127dd473Swhl739 av++;
4784127dd473Swhl739 break;
4785127dd473Swhl739
4786127dd473Swhl739 case TOK_TCPOPTS:
4787127dd473Swhl739 NEED1("missing argument for tcpoptions");
4788127dd473Swhl739 fill_flags_cmd(cmd, O_TCPOPTS, f_tcpopts, *av);
4789127dd473Swhl739 av++;
4790127dd473Swhl739 break;
4791127dd473Swhl739
4792127dd473Swhl739 case TOK_TCPSEQ:
4793127dd473Swhl739 case TOK_TCPACK:
4794127dd473Swhl739 NEED1("tcpseq/tcpack requires argument");
4795127dd473Swhl739 cmd->len = F_INSN_SIZE(ipfw_insn_u32);
4796127dd473Swhl739 cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK;
4797127dd473Swhl739 cmd32->d[0] = htonl(strtoul(*av, NULL, 0));
4798127dd473Swhl739 av++;
4799127dd473Swhl739 break;
4800127dd473Swhl739
480122ce4affSfengbojiang case TOK_TCPMSS:
4802127dd473Swhl739 case TOK_TCPWIN:
480322ce4affSfengbojiang NEED1("tcpmss/tcpwin requires size");
4804127dd473Swhl739 if (strpbrk(*av, "-,")) {
480522ce4affSfengbojiang if (add_ports(cmd, *av, 0,
480622ce4affSfengbojiang i == TOK_TCPWIN ? O_TCPWIN : O_TCPMSS,
480722ce4affSfengbojiang cblen) == NULL)
480822ce4affSfengbojiang errx(EX_DATAERR, "invalid %s size %s",
480922ce4affSfengbojiang s, *av);
4810127dd473Swhl739 } else
481122ce4affSfengbojiang fill_cmd(cmd, i == TOK_TCPWIN ? O_TCPWIN :
481222ce4affSfengbojiang O_TCPMSS, 0, strtoul(*av, NULL, 0));
4813127dd473Swhl739 av++;
4814127dd473Swhl739 break;
4815127dd473Swhl739
4816127dd473Swhl739 case TOK_TCPFLAGS:
4817127dd473Swhl739 NEED1("missing argument for tcpflags");
4818127dd473Swhl739 cmd->opcode = O_TCPFLAGS;
4819127dd473Swhl739 fill_flags_cmd(cmd, O_TCPFLAGS, f_tcpflags, *av);
4820127dd473Swhl739 av++;
4821127dd473Swhl739 break;
4822127dd473Swhl739
4823127dd473Swhl739 case TOK_KEEPSTATE:
482422ce4affSfengbojiang case TOK_RECORDSTATE: {
482522ce4affSfengbojiang uint16_t uidx;
482622ce4affSfengbojiang
4827127dd473Swhl739 if (open_par)
482822ce4affSfengbojiang errx(EX_USAGE, "keep-state or record-state cannot be part "
4829127dd473Swhl739 "of an or block");
4830127dd473Swhl739 if (have_state)
483122ce4affSfengbojiang errx(EX_USAGE, "only one of keep-state, record-state, "
483222ce4affSfengbojiang " limit and set-limit is allowed");
483322ce4affSfengbojiang if (*av != NULL && *av[0] == ':') {
483422ce4affSfengbojiang if (state_check_name(*av + 1) != 0)
483522ce4affSfengbojiang errx(EX_DATAERR,
483622ce4affSfengbojiang "Invalid state name %s", *av);
483722ce4affSfengbojiang uidx = pack_object(tstate, *av + 1,
483822ce4affSfengbojiang IPFW_TLV_STATE_NAME);
483922ce4affSfengbojiang av++;
484022ce4affSfengbojiang } else
484122ce4affSfengbojiang uidx = pack_object(tstate, default_state_name,
484222ce4affSfengbojiang IPFW_TLV_STATE_NAME);
4843127dd473Swhl739 have_state = cmd;
484422ce4affSfengbojiang have_rstate = i == TOK_RECORDSTATE;
484522ce4affSfengbojiang fill_cmd(cmd, O_KEEP_STATE, 0, uidx);
4846127dd473Swhl739 break;
484722ce4affSfengbojiang }
4848127dd473Swhl739
484922ce4affSfengbojiang case TOK_LIMIT:
485022ce4affSfengbojiang case TOK_SETLIMIT: {
4851127dd473Swhl739 ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
4852127dd473Swhl739 int val;
4853127dd473Swhl739
4854127dd473Swhl739 if (open_par)
4855127dd473Swhl739 errx(EX_USAGE,
485622ce4affSfengbojiang "limit or set-limit cannot be part of an or block");
4857127dd473Swhl739 if (have_state)
485822ce4affSfengbojiang errx(EX_USAGE, "only one of keep-state, record-state, "
485922ce4affSfengbojiang " limit and set-limit is allowed");
4860127dd473Swhl739 have_state = cmd;
486122ce4affSfengbojiang have_rstate = i == TOK_SETLIMIT;
4862127dd473Swhl739
4863127dd473Swhl739 cmd->len = F_INSN_SIZE(ipfw_insn_limit);
4864127dd473Swhl739 CHECK_CMDLEN;
4865127dd473Swhl739 cmd->opcode = O_LIMIT;
4866127dd473Swhl739 c->limit_mask = c->conn_limit = 0;
4867127dd473Swhl739
4868127dd473Swhl739 while ( av[0] != NULL ) {
4869127dd473Swhl739 if ((val = match_token(limit_masks, *av)) <= 0)
4870127dd473Swhl739 break;
4871127dd473Swhl739 c->limit_mask |= val;
4872127dd473Swhl739 av++;
4873127dd473Swhl739 }
4874127dd473Swhl739
4875127dd473Swhl739 if (c->limit_mask == 0)
4876127dd473Swhl739 errx(EX_USAGE, "limit: missing limit mask");
4877127dd473Swhl739
4878127dd473Swhl739 GET_UINT_ARG(c->conn_limit, IPFW_ARG_MIN, IPFW_ARG_MAX,
4879127dd473Swhl739 TOK_LIMIT, rule_options);
4880127dd473Swhl739 av++;
488122ce4affSfengbojiang
488222ce4affSfengbojiang if (*av != NULL && *av[0] == ':') {
488322ce4affSfengbojiang if (state_check_name(*av + 1) != 0)
488422ce4affSfengbojiang errx(EX_DATAERR,
488522ce4affSfengbojiang "Invalid state name %s", *av);
488622ce4affSfengbojiang cmd->arg1 = pack_object(tstate, *av + 1,
488722ce4affSfengbojiang IPFW_TLV_STATE_NAME);
488822ce4affSfengbojiang av++;
488922ce4affSfengbojiang } else
489022ce4affSfengbojiang cmd->arg1 = pack_object(tstate,
489122ce4affSfengbojiang default_state_name, IPFW_TLV_STATE_NAME);
4892127dd473Swhl739 break;
4893127dd473Swhl739 }
4894127dd473Swhl739
4895127dd473Swhl739 case TOK_PROTO:
4896127dd473Swhl739 NEED1("missing protocol");
4897127dd473Swhl739 if (add_proto(cmd, *av, &proto)) {
4898127dd473Swhl739 av++;
4899127dd473Swhl739 } else
4900127dd473Swhl739 errx(EX_DATAERR, "invalid protocol ``%s''",
4901127dd473Swhl739 *av);
4902127dd473Swhl739 break;
4903127dd473Swhl739
4904127dd473Swhl739 case TOK_SRCIP:
4905127dd473Swhl739 NEED1("missing source IP");
4906127dd473Swhl739 if (add_srcip(cmd, *av, cblen, tstate)) {
4907127dd473Swhl739 av++;
4908127dd473Swhl739 }
4909127dd473Swhl739 break;
4910127dd473Swhl739
4911127dd473Swhl739 case TOK_DSTIP:
4912127dd473Swhl739 NEED1("missing destination IP");
4913127dd473Swhl739 if (add_dstip(cmd, *av, cblen, tstate)) {
4914127dd473Swhl739 av++;
4915127dd473Swhl739 }
4916127dd473Swhl739 break;
4917127dd473Swhl739
4918127dd473Swhl739 case TOK_SRCIP6:
4919127dd473Swhl739 NEED1("missing source IP6");
492022ce4affSfengbojiang if (add_srcip6(cmd, *av, cblen, tstate)) {
4921127dd473Swhl739 av++;
4922127dd473Swhl739 }
4923127dd473Swhl739 break;
4924127dd473Swhl739
4925127dd473Swhl739 case TOK_DSTIP6:
4926127dd473Swhl739 NEED1("missing destination IP6");
492722ce4affSfengbojiang if (add_dstip6(cmd, *av, cblen, tstate)) {
4928127dd473Swhl739 av++;
4929127dd473Swhl739 }
4930127dd473Swhl739 break;
4931127dd473Swhl739
4932127dd473Swhl739 case TOK_SRCPORT:
4933127dd473Swhl739 NEED1("missing source port");
4934127dd473Swhl739 if (_substrcmp(*av, "any") == 0 ||
4935127dd473Swhl739 add_ports(cmd, *av, proto, O_IP_SRCPORT, cblen)) {
4936127dd473Swhl739 av++;
4937127dd473Swhl739 } else
4938127dd473Swhl739 errx(EX_DATAERR, "invalid source port %s", *av);
4939127dd473Swhl739 break;
4940127dd473Swhl739
4941127dd473Swhl739 case TOK_DSTPORT:
4942127dd473Swhl739 NEED1("missing destination port");
4943127dd473Swhl739 if (_substrcmp(*av, "any") == 0 ||
4944127dd473Swhl739 add_ports(cmd, *av, proto, O_IP_DSTPORT, cblen)) {
4945127dd473Swhl739 av++;
4946127dd473Swhl739 } else
4947127dd473Swhl739 errx(EX_DATAERR, "invalid destination port %s",
4948127dd473Swhl739 *av);
4949127dd473Swhl739 break;
4950127dd473Swhl739
4951127dd473Swhl739 case TOK_MAC:
4952127dd473Swhl739 if (add_mac(cmd, av, cblen))
4953127dd473Swhl739 av += 2;
4954127dd473Swhl739 break;
4955127dd473Swhl739
4956127dd473Swhl739 case TOK_MACTYPE:
4957127dd473Swhl739 NEED1("missing mac type");
4958127dd473Swhl739 if (!add_mactype(cmd, *av, cblen))
4959127dd473Swhl739 errx(EX_DATAERR, "invalid mac type %s", *av);
4960127dd473Swhl739 av++;
4961127dd473Swhl739 break;
4962127dd473Swhl739
4963127dd473Swhl739 case TOK_VERREVPATH:
4964127dd473Swhl739 fill_cmd(cmd, O_VERREVPATH, 0, 0);
4965127dd473Swhl739 break;
4966127dd473Swhl739
4967127dd473Swhl739 case TOK_VERSRCREACH:
4968127dd473Swhl739 fill_cmd(cmd, O_VERSRCREACH, 0, 0);
4969127dd473Swhl739 break;
4970127dd473Swhl739
4971127dd473Swhl739 case TOK_ANTISPOOF:
4972127dd473Swhl739 fill_cmd(cmd, O_ANTISPOOF, 0, 0);
4973127dd473Swhl739 break;
4974127dd473Swhl739
4975127dd473Swhl739 case TOK_IPSEC:
4976127dd473Swhl739 fill_cmd(cmd, O_IPSEC, 0, 0);
4977127dd473Swhl739 break;
4978127dd473Swhl739
4979127dd473Swhl739 case TOK_IPV6:
4980127dd473Swhl739 fill_cmd(cmd, O_IP6, 0, 0);
4981127dd473Swhl739 break;
4982127dd473Swhl739
4983127dd473Swhl739 case TOK_IPV4:
4984127dd473Swhl739 fill_cmd(cmd, O_IP4, 0, 0);
4985127dd473Swhl739 break;
4986127dd473Swhl739
4987127dd473Swhl739 case TOK_EXT6HDR:
498822ce4affSfengbojiang NEED1("missing extension header");
4989127dd473Swhl739 fill_ext6hdr( cmd, *av );
4990127dd473Swhl739 av++;
4991127dd473Swhl739 break;
4992127dd473Swhl739
4993127dd473Swhl739 case TOK_FLOWID:
4994127dd473Swhl739 if (proto != IPPROTO_IPV6 )
4995127dd473Swhl739 errx( EX_USAGE, "flow-id filter is active "
4996127dd473Swhl739 "only for ipv6 protocol\n");
4997127dd473Swhl739 fill_flow6( (ipfw_insn_u32 *) cmd, *av, cblen);
4998127dd473Swhl739 av++;
4999127dd473Swhl739 break;
5000127dd473Swhl739
5001127dd473Swhl739 case TOK_COMMENT:
5002127dd473Swhl739 fill_comment(cmd, av, cblen);
5003127dd473Swhl739 av[0]=NULL;
5004127dd473Swhl739 break;
5005127dd473Swhl739
5006127dd473Swhl739 case TOK_TAGGED:
5007127dd473Swhl739 if (av[0] && strpbrk(*av, "-,")) {
5008127dd473Swhl739 if (!add_ports(cmd, *av, 0, O_TAGGED, cblen))
5009127dd473Swhl739 errx(EX_DATAERR, "tagged: invalid tag"
5010127dd473Swhl739 " list: %s", *av);
5011127dd473Swhl739 }
5012127dd473Swhl739 else {
5013127dd473Swhl739 uint16_t tag;
5014127dd473Swhl739
5015127dd473Swhl739 GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX,
5016127dd473Swhl739 TOK_TAGGED, rule_options);
5017127dd473Swhl739 fill_cmd(cmd, O_TAGGED, 0, tag);
5018127dd473Swhl739 }
5019127dd473Swhl739 av++;
5020127dd473Swhl739 break;
5021127dd473Swhl739
5022127dd473Swhl739 case TOK_FIB:
5023127dd473Swhl739 NEED1("fib requires fib number");
5024127dd473Swhl739 fill_cmd(cmd, O_FIB, 0, strtoul(*av, NULL, 0));
5025127dd473Swhl739 av++;
5026127dd473Swhl739 break;
5027127dd473Swhl739 case TOK_SOCKARG:
5028127dd473Swhl739 fill_cmd(cmd, O_SOCKARG, 0, 0);
5029127dd473Swhl739 break;
5030127dd473Swhl739
5031127dd473Swhl739 case TOK_LOOKUP: {
5032127dd473Swhl739 ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd;
5033127dd473Swhl739 int j;
5034127dd473Swhl739
5035127dd473Swhl739 if (!av[0] || !av[1])
5036127dd473Swhl739 errx(EX_USAGE, "format: lookup argument tablenum");
5037127dd473Swhl739 cmd->opcode = O_IP_DST_LOOKUP;
5038127dd473Swhl739 cmd->len |= F_INSN_SIZE(ipfw_insn) + 2;
5039127dd473Swhl739 i = match_token(rule_options, *av);
5040127dd473Swhl739 for (j = 0; lookup_key[j] >= 0 ; j++) {
5041127dd473Swhl739 if (i == lookup_key[j])
5042127dd473Swhl739 break;
5043127dd473Swhl739 }
5044127dd473Swhl739 if (lookup_key[j] <= 0)
5045127dd473Swhl739 errx(EX_USAGE, "format: cannot lookup on %s", *av);
5046127dd473Swhl739 __PAST_END(c->d, 1) = j; // i converted to option
5047127dd473Swhl739 av++;
5048127dd473Swhl739
5049127dd473Swhl739 if ((j = pack_table(tstate, *av)) == 0)
5050127dd473Swhl739 errx(EX_DATAERR, "Invalid table name: %s", *av);
5051127dd473Swhl739
5052127dd473Swhl739 cmd->arg1 = j;
5053127dd473Swhl739 av++;
5054127dd473Swhl739 }
5055127dd473Swhl739 break;
5056127dd473Swhl739 case TOK_FLOW:
5057127dd473Swhl739 NEED1("missing table name");
5058127dd473Swhl739 if (strncmp(*av, "table(", 6) != 0)
5059127dd473Swhl739 errx(EX_DATAERR,
5060127dd473Swhl739 "enclose table name into \"table()\"");
5061127dd473Swhl739 fill_table(cmd, *av, O_IP_FLOW_LOOKUP, tstate);
5062127dd473Swhl739 av++;
5063127dd473Swhl739 break;
5064127dd473Swhl739
506522ce4affSfengbojiang case TOK_SKIPACTION:
506622ce4affSfengbojiang if (have_skipcmd)
506722ce4affSfengbojiang errx(EX_USAGE, "only one defer-action "
506822ce4affSfengbojiang "is allowed");
506922ce4affSfengbojiang have_skipcmd = cmd;
507022ce4affSfengbojiang fill_cmd(cmd, O_SKIP_ACTION, 0, 0);
507122ce4affSfengbojiang break;
507222ce4affSfengbojiang
5073127dd473Swhl739 default:
5074127dd473Swhl739 errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
5075127dd473Swhl739 }
5076127dd473Swhl739 if (F_LEN(cmd) > 0) { /* prepare to advance */
5077127dd473Swhl739 prev = cmd;
5078127dd473Swhl739 cmd = next_cmd(cmd, &cblen);
5079127dd473Swhl739 }
5080127dd473Swhl739 }
5081127dd473Swhl739
5082127dd473Swhl739 done:
508322ce4affSfengbojiang
508422ce4affSfengbojiang if (!have_state && have_skipcmd)
508522ce4affSfengbojiang warnx("Rule contains \"defer-immediate-action\" "
508622ce4affSfengbojiang "and doesn't contain any state-related options.");
508722ce4affSfengbojiang
5088127dd473Swhl739 /*
5089127dd473Swhl739 * Now copy stuff into the rule.
5090127dd473Swhl739 * If we have a keep-state option, the first instruction
5091127dd473Swhl739 * must be a PROBE_STATE (which is generated here).
5092127dd473Swhl739 * If we have a LOG option, it was stored as the first command,
5093127dd473Swhl739 * and now must be moved to the top of the action part.
5094127dd473Swhl739 */
5095127dd473Swhl739 dst = (ipfw_insn *)rule->cmd;
5096127dd473Swhl739
5097127dd473Swhl739 /*
5098127dd473Swhl739 * First thing to write into the command stream is the match probability.
5099127dd473Swhl739 */
5100127dd473Swhl739 if (match_prob != 1) { /* 1 means always match */
5101127dd473Swhl739 dst->opcode = O_PROB;
5102127dd473Swhl739 dst->len = 2;
5103127dd473Swhl739 *((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff);
5104127dd473Swhl739 dst += dst->len;
5105127dd473Swhl739 }
5106127dd473Swhl739
5107127dd473Swhl739 /*
5108127dd473Swhl739 * generate O_PROBE_STATE if necessary
5109127dd473Swhl739 */
511022ce4affSfengbojiang if (have_state && have_state->opcode != O_CHECK_STATE && !have_rstate) {
511122ce4affSfengbojiang fill_cmd(dst, O_PROBE_STATE, 0, have_state->arg1);
5112127dd473Swhl739 dst = next_cmd(dst, &rblen);
5113127dd473Swhl739 }
5114127dd473Swhl739
511522ce4affSfengbojiang /*
511622ce4affSfengbojiang * copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG,
511722ce4affSfengbojiang * O_SKIP_ACTION
511822ce4affSfengbojiang */
5119127dd473Swhl739 for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
5120127dd473Swhl739 i = F_LEN(src);
5121127dd473Swhl739 CHECK_RBUFLEN(i);
5122127dd473Swhl739
5123127dd473Swhl739 switch (src->opcode) {
5124127dd473Swhl739 case O_LOG:
5125127dd473Swhl739 case O_KEEP_STATE:
5126127dd473Swhl739 case O_LIMIT:
5127127dd473Swhl739 case O_ALTQ:
5128127dd473Swhl739 case O_TAG:
512922ce4affSfengbojiang case O_SKIP_ACTION:
5130127dd473Swhl739 break;
5131127dd473Swhl739 default:
5132127dd473Swhl739 bcopy(src, dst, i * sizeof(uint32_t));
5133127dd473Swhl739 dst += i;
5134127dd473Swhl739 }
5135127dd473Swhl739 }
5136127dd473Swhl739
5137127dd473Swhl739 /*
5138127dd473Swhl739 * put back the have_state command as last opcode
5139127dd473Swhl739 */
5140127dd473Swhl739 if (have_state && have_state->opcode != O_CHECK_STATE) {
5141127dd473Swhl739 i = F_LEN(have_state);
5142127dd473Swhl739 CHECK_RBUFLEN(i);
5143127dd473Swhl739 bcopy(have_state, dst, i * sizeof(uint32_t));
5144127dd473Swhl739 dst += i;
5145127dd473Swhl739 }
514622ce4affSfengbojiang
514722ce4affSfengbojiang /*
514822ce4affSfengbojiang * put back the have_skipcmd command as very last opcode
514922ce4affSfengbojiang */
515022ce4affSfengbojiang if (have_skipcmd) {
515122ce4affSfengbojiang i = F_LEN(have_skipcmd);
515222ce4affSfengbojiang CHECK_RBUFLEN(i);
515322ce4affSfengbojiang bcopy(have_skipcmd, dst, i * sizeof(uint32_t));
515422ce4affSfengbojiang dst += i;
515522ce4affSfengbojiang }
515622ce4affSfengbojiang
5157127dd473Swhl739 /*
5158127dd473Swhl739 * start action section
5159127dd473Swhl739 */
5160127dd473Swhl739 rule->act_ofs = dst - rule->cmd;
5161127dd473Swhl739
5162127dd473Swhl739 /* put back O_LOG, O_ALTQ, O_TAG if necessary */
5163127dd473Swhl739 if (have_log) {
5164127dd473Swhl739 i = F_LEN(have_log);
5165127dd473Swhl739 CHECK_RBUFLEN(i);
5166127dd473Swhl739 bcopy(have_log, dst, i * sizeof(uint32_t));
5167127dd473Swhl739 dst += i;
5168127dd473Swhl739 }
5169127dd473Swhl739 if (have_altq) {
5170127dd473Swhl739 i = F_LEN(have_altq);
5171127dd473Swhl739 CHECK_RBUFLEN(i);
5172127dd473Swhl739 bcopy(have_altq, dst, i * sizeof(uint32_t));
5173127dd473Swhl739 dst += i;
5174127dd473Swhl739 }
5175127dd473Swhl739 if (have_tag) {
5176127dd473Swhl739 i = F_LEN(have_tag);
5177127dd473Swhl739 CHECK_RBUFLEN(i);
5178127dd473Swhl739 bcopy(have_tag, dst, i * sizeof(uint32_t));
5179127dd473Swhl739 dst += i;
5180127dd473Swhl739 }
5181127dd473Swhl739
5182127dd473Swhl739 /*
5183127dd473Swhl739 * copy all other actions
5184127dd473Swhl739 */
5185127dd473Swhl739 for (src = (ipfw_insn *)actbuf; src != action; src += i) {
5186127dd473Swhl739 i = F_LEN(src);
5187127dd473Swhl739 CHECK_RBUFLEN(i);
5188127dd473Swhl739 bcopy(src, dst, i * sizeof(uint32_t));
5189127dd473Swhl739 dst += i;
5190127dd473Swhl739 }
5191127dd473Swhl739
5192127dd473Swhl739 rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd);
5193127dd473Swhl739 *rbufsize = (char *)dst - (char *)rule;
5194127dd473Swhl739 }
5195127dd473Swhl739
5196127dd473Swhl739 static int
compare_ntlv(const void * _a,const void * _b)5197127dd473Swhl739 compare_ntlv(const void *_a, const void *_b)
5198127dd473Swhl739 {
519922ce4affSfengbojiang const ipfw_obj_ntlv *a, *b;
5200127dd473Swhl739
520122ce4affSfengbojiang a = (const ipfw_obj_ntlv *)_a;
520222ce4affSfengbojiang b = (const ipfw_obj_ntlv *)_b;
5203127dd473Swhl739
5204127dd473Swhl739 if (a->set < b->set)
5205127dd473Swhl739 return (-1);
5206127dd473Swhl739 else if (a->set > b->set)
5207127dd473Swhl739 return (1);
5208127dd473Swhl739
5209127dd473Swhl739 if (a->idx < b->idx)
5210127dd473Swhl739 return (-1);
5211127dd473Swhl739 else if (a->idx > b->idx)
5212127dd473Swhl739 return (1);
5213127dd473Swhl739
5214127dd473Swhl739 if (a->head.type < b->head.type)
5215127dd473Swhl739 return (-1);
5216127dd473Swhl739 else if (a->head.type > b->head.type)
5217127dd473Swhl739 return (1);
5218127dd473Swhl739
5219127dd473Swhl739 return (0);
5220127dd473Swhl739 }
5221127dd473Swhl739
5222127dd473Swhl739 /*
5223127dd473Swhl739 * Provide kernel with sorted list of referenced objects
5224127dd473Swhl739 */
5225127dd473Swhl739 static void
object_sort_ctlv(ipfw_obj_ctlv * ctlv)5226127dd473Swhl739 object_sort_ctlv(ipfw_obj_ctlv *ctlv)
5227127dd473Swhl739 {
5228127dd473Swhl739
5229127dd473Swhl739 qsort(ctlv + 1, ctlv->count, ctlv->objsize, compare_ntlv);
5230127dd473Swhl739 }
5231127dd473Swhl739
5232127dd473Swhl739 struct object_kt {
5233127dd473Swhl739 uint16_t uidx;
5234127dd473Swhl739 uint16_t type;
5235127dd473Swhl739 };
5236127dd473Swhl739 static int
compare_object_kntlv(const void * k,const void * v)5237127dd473Swhl739 compare_object_kntlv(const void *k, const void *v)
5238127dd473Swhl739 {
523922ce4affSfengbojiang const ipfw_obj_ntlv *ntlv;
5240127dd473Swhl739 struct object_kt key;
5241127dd473Swhl739
524222ce4affSfengbojiang key = *((const struct object_kt *)k);
524322ce4affSfengbojiang ntlv = (const ipfw_obj_ntlv *)v;
5244127dd473Swhl739
5245127dd473Swhl739 if (key.uidx < ntlv->idx)
5246127dd473Swhl739 return (-1);
5247127dd473Swhl739 else if (key.uidx > ntlv->idx)
5248127dd473Swhl739 return (1);
5249127dd473Swhl739
5250127dd473Swhl739 if (key.type < ntlv->head.type)
5251127dd473Swhl739 return (-1);
5252127dd473Swhl739 else if (key.type > ntlv->head.type)
5253127dd473Swhl739 return (1);
5254127dd473Swhl739
5255127dd473Swhl739 return (0);
5256127dd473Swhl739 }
5257127dd473Swhl739
5258127dd473Swhl739 /*
5259127dd473Swhl739 * Finds object name in @ctlv by @idx and @type.
5260127dd473Swhl739 * Uses the following facts:
5261127dd473Swhl739 * 1) All TLVs are the same size
5262127dd473Swhl739 * 2) Kernel implementation provides already sorted list.
5263127dd473Swhl739 *
5264127dd473Swhl739 * Returns table name or NULL.
5265127dd473Swhl739 */
5266127dd473Swhl739 static char *
object_search_ctlv(ipfw_obj_ctlv * ctlv,uint16_t idx,uint16_t type)5267127dd473Swhl739 object_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx, uint16_t type)
5268127dd473Swhl739 {
5269127dd473Swhl739 ipfw_obj_ntlv *ntlv;
5270127dd473Swhl739 struct object_kt key;
5271127dd473Swhl739
5272127dd473Swhl739 key.uidx = idx;
5273127dd473Swhl739 key.type = type;
5274127dd473Swhl739
5275127dd473Swhl739 ntlv = bsearch(&key, (ctlv + 1), ctlv->count, ctlv->objsize,
5276127dd473Swhl739 compare_object_kntlv);
5277127dd473Swhl739
5278127dd473Swhl739 if (ntlv != NULL)
5279127dd473Swhl739 return (ntlv->name);
5280127dd473Swhl739
5281127dd473Swhl739 return (NULL);
5282127dd473Swhl739 }
5283127dd473Swhl739
5284127dd473Swhl739 static char *
table_search_ctlv(ipfw_obj_ctlv * ctlv,uint16_t idx)5285127dd473Swhl739 table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx)
5286127dd473Swhl739 {
5287127dd473Swhl739
5288127dd473Swhl739 return (object_search_ctlv(ctlv, idx, IPFW_TLV_TBL_NAME));
5289127dd473Swhl739 }
5290127dd473Swhl739
5291127dd473Swhl739 /*
5292127dd473Swhl739 * Adds one or more rules to ipfw chain.
5293127dd473Swhl739 * Data layout:
5294127dd473Swhl739 * Request:
5295127dd473Swhl739 * [
5296127dd473Swhl739 * ip_fw3_opheader
5297127dd473Swhl739 * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1)
5298127dd473Swhl739 * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) [ ip_fw_rule ip_fw_insn ] x N ] (*2) (*3)
5299127dd473Swhl739 * ]
5300127dd473Swhl739 * Reply:
5301127dd473Swhl739 * [
5302127dd473Swhl739 * ip_fw3_opheader
5303127dd473Swhl739 * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional)
5304127dd473Swhl739 * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) [ ip_fw_rule ip_fw_insn ] x N ]
5305127dd473Swhl739 * ]
5306127dd473Swhl739 *
5307127dd473Swhl739 * Rules in reply are modified to store their actual ruleset number.
5308127dd473Swhl739 *
5309127dd473Swhl739 * (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending
5310127dd473Swhl739 * according to their idx field and there has to be no duplicates.
5311127dd473Swhl739 * (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending.
5312127dd473Swhl739 * (*3) Each ip_fw structure needs to be aligned to u64 boundary.
5313127dd473Swhl739 */
5314127dd473Swhl739 void
ipfw_add(char * av[])5315127dd473Swhl739 ipfw_add(char *av[])
5316127dd473Swhl739 {
5317127dd473Swhl739 uint32_t rulebuf[1024];
5318127dd473Swhl739 int rbufsize, default_off, tlen, rlen;
5319127dd473Swhl739 size_t sz;
5320127dd473Swhl739 struct tidx ts;
5321127dd473Swhl739 struct ip_fw_rule *rule;
5322127dd473Swhl739 caddr_t tbuf;
5323127dd473Swhl739 ip_fw3_opheader *op3;
5324127dd473Swhl739 ipfw_obj_ctlv *ctlv, *tstate;
5325127dd473Swhl739
5326127dd473Swhl739 rbufsize = sizeof(rulebuf);
5327127dd473Swhl739 memset(rulebuf, 0, rbufsize);
5328127dd473Swhl739 memset(&ts, 0, sizeof(ts));
5329127dd473Swhl739
5330127dd473Swhl739 /* Optimize case with no tables */
5331127dd473Swhl739 default_off = sizeof(ipfw_obj_ctlv) + sizeof(ip_fw3_opheader);
5332127dd473Swhl739 op3 = (ip_fw3_opheader *)rulebuf;
5333127dd473Swhl739 ctlv = (ipfw_obj_ctlv *)(op3 + 1);
5334127dd473Swhl739 rule = (struct ip_fw_rule *)(ctlv + 1);
5335127dd473Swhl739 rbufsize -= default_off;
5336127dd473Swhl739
5337127dd473Swhl739 compile_rule(av, (uint32_t *)rule, &rbufsize, &ts);
5338127dd473Swhl739 /* Align rule size to u64 boundary */
5339127dd473Swhl739 rlen = roundup2(rbufsize, sizeof(uint64_t));
5340127dd473Swhl739
5341127dd473Swhl739 tbuf = NULL;
5342127dd473Swhl739 sz = 0;
5343127dd473Swhl739 tstate = NULL;
5344127dd473Swhl739 if (ts.count != 0) {
5345127dd473Swhl739 /* Some tables. We have to alloc more data */
5346127dd473Swhl739 tlen = ts.count * sizeof(ipfw_obj_ntlv);
5347127dd473Swhl739 sz = default_off + sizeof(ipfw_obj_ctlv) + tlen + rlen;
5348127dd473Swhl739
5349127dd473Swhl739 if ((tbuf = calloc(1, sz)) == NULL)
5350127dd473Swhl739 err(EX_UNAVAILABLE, "malloc() failed for IP_FW_ADD");
5351127dd473Swhl739 op3 = (ip_fw3_opheader *)tbuf;
5352127dd473Swhl739 /* Tables first */
5353127dd473Swhl739 ctlv = (ipfw_obj_ctlv *)(op3 + 1);
5354127dd473Swhl739 ctlv->head.type = IPFW_TLV_TBLNAME_LIST;
5355127dd473Swhl739 ctlv->head.length = sizeof(ipfw_obj_ctlv) + tlen;
5356127dd473Swhl739 ctlv->count = ts.count;
5357127dd473Swhl739 ctlv->objsize = sizeof(ipfw_obj_ntlv);
5358127dd473Swhl739 memcpy(ctlv + 1, ts.idx, tlen);
5359127dd473Swhl739 object_sort_ctlv(ctlv);
5360127dd473Swhl739 tstate = ctlv;
5361127dd473Swhl739 /* Rule next */
5362127dd473Swhl739 ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
5363127dd473Swhl739 ctlv->head.type = IPFW_TLV_RULE_LIST;
5364127dd473Swhl739 ctlv->head.length = sizeof(ipfw_obj_ctlv) + rlen;
5365127dd473Swhl739 ctlv->count = 1;
5366127dd473Swhl739 memcpy(ctlv + 1, rule, rbufsize);
5367127dd473Swhl739 } else {
5368127dd473Swhl739 /* Simply add header */
5369127dd473Swhl739 sz = rlen + default_off;
5370127dd473Swhl739 memset(ctlv, 0, sizeof(*ctlv));
5371127dd473Swhl739 ctlv->head.type = IPFW_TLV_RULE_LIST;
5372127dd473Swhl739 ctlv->head.length = sizeof(ipfw_obj_ctlv) + rlen;
5373127dd473Swhl739 ctlv->count = 1;
5374127dd473Swhl739 }
5375127dd473Swhl739
5376127dd473Swhl739 if (do_get3(IP_FW_XADD, op3, &sz) != 0)
5377127dd473Swhl739 err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_XADD");
5378127dd473Swhl739
537922ce4affSfengbojiang if (!g_co.do_quiet) {
5380127dd473Swhl739 struct format_opts sfo;
5381127dd473Swhl739 struct buf_pr bp;
5382127dd473Swhl739 memset(&sfo, 0, sizeof(sfo));
5383127dd473Swhl739 sfo.tstate = tstate;
5384127dd473Swhl739 sfo.set_mask = (uint32_t)(-1);
5385127dd473Swhl739 bp_alloc(&bp, 4096);
538622ce4affSfengbojiang show_static_rule(&g_co, &sfo, &bp, rule, NULL);
5387127dd473Swhl739 printf("%s", bp.buf);
5388127dd473Swhl739 bp_free(&bp);
5389127dd473Swhl739 }
5390127dd473Swhl739
5391127dd473Swhl739 if (tbuf != NULL)
5392127dd473Swhl739 free(tbuf);
5393127dd473Swhl739
5394127dd473Swhl739 if (ts.idx != NULL)
5395127dd473Swhl739 free(ts.idx);
5396127dd473Swhl739 }
5397127dd473Swhl739
5398127dd473Swhl739 /*
5399127dd473Swhl739 * clear the counters or the log counters.
5400127dd473Swhl739 * optname has the following values:
5401127dd473Swhl739 * 0 (zero both counters and logging)
5402127dd473Swhl739 * 1 (zero logging only)
5403127dd473Swhl739 */
5404127dd473Swhl739 void
ipfw_zero(int ac,char * av[],int optname)5405127dd473Swhl739 ipfw_zero(int ac, char *av[], int optname)
5406127dd473Swhl739 {
5407127dd473Swhl739 ipfw_range_tlv rt;
5408127dd473Swhl739 char const *errstr;
5409127dd473Swhl739 char const *name = optname ? "RESETLOG" : "ZERO";
541022ce4affSfengbojiang uint32_t arg;
541122ce4affSfengbojiang int failed = EX_OK;
5412127dd473Swhl739
5413127dd473Swhl739 optname = optname ? IP_FW_XRESETLOG : IP_FW_XZERO;
5414127dd473Swhl739 av++; ac--;
5415127dd473Swhl739
5416127dd473Swhl739 if (ac == 0) {
5417127dd473Swhl739 /* clear all entries */
541822ce4affSfengbojiang memset(&rt, 0, sizeof(rt));
5419127dd473Swhl739 rt.flags = IPFW_RCFLAG_ALL;
5420127dd473Swhl739 if (do_range_cmd(optname, &rt) < 0)
5421127dd473Swhl739 err(EX_UNAVAILABLE, "setsockopt(IP_FW_X%s)", name);
542222ce4affSfengbojiang if (!g_co.do_quiet)
5423127dd473Swhl739 printf("%s.\n", optname == IP_FW_XZERO ?
5424127dd473Swhl739 "Accounting cleared":"Logging counts reset");
5425127dd473Swhl739
5426127dd473Swhl739 return;
5427127dd473Swhl739 }
5428127dd473Swhl739
5429127dd473Swhl739 while (ac) {
5430127dd473Swhl739 /* Rule number */
5431127dd473Swhl739 if (isdigit(**av)) {
5432127dd473Swhl739 arg = strtonum(*av, 0, 0xffff, &errstr);
5433127dd473Swhl739 if (errstr)
5434127dd473Swhl739 errx(EX_DATAERR,
5435127dd473Swhl739 "invalid rule number %s\n", *av);
543622ce4affSfengbojiang memset(&rt, 0, sizeof(rt));
5437127dd473Swhl739 rt.start_rule = arg;
5438127dd473Swhl739 rt.end_rule = arg;
5439127dd473Swhl739 rt.flags |= IPFW_RCFLAG_RANGE;
544022ce4affSfengbojiang if (g_co.use_set != 0) {
544122ce4affSfengbojiang rt.set = g_co.use_set - 1;
5442127dd473Swhl739 rt.flags |= IPFW_RCFLAG_SET;
5443127dd473Swhl739 }
5444127dd473Swhl739 if (do_range_cmd(optname, &rt) != 0) {
5445127dd473Swhl739 warn("rule %u: setsockopt(IP_FW_X%s)",
5446127dd473Swhl739 arg, name);
5447127dd473Swhl739 failed = EX_UNAVAILABLE;
5448127dd473Swhl739 } else if (rt.new_set == 0) {
5449127dd473Swhl739 printf("Entry %d not found\n", arg);
5450127dd473Swhl739 failed = EX_UNAVAILABLE;
545122ce4affSfengbojiang } else if (!g_co.do_quiet)
5452127dd473Swhl739 printf("Entry %d %s.\n", arg,
5453127dd473Swhl739 optname == IP_FW_XZERO ?
5454127dd473Swhl739 "cleared" : "logging count reset");
5455127dd473Swhl739 } else {
5456127dd473Swhl739 errx(EX_USAGE, "invalid rule number ``%s''", *av);
5457127dd473Swhl739 }
5458127dd473Swhl739 av++; ac--;
5459127dd473Swhl739 }
5460127dd473Swhl739 if (failed != EX_OK)
5461127dd473Swhl739 exit(failed);
5462127dd473Swhl739 }
5463127dd473Swhl739
5464127dd473Swhl739 void
ipfw_flush(int force)5465127dd473Swhl739 ipfw_flush(int force)
5466127dd473Swhl739 {
5467127dd473Swhl739 ipfw_range_tlv rt;
5468127dd473Swhl739
546922ce4affSfengbojiang if (!force && !g_co.do_quiet) { /* need to ask user */
5470127dd473Swhl739 int c;
5471127dd473Swhl739
5472127dd473Swhl739 printf("Are you sure? [yn] ");
5473127dd473Swhl739 fflush(stdout);
5474127dd473Swhl739 do {
5475127dd473Swhl739 c = toupper(getc(stdin));
5476127dd473Swhl739 while (c != '\n' && getc(stdin) != '\n')
5477127dd473Swhl739 if (feof(stdin))
5478127dd473Swhl739 return; /* and do not flush */
5479127dd473Swhl739 } while (c != 'Y' && c != 'N');
5480127dd473Swhl739 printf("\n");
5481127dd473Swhl739 if (c == 'N') /* user said no */
5482127dd473Swhl739 return;
5483127dd473Swhl739 }
548422ce4affSfengbojiang if (g_co.do_pipe) {
5485*d4a07e70Sfengbojiang #ifdef DUMMYNET
5486127dd473Swhl739 dummynet_flush();
5487*d4a07e70Sfengbojiang #else
5488*d4a07e70Sfengbojiang fprintf(stderr, "dummynet_flush not supported\n");
5489*d4a07e70Sfengbojiang #endif
5490127dd473Swhl739 return;
5491127dd473Swhl739 }
5492127dd473Swhl739 /* `ipfw set N flush` - is the same that `ipfw delete set N` */
5493127dd473Swhl739 memset(&rt, 0, sizeof(rt));
549422ce4affSfengbojiang if (g_co.use_set != 0) {
549522ce4affSfengbojiang rt.set = g_co.use_set - 1;
5496127dd473Swhl739 rt.flags = IPFW_RCFLAG_SET;
5497127dd473Swhl739 } else
5498127dd473Swhl739 rt.flags = IPFW_RCFLAG_ALL;
5499127dd473Swhl739 if (do_range_cmd(IP_FW_XDEL, &rt) != 0)
5500127dd473Swhl739 err(EX_UNAVAILABLE, "setsockopt(IP_FW_XDEL)");
550122ce4affSfengbojiang if (!g_co.do_quiet)
550222ce4affSfengbojiang printf("Flushed all %s.\n", g_co.do_pipe ? "pipes" : "rules");
5503127dd473Swhl739 }
5504127dd473Swhl739
5505127dd473Swhl739 static struct _s_x intcmds[] = {
5506127dd473Swhl739 { "talist", TOK_TALIST },
5507127dd473Swhl739 { "iflist", TOK_IFLIST },
5508127dd473Swhl739 { "olist", TOK_OLIST },
5509127dd473Swhl739 { "vlist", TOK_VLIST },
5510127dd473Swhl739 { NULL, 0 }
5511127dd473Swhl739 };
5512127dd473Swhl739
5513127dd473Swhl739 static struct _s_x otypes[] = {
5514127dd473Swhl739 { "EACTION", IPFW_TLV_EACTION },
551522ce4affSfengbojiang { "DYNSTATE", IPFW_TLV_STATE_NAME },
5516127dd473Swhl739 { NULL, 0 }
5517127dd473Swhl739 };
5518127dd473Swhl739
5519127dd473Swhl739 static const char*
lookup_eaction_name(ipfw_obj_ntlv * ntlv,int cnt,uint16_t type)5520127dd473Swhl739 lookup_eaction_name(ipfw_obj_ntlv *ntlv, int cnt, uint16_t type)
5521127dd473Swhl739 {
5522127dd473Swhl739 const char *name;
5523127dd473Swhl739 int i;
5524127dd473Swhl739
5525127dd473Swhl739 name = NULL;
5526127dd473Swhl739 for (i = 0; i < cnt; i++) {
5527127dd473Swhl739 if (ntlv[i].head.type != IPFW_TLV_EACTION)
5528127dd473Swhl739 continue;
5529127dd473Swhl739 if (IPFW_TLV_EACTION_NAME(ntlv[i].idx) != type)
5530127dd473Swhl739 continue;
5531127dd473Swhl739 name = ntlv[i].name;
5532127dd473Swhl739 break;
5533127dd473Swhl739 }
5534127dd473Swhl739 return (name);
5535127dd473Swhl739 }
5536127dd473Swhl739
5537127dd473Swhl739 static void
ipfw_list_objects(int ac __unused,char * av[]__unused)553822ce4affSfengbojiang ipfw_list_objects(int ac __unused, char *av[] __unused)
5539127dd473Swhl739 {
5540127dd473Swhl739 ipfw_obj_lheader req, *olh;
5541127dd473Swhl739 ipfw_obj_ntlv *ntlv;
5542127dd473Swhl739 const char *name;
5543127dd473Swhl739 size_t sz;
554422ce4affSfengbojiang uint32_t i;
5545127dd473Swhl739
5546127dd473Swhl739 memset(&req, 0, sizeof(req));
5547127dd473Swhl739 sz = sizeof(req);
5548127dd473Swhl739 if (do_get3(IP_FW_DUMP_SRVOBJECTS, &req.opheader, &sz) != 0)
5549127dd473Swhl739 if (errno != ENOMEM)
5550127dd473Swhl739 return;
5551127dd473Swhl739
5552127dd473Swhl739 sz = req.size;
5553127dd473Swhl739 if ((olh = calloc(1, sz)) == NULL)
5554127dd473Swhl739 return;
5555127dd473Swhl739
5556127dd473Swhl739 olh->size = sz;
5557127dd473Swhl739 if (do_get3(IP_FW_DUMP_SRVOBJECTS, &olh->opheader, &sz) != 0) {
5558127dd473Swhl739 free(olh);
5559127dd473Swhl739 return;
5560127dd473Swhl739 }
5561127dd473Swhl739
5562127dd473Swhl739 if (olh->count > 0)
5563127dd473Swhl739 printf("Objects list:\n");
5564127dd473Swhl739 else
5565127dd473Swhl739 printf("There are no objects\n");
5566127dd473Swhl739 ntlv = (ipfw_obj_ntlv *)(olh + 1);
5567127dd473Swhl739 for (i = 0; i < olh->count; i++) {
5568127dd473Swhl739 name = match_value(otypes, ntlv->head.type);
5569127dd473Swhl739 if (name == NULL)
5570127dd473Swhl739 name = lookup_eaction_name(
5571127dd473Swhl739 (ipfw_obj_ntlv *)(olh + 1), olh->count,
5572127dd473Swhl739 ntlv->head.type);
5573127dd473Swhl739 if (name == NULL)
5574127dd473Swhl739 printf(" kidx: %4d\ttype: %10d\tname: %s\n",
5575127dd473Swhl739 ntlv->idx, ntlv->head.type, ntlv->name);
5576127dd473Swhl739 else
5577127dd473Swhl739 printf(" kidx: %4d\ttype: %10s\tname: %s\n",
5578127dd473Swhl739 ntlv->idx, name, ntlv->name);
5579127dd473Swhl739 ntlv++;
5580127dd473Swhl739 }
5581127dd473Swhl739 free(olh);
5582127dd473Swhl739 }
5583127dd473Swhl739
5584127dd473Swhl739 void
ipfw_internal_handler(int ac,char * av[])5585127dd473Swhl739 ipfw_internal_handler(int ac, char *av[])
5586127dd473Swhl739 {
5587127dd473Swhl739 int tcmd;
5588127dd473Swhl739
5589127dd473Swhl739 ac--; av++;
5590127dd473Swhl739 NEED1("internal cmd required");
5591127dd473Swhl739
5592127dd473Swhl739 if ((tcmd = match_token(intcmds, *av)) == -1)
5593127dd473Swhl739 errx(EX_USAGE, "invalid internal sub-cmd: %s", *av);
5594127dd473Swhl739
5595127dd473Swhl739 switch (tcmd) {
5596127dd473Swhl739 case TOK_IFLIST:
5597127dd473Swhl739 ipfw_list_tifaces();
5598127dd473Swhl739 break;
5599127dd473Swhl739 case TOK_TALIST:
5600127dd473Swhl739 ipfw_list_ta(ac, av);
5601127dd473Swhl739 break;
5602127dd473Swhl739 case TOK_OLIST:
5603127dd473Swhl739 ipfw_list_objects(ac, av);
5604127dd473Swhl739 break;
5605127dd473Swhl739 case TOK_VLIST:
5606127dd473Swhl739 ipfw_list_values(ac, av);
5607127dd473Swhl739 break;
5608127dd473Swhl739 }
5609127dd473Swhl739 }
5610127dd473Swhl739
5611127dd473Swhl739 static int
ipfw_get_tracked_ifaces(ipfw_obj_lheader ** polh)5612127dd473Swhl739 ipfw_get_tracked_ifaces(ipfw_obj_lheader **polh)
5613127dd473Swhl739 {
5614127dd473Swhl739 ipfw_obj_lheader req, *olh;
5615127dd473Swhl739 size_t sz;
5616127dd473Swhl739
5617127dd473Swhl739 memset(&req, 0, sizeof(req));
5618127dd473Swhl739 sz = sizeof(req);
5619127dd473Swhl739
5620127dd473Swhl739 if (do_get3(IP_FW_XIFLIST, &req.opheader, &sz) != 0) {
5621127dd473Swhl739 if (errno != ENOMEM)
5622127dd473Swhl739 return (errno);
5623127dd473Swhl739 }
5624127dd473Swhl739
5625127dd473Swhl739 sz = req.size;
5626127dd473Swhl739 if ((olh = calloc(1, sz)) == NULL)
5627127dd473Swhl739 return (ENOMEM);
5628127dd473Swhl739
5629127dd473Swhl739 olh->size = sz;
5630127dd473Swhl739 if (do_get3(IP_FW_XIFLIST, &olh->opheader, &sz) != 0) {
5631127dd473Swhl739 free(olh);
5632127dd473Swhl739 return (errno);
5633127dd473Swhl739 }
5634127dd473Swhl739
5635127dd473Swhl739 *polh = olh;
5636127dd473Swhl739 return (0);
5637127dd473Swhl739 }
5638127dd473Swhl739
5639127dd473Swhl739 static int
ifinfo_cmp(const void * a,const void * b)5640127dd473Swhl739 ifinfo_cmp(const void *a, const void *b)
5641127dd473Swhl739 {
564222ce4affSfengbojiang const ipfw_iface_info *ia, *ib;
5643127dd473Swhl739
564422ce4affSfengbojiang ia = (const ipfw_iface_info *)a;
564522ce4affSfengbojiang ib = (const ipfw_iface_info *)b;
5646127dd473Swhl739
5647127dd473Swhl739 return (stringnum_cmp(ia->ifname, ib->ifname));
5648127dd473Swhl739 }
5649127dd473Swhl739
5650127dd473Swhl739 /*
5651127dd473Swhl739 * Retrieves table list from kernel,
5652127dd473Swhl739 * optionally sorts it and calls requested function for each table.
5653127dd473Swhl739 * Returns 0 on success.
5654127dd473Swhl739 */
5655127dd473Swhl739 static void
ipfw_list_tifaces(void)565622ce4affSfengbojiang ipfw_list_tifaces(void)
5657127dd473Swhl739 {
565822ce4affSfengbojiang ipfw_obj_lheader *olh = NULL;
5659127dd473Swhl739 ipfw_iface_info *info;
566022ce4affSfengbojiang uint32_t i;
566122ce4affSfengbojiang int error;
5662127dd473Swhl739
5663127dd473Swhl739 if ((error = ipfw_get_tracked_ifaces(&olh)) != 0)
5664127dd473Swhl739 err(EX_OSERR, "Unable to request ipfw tracked interface list");
5665127dd473Swhl739
5666127dd473Swhl739 qsort(olh + 1, olh->count, olh->objsize, ifinfo_cmp);
5667127dd473Swhl739
5668127dd473Swhl739 info = (ipfw_iface_info *)(olh + 1);
5669127dd473Swhl739 for (i = 0; i < olh->count; i++) {
5670127dd473Swhl739 if (info->flags & IPFW_IFFLAG_RESOLVED)
5671127dd473Swhl739 printf("%s ifindex: %d refcount: %u changes: %u\n",
5672127dd473Swhl739 info->ifname, info->ifindex, info->refcnt,
5673127dd473Swhl739 info->gencnt);
5674127dd473Swhl739 else
5675127dd473Swhl739 printf("%s ifindex: unresolved refcount: %u changes: %u\n",
5676127dd473Swhl739 info->ifname, info->refcnt, info->gencnt);
5677127dd473Swhl739 info = (ipfw_iface_info *)((caddr_t)info + olh->objsize);
5678127dd473Swhl739 }
5679127dd473Swhl739
5680127dd473Swhl739 free(olh);
5681127dd473Swhl739 }
5682