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