ipfw: Add ipfrag filter.
[dragonfly.git] / sbin / ipfw / ipfw2.c
<
CommitLineData
984263bc
MD
1/*
2 * Copyright (c) 2002 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: src/sbin/ipfw/ipfw2.c,v 1.4.2.13 2003/05/27 22:21:11 gshapiro Exp $
21 */
22
23#include <sys/param.h>
24#include <sys/mbuf.h>
25#include <sys/socket.h>
26#include <sys/sockio.h>
27#include <sys/sysctl.h>
28#include <sys/time.h>
29#include <sys/wait.h>
30
31#include <ctype.h>
32#include <err.h>
33#include <errno.h>
34#include <grp.h>
35#include <limits.h>
36#include <netdb.h>
37#include <pwd.h>
38#include <signal.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <stdarg.h>
42#include <string.h>
790a83e5 43#include <timeconv.h>
984263bc
MD
44#include <unistd.h>
45#include <sysexits.h>
46
47#include <net/if.h>
48#include <netinet/in.h>
49#include <netinet/in_systm.h>
50#include <netinet/ip.h>
51#include <netinet/ip_icmp.h>
38a690d7 52#include <net/ipfw/ip_fw.h>
984263bc 53#include <net/route.h> /* def. of struct route */
f9edcb74 54#include <net/ethernet.h>
38a690d7 55#include <net/dummynet/ip_dummynet.h>
984263bc
MD
56#include <netinet/tcp.h>
57#include <arpa/inet.h>
58
59int s, /* main RAW socket */
60 do_resolv, /* Would try to resolve all */
61 do_acct, /* Show packet/byte count */
62 do_time, /* Show time stamps */
63 do_quiet, /* Be quiet in add and flush */
64 do_force, /* Don't ask for confirmation */
65 do_pipe, /* this cmd refers to a pipe */
d938108c 66 do_table, /* this cmd referes to a table */
984263bc
MD
67 do_sort, /* field to sort results (0 = no) */
68 do_dynamic, /* display dynamic rules */
69 do_expired, /* display expired dynamic rules */
70 do_compact, /* show rules in compact mode */
71 show_sets, /* display rule sets */
72 verbose;
73
74#define IP_MASK_ALL 0xffffffff
75
76/*
77 * structure to hold flag names and associated values to be
78 * set in the appropriate masks.
79 * A NULL string terminates the array.
80 * Often, an element with 0 value contains an error string.
81 *
82 */
83struct _s_x {
75a579b7 84 const char *s;
9c2d26d1 85 uint32_t x;
984263bc
MD
86};
87
88static struct _s_x f_tcpflags[] = {
89 { "syn", TH_SYN },
90 { "fin", TH_FIN },
91 { "ack", TH_ACK },
92 { "psh", TH_PUSH },
93 { "rst", TH_RST },
94 { "urg", TH_URG },
95 { "tcp flag", 0 },
96 { NULL, 0 }
97};
98
99static struct _s_x f_tcpopts[] = {
100 { "mss", IP_FW_TCPOPT_MSS },
101 { "maxseg", IP_FW_TCPOPT_MSS },
102 { "window", IP_FW_TCPOPT_WINDOW },
103 { "sack", IP_FW_TCPOPT_SACK },
104 { "ts", IP_FW_TCPOPT_TS },
105 { "timestamp", IP_FW_TCPOPT_TS },
106 { "cc", IP_FW_TCPOPT_CC },
107 { "tcp option", 0 },
108 { NULL, 0 }
109};
110
111/*
112 * IP options span the range 0 to 255 so we need to remap them
113 * (though in fact only the low 5 bits are significant).
114 */
115static struct _s_x f_ipopts[] = {
116 { "ssrr", IP_FW_IPOPT_SSRR},
117 { "lsrr", IP_FW_IPOPT_LSRR},
118 { "rr", IP_FW_IPOPT_RR},
119 { "ts", IP_FW_IPOPT_TS},
120 { "ip option", 0 },
121 { NULL, 0 }
122};
123
124static struct _s_x f_iptos[] = {
125 { "lowdelay", IPTOS_LOWDELAY},
126 { "throughput", IPTOS_THROUGHPUT},
127 { "reliability", IPTOS_RELIABILITY},
128 { "mincost", IPTOS_MINCOST},
129 { "congestion", IPTOS_CE},
130 { "ecntransport", IPTOS_ECT},
131 { "ip tos option", 0},
132 { NULL, 0 }
133};
134
135static struct _s_x limit_masks[] = {
136 {"all", DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
137 {"src-addr", DYN_SRC_ADDR},
138 {"src-port", DYN_SRC_PORT},
139 {"dst-addr", DYN_DST_ADDR},
140 {"dst-port", DYN_DST_PORT},
141 {NULL, 0}
142};
143
144/*
145 * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines
146 * This is only used in this code.
147 */
148#define IPPROTO_ETHERTYPE 0x1000
149static struct _s_x ether_types[] = {
150 /*
151 * Note, we cannot use "-:&/" in the names because they are field
152 * separators in the type specifications. Also, we use s = NULL as
153 * end-delimiter, because a type of 0 can be legal.
154 */
155 { "ip", 0x0800 },
156 { "ipv4", 0x0800 },
157 { "ipv6", 0x86dd },
158 { "arp", 0x0806 },
159 { "rarp", 0x8035 },
160 { "vlan", 0x8100 },
161 { "loop", 0x9000 },
162 { "trail", 0x1000 },
984263bc
MD
163 { "pppoe_disc", 0x8863 },
164 { "pppoe_sess", 0x8864 },
165 { "ipx_8022", 0x00E0 },
166 { "ipx_8023", 0x0000 },
167 { "ipx_ii", 0x8137 },
168 { "ipx_snap", 0x8137 },
169 { "ipx", 0x8137 },
170 { "ns", 0x0600 },
171 { NULL, 0 }
172};
173
174static void show_usage(void);
175
176enum tokens {
177 TOK_NULL=0,
178
179 TOK_OR,
180 TOK_NOT,
181 TOK_STARTBRACE,
182 TOK_ENDBRACE,
183
184 TOK_ACCEPT,
185 TOK_COUNT,
6998b243 186 TOK_DEFRAG,
984263bc
MD
187 TOK_PIPE,
188 TOK_QUEUE,
189 TOK_DIVERT,
190 TOK_TEE,
191 TOK_FORWARD,
192 TOK_SKIPTO,
193 TOK_DENY,
194 TOK_REJECT,
195 TOK_RESET,
196 TOK_UNREACH,
197 TOK_CHECKSTATE,
198
199 TOK_UID,
200 TOK_GID,
201 TOK_IN,
202 TOK_LIMIT,
203 TOK_KEEPSTATE,
204 TOK_LAYER2,
205 TOK_OUT,
206 TOK_XMIT,
207 TOK_RECV,
208 TOK_VIA,
209 TOK_FRAG,
cc9ef3d3 210 TOK_IPFRAG,
984263bc
MD
211 TOK_IPOPTS,
212 TOK_IPLEN,
213 TOK_IPID,
214 TOK_IPPRECEDENCE,
215 TOK_IPTOS,
216 TOK_IPTTL,
217 TOK_IPVER,
218 TOK_ESTAB,
219 TOK_SETUP,
220 TOK_TCPFLAGS,
221 TOK_TCPOPTS,
222 TOK_TCPSEQ,
223 TOK_TCPACK,
224 TOK_TCPWIN,
225 TOK_ICMPTYPES,
226 TOK_MAC,
227 TOK_MACTYPE,
228
229 TOK_PLR,
230 TOK_NOERROR,
231 TOK_BUCKETS,
232 TOK_DSTIP,
233 TOK_SRCIP,
234 TOK_DSTPORT,
235 TOK_SRCPORT,
236 TOK_ALL,
237 TOK_MASK,
238 TOK_BW,
239 TOK_DELAY,
240 TOK_RED,
241 TOK_GRED,
242 TOK_DROPTAIL,
243 TOK_PROTO,
244 TOK_WEIGHT,
245};
246
247struct _s_x dummynet_params[] = {
248 { "plr", TOK_PLR },
249 { "noerror", TOK_NOERROR },
250 { "buckets", TOK_BUCKETS },
251 { "dst-ip", TOK_DSTIP },
252 { "src-ip", TOK_SRCIP },
253 { "dst-port", TOK_DSTPORT },
254 { "src-port", TOK_SRCPORT },
255 { "proto", TOK_PROTO },
256 { "weight", TOK_WEIGHT },
257 { "all", TOK_ALL },
258 { "mask", TOK_MASK },
259 { "droptail", TOK_DROPTAIL },
260 { "red", TOK_RED },
261 { "gred", TOK_GRED },
262 { "bw", TOK_BW },
263 { "bandwidth", TOK_BW },
264 { "delay", TOK_DELAY },
265 { "pipe", TOK_PIPE },
266 { "queue", TOK_QUEUE },
267 { "dummynet-params", TOK_NULL },
268 { NULL, 0 }
269};
270
271struct _s_x rule_actions[] = {
272 { "accept", TOK_ACCEPT },
273 { "pass", TOK_ACCEPT },
274 { "allow", TOK_ACCEPT },
275 { "permit", TOK_ACCEPT },
276 { "count", TOK_COUNT },
277 { "pipe", TOK_PIPE },
278 { "queue", TOK_QUEUE },
279 { "divert", TOK_DIVERT },
280 { "tee", TOK_TEE },
281 { "fwd", TOK_FORWARD },
282 { "forward", TOK_FORWARD },
283 { "skipto", TOK_SKIPTO },
284 { "deny", TOK_DENY },
285 { "drop", TOK_DENY },
286 { "reject", TOK_REJECT },
287 { "reset", TOK_RESET },
288 { "unreach", TOK_UNREACH },
289 { "check-state", TOK_CHECKSTATE },
6998b243 290 { "defrag", TOK_DEFRAG },
984263bc
MD
291 { NULL, TOK_NULL },
292 { NULL, 0 }
293};
294
295struct _s_x rule_options[] = {
296 { "uid", TOK_UID },
297 { "gid", TOK_GID },
298 { "in", TOK_IN },
299 { "limit", TOK_LIMIT },
300 { "keep-state", TOK_KEEPSTATE },
984263bc
MD
301 { "layer2", TOK_LAYER2 },
302 { "out", TOK_OUT },
303 { "xmit", TOK_XMIT },
304 { "recv", TOK_RECV },
305 { "via", TOK_VIA },
306 { "fragment", TOK_FRAG },
307 { "frag", TOK_FRAG },
cc9ef3d3 308 { "ipfrag", TOK_IPFRAG },
984263bc
MD
309 { "ipoptions", TOK_IPOPTS },
310 { "ipopts", TOK_IPOPTS },
311 { "iplen", TOK_IPLEN },
312 { "ipid", TOK_IPID },
313 { "ipprecedence", TOK_IPPRECEDENCE },
314 { "iptos", TOK_IPTOS },
315 { "ipttl", TOK_IPTTL },
316 { "ipversion", TOK_IPVER },
317 { "ipver", TOK_IPVER },
318 { "estab", TOK_ESTAB },
319 { "established", TOK_ESTAB },
320 { "setup", TOK_SETUP },
321 { "tcpflags", TOK_TCPFLAGS },
322 { "tcpflgs", TOK_TCPFLAGS },
323 { "tcpoptions", TOK_TCPOPTS },
324 { "tcpopts", TOK_TCPOPTS },
325 { "tcpseq", TOK_TCPSEQ },
326 { "tcpack", TOK_TCPACK },
327 { "tcpwin", TOK_TCPWIN },
328 { "icmptype", TOK_ICMPTYPES },
329 { "icmptypes", TOK_ICMPTYPES },
330 { "dst-ip", TOK_DSTIP },
331 { "src-ip", TOK_SRCIP },
332 { "dst-port", TOK_DSTPORT },
333 { "src-port", TOK_SRCPORT },
334 { "proto", TOK_PROTO },
335 { "MAC", TOK_MAC },
336 { "mac", TOK_MAC },
337 { "mac-type", TOK_MACTYPE },
338
339 { "not", TOK_NOT }, /* pseudo option */
340 { "!", /* escape ? */ TOK_NOT }, /* pseudo option */
341 { "or", TOK_OR }, /* pseudo option */
342 { "|", /* escape */ TOK_OR }, /* pseudo option */
343 { "{", TOK_STARTBRACE }, /* pseudo option */
344 { "(", TOK_STARTBRACE }, /* pseudo option */
345 { "}", TOK_ENDBRACE }, /* pseudo option */
346 { ")", TOK_ENDBRACE }, /* pseudo option */
347 { NULL, TOK_NULL },
348 { NULL, 0 }
349};
350
351/**
352 * match_token takes a table and a string, returns the value associated
353 * with the string (0 meaning an error in most cases)
354 */
355static int
356match_token(struct _s_x *table, char *string)
357{
358 struct _s_x *pt;
359 int i = strlen(string);
360
361 for (pt = table ; i && pt->s != NULL ; pt++)
9c2d26d1 362 if ((int)strlen(pt->s) == i && !bcmp(string, pt->s, i))
984263bc
MD
363 return pt->x;
364 return -1;
365};
366
75a579b7 367static const char *
984263bc
MD
368match_value(struct _s_x *p, u_int32_t value)
369{
370 for (; p->s != NULL; p++)
371 if (p->x == value)
372 return p->s;
373 return NULL;
374}
375
376/*
377 * prints one port, symbolic or numeric
378 */
379static void
380print_port(int proto, u_int16_t port)
381{
382
383 if (proto == IPPROTO_ETHERTYPE) {
75a579b7 384 const char *str;
984263bc 385
75a579b7
SZ
386 if (do_resolv && (str = match_value(ether_types, port)) )
387 printf("%s", str);
984263bc
MD
388 else
389 printf("0x%04x", port);
390 } else {
391 struct servent *se = NULL;
392 if (do_resolv) {
393 struct protoent *pe = getprotobynumber(proto);
394
395 se = getservbyport(htons(port), pe ? pe->p_name : NULL);
396 }
397 if (se)
398 printf("%s", se->s_name);
399 else
400 printf("%d", port);
401 }
402}
403
404/*
405 * print the values in a list of ports
406 * XXX todo: add support for mask.
407 */
408static void
409print_newports(ipfw_insn_u16 *cmd, int proto, int opcode)
410{
411 u_int16_t *p = cmd->ports;
412 int i;
75a579b7 413 const char *sep= " ";
984263bc
MD
414
415 if (cmd->o.len & F_NOT)
416 printf(" not");
417 if (opcode != 0)
418 printf ("%s", opcode == O_MAC_TYPE ? " mac-type" :
419 (opcode == O_IP_DSTPORT ? " dst-port" : " src-port"));
420 for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) {
9462167a 421 printf("%s", sep);
984263bc
MD
422 print_port(proto, p[0]);
423 if (p[0] != p[1]) {
424 printf("-");
425 print_port(proto, p[1]);
426 }
427 sep = ",";
428 }
429}
430
431/*
432 * Like strtol, but also translates service names into port numbers
433 * for some protocols.
434 * In particular:
435 * proto == -1 disables the protocol check;
436 * proto == IPPROTO_ETHERTYPE looks up an internal table
437 * proto == <some value in /etc/protocols> matches the values there.
438 * Returns *end == s in case the parameter is not found.
439 */
440static int
75a579b7 441strtoport(char *str, char **end, int base, int proto)
984263bc
MD
442{
443 char *p, *buf;
444 char *s1;
445 int i;
446
75a579b7
SZ
447 *end = str; /* default - not found */
448 if (*str == '\0')
984263bc
MD
449 return 0; /* not found */
450
75a579b7
SZ
451 if (isdigit(*str))
452 return strtol(str, end, base);
984263bc
MD
453
454 /*
455 * find separator. '\\' escapes the next char.
456 */
75a579b7 457 for (s1 = str; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++)
984263bc
MD
458 if (*s1 == '\\' && s1[1] != '\0')
459 s1++;
460
75a579b7 461 buf = malloc(s1 - str + 1);
984263bc
MD
462 if (buf == NULL)
463 return 0;
464
465 /*
466 * copy into a buffer skipping backslashes
467 */
75a579b7 468 for (p = str, i = 0; p != s1 ; p++)
984263bc
MD
469 if ( *p != '\\')
470 buf[i++] = *p;
471 buf[i++] = '\0';
472
473 if (proto == IPPROTO_ETHERTYPE) {
474 i = match_token(ether_types, buf);
475 free(buf);
476 if (i != -1) { /* found */
477 *end = s1;
478 return i;
479 }
480 } else {
481 struct protoent *pe = NULL;
482 struct servent *se;
483
484 if (proto != 0)
485 pe = getprotobynumber(proto);
486 setservent(1);
487 se = getservbyname(buf, pe ? pe->p_name : NULL);
488 free(buf);
489 if (se != NULL) {
490 *end = s1;
491 return ntohs(se->s_port);
492 }
493 }
494 return 0; /* not found */
495}
496
497/*
498 * fill the body of the command with the list of port ranges.
499 * At the moment it only understands numeric ranges.
500 */
501static int
502fill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
503{
504 u_int16_t *p = cmd->ports;
505 int i = 0;
75a579b7 506 char *str = av;
984263bc 507
75a579b7 508 while (*str) {
984263bc
MD
509 u_int16_t a, b;
510
75a579b7
SZ
511 a = strtoport(av, &str, 0, proto);
512 if (str == av) /* no parameter */
984263bc 513 break;
75a579b7
SZ
514 if (*str == '-') { /* a range */
515 av = str+1;
516 b = strtoport(av, &str, 0, proto);
517 if (str == av) /* no parameter */
984263bc
MD
518 break;
519 p[0] = a;
520 p[1] = b;
75a579b7 521 } else if (*str == ',' || *str == '\0' ) {
984263bc
MD
522 p[0] = p[1] = a;
523 } else { /* invalid separator */
524 errx(EX_DATAERR, "invalid separator <%c> in <%s>\n",
75a579b7 525 *str, av);
984263bc
MD
526 }
527 i++;
528 p += 2;
75a579b7 529 av = str+1;
984263bc
MD
530 }
531 if (i > 0) {
532 if (i+1 > F_LEN_MASK)
533 errx(EX_DATAERR, "too many ports/ranges\n");
534 cmd->o.len |= i+1; /* leave F_NOT and F_OR untouched */
535 }
536 return i;
537}
538
539static struct _s_x icmpcodes[] = {
540 { "net", ICMP_UNREACH_NET },
541 { "host", ICMP_UNREACH_HOST },
542 { "protocol", ICMP_UNREACH_PROTOCOL },
543 { "port", ICMP_UNREACH_PORT },
544 { "needfrag", ICMP_UNREACH_NEEDFRAG },
545 { "srcfail", ICMP_UNREACH_SRCFAIL },
546 { "net-unknown", ICMP_UNREACH_NET_UNKNOWN },
547 { "host-unknown", ICMP_UNREACH_HOST_UNKNOWN },
548 { "isolated", ICMP_UNREACH_ISOLATED },
549 { "net-prohib", ICMP_UNREACH_NET_PROHIB },
550 { "host-prohib", ICMP_UNREACH_HOST_PROHIB },
551 { "tosnet", ICMP_UNREACH_TOSNET },
552 { "toshost", ICMP_UNREACH_TOSHOST },
553 { "filter-prohib", ICMP_UNREACH_FILTER_PROHIB },
554 { "host-precedence", ICMP_UNREACH_HOST_PRECEDENCE },
555 { "precedence-cutoff", ICMP_UNREACH_PRECEDENCE_CUTOFF },
556 { NULL, 0 }
557};
558
559static void
560fill_reject_code(u_short *codep, char *str)
561{
562 int val;
75a579b7 563 char *s1;
984263bc 564
75a579b7
SZ
565 val = strtoul(str, &s1, 0);
566 if (s1 == str || *s1 != '\0' || val >= 0x100)
984263bc
MD
567 val = match_token(icmpcodes, str);
568 if (val < 0)
569 errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
570 *codep = val;
571 return;
572}
573
574static void
575print_reject_code(u_int16_t code)
576{
75a579b7 577 const char *str = match_value(icmpcodes, code);
984263bc 578
75a579b7
SZ
579 if (str != NULL)
580 printf("unreach %s", str);
984263bc
MD
581 else
582 printf("unreach %u", code);
583}
584
585/*
586 * Returns the number of bits set (from left) in a contiguous bitmask,
587 * or -1 if the mask is not contiguous.
588 * XXX this needs a proper fix.
589 * This effectively works on masks in big-endian (network) format.
590 * when compiled on little endian architectures.
591 *
592 * First bit is bit 7 of the first byte -- note, for MAC addresses,
593 * the first bit on the wire is bit 0 of the first byte.
594 * len is the max length in bits.
595 */
596static int
597contigmask(u_char *p, int len)
598{
599 int i, n;
600 for (i=0; i<len ; i++)
601 if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
602 break;
603 for (n=i+1; n < len; n++)
604 if ( (p[n/8] & (1 << (7 - (n%8)))) != 0)
605 return -1; /* mask not contiguous */
606 return i;
607}
608
609/*
610 * print flags set/clear in the two bitmasks passed as parameters.
611 * There is a specialized check for f_tcpflags.
612 */
613static void
75a579b7 614print_flags(const char *name, ipfw_insn *cmd, struct _s_x *list)
984263bc 615{
75a579b7 616 const char *comma="";
984263bc
MD
617 int i;
618 u_char set = cmd->arg1 & 0xff;
619 u_char clear = (cmd->arg1 >> 8) & 0xff;
620
621 if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) {
622 printf(" setup");
623 return;
624 }
625
626 printf(" %s ", name);
627 for (i=0; list[i].x != 0; i++) {
628 if (set & list[i].x) {
629 set &= ~list[i].x;
630 printf("%s%s", comma, list[i].s);
631 comma = ",";
632 }
633 if (clear & list[i].x) {
634 clear &= ~list[i].x;
635 printf("%s!%s", comma, list[i].s);
636 comma = ",";
637 }
638 }
639}
640
641/*
642 * Print the ip address contained in a command.
643 */
644static void
75a579b7 645print_ip(ipfw_insn_ip *cmd, const char *str)
984263bc
MD
646{
647 struct hostent *he = NULL;
648 int mb;
649
75a579b7 650 printf("%s%s ", cmd->o.len & F_NOT ? " not": "", str);
984263bc
MD
651
652 if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) {
653 printf("me");
654 return;
655 }
d938108c
SZ
656 if (cmd->o.opcode == O_IP_SRC_TABLE ||
657 cmd->o.opcode == O_IP_DST_TABLE) {
658 printf("<%u>", cmd->o.arg1);
659 return;
660 }
984263bc
MD
661 if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) {
662 u_int32_t x, *d;
663 int i;
664 char comma = '{';
665
666 x = cmd->o.arg1 - 1;
667 x = htonl( ~x );
668 cmd->addr.s_addr = htonl(cmd->addr.s_addr);
669 printf("%s/%d", inet_ntoa(cmd->addr),
670 contigmask((u_char *)&x, 32));
671 x = cmd->addr.s_addr = htonl(cmd->addr.s_addr);
672 x &= 0xff; /* base */
673 d = (u_int32_t *)&(cmd->mask);
674 for (i=0; i < cmd->o.arg1; i++)
675 if (d[ i/32] & (1<<(i & 31))) {
676 printf("%c%d", comma, i+x);
677 comma = ',';
678 }
679 printf("}");
680 return;
681 }
682 if (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST)
683 mb = 32;
684 else
685 mb = contigmask((u_char *)&(cmd->mask.s_addr), 32);
686 if (mb == 32 && do_resolv)
15b85273
SW
687 he = gethostbyaddr(&(cmd->addr.s_addr), sizeof(u_long),
688 AF_INET);
984263bc
MD
689 if (he != NULL) /* resolved to name */
690 printf("%s", he->h_name);
691 else if (mb == 0) /* any */
692 printf("any");
693 else { /* numeric IP followed by some kind of mask */
694 printf("%s", inet_ntoa(cmd->addr));
695 if (mb < 0)
696 printf(":%s", inet_ntoa(cmd->mask));
697 else if (mb < 32)
698 printf("/%d", mb);
699 }
700}
701
702/*
703 * prints a MAC address/mask pair
704 */
705static void
706print_mac(u_char *addr, u_char *mask)
707{
708 int l = contigmask(mask, 48);
709
710 if (l == 0)
711 printf(" any");
712 else {
713 printf(" %02x:%02x:%02x:%02x:%02x:%02x",
714 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
715 if (l == -1)
716 printf("&%02x:%02x:%02x:%02x:%02x:%02x",
717 mask[0], mask[1], mask[2],
718 mask[3], mask[4], mask[5]);
719 else if (l < 48)
720 printf("/%d", l);
721 }
722}
723
724static void
725fill_icmptypes(ipfw_insn_u32 *cmd, char *av)
726{
727 u_int8_t type;
728
729 cmd->d[0] = 0;
730 while (*av) {
731 if (*av == ',')
732 av++;
733
734 type = strtoul(av, &av, 0);
735
736 if (*av != ',' && *av != '\0')
737 errx(EX_DATAERR, "invalid ICMP type");
738
739 if (type > 31)
740 errx(EX_DATAERR, "ICMP type out of range");
741
742 cmd->d[0] |= 1 << type;
743 }
744 cmd->o.opcode = O_ICMPTYPE;
745 cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
746}
747
748static void
749print_icmptypes(ipfw_insn_u32 *cmd)
750{
751 int i;
752 char sep= ' ';
753
754 printf(" icmptypes");
755 for (i = 0; i < 32; i++) {
756 if ( (cmd->d[0] & (1 << (i))) == 0)
757 continue;
758 printf("%c%d", sep, i);
759 sep = ',';
760 }
761}
762
763/*
764 * show_ipfw() prints the body of an ipfw rule.
765 * Because the standard rule has at least proto src_ip dst_ip, we use
766 * a helper function to produce these entries if not provided explicitly.
767 * The first argument is the list of fields we have, the second is
768 * the list of fields we want to be printed.
769 *
770 * Special cases if we have provided a MAC header:
771 * + if the rule does not contain IP addresses/ports, do not print them;
772 * + if the rule does not contain an IP proto, print "all" instead of "ip";
773 *
774 * Once we have 'have_options', IP header fields are printed as options.
775 */
776#define HAVE_PROTO 0x0001
777#define HAVE_SRCIP 0x0002
778#define HAVE_DSTIP 0x0004
779#define HAVE_MAC 0x0008
780#define HAVE_MACTYPE 0x0010
781#define HAVE_OPTIONS 0x8000
782
783#define HAVE_IP (HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP)
784static void
785show_prerequisites(int *flags, int want, int cmd)
786{
787 if ( (*flags & HAVE_IP) == HAVE_IP)
788 *flags |= HAVE_OPTIONS;
789
790 if ( (*flags & (HAVE_MAC|HAVE_MACTYPE|HAVE_OPTIONS)) == HAVE_MAC &&
791 cmd != O_MAC_TYPE) {
792 /*
793 * mac-type was optimized out by the compiler,
794 * restore it
795 */
796 printf(" any");
797 *flags |= HAVE_MACTYPE | HAVE_OPTIONS;
798 return;
799 }
800 if ( !(*flags & HAVE_OPTIONS)) {
801 if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO))
802 printf(" ip");
803 if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP))
804 printf(" from any");
805 if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP))
806 printf(" to any");
807 }
808 *flags |= want;
809}
810
811static void
b78533e2 812show_ipfw(struct ipfw_ioc_rule *rule, int pcwidth, int bcwidth)
984263bc
MD
813{
814 static int twidth = 0;
815 int l;
816 ipfw_insn *cmd;
817 int proto = 0; /* default */
818 int flags = 0; /* prerequisites */
819 ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */
820 int or_block = 0; /* we are in an or block */
821
b78533e2 822 u_int32_t set_disable = rule->set_disable;
984263bc
MD
823
824 if (set_disable & (1 << rule->set)) { /* disabled */
825 if (!show_sets)
826 return;
827 else
828 printf("# DISABLED ");
829 }
830 printf("%05u ", rule->rulenum);
831
832 if (do_acct)
c988a1de
SW
833 printf("%*ju %*ju ", pcwidth, (uintmax_t)rule->pcnt, bcwidth,
834 (uintmax_t)rule->bcnt);
984263bc
MD
835
836 if (do_time) {
837 char timestr[30];
838
839 if (twidth == 0) {
d938108c
SZ
840 time_t t0 = 0;
841
842 strcpy(timestr, ctime((time_t *)&t0));
984263bc
MD
843 *strchr(timestr, '\n') = '\0';
844 twidth = strlen(timestr);
845 }
846 if (rule->timestamp) {
984263bc
MD
847 time_t t = _long_to_time(rule->timestamp);
848
849 strcpy(timestr, ctime(&t));
850 *strchr(timestr, '\n') = '\0';
851 printf("%s ", timestr);
852 } else {
853 printf("%*s ", twidth, " ");
854 }
855 }
856
857 if (show_sets)
858 printf("set %d ", rule->set);
859
860 /*
861 * print the optional "match probability"
862 */
863 if (rule->cmd_len > 0) {
864 cmd = rule->cmd ;
865 if (cmd->opcode == O_PROB) {
866 ipfw_insn_u32 *p = (ipfw_insn_u32 *)cmd;
867 double d = 1.0 * p->d[0];
868
869 d = (d / 0x7fffffff);
870 printf("prob %f ", d);
871 }
872 }
873
874 /*
875 * first print actions
876 */
877 for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule);
878 l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) {
879 switch(cmd->opcode) {
880 case O_CHECK_STATE:
881 printf("check-state");
882 flags = HAVE_IP; /* avoid printing anything else */
883 break;
884
885 case O_ACCEPT:
886 printf("allow");
887 break;
888
889 case O_COUNT:
890 printf("count");
891 break;
892
6998b243
SZ
893 case O_DEFRAG:
894 printf("defrag");
895 break;
896
984263bc
MD
897 case O_DENY:
898 printf("deny");
899 break;
900
901 case O_REJECT:
902 if (cmd->arg1 == ICMP_REJECT_RST)
903 printf("reset");
904 else if (cmd->arg1 == ICMP_UNREACH_HOST)
905 printf("reject");
906 else
907 print_reject_code(cmd->arg1);
908 break;
909
910 case O_SKIPTO:
911 printf("skipto %u", cmd->arg1);
912 break;
913
914 case O_PIPE:
915 printf("pipe %u", cmd->arg1);
916 break;
917
918 case O_QUEUE:
919 printf("queue %u", cmd->arg1);
920 break;
921
922 case O_DIVERT:
923 printf("divert %u", cmd->arg1);
924 break;
925
926 case O_TEE:
927 printf("tee %u", cmd->arg1);
928 break;
929
930 case O_FORWARD_IP:
931 {
75a579b7 932 ipfw_insn_sa *sa = (ipfw_insn_sa *)cmd;
984263bc 933
75a579b7
SZ
934 printf("fwd %s", inet_ntoa(sa->sa.sin_addr));
935 if (sa->sa.sin_port)
936 printf(",%d", sa->sa.sin_port);
984263bc
MD
937 }
938 break;
939
940 case O_LOG: /* O_LOG is printed last */
941 logptr = (ipfw_insn_log *)cmd;
942 break;
943
944 default:
945 printf("** unrecognized action %d len %d",
946 cmd->opcode, cmd->len);
947 }
948 }
949 if (logptr) {
950 if (logptr->max_log > 0)
951 printf(" log logamount %d", logptr->max_log);
952 else
953 printf(" log");
954 }
955
956 /*
957 * then print the body.
958 */
b78533e2
SZ
959 if (rule->usr_flags & IPFW_USR_F_NORULE) {
960 /* empty rules before options */
984263bc
MD
961 if (!do_compact)
962 printf(" ip from any to any");
963 flags |= HAVE_IP | HAVE_OPTIONS;
964 }
965
966 for (l = rule->act_ofs, cmd = rule->cmd ;
967 l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
968 /* useful alias */
969 ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
970
971 show_prerequisites(&flags, 0, cmd->opcode);
972
973 switch(cmd->opcode) {
974 case O_PROB:
975 break; /* done already */
976
977 case O_PROBE_STATE:
978 break; /* no need to print anything here */
979
980 case O_MACADDR2: {
981 ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
982
983 if ((cmd->len & F_OR) && !or_block)
984 printf(" {");
985 if (cmd->len & F_NOT)
986 printf(" not");
987 printf(" MAC");
988 flags |= HAVE_MAC;
989 print_mac( m->addr, m->mask);
990 print_mac( m->addr + 6, m->mask + 6);
991 }
992 break;
993
994 case O_MAC_TYPE:
995 if ((cmd->len & F_OR) && !or_block)
996 printf(" {");
997 print_newports((ipfw_insn_u16 *)cmd, IPPROTO_ETHERTYPE,
998 (flags & HAVE_OPTIONS) ? cmd->opcode : 0);
999 flags |= HAVE_MAC | HAVE_MACTYPE | HAVE_OPTIONS;
1000 break;
1001
1002 case O_IP_SRC:
1003 case O_IP_SRC_MASK:
1004 case O_IP_SRC_ME:
1005 case O_IP_SRC_SET:
d938108c 1006 case O_IP_SRC_TABLE:
984263bc
MD
1007 show_prerequisites(&flags, HAVE_PROTO, 0);
1008 if (!(flags & HAVE_SRCIP))
1009 printf(" from");
1010 if ((cmd->len & F_OR) && !or_block)
1011 printf(" {");
1012 print_ip((ipfw_insn_ip *)cmd,
1013 (flags & HAVE_OPTIONS) ? " src-ip" : "");
1014 flags |= HAVE_SRCIP;
1015 break;
1016
1017 case O_IP_DST:
1018 case O_IP_DST_MASK:
1019 case O_IP_DST_ME:
1020 case O_IP_DST_SET:
d938108c 1021 case O_IP_DST_TABLE:
984263bc
MD
1022 show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1023 if (!(flags & HAVE_DSTIP))
1024 printf(" to");
1025 if ((cmd->len & F_OR) && !or_block)
1026 printf(" {");
1027 print_ip((ipfw_insn_ip *)cmd,
1028 (flags & HAVE_OPTIONS) ? " dst-ip" : "");
1029 flags |= HAVE_DSTIP;
1030 break;
1031
1032 case O_IP_DSTPORT:
1033 show_prerequisites(&flags, HAVE_IP, 0);
1034 case O_IP_SRCPORT:
1035 show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0);
1036 if ((cmd->len & F_OR) && !or_block)
1037 printf(" {");
1038 print_newports((ipfw_insn_u16 *)cmd, proto,
1039 (flags & HAVE_OPTIONS) ? cmd->opcode : 0);
1040 break;
1041
1042 case O_PROTO: {
1043 struct protoent *pe;
1044
1045 if ((cmd->len & F_OR) && !or_block)
1046 printf(" {");
1047 if (cmd->len & F_NOT)
1048 printf(" not");
1049 proto = cmd->arg1;
1050 pe = getprotobynumber(cmd->arg1);
1051 if (flags & HAVE_OPTIONS)
1052 printf(" proto");
1053 if (pe)
1054 printf(" %s", pe->p_name);
1055 else
1056 printf(" %u", cmd->arg1);
1057 }
1058 flags |= HAVE_PROTO;
1059 break;
1060
1061 default: /*options ... */
1062 show_prerequisites(&flags, HAVE_IP | HAVE_OPTIONS, 0);
1063 if ((cmd->len & F_OR) && !or_block)
1064 printf(" {");
1065 if (cmd->len & F_NOT && cmd->opcode != O_IN)
1066 printf(" not");
1067 switch(cmd->opcode) {
1068 case O_FRAG:
1069 printf(" frag");
1070 break;
1071
cc9ef3d3
SZ
1072 case O_IPFRAG:
1073 printf(" ipfrag");
1074 break;
1075
984263bc
MD
1076 case O_IN:
1077 printf(cmd->len & F_NOT ? " out" : " in");
1078 break;
1079
1080 case O_LAYER2:
1081 printf(" layer2");
1082 break;
1083 case O_XMIT:
1084 case O_RECV:
1085 case O_VIA: {
75a579b7 1086 const char *dir;
984263bc
MD
1087 ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd;
1088
1089 if (cmd->opcode == O_XMIT)
75a579b7 1090 dir = "xmit";
984263bc 1091 else if (cmd->opcode == O_RECV)
75a579b7 1092 dir = "recv";
984263bc 1093 else if (cmd->opcode == O_VIA)
75a579b7 1094 dir = "via";
7700730e 1095 else
75a579b7 1096 dir = "?huh?";
984263bc 1097 if (cmdif->name[0] == '\0')
75a579b7 1098 printf(" %s %s", dir,
984263bc 1099 inet_ntoa(cmdif->p.ip));
75a579b7 1100 printf(" %s %s", dir, cmdif->name);
984263bc
MD
1101 }
1102 break;
1103
1104 case O_IPID:
1105 printf(" ipid %u", cmd->arg1 );
1106 break;
1107
1108 case O_IPTTL:
1109 printf(" ipttl %u", cmd->arg1 );
1110 break;
1111
1112 case O_IPVER:
1113 printf(" ipver %u", cmd->arg1 );
1114 break;
1115
1116 case O_IPPRECEDENCE:
1117 printf(" ipprecedence %u", (cmd->arg1) >> 5 );
1118 break;
1119
1120 case O_IPLEN:
1121 printf(" iplen %u", cmd->arg1 );
1122 break;
1123
1124 case O_IPOPT:
1125 print_flags("ipoptions", cmd, f_ipopts);
1126 break;
1127
1128 case O_IPTOS:
1129 print_flags("iptos", cmd, f_iptos);
1130 break;
1131
1132 case O_ICMPTYPE:
1133 print_icmptypes((ipfw_insn_u32 *)cmd);
1134 break;
1135
1136 case O_ESTAB:
1137 printf(" established");
1138 break;
1139
1140 case O_TCPFLAGS:
1141 print_flags("tcpflags", cmd, f_tcpflags);
1142 break;
1143
1144 case O_TCPOPTS:
1145 print_flags("tcpoptions", cmd, f_tcpopts);
1146 break;
1147
1148 case O_TCPWIN:
1149 printf(" tcpwin %d", ntohs(cmd->arg1));
1150 break;
1151
1152 case O_TCPACK:
c988a1de 1153 printf(" tcpack %u", ntohl(cmd32->d[0]));
984263bc
MD
1154 break;
1155
1156 case O_TCPSEQ:
c988a1de 1157 printf(" tcpseq %u", ntohl(cmd32->d[0]));
984263bc
MD
1158 break;
1159
1160 case O_UID:
1161 {
1162 struct passwd *pwd = getpwuid(cmd32->d[0]);
1163
1164 if (pwd)
1165 printf(" uid %s", pwd->pw_name);
1166 else
1167 printf(" uid %u", cmd32->d[0]);
1168 }
1169 break;
1170
1171 case O_GID:
1172 {
1173 struct group *grp = getgrgid(cmd32->d[0]);
1174
1175 if (grp)
1176 printf(" gid %s", grp->gr_name);
1177 else
1178 printf(" gid %u", cmd32->d[0]);
1179 }
1180 break;
1181
1182 case O_KEEP_STATE:
1183 printf(" keep-state");
1184 break;
1185
1186 case O_LIMIT:
1187 {
1188 struct _s_x *p = limit_masks;
1189 ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
1190 u_int8_t x = c->limit_mask;
75a579b7 1191 const char *comma = " ";
984263bc
MD
1192
1193 printf(" limit");
1194 for ( ; p->x != 0 ; p++)
1195 if ((x & p->x) == p->x) {
1196 x &= ~p->x;
1197 printf("%s%s", comma, p->s);
1198 comma = ",";
1199 }
1200 printf(" %d", c->conn_limit);
1201 }
1202 break;
1203
1204 default:
1205 printf(" [opcode %d len %d]",
1206 cmd->opcode, cmd->len);
1207 }
1208 }
1209 if (cmd->len & F_OR) {
1210 printf(" or");
1211 or_block = 1;
1212 } else if (or_block) {
1213 printf(" }");
1214 or_block = 0;
1215 }
1216 }
1217 show_prerequisites(&flags, HAVE_IP, 0);
1218
1219 printf("\n");
1220}
1221
1222static void
b78533e2 1223show_dyn_ipfw(struct ipfw_ioc_state *d, int pcwidth, int bcwidth)
984263bc
MD
1224{
1225 struct protoent *pe;
1226 struct in_addr a;
1227
1228 if (!do_expired) {
1229 if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT))
1230 return;
1231 }
1232
c988a1de
SW
1233 printf("%05u %*ju %*ju (%ds)", d->rulenum, pcwidth, (uintmax_t)d->pcnt,
1234 bcwidth, (uintmax_t)d->bcnt, d->expire);
984263bc
MD
1235 switch (d->dyn_type) {
1236 case O_LIMIT_PARENT:
1237 printf(" PARENT %d", d->count);
1238 break;
1239 case O_LIMIT:
1240 printf(" LIMIT");
1241 break;
1242 case O_KEEP_STATE: /* bidir, no mask */
1243 printf(" STATE");
1244 break;
1245 }
1246
b78533e2 1247 if ((pe = getprotobynumber(d->id.u.ip.proto)) != NULL)
984263bc
MD
1248 printf(" %s", pe->p_name);
1249 else
b78533e2 1250 printf(" proto %u", d->id.u.ip.proto);
984263bc 1251
b78533e2
SZ
1252 a.s_addr = htonl(d->id.u.ip.src_ip);
1253 printf(" %s %d", inet_ntoa(a), d->id.u.ip.src_port);
984263bc 1254
b78533e2
SZ
1255 a.s_addr = htonl(d->id.u.ip.dst_ip);
1256 printf(" <-> %s %d", inet_ntoa(a), d->id.u.ip.dst_port);
984263bc
MD
1257 printf("\n");
1258}
1259
9c2d26d1 1260static int
984263bc
MD
1261sort_q(const void *pa, const void *pb)
1262{
1263 int rev = (do_sort < 0);
1264 int field = rev ? -do_sort : do_sort;
1265 long long res = 0;
f9edcb74
SZ
1266 const struct dn_ioc_flowqueue *a = pa;
1267 const struct dn_ioc_flowqueue *b = pb;
984263bc
MD
1268
1269 switch (field) {
1270 case 1: /* pkts */
1271 res = a->len - b->len;
1272 break;
1273 case 2: /* bytes */
1274 res = a->len_bytes - b->len_bytes;
1275 break;
1276
1277 case 3: /* tot pkts */
1278 res = a->tot_pkts - b->tot_pkts;
1279 break;
1280
1281 case 4: /* tot bytes */
1282 res = a->tot_bytes - b->tot_bytes;
1283 break;
1284 }
1285 if (res < 0)
1286 res = -1;
1287 if (res > 0)
1288 res = 1;
1289 return (int)(rev ? res : -res);
1290}
1291
1292static void
f9edcb74 1293list_queues(struct dn_ioc_flowset *fs, struct dn_ioc_flowqueue *q)
984263bc
MD
1294{
1295 int l;
1296
1297 printf(" mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
f9edcb74
SZ
1298 fs->flow_mask.u.ip.proto,
1299 fs->flow_mask.u.ip.src_ip, fs->flow_mask.u.ip.src_port,
1300 fs->flow_mask.u.ip.dst_ip, fs->flow_mask.u.ip.dst_port);
984263bc
MD
1301 if (fs->rq_elements == 0)
1302 return;
1303
1304 printf("BKT Prot ___Source IP/port____ "
1305 "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n");
1306 if (do_sort != 0)
f9edcb74 1307 heapsort(q, fs->rq_elements, sizeof(*q), sort_q);
984263bc
MD
1308 for (l = 0; l < fs->rq_elements; l++) {
1309 struct in_addr ina;
1310 struct protoent *pe;
1311
f9edcb74 1312 ina.s_addr = htonl(q[l].id.u.ip.src_ip);
984263bc 1313 printf("%3d ", q[l].hash_slot);
f9edcb74 1314 pe = getprotobynumber(q[l].id.u.ip.proto);
984263bc
MD
1315 if (pe)
1316 printf("%-4s ", pe->p_name);
1317 else
f9edcb74 1318 printf("%4u ", q[l].id.u.ip.proto);
984263bc 1319 printf("%15s/%-5d ",
f9edcb74
SZ
1320 inet_ntoa(ina), q[l].id.u.ip.src_port);
1321 ina.s_addr = htonl(q[l].id.u.ip.dst_ip);
984263bc 1322 printf("%15s/%-5d ",
f9edcb74 1323 inet_ntoa(ina), q[l].id.u.ip.dst_port);
c988a1de
SW
1324 printf("%4ju %8ju %2u %4u %3u\n",
1325 (uintmax_t)q[l].tot_pkts, (uintmax_t)q[l].tot_bytes,
984263bc
MD
1326 q[l].len, q[l].len_bytes, q[l].drops);
1327 if (verbose)
c988a1de
SW
1328 printf(" S %20ju F %20ju\n",
1329 (uintmax_t)q[l].S, (uintmax_t)q[l].F);
984263bc
MD
1330 }
1331}
1332
1333static void
f9edcb74 1334print_flowset_parms(struct dn_ioc_flowset *fs, char *prefix)
984263bc
MD
1335{
1336 int l;
1337 char qs[30];
1338 char plr[30];
1339 char red[90]; /* Display RED parameters */
1340
1341 l = fs->qsize;
1342 if (fs->flags_fs & DN_QSIZE_IS_BYTES) {
1343 if (l >= 8192)
1344 sprintf(qs, "%d KB", l / 1024);
1345 else
1346 sprintf(qs, "%d B", l);
1347 } else
1348 sprintf(qs, "%3d sl.", l);
1349 if (fs->plr)
1350 sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
1351 else
1352 plr[0] = '\0';
1353 if (fs->flags_fs & DN_IS_RED) /* RED parameters */
1354 sprintf(red,
1355 "\n\t %cRED w_q %f min_th %d max_th %d max_p %f",
1356 (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ',
1357 1.0 * fs->w_q / (double)(1 << SCALE_RED),
1358 SCALE_VAL(fs->min_th),
1359 SCALE_VAL(fs->max_th),
1360 1.0 * fs->max_p / (double)(1 << SCALE_RED));
1361 else
1362 sprintf(red, "droptail");
1363
1364 printf("%s %s%s %d queues (%d buckets) %s\n",
1365 prefix, qs, plr, fs->rq_elements, fs->rq_size, red);
1366}
1367
1368static void
1369list_pipes(void *data, int nbytes, int ac, char *av[])
1370{
1371 u_long rulenum;
1372 void *next = data;
f9edcb74
SZ
1373 struct dn_ioc_pipe *p = (struct dn_ioc_pipe *)data;
1374 struct dn_ioc_flowset *fs;
1375 struct dn_ioc_flowqueue *q;
984263bc
MD
1376 int l;
1377
1378 if (ac > 0)
1379 rulenum = strtoul(*av++, NULL, 10);
1380 else
1381 rulenum = 0;
9c2d26d1 1382 for (; nbytes >= (int)sizeof(*p); p = (struct dn_ioc_pipe *)next) {
984263bc
MD
1383 double b = p->bandwidth;
1384 char buf[30];
1385 char prefix[80];
1386
f9edcb74 1387 if (p->fs.fs_type != DN_IS_PIPE)
984263bc
MD
1388 break; /* done with pipes, now queues */
1389
1390 /*
1391 * compute length, as pipe have variable size
1392 */
1393 l = sizeof(*p) + p->fs.rq_elements * sizeof(*q);
9c2d26d1 1394 next = (uint8_t *)p + l;
984263bc
MD
1395 nbytes -= l;
1396
9c2d26d1 1397 if (rulenum != 0 && rulenum != (u_long)p->pipe_nr)
984263bc
MD
1398 continue;
1399
1400 /*
ff6f118a 1401 * Print rate
984263bc 1402 */
ff6f118a 1403 if (b == 0)
984263bc
MD
1404 sprintf(buf, "unlimited");
1405 else if (b >= 1000000)
1406 sprintf(buf, "%7.3f Mbit/s", b/1000000);
1407 else if (b >= 1000)
1408 sprintf(buf, "%7.3f Kbit/s", b/1000);
1409 else
1410 sprintf(buf, "%7.3f bit/s ", b);
1411
1412 sprintf(prefix, "%05d: %s %4d ms ",
1413 p->pipe_nr, buf, p->delay);
f9edcb74 1414 print_flowset_parms(&p->fs, prefix);
984263bc 1415 if (verbose)
c988a1de 1416 printf(" V %20ju\n", (uintmax_t)p->V >> MY_M);
984263bc 1417
f9edcb74
SZ
1418 q = (struct dn_ioc_flowqueue *)(p+1);
1419 list_queues(&p->fs, q);
984263bc 1420 }
9c2d26d1 1421 for (fs = next; nbytes >= (int)sizeof(*fs); fs = next) {
984263bc
MD
1422 char prefix[80];
1423
f9edcb74 1424 if (fs->fs_type != DN_IS_QUEUE)
984263bc
MD
1425 break;
1426 l = sizeof(*fs) + fs->rq_elements * sizeof(*q);
9c2d26d1 1427 next = (uint8_t *)fs + l;
984263bc 1428 nbytes -= l;
f9edcb74 1429 q = (struct dn_ioc_flowqueue *)(fs+1);
984263bc
MD
1430 sprintf(prefix, "q%05d: weight %d pipe %d ",
1431 fs->fs_nr, fs->weight, fs->parent_nr);
1432 print_flowset_parms(fs, prefix);
1433 list_queues(fs, q);
1434 }
1435}
1436
1437/*
1438 * This one handles all set-related commands
1439 * ipfw set { show | enable | disable }
1440 * ipfw set swap X Y
1441 * ipfw set move X to Y
1442 * ipfw set move rule X to Y
1443 */
1444static void
1445sets_handler(int ac, char *av[])
1446{
1447 u_int32_t set_disable, masks[2];
1448 int i, nbytes;
1449 u_int16_t rulenum;
1450 u_int8_t cmd, new_set;
1451
1452 ac--;
1453 av++;
1454
1455 if (!ac)
1456 errx(EX_USAGE, "set needs command");
1457 if (!strncmp(*av, "show", strlen(*av)) ) {
1458 void *data;
75a579b7 1459 const char *msg;
984263bc 1460
b78533e2 1461 nbytes = sizeof(struct ipfw_ioc_rule);
984263bc
MD
1462 if ((data = malloc(nbytes)) == NULL)
1463 err(EX_OSERR, "malloc");
1464 if (getsockopt(s, IPPROTO_IP, IP_FW_GET, data, &nbytes) < 0)
1465 err(EX_OSERR, "getsockopt(IP_FW_GET)");
b78533e2 1466 set_disable = ((struct ipfw_ioc_rule *)data)->set_disable;
984263bc
MD
1467
1468 for (i = 0, msg = "disable" ; i < 31; i++)
1469 if ( (set_disable & (1<<i))) {
1470 printf("%s %d", msg, i);
1471 msg = "";
1472 }
1473 msg = (set_disable) ? " enable" : "enable";
1474 for (i = 0; i < 31; i++)
1475 if ( !(set_disable & (1<<i))) {
1476 printf("%s %d", msg, i);
1477 msg = "";
1478 }
1479 printf("\n");
1480 } else if (!strncmp(*av, "swap", strlen(*av))) {
1481 ac--; av++;
1482 if (ac != 2)
1483 errx(EX_USAGE, "set swap needs 2 set numbers\n");
1484 rulenum = atoi(av[0]);
1485 new_set = atoi(av[1]);
1486 if (!isdigit(*(av[0])) || rulenum > 30)
1487 errx(EX_DATAERR, "invalid set number %s\n", av[0]);
1488 if (!isdigit(*(av[1])) || new_set > 30)
1489 errx(EX_DATAERR, "invalid set number %s\n", av[1]);
1490 masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
1491 i = setsockopt(s, IPPROTO_IP, IP_FW_DEL,
1492 masks, sizeof(u_int32_t));
1493 } else if (!strncmp(*av, "move", strlen(*av))) {
1494 ac--; av++;
1495 if (ac && !strncmp(*av, "rule", strlen(*av))) {
1496 cmd = 2;
1497 ac--; av++;
1498 } else
1499 cmd = 3;
1500 if (ac != 3 || strncmp(av[1], "to", strlen(*av)))
1501 errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
1502 rulenum = atoi(av[0]);
1503 new_set = atoi(av[2]);
1504 if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > 30) ||
1505 (cmd == 2 && rulenum == 65535) )
1506 errx(EX_DATAERR, "invalid source number %s\n", av[0]);
1507 if (!isdigit(*(av[2])) || new_set > 30)
1508 errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
1509 masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
1510 i = setsockopt(s, IPPROTO_IP, IP_FW_DEL,
1511 masks, sizeof(u_int32_t));
1512 } else if (!strncmp(*av, "disable", strlen(*av)) ||
1513 !strncmp(*av, "enable", strlen(*av)) ) {
1514 int which = !strncmp(*av, "enable", strlen(*av)) ? 1 : 0;
1515
1516 ac--; av++;
1517 masks[0] = masks[1] = 0;
1518
1519 while (ac) {
1520 if (isdigit(**av)) {
1521 i = atoi(*av);
1522 if (i < 0 || i > 30)
1523 errx(EX_DATAERR,
1524 "invalid set number %d\n", i);
1525 masks[which] |= (1<<i);
1526 } else if (!strncmp(*av, "disable", strlen(*av)))
1527 which = 0;
1528 else if (!strncmp(*av, "enable", strlen(*av)))
1529 which = 1;
1530 else
1531 errx(EX_DATAERR,
1532 "invalid set command %s\n", *av);
1533 av++; ac--;
1534 }
1535 if ( (masks[0] & masks[1]) != 0 )
1536 errx(EX_DATAERR,
1537 "cannot enable and disable the same set\n");
1538
1539 i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, masks, sizeof(masks));
1540 if (i)
1541 warn("set enable/disable: setsockopt(IP_FW_DEL)");
1542 } else
1543 errx(EX_USAGE, "invalid set command %s\n", *av);
1544}
1545
1546static void
1547sysctl_handler(int ac, char *av[], int which)
1548{
1549 ac--;
1550 av++;
1551
1552 if (*av == NULL) {
1553 warnx("missing keyword to enable/disable\n");
1554 } else if (strncmp(*av, "firewall", strlen(*av)) == 0) {
1555 sysctlbyname("net.inet.ip.fw.enable", NULL, 0,
1556 &which, sizeof(which));
1557 } else if (strncmp(*av, "one_pass", strlen(*av)) == 0) {
1558 sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0,
1559 &which, sizeof(which));
1560 } else if (strncmp(*av, "debug", strlen(*av)) == 0) {
1561 sysctlbyname("net.inet.ip.fw.debug", NULL, 0,
1562 &which, sizeof(which));
1563 } else if (strncmp(*av, "verbose", strlen(*av)) == 0) {
1564 sysctlbyname("net.inet.ip.fw.verbose", NULL, 0,
1565 &which, sizeof(which));
1566 } else if (strncmp(*av, "dyn_keepalive", strlen(*av)) == 0) {
1567 sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0,
1568 &which, sizeof(which));
1569 } else {
1570 warnx("unrecognize enable/disable keyword: %s\n", *av);
1571 }
1572}
1573
1574static void
1575list(int ac, char *av[])
1576{
b78533e2
SZ
1577 struct ipfw_ioc_rule *r;
1578 struct ipfw_ioc_state *dynrules, *d;
984263bc 1579
b78533e2 1580 void *data = NULL;
984263bc
MD
1581 int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width;
1582 int exitval = EX_OK;
1583 int lac;
1584 char **lav;
1585 u_long rnum;
1586 char *endptr;
1587 int seen = 0;
1588
1589 const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
1590 int nalloc = 1024; /* start somewhere... */
1591
1592 ac--;
1593 av++;
1594
1595 /* get rules or pipes from kernel, resizing array as necessary */
1596 nbytes = nalloc;
1597
1598 while (nbytes >= nalloc) {
1599 nalloc = nalloc * 2 + 200;
1600 nbytes = nalloc;
1601 if ((data = realloc(data, nbytes)) == NULL)
1602 err(EX_OSERR, "realloc");
1603 if (getsockopt(s, IPPROTO_IP, ocmd, data, &nbytes) < 0)
1604 err(EX_OSERR, "getsockopt(IP_%s_GET)",
1605 do_pipe ? "DUMMYNET" : "FW");
1606 }
1607
1608 if (do_pipe) {
1609 list_pipes(data, nbytes, ac, av);
1610 goto done;
1611 }
1612
1613 /*
b78533e2 1614 * Count static rules.
984263bc 1615 */
b78533e2
SZ
1616 r = data;
1617 nstat = r->static_count;
984263bc
MD
1618
1619 /*
1620 * Count dynamic rules. This is easier as they have
1621 * fixed size.
1622 */
9c2d26d1 1623 dynrules = (struct ipfw_ioc_state *)((uint8_t *)r + r->static_len);
b78533e2 1624 ndyn = (nbytes - r->static_len) / sizeof(*dynrules);
984263bc
MD
1625
1626 /* if showing stats, figure out column widths ahead of time */
1627 bcwidth = pcwidth = 0;
1628 if (do_acct) {
9c2d26d1
SZ
1629 for (n = 0, r = data; n < nstat; n++,
1630 r = (struct ipfw_ioc_rule *)((uint8_t *)r + IOC_RULESIZE(r))) {
984263bc 1631 /* packet counter */
c988a1de 1632 width = snprintf(NULL, 0, "%ju", (uintmax_t)r->pcnt);
984263bc
MD
1633 if (width > pcwidth)
1634 pcwidth = width;
1635
1636 /* byte counter */
c988a1de 1637 width = snprintf(NULL, 0, "%ju", (uintmax_t)r->bcnt);
984263bc
MD
1638 if (width > bcwidth)
1639 bcwidth = width;
1640 }
1641 }
1642 if (do_dynamic && ndyn) {
1643 for (n = 0, d = dynrules; n < ndyn; n++, d++) {
c988a1de 1644 width = snprintf(NULL, 0, "%ju", (uintmax_t)d->pcnt);
984263bc
MD
1645 if (width > pcwidth)
1646 pcwidth = width;
1647
c988a1de 1648 width = snprintf(NULL, 0, "%ju", (uintmax_t)d->bcnt);
984263bc
MD
1649 if (width > bcwidth)
1650 bcwidth = width;
1651 }
1652 }
1653 /* if no rule numbers were specified, list all rules */
1654 if (ac == 0) {
9c2d26d1
SZ
1655 for (n = 0, r = data; n < nstat; n++,
1656 r = (struct ipfw_ioc_rule *)((uint8_t *)r + IOC_RULESIZE(r)))
984263bc
MD
1657 show_ipfw(r, pcwidth, bcwidth);
1658
1659 if (do_dynamic && ndyn) {
1660 printf("## Dynamic rules (%d):\n", ndyn);
1661 for (n = 0, d = dynrules; n < ndyn; n++, d++)
1662 show_dyn_ipfw(d, pcwidth, bcwidth);
1663 }
1664 goto done;
1665 }
1666
1667 /* display specific rules requested on command line */
1668
1669 for (lac = ac, lav = av; lac != 0; lac--) {
1670 /* convert command line rule # */
1671 rnum = strtoul(*lav++, &endptr, 10);
1672 if (*endptr) {
1673 exitval = EX_USAGE;
1674 warnx("invalid rule number: %s", *(lav - 1));
1675 continue;
1676 }
9c2d26d1
SZ
1677 for (n = seen = 0, r = data; n < nstat; n++,
1678 r = (struct ipfw_ioc_rule *)((uint8_t *)r + IOC_RULESIZE(r))) {
984263bc
MD
1679 if (r->rulenum > rnum)
1680 break;
1681 if (r->rulenum == rnum) {
1682 show_ipfw(r, pcwidth, bcwidth);
1683 seen = 1;
1684 }
1685 }
1686 if (!seen) {
1687 /* give precedence to other error(s) */
1688 if (exitval == EX_OK)
1689 exitval = EX_UNAVAILABLE;
1690 warnx("rule %lu does not exist", rnum);
1691 }
1692 }
1693
1694 if (do_dynamic && ndyn) {
1695 printf("## Dynamic rules:\n");
1696 for (lac = ac, lav = av; lac != 0; lac--) {
1697 rnum = strtoul(*lav++, &endptr, 10);
1698 if (*endptr)
1699 /* already warned */
1700 continue;
1701 for (n = 0, d = dynrules; n < ndyn; n++, d++) {
b78533e2 1702 if (d->rulenum > rnum)
984263bc 1703 break;
b78533e2 1704 if (d->rulenum == rnum)
984263bc
MD
1705 show_dyn_ipfw(d, pcwidth, bcwidth);
1706 }
1707 }
1708 }
1709
1710 ac = 0;
1711
1712done:
1713 free(data);
1714
1715 if (exitval != EX_OK)
1716 exit(exitval);
1717}
1718
1719static void
1720show_usage(void)
1721{
1722 fprintf(stderr, "usage: ipfw [options]\n"
1723" add [number] rule\n"
1724" pipe number config [pipeconfig]\n"
1725" queue number config [queueconfig]\n"
1726" [pipe] flush\n"
1727" [pipe] delete number ...\n"
1728" [pipe] {list|show} [number ...]\n"
1729" {zero|resetlog} [number ...]\n"
1730"do \"ipfw -h\" or see ipfw manpage for details\n"
1731);
1732
1733 exit(EX_USAGE);
1734}
1735
1736static void
1737help(void)
1738{
1739
1740 fprintf(stderr, "ipfw syntax summary:\n"
1741"ipfw add [N] [prob {0..1}] ACTION [log [logamount N]] ADDR OPTIONS\n"
1742"ipfw {pipe|queue} N config BODY\n"
1743"ipfw [pipe] {zero|delete|show} [N{,N}]\n"
1744"\n"
1745"RULE: [1..] [PROB] BODY\n"
1746"RULENUM: INTEGER(1..65534)\n"
1747"PROB: prob REAL(0..1)\n"
1748"BODY: check-state [LOG] (no body) |\n"
1749" ACTION [LOG] MATCH_ADDR [OPTION_LIST]\n"
1750"ACTION: check-state | allow | count | deny | reject | skipto N |\n"
1751" {divert|tee} PORT | forward ADDR | pipe N | queue N\n"
1752"ADDR: [ MAC dst src ether_type ] \n"
1753" [ from IPLIST [ PORT ] to IPLIST [ PORTLIST ] ]\n"
1754"IPLIST: IPADDR | ( IPADDR or ... or IPADDR )\n"
1755"IPADDR: [not] { any | me | ip | ip/bits | ip:mask | ip/bits{x,y,z} }\n"
1756"OPTION_LIST: OPTION [,OPTION_LIST]\n"
1757);
1758exit(0);
1759}
1760
1761
1762static int
1763lookup_host (char *host, struct in_addr *ipaddr)
1764{
1765 struct hostent *he;
1766
1767 if (!inet_aton(host, ipaddr)) {
1768 if ((he = gethostbyname(host)) == NULL)
1769 return(-1);
1770 *ipaddr = *(struct in_addr *)he->h_addr_list[0];
1771 }
1772 return(0);
1773}
1774
1775/*
1776 * fills the addr and mask fields in the instruction as appropriate from av.
1777 * Update length as appropriate.
1778 * The following formats are allowed:
1779 * any matches any IP. Actually returns an empty instruction.
1780 * me returns O_IP_*_ME
1781 * 1.2.3.4 single IP address
1782 * 1.2.3.4:5.6.7.8 address:mask
1783 * 1.2.3.4/24 address/mask
1784 * 1.2.3.4/26{1,6,5,4,23} set of addresses in a subnet
1785 */
1786static void
1787fill_ip(ipfw_insn_ip *cmd, char *av)
1788{
678e8cc6 1789 char *p = NULL, md = 0;
984263bc
MD
1790 u_int32_t i;
1791
1792 cmd->o.len &= ~F_LEN_MASK; /* zero len */
1793
1794 if (!strncmp(av, "any", strlen(av)))
1795 return;
1796
1797 if (!strncmp(av, "me", strlen(av))) {
1798 cmd->o.len |= F_INSN_SIZE(ipfw_insn);
1799 return;
1800 }
1801
d938108c
SZ
1802 if (strlen(av) >= 3 && av[0] == '<' && av[strlen(av) - 1] == '>') {
1803 int pos = strlen(av) - 1;
1804 uint16_t tableid;
1805 char *eptr;
1806
1807 /*
1808 * Table: "<tableid>"
1809 */
1810 av[pos] = '\0';
1811 tableid = strtoul(&av[1], &eptr, 0);
1812 if (*eptr != '\0') {
1813 av[pos] = '>';
1814 errx(EX_DATAERR, "invalid tableid ``%s''", av);
1815 }
1816 av[pos] = '>';
1817 cmd->o.len |= F_INSN_SIZE(ipfw_insn);
1818 cmd->o.opcode = O_IP_DST_TABLE;
1819 cmd->o.arg1 = tableid;
1820 return;
1821 }
1822
984263bc
MD
1823 p = strchr(av, '/');
1824 if (!p)
1825 p = strchr(av, ':');
1826 if (p) {
1827 md = *p;
1828 *p++ = '\0';
1829 }
1830
1831 if (lookup_host(av, &cmd->addr) != 0)
1832 errx(EX_NOHOST, "hostname ``%s'' unknown", av);
1833 switch (md) {
1834 case ':':
1835 if (!inet_aton(p, &cmd->mask))
1836 errx(EX_DATAERR, "bad netmask ``%s''", p);
1837 break;
1838 case '/':
1839 i = atoi(p);
1840 if (i == 0)
1841 cmd->mask.s_addr = htonl(0);
1842 else if (i > 32)
1843 errx(EX_DATAERR, "bad width ``%s''", p);
1844 else
1845 cmd->mask.s_addr = htonl(~0 << (32 - i));
1846 break;
1847 default:
1848 cmd->mask.s_addr = htonl(~0);
1849 break;
1850 }
1851 cmd->addr.s_addr &= cmd->mask.s_addr;
1852 /*
1853 * now look if we have a set of addresses. They are stored as follows:
1854 * arg1 is the set size (powers of 2, 2..256)
1855 * addr is the base address IN HOST FORMAT
1856 * mask.. is an array of u_int32_t with bits set.
1857 */
1858 if (p)
1859 p = strchr(p, '{');
1860 if (p) { /* fetch addresses */
1861 u_int32_t *d;
1862 int low, high;
75a579b7 1863 int ii = contigmask((u_char *)&(cmd->mask), 32);
984263bc 1864
75a579b7 1865 if (ii < 24 || ii > 31) {
984263bc 1866 fprintf(stderr, "invalid set with mask %d\n",
75a579b7 1867 ii);
984263bc
MD
1868 exit(0);
1869 }
75a579b7 1870 cmd->o.arg1 = 1<<(32-ii);
984263bc
MD
1871 cmd->addr.s_addr = ntohl(cmd->addr.s_addr);
1872 d = (u_int32_t *)&cmd->mask;
1873 cmd->o.opcode = O_IP_DST_SET; /* default */
1874 cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32;
75a579b7
SZ
1875 for (ii = 0; ii < (cmd->o.arg1+31)/32 ; ii++)
1876 d[ii] = 0; /* clear masks */
984263bc
MD
1877
1878 av = p+1;
1879 low = cmd->addr.s_addr & 0xff;
1880 high = low + cmd->o.arg1 - 1;
1881 while (isdigit(*av)) {
75a579b7
SZ
1882 char *str;
1883 u_int16_t a = strtol(av, &str, 0);
984263bc 1884
75a579b7 1885 if (str == av) /* no parameter */
984263bc
MD
1886 break;
1887 if (a < low || a > high) {
1888 fprintf(stderr, "addr %d out of range [%d-%d]\n",
1889 a, low, high);
1890 exit(0);
1891 }
1892 a -= low;
1893 d[ a/32] |= 1<<(a & 31);
75a579b7 1894 if (*str != ',')
984263bc 1895 break;
75a579b7 1896 av = str+1;
984263bc
MD
1897 }
1898 return;
1899 }
1900
1901 if (cmd->mask.s_addr == 0) { /* any */
1902 if (cmd->o.len & F_NOT)
1903 errx(EX_DATAERR, "not any never matches");
1904 else /* useless, nuke it */
1905 return;
1906 } else if (cmd->mask.s_addr == IP_MASK_ALL) /* one IP */
1907 cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
1908 else /* addr/mask */
1909 cmd->o.len |= F_INSN_SIZE(ipfw_insn_ip);
1910}
1911
1912
1913/*
1914 * helper function to process a set of flags and set bits in the
1915 * appropriate masks.
1916 */
1917static void
1918fill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
1919 struct _s_x *flags, char *p)
1920{
1921 u_int8_t set=0, clear=0;
1922
1923 while (p && *p) {
1924 char *q; /* points to the separator */
1925 int val;
1926 u_int8_t *which; /* mask we are working on */
1927
1928 if (*p == '!') {
1929 p++;
1930 which = &clear;
1931 } else
1932 which = &set;
1933 q = strchr(p, ',');
1934 if (q)
1935 *q++ = '\0';
1936 val = match_token(flags, p);
1937 if (val <= 0)
1938 errx(EX_DATAERR, "invalid flag %s", p);
1939 *which |= (u_int8_t)val;
1940 p = q;
1941 }
1942 cmd->opcode = opcode;
1943 cmd->len = (cmd->len & (F_NOT | F_OR)) | 1;
1944 cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
1945}
1946
1947
1948static void
1949delete(int ac, char *av[])
1950{
1951 u_int32_t rulenum;
f9edcb74 1952 struct dn_ioc_pipe pipe;
984263bc
MD
1953 int i;
1954 int exitval = EX_OK;
1955 int do_set = 0;
1956
1957 memset(&pipe, 0, sizeof pipe);
1958
1959 av++; ac--;
1960 if (ac > 0 && !strncmp(*av, "set", strlen(*av))) {
1961 do_set = 1; /* delete set */
1962 ac--; av++;
1963 }
1964
1965 /* Rule number */
1966 while (ac && isdigit(**av)) {
1967 i = atoi(*av); av++; ac--;
1968 if (do_pipe) {
1969 if (do_pipe == 1)
1970 pipe.pipe_nr = i;
1971 else
1972 pipe.fs.fs_nr = i;
1973 i = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_DEL,
1974 &pipe, sizeof pipe);
1975 if (i) {
1976 exitval = 1;
1977 warn("rule %u: setsockopt(IP_DUMMYNET_DEL)",
1978 do_pipe == 1 ? pipe.pipe_nr :
1979 pipe.fs.fs_nr);
1980 }
1981 } else {
1982 rulenum = (i & 0xffff) | (do_set << 24);
1983 i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, &rulenum,
1984 sizeof rulenum);
1985 if (i) {
1986 exitval = EX_UNAVAILABLE;
1987 warn("rule %u: setsockopt(IP_FW_DEL)",
1988 rulenum);
1989 }
1990 }
1991 }
1992 if (exitval != EX_OK)
1993 exit(exitval);
1994}
1995
1996
1997/*
1998 * fill the interface structure. We do not check the name as we can
1999 * create interfaces dynamically, so checking them at insert time
2000 * makes relatively little sense.
3e4a09e7
MD
2001 * Interface names containing '*', '?', or '[' are assumed to be shell
2002 * patterns which match interfaces.
984263bc
MD
2003 */
2004static void
2005fill_iface(ipfw_insn_if *cmd, char *arg)
2006{
2007 cmd->name[0] = '\0';
2008 cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
2009
2010 /* Parse the interface or address */
2011 if (!strcmp(arg, "any"))
2012 cmd->o.len = 0; /* effectively ignore this command */
2013 else if (!isdigit(*arg)) {
3e4a09e7
MD
2014 strlcpy(cmd->name, arg, sizeof(cmd->name));
2015 cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
984263bc
MD
2016 } else if (!inet_aton(arg, &cmd->p.ip))
2017 errx(EX_DATAERR, "bad ip address ``%s''", arg);
2018}
2019
7700730e
MD
2020static unsigned long
2021getbw(const char *str, u_short *flags, int kb)
2022{
2023 char *end;
2024 unsigned long val;
2025 int inbytes = 0;
2026
2027 val = strtoul(str, &end, 0);
2028 if (*end == 'k' || *end == 'K') {
2029 ++end;
2030 val *= kb;
2031 } else if (*end == 'm' || *end == 'M') {
2032 ++end;
2033 val *= kb * kb;
2034 }
2035
2036 /*
2037 * Deal with bits or bytes or b(bits) or B(bytes). If there is no
2038 * trailer assume bits.
2039 */
2040 if (strncasecmp(end, "bit", 3) == 0) {
2041 ;
2042 } else if (strncasecmp(end, "byte", 4) == 0) {
2043 inbytes = 1;
2044 } else if (*end == 'b') {
2045 ;
2046 } else if (*end == 'B') {
2047 inbytes = 1;
2048 }
2049
2050 /*
2051 * Return in bits if flags is NULL, else flag bits
2052 * or bytes in flags and return the unconverted value.
2053 */
2054 if (inbytes && flags)
2055 *flags |= DN_QSIZE_IS_BYTES;
2056 else if (inbytes && flags == NULL)
2057 val *= 8;
2058 return(val);
2059}
2060
984263bc
MD
2061/*
2062 * the following macro returns an error message if we run out of
2063 * arguments.
2064 */
2065#define NEED1(msg) {if (!ac) errx(EX_USAGE, msg);}
2066
2067static void
2068config_pipe(int ac, char **av)
2069{
f9edcb74 2070 struct dn_ioc_pipe pipe;
984263bc
MD
2071 int i;
2072 char *end;
2073 u_int32_t a;
984263bc
MD
2074
2075 memset(&pipe, 0, sizeof pipe);
2076
2077 av++; ac--;
2078 /* Pipe number */
2079 if (ac && isdigit(**av)) {
2080 i = atoi(*av); av++; ac--;
2081 if (do_pipe == 1)
2082 pipe.pipe_nr = i;
2083 else
2084 pipe.fs.fs_nr = i;
2085 }
2086 while (ac > 0) {
2087 double d;
2088 int tok = match_token(dummynet_params, *av);
2089 ac--; av++;
2090
2091 switch(tok) {
2092 case TOK_NOERROR:
2093 pipe.fs.flags_fs |= DN_NOERROR;
2094 break;
2095
2096 case TOK_PLR:
2097 NEED1("plr needs argument 0..1\n");
2098 d = strtod(av[0], NULL);
2099 if (d > 1)
2100 d = 1;
2101 else if (d < 0)
2102 d = 0;
2103 pipe.fs.plr = (int)(d*0x7fffffff);
2104 ac--; av++;
2105 break;
2106
2107 case TOK_QUEUE:
2108 NEED1("queue needs queue size\n");
2109 end = NULL;
7700730e 2110 pipe.fs.qsize = getbw(av[0], &pipe.fs.flags_fs, 1024);
984263bc
MD
2111 ac--; av++;
2112 break;
2113
2114 case TOK_BUCKETS:
2115 NEED1("buckets needs argument\n");
2116 pipe.fs.rq_size = strtoul(av[0], NULL, 0);
2117 ac--; av++;
2118 break;
2119
2120 case TOK_MASK:
2121 NEED1("mask needs mask specifier\n");
2122 /*
2123 * per-flow queue, mask is dst_ip, dst_port,
2124 * src_ip, src_port, proto measured in bits
2125 */
984263bc 2126
f9edcb74
SZ
2127 pipe.fs.flow_mask.type = ETHERTYPE_IP;
2128 pipe.fs.flow_mask.u.ip.dst_ip = 0;
2129 pipe.fs.flow_mask.u.ip.src_ip = 0;
2130 pipe.fs.flow_mask.u.ip.dst_port = 0;
2131 pipe.fs.flow_mask.u.ip.src_port = 0;
2132 pipe.fs.flow_mask.u.ip.proto = 0;
984263bc
MD
2133 end = NULL;
2134
2135 while (ac >= 1) {
2136 u_int32_t *p32 = NULL;
2137 u_int16_t *p16 = NULL;
2138
2139 tok = match_token(dummynet_params, *av);
2140 ac--; av++;
2141 switch(tok) {
2142 case TOK_ALL:
2143 /*
2144 * special case, all bits significant
2145 */
f9edcb74
SZ
2146 pipe.fs.flow_mask.u.ip.dst_ip = ~0;
2147 pipe.fs.flow_mask.u.ip.src_ip = ~0;
2148 pipe.fs.flow_mask.u.ip.dst_port = ~0;
2149 pipe.fs.flow_mask.u.ip.src_port = ~0;
2150 pipe.fs.flow_mask.u.ip.proto = ~0;
984263bc
MD
2151 pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK;
2152 goto end_mask;
2153
2154 case TOK_DSTIP:
f9edcb74 2155 p32 = &pipe.fs.flow_mask.u.ip.dst_ip;
984263bc
MD
2156 break;
2157
2158 case TOK_SRCIP:
f9edcb74 2159 p32 = &pipe.fs.flow_mask.u.ip.src_ip;
984263bc
MD
2160 break;
2161
2162 case TOK_DSTPORT:
f9edcb74 2163 p16 = &pipe.fs.flow_mask.u.ip.dst_port;
984263bc
MD
2164 break;
2165
2166 case TOK_SRCPORT:
f9edcb74 2167 p16 = &pipe.fs.flow_mask.u.ip.src_port;
984263bc
MD
2168 break;
2169
2170 case TOK_PROTO:
2171 break;
2172
2173 default:
2174 ac++; av--; /* backtrack */
2175 goto end_mask;
2176 }
2177 if (ac < 1)
2178 errx(EX_USAGE, "mask: value missing");
2179 if (*av[0] == '/') {
2180 a = strtoul(av[0]+1, &end, 0);
2181 a = (a == 32) ? ~0 : (1 << a) - 1;
2182 } else
2183 a = strtoul(av[0], &end, 0);
2184 if (p32 != NULL)
2185 *p32 = a;
2186 else if (p16 != NULL) {
2187 if (a > 65535)
2188 errx(EX_DATAERR,
2189 "mask: must be 16 bit");
2190 *p16 = (u_int16_t)a;
2191 } else {
2192 if (a > 255)
2193 errx(EX_DATAERR,
2194 "mask: must be 8 bit");
f9edcb74 2195 pipe.fs.flow_mask.u.ip.proto = (uint8_t)a;
984263bc
MD
2196 }
2197 if (a != 0)
2198 pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK;
2199 ac--; av++;
2200 } /* end while, config masks */
2201end_mask:
2202 break;
2203
2204 case TOK_RED:
2205 case TOK_GRED:
2206 NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
2207 pipe.fs.flags_fs |= DN_IS_RED;
2208 if (tok == TOK_GRED)
2209 pipe.fs.flags_fs |= DN_IS_GENTLE_RED;
2210 /*
2211 * the format for parameters is w_q/min_th/max_th/max_p
2212 */
2213 if ((end = strsep(&av[0], "/"))) {
2214 double w_q = strtod(end, NULL);
2215 if (w_q > 1 || w_q <= 0)
2216 errx(EX_DATAERR, "0 < w_q <= 1");
2217 pipe.fs.w_q = (int) (w_q * (1 << SCALE_RED));
2218 }
2219 if ((end = strsep(&av[0], "/"))) {
2220 pipe.fs.min_th = strtoul(end, &end, 0);
2221 if (*end == 'K' || *end == 'k')
2222 pipe.fs.min_th *= 1024;
2223 }
2224 if ((end = strsep(&av[0], "/"))) {
2225 pipe.fs.max_th = strtoul(end, &end, 0);
2226 if (*end == 'K' || *end == 'k')
2227 pipe.fs.max_th *= 1024;
2228 }
2229 if ((end = strsep(&av[0], "/"))) {
2230 double max_p = strtod(end, NULL);
2231 if (max_p > 1 || max_p <= 0)
2232 errx(EX_DATAERR, "0 < max_p <= 1");
2233 pipe.fs.max_p = (int)(max_p * (1 << SCALE_RED));
2234 }
2235 ac--; av++;
2236 break;
2237
2238 case TOK_DROPTAIL:
2239 pipe.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
2240 break;
2241
2242 case TOK_BW:
ff6f118a 2243 NEED1("bw needs bandwidth\n");
984263bc
MD
2244 if (do_pipe != 1)
2245 errx(EX_DATAERR, "bandwidth only valid for pipes");
2246 /*
ff6f118a 2247 * set bandwidth value
984263bc 2248 */
ff6f118a
SZ
2249 pipe.bandwidth = getbw(av[0], NULL, 1000);
2250 if (pipe.bandwidth < 0)
2251 errx(EX_DATAERR, "bandwidth too large");
984263bc
MD
2252 ac--; av++;
2253 break;
2254
2255 case TOK_DELAY:
2256 if (do_pipe != 1)
2257 errx(EX_DATAERR, "delay only valid for pipes");
2258 NEED1("delay needs argument 0..10000ms\n");
2259 pipe.delay = strtoul(av[0], NULL, 0);
2260 ac--; av++;
2261 break;
2262
2263 case TOK_WEIGHT:
2264 if (do_pipe == 1)
2265 errx(EX_DATAERR,"weight only valid for queues");
2266 NEED1("weight needs argument 0..100\n");
2267 pipe.fs.weight = strtoul(av[0], &end, 0);
2268 ac--; av++;
2269 break;
2270
2271 case TOK_PIPE:
2272 if (do_pipe == 1)
2273 errx(EX_DATAERR,"pipe only valid for queues");
2274 NEED1("pipe needs pipe_number\n");
2275 pipe.fs.parent_nr = strtoul(av[0], &end, 0);
2276 ac--; av++;
2277 break;
2278
2279 default:
2280 errx(EX_DATAERR, "unrecognised option ``%s''", *av);
2281 }
2282 }
2283 if (do_pipe == 1) {
2284 if (pipe.pipe_nr == 0)
2285 errx(EX_DATAERR, "pipe_nr must be > 0");
2286 if (pipe.delay > 10000)
2287 errx(EX_DATAERR, "delay must be < 10000");
2288 } else { /* do_pipe == 2, queue */
2289 if (pipe.fs.parent_nr == 0)
2290 errx(EX_DATAERR, "pipe must be > 0");
2291 if (pipe.fs.weight >100)
2292 errx(EX_DATAERR, "weight must be <= 100");
2293 }
2294 if (pipe.fs.flags_fs & DN_QSIZE_IS_BYTES) {
2295 if (pipe.fs.qsize > 1024*1024)
2296 errx(EX_DATAERR, "queue size must be < 1MB");
2297 } else {
2298 if (pipe.fs.qsize > 100)
2299 errx(EX_DATAERR, "2 <= queue size <= 100");
2300 }
2301 if (pipe.fs.flags_fs & DN_IS_RED) {
2302 size_t len;
2303 int lookup_depth, avg_pkt_size;
75a579b7 2304 double step, idle, weight, w_q;
9caa622c 2305 int clock_hz;
984263bc
MD
2306 int t;
2307
2308 if (pipe.fs.min_th >= pipe.fs.max_th)
2309 errx(EX_DATAERR, "min_th %d must be < than max_th %d",
2310 pipe.fs.min_th, pipe.fs.max_th);
2311 if (pipe.fs.max_th == 0)
2312 errx(EX_DATAERR, "max_th must be > 0");
2313
2314 len = sizeof(int);
2315 if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
2316 &lookup_depth, &len, NULL, 0) == -1)
2317
2318 errx(1, "sysctlbyname(\"%s\")",
2319 "net.inet.ip.dummynet.red_lookup_depth");
2320 if (lookup_depth == 0)
2321 errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth"
2322 " must be greater than zero");
2323
2324 len = sizeof(int);
2325 if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
2326 &avg_pkt_size, &len, NULL, 0) == -1)
2327
2328 errx(1, "sysctlbyname(\"%s\")",
2329 "net.inet.ip.dummynet.red_avg_pkt_size");
2330 if (avg_pkt_size == 0)
2331 errx(EX_DATAERR,
2332 "net.inet.ip.dummynet.red_avg_pkt_size must"
2333 " be greater than zero");
2334
9caa622c
SZ
2335 len = sizeof(clock_hz);
2336 if (sysctlbyname("net.inet.ip.dummynet.hz", &clock_hz, &len,
2337 NULL, 0) == -1) {
2338 errx(1, "sysctlbyname(\"%s\")",
2339 "net.inet.ip.dummynet.hz");
2340 }
984263bc
MD
2341
2342 /*
2343 * Ticks needed for sending a medium-sized packet.
2344 * Unfortunately, when we are configuring a WF2Q+ queue, we
2345 * do not have bandwidth information, because that is stored
2346 * in the parent pipe, and also we have multiple queues
75a579b7 2347 * competing for it. So we set step=0, which is not very
984263bc
MD
2348 * correct. But on the other hand, why do we want RED with
2349 * WF2Q+ ?
2350 */
2351 if (pipe.bandwidth==0) /* this is a WF2Q+ queue */
75a579b7 2352 step = 0;
984263bc 2353 else
75a579b7 2354 step = clock_hz * avg_pkt_size * 8 / pipe.bandwidth;
984263bc
MD
2355
2356 /*
2357 * max idle time (in ticks) before avg queue size becomes 0.
2358 * NOTA: (3/w_q) is approx the value x so that
2359 * (1-w_q)^x < 10^-3.
2360 */
2361 w_q = ((double)pipe.fs.w_q) / (1 << SCALE_RED);
75a579b7 2362 idle = step * 3. / w_q;
984263bc
MD
2363 pipe.fs.lookup_step = (int)idle / lookup_depth;
2364 if (!pipe.fs.lookup_step)
2365 pipe.fs.lookup_step = 1;
2366 weight = 1 - w_q;
2367 for (t = pipe.fs.lookup_step; t > 0; --t)
2368 weight *= weight;
2369 pipe.fs.lookup_weight = (int)(weight * (1 << SCALE_RED));
2370 }
2371 i = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_CONFIGURE, &pipe,
2372 sizeof pipe);
2373 if (i)
2374 err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
2375}
2376
2377static void
2378get_mac_addr_mask(char *p, u_char *addr, u_char *mask)
2379{
2380 int i, l;
2381
2382 for (i=0; i<6; i++)
2383 addr[i] = mask[i] = 0;
2384 if (!strcmp(p, "any"))
2385 return;
2386
2387 for (i=0; *p && i<6;i++, p++) {
2388 addr[i] = strtol(p, &p, 16);
2389 if (*p != ':') /* we start with the mask */
2390 break;
2391 }
2392 if (*p == '/') { /* mask len */
2393 l = strtol(p+1, &p, 0);
2394 for (i=0; l>0; l -=8, i++)
2395 mask[i] = (l >=8) ? 0xff : (~0) << (8-l);
2396 } else if (*p == '&') { /* mask */
2397 for (i=0, p++; *p && i<6;i++, p++) {
2398 mask[i] = strtol(p, &p, 16);
2399 if (*p != ':')
2400 break;
2401 }
2402 } else if (*p == '\0') {
2403 for (i=0; i<6; i++)
2404 mask[i] = 0xff;
2405 }
2406 for (i=0; i<6; i++)
2407 addr[i] &= mask[i];
2408}
2409
2410/*
2411 * helper function, updates the pointer to cmd with the length
2412 * of the current command, and also cleans up the first word of
2413 * the new command in case it has been clobbered before.
2414 */
2415static ipfw_insn *
2416next_cmd(ipfw_insn *cmd)
2417{
2418 cmd += F_LEN(cmd);
2419 bzero(cmd, sizeof(*cmd));
2420 return cmd;
2421}
2422
2423/*
2424 * A function to fill simple commands of size 1.
2425 * Existing flags are preserved.
2426 */
2427static void
2428fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, u_int16_t arg)
2429{
2430 cmd->opcode = opcode;
2431 cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | 1;
2432 cmd->arg1 = arg;
2433}
2434
2435/*
2436 * Fetch and add the MAC address and type, with masks. This generates one or
2437 * two microinstructions, and returns the pointer to the last one.
2438 */
2439static ipfw_insn *
2440add_mac(ipfw_insn *cmd, int ac, char *av[])
2441{
2442 ipfw_insn_mac *mac;
2443
2444 if (ac < 2)
2445 errx(EX_DATAERR, "MAC dst src");
2446
2447 cmd->opcode = O_MACADDR2;
2448 cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
2449
2450 mac = (ipfw_insn_mac *)cmd;
2451 get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */
2452 get_mac_addr_mask(av[1], &(mac->addr[6]), &(mac->mask[6])); /* src */
2453 return cmd;
2454}
2455
2456static ipfw_insn *
2457add_mactype(ipfw_insn *cmd, int ac, char *av)
2458{
2459 if (ac < 1)
2460 errx(EX_DATAERR, "missing MAC type");
2461 if (strcmp(av, "any") != 0) { /* we have a non-null type */
2462 fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE);
2463 cmd->opcode = O_MAC_TYPE;
2464 return cmd;
2465 } else
2466 return NULL;
2467}
2468
2469static ipfw_insn *
2470add_proto(ipfw_insn *cmd, char *av)
2471{
2472 struct protoent *pe;
2473 u_char proto = 0;
2474
2475 if (!strncmp(av, "all", strlen(av)))
2476 ; /* same as "ip" */
2477 else if ((proto = atoi(av)) > 0)
2478 ; /* all done! */
2479 else if ((pe = getprotobyname(av)) != NULL)
2480 proto = pe->p_proto;
2481 else
2482 return NULL;
2483 if (proto != IPPROTO_IP)
2484 fill_cmd(cmd, O_PROTO, 0, proto);
2485 return cmd;
2486}
2487
2488static ipfw_insn *
2489add_srcip(ipfw_insn *cmd, char *av)
2490{
2491 fill_ip((ipfw_insn_ip *)cmd, av);
2492 if (cmd->opcode == O_IP_DST_SET) /* set */
2493 cmd->opcode = O_IP_SRC_SET;
d938108c
SZ
2494 else if (cmd->opcode == O_IP_DST_TABLE) /* table */
2495 cmd->opcode = O_IP_SRC_TABLE;
984263bc
MD
2496 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */
2497 cmd->opcode = O_IP_SRC_ME;
2498 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */
2499 cmd->opcode = O_IP_SRC;
2500 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_ip)) /* addr/mask */
2501 cmd->opcode = O_IP_SRC_MASK;
2502 return cmd;
2503}
2504
2505static ipfw_insn *
2506add_dstip(ipfw_insn *cmd, char *av)
2507{
2508 fill_ip((ipfw_insn_ip *)cmd, av);
2509 if (cmd->opcode == O_IP_DST_SET) /* set */
2510 ;
d938108c
SZ
2511 else if (cmd->opcode == O_IP_DST_TABLE) /* table */
2512 ;
984263bc
MD
2513 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */
2514 cmd->opcode = O_IP_DST_ME;
2515 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */
2516 cmd->opcode = O_IP_DST;
2517 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_ip)) /* addr/mask */
2518 cmd->opcode = O_IP_DST_MASK;
2519 return cmd;
2520}
2521
2522static ipfw_insn *
2523add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
2524{
2525 if (!strncmp(av, "any", strlen(av))) {
2526 return NULL;
2527 } else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) {
2528 /* XXX todo: check that we have a protocol with ports */
2529 cmd->opcode = opcode;
2530 return cmd;
2531 }
2532 return NULL;
2533}
2534
2535/*
2536 * Parse arguments and assemble the microinstructions which make up a rule.
2537 * Rules are added into the 'rulebuf' and then copied in the correct order
2538 * into the actual rule.
2539 *
2540 * The syntax for a rule starts with the action, followed by an
2541 * optional log action, and the various match patterns.
2542 * In the assembled microcode, the first opcode must be a O_PROBE_STATE
2543 * (generated if the rule includes a keep-state option), then the
2544 * various match patterns, the "log" action, and the actual action.
2545 *
2546 */
2547static void
2548add(int ac, char *av[])
2549{
2550 /*
2551 * rules are added into the 'rulebuf' and then copied in
2552 * the correct order into the actual rule.
2553 * Some things that need to go out of order (prob, action etc.)
2554 * go into actbuf[].
2555 */
b78533e2
SZ
2556 static uint32_t rulebuf[IPFW_RULE_SIZE_MAX];
2557 static uint32_t actbuf[IPFW_RULE_SIZE_MAX];
2558 static uint32_t cmdbuf[IPFW_RULE_SIZE_MAX];
984263bc 2559
7700730e 2560 ipfw_insn *src, *dst, *cmd, *action, *prev = NULL;
984263bc
MD
2561 ipfw_insn *first_cmd; /* first match pattern */
2562
b78533e2 2563 struct ipfw_ioc_rule *rule;
984263bc
MD
2564
2565 /*
2566 * various flags used to record that we entered some fields.
2567 */
2568 ipfw_insn *have_state = NULL; /* check-state or keep-state */
2569
2570 int i;
2571
2572 int open_par = 0; /* open parenthesis ( */
2573
2574 /* proto is here because it is used to fetch ports */
2575 u_char proto = IPPROTO_IP; /* default protocol */
2576
2577 double match_prob = 1; /* match probability, default is always match */
2578
2579 bzero(actbuf, sizeof(actbuf)); /* actions go here */
2580 bzero(cmdbuf, sizeof(cmdbuf));
2581 bzero(rulebuf, sizeof(rulebuf));
2582
b78533e2 2583 rule = (struct ipfw_ioc_rule *)rulebuf;
984263bc
MD
2584 cmd = (ipfw_insn *)cmdbuf;
2585 action = (ipfw_insn *)actbuf;
2586
2587 av++; ac--;
2588
2589 /* [rule N] -- Rule number optional */
2590 if (ac && isdigit(**av)) {
2591 rule->rulenum = atoi(*av);
2592 av++;
2593 ac--;
2594 }
2595
2596 /* [set N] -- set number (0..30), optional */
2597 if (ac > 1 && !strncmp(*av, "set", strlen(*av))) {
2598 int set = strtoul(av[1], NULL, 10);
2599 if (set < 0 || set > 30)
2600 errx(EX_DATAERR, "illegal set %s", av[1]);
2601 rule->set = set;
2602 av += 2; ac -= 2;
2603 }
2604
2605 /* [prob D] -- match probability, optional */
2606 if (ac > 1 && !strncmp(*av, "prob", strlen(*av))) {
2607 match_prob = strtod(av[1], NULL);
2608
2609 if (match_prob <= 0 || match_prob > 1)
2610 errx(EX_DATAERR, "illegal match prob. %s", av[1]);
2611 av += 2; ac -= 2;
2612 }
2613
2614 /* action -- mandatory */
2615 NEED1("missing action");
2616 i = match_token(rule_actions, *av);
2617 ac--; av++;
2618 action->len = 1; /* default */
2619 switch(i) {
2620 case TOK_CHECKSTATE:
2621 have_state = action;
2622 action->opcode = O_CHECK_STATE;
2623 break;
2624
2625 case TOK_ACCEPT:
2626 action->opcode = O_ACCEPT;
2627 break;
2628
6998b243
SZ
2629 case TOK_DEFRAG:
2630 action->opcode = O_DEFRAG;
2631 action->arg1 = 0;
2632 break;
2633
984263bc
MD
2634 case TOK_DENY:
2635 action->opcode = O_DENY;
2636 action->arg1 = 0;
2637 break;
2638
2639 case TOK_REJECT:
2640 action->opcode = O_REJECT;
2641 action->arg1 = ICMP_UNREACH_HOST;
2642 break;
2643
2644 case TOK_RESET:
2645 action->opcode = O_REJECT;
2646 action->arg1 = ICMP_REJECT_RST;
2647 break;
2648
2649 case TOK_UNREACH:
2650 action->opcode = O_REJECT;
2651 NEED1("missing reject code");
2652 fill_reject_code(&action->arg1, *av);
2653 ac--; av++;
2654 break;
2655
2656 case TOK_COUNT:
2657 action->opcode = O_COUNT;
2658 break;
2659
2660 case TOK_QUEUE:
2661 case TOK_PIPE:
2662 action->len = F_INSN_SIZE(ipfw_insn_pipe);
2663 case TOK_SKIPTO:
2664 if (i == TOK_QUEUE)
2665 action->opcode = O_QUEUE;
2666 else if (i == TOK_PIPE)
2667 action->opcode = O_PIPE;
2668 else if (i == TOK_SKIPTO)
2669 action->opcode = O_SKIPTO;
2670 NEED1("missing skipto/pipe/queue number");
2671 action->arg1 = strtoul(*av, NULL, 10);
2672 av++; ac--;
2673 break;
2674
2675 case TOK_DIVERT:
2676 case TOK_TEE:
2677 action->opcode = (i == TOK_DIVERT) ? O_DIVERT : O_TEE;
2678 NEED1("missing divert/tee port");
2679 action->arg1 = strtoul(*av, NULL, 0);
2680 if (action->arg1 == 0) {
75a579b7 2681 struct servent *serv;
984263bc 2682 setservent(1);
75a579b7
SZ
2683 serv = getservbyname(av[0], "divert");
2684 if (serv != NULL)
2685 action->arg1 = ntohs(serv->s_port);
984263bc
MD
2686 else
2687 errx(EX_DATAERR, "illegal divert/tee port");
2688 }
2689 ac--; av++;
2690 break;
2691
2692 case TOK_FORWARD: {
2693 ipfw_insn_sa *p = (ipfw_insn_sa *)action;
75a579b7 2694 char *st, *end;
984263bc
MD
2695
2696 NEED1("missing forward address[:port]");
2697
2698 action->opcode = O_FORWARD_IP;
2699 action->len = F_INSN_SIZE(ipfw_insn_sa);
2700
2701 p->sa.sin_len = sizeof(struct sockaddr_in);
2702 p->sa.sin_family = AF_INET;
2703 p->sa.sin_port = 0;
2704 /*
2705 * locate the address-port separator (':' or ',')
2706 */
75a579b7
SZ
2707 st = strchr(*av, ':');
2708 if (st == NULL)
2709 st = strchr(*av, ',');
2710 if (st != NULL) {
2711 *(st++) = '\0';
2712 i = strtoport(st, &end, 0 /* base */, 0 /* proto */);
2713 if (st == end)
984263bc 2714 errx(EX_DATAERR,
75a579b7 2715 "illegal forwarding port ``%s''", st);
984263bc
MD
2716 p->sa.sin_port = (u_short)i;
2717 }
2718 lookup_host(*av, &(p->sa.sin_addr));
2719 }
2720 ac--; av++;
2721 break;
2722
2723 default:
2724 errx(EX_DATAERR, "invalid action %s\n", av[-1]);
2725 }
2726 action = next_cmd(action);
2727
2728 /*
2729 * [log [logamount N]] -- log, optional
2730 *
2731 * If exists, it goes first in the cmdbuf, but then it is
2732 * skipped in the copy section to the end of the buffer.
2733 */
2734 if (ac && !strncmp(*av, "log", strlen(*av))) {
2735 ipfw_insn_log *c = (ipfw_insn_log *)cmd;
2736
2737 cmd->len = F_INSN_SIZE(ipfw_insn_log);
2738 cmd->opcode = O_LOG;
2739 av++; ac--;
2740 if (ac && !strncmp(*av, "logamount", strlen(*av))) {
2741 ac--; av++;
2742 NEED1("logamount requires argument");
2743 c->max_log = atoi(*av);
984263bc
MD
2744 ac--; av++;
2745 }
2746 cmd = next_cmd(cmd);
2747 }
2748
2749 if (have_state) /* must be a check-state, we are done */
2750 goto done;
2751
2752#define OR_START(target) \
2753 if (ac && (*av[0] == '(' || *av[0] == '{')) { \
2754 if (open_par) \
2755 errx(EX_USAGE, "nested \"(\" not allowed\n"); \
2756 prev = NULL; \
2757 open_par = 1; \
2758 if ( (av[0])[1] == '\0') { \
2759 ac--; av++; \
2760 } else \
2761 (*av)++; \
2762 } \
2763 target: \
2764
2765
2766#define CLOSE_PAR \
2767 if (open_par) { \
2768 if (ac && ( \
2769 !strncmp(*av, ")", strlen(*av)) || \
2770 !strncmp(*av, "}", strlen(*av)) )) { \
2771 prev = NULL; \
2772 open_par = 0; \
2773 ac--; av++; \
2774 } else \
2775 errx(EX_USAGE, "missing \")\"\n"); \
2776 }
2777
2778#define NOT_BLOCK \
2779 if (ac && !strncmp(*av, "not", strlen(*av))) { \
2780 if (cmd->len & F_NOT) \
2781 errx(EX_USAGE, "double \"not\" not allowed\n"); \
2782 cmd->len |= F_NOT; \
2783 ac--; av++; \
2784 }
2785
2786#define OR_BLOCK(target) \
2787 if (ac && !strncmp(*av, "or", strlen(*av))) { \
2788 if (prev == NULL || open_par == 0) \
2789 errx(EX_DATAERR, "invalid OR block"); \
2790 prev->len |= F_OR; \
2791 ac--; av++; \
2792 goto target; \
2793 } \
2794 CLOSE_PAR;
2795
2796 first_cmd = cmd;
2797
2798#if 0
2799 /*
2800 * MAC addresses, optional.
2801 * If we have this, we skip the part "proto from src to dst"
2802 * and jump straight to the option parsing.
2803 */
2804 NOT_BLOCK;
2805 NEED1("missing protocol");
2806 if (!strncmp(*av, "MAC", strlen(*av)) ||
2807 !strncmp(*av, "mac", strlen(*av))) {
2808 ac--; av++; /* the "MAC" keyword */
2809 add_mac(cmd, ac, av); /* exits in case of errors */
2810 cmd = next_cmd(cmd);
2811 ac -= 2; av += 2; /* dst-mac and src-mac */
2812 NOT_BLOCK;
2813 NEED1("missing mac type");
2814 if (add_mactype(cmd, ac, av[0]))
2815 cmd = next_cmd(cmd);
2816 ac--; av++; /* any or mac-type */
2817 goto read_options;
2818 }
2819#endif
2820
2821 /*
2822 * protocol, mandatory
2823 */
2824 OR_START(get_proto);
2825 NOT_BLOCK;
2826 NEED1("missing protocol");
2827 if (add_proto(cmd, *av)) {
2828 av++; ac--;
2829 if (F_LEN(cmd) == 0) /* plain IP */
2830 proto = 0;
2831 else {
2832 proto = cmd->arg1;
2833 prev = cmd;
2834 cmd = next_cmd(cmd);
2835 }
2836 } else if (first_cmd != cmd) {
7700730e 2837 errx(EX_DATAERR, "invalid protocol ``%s''", *av);
984263bc
MD
2838 } else
2839 goto read_options;
2840 OR_BLOCK(get_proto);
2841
2842 /*
2843 * "from", mandatory
2844 */
2845 if (!ac || strncmp(*av, "from", strlen(*av)))
2846 errx(EX_USAGE, "missing ``from''");
2847 ac--; av++;
2848
2849 /*
2850 * source IP, mandatory
2851 */
2852 OR_START(source_ip);
2853 NOT_BLOCK; /* optional "not" */
2854 NEED1("missing source address");
2855 if (add_srcip(cmd, *av)) {
2856 ac--; av++;
2857 if (F_LEN(cmd) != 0) { /* ! any */
2858 prev = cmd;
2859 cmd = next_cmd(cmd);
2860 }
2861 }
2862 OR_BLOCK(source_ip);
2863
2864 /*
2865 * source ports, optional
2866 */
2867 NOT_BLOCK; /* optional "not" */
2868 if (ac) {
2869 if (!strncmp(*av, "any", strlen(*av)) ||
2870 add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
2871 ac--; av++;
2872 if (F_LEN(cmd) != 0)
2873 cmd = next_cmd(cmd);
2874 }
2875 }
2876
2877 /*
2878 * "to", mandatory
2879 */
2880 if (!ac || strncmp(*av, "to", strlen(*av)))
2881 errx(EX_USAGE, "missing ``to''");
2882 av++; ac--;
2883
2884 /*
2885 * destination, mandatory
2886 */
2887 OR_START(dest_ip);
2888 NOT_BLOCK; /* optional "not" */
2889 NEED1("missing dst address");
2890 if (add_dstip(cmd, *av)) {
2891 ac--; av++;
2892 if (F_LEN(cmd) != 0) { /* ! any */
2893 prev = cmd;
2894 cmd = next_cmd(cmd);
2895 }
2896 }
2897 OR_BLOCK(dest_ip);
2898
2899 /*
2900 * dest. ports, optional
2901 */
2902 NOT_BLOCK; /* optional "not" */
2903 if (ac) {
2904 if (!strncmp(*av, "any", strlen(*av)) ||
2905 add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
2906 ac--; av++;
2907 if (F_LEN(cmd) != 0)
2908 cmd = next_cmd(cmd);
2909 }
2910 }
2911
2912read_options:
2913 if (ac && first_cmd == cmd) {
2914 /*
2915 * nothing specified so far, store in the rule to ease
2916 * printout later.
2917 */
b78533e2 2918 rule->usr_flags = IPFW_USR_F_NORULE;
984263bc
MD
2919 }
2920 prev = NULL;
2921 while (ac) {
75a579b7 2922 char *s1;
984263bc
MD
2923 ipfw_insn_u32 *cmd32; /* alias for cmd */
2924
75a579b7 2925 s1 = *av;
984263bc
MD
2926 cmd32 = (ipfw_insn_u32 *)cmd;
2927
75a579b7 2928 if (*s1 == '!') { /* alternate syntax for NOT */
984263bc
MD
2929 if (cmd->len & F_NOT)
2930 errx(EX_USAGE, "double \"not\" not allowed\n");
2931 cmd->len = F_NOT;
75a579b7 2932 s1++;
984263bc 2933 }
75a579b7 2934 i = match_token(rule_options, s1);
984263bc
MD
2935 ac--; av++;
2936 switch(i) {
2937 case TOK_NOT:
2938 if (cmd->len & F_NOT)
2939 errx(EX_USAGE, "double \"not\" not allowed\n");
2940 cmd->len = F_NOT;
2941 break;
2942
2943 case TOK_OR:
2944 if (open_par == 0 || prev == NULL)
2945 errx(EX_USAGE, "invalid \"or\" block\n");
2946 prev->len |= F_OR;
2947 break;
2948
2949 case TOK_STARTBRACE:
2950 if (open_par)
2951 errx(EX_USAGE, "+nested \"(\" not allowed\n");
2952 open_par = 1;
2953 break;
2954
2955 case TOK_ENDBRACE:
2956 if (!open_par)
2957 errx(EX_USAGE, "+missing \")\"\n");
2958 open_par = 0;
2959 prev = NULL;
2960 break;
2961
2962 case TOK_IN:
2963 fill_cmd(cmd, O_IN, 0, 0);
2964 break;
2965
2966 case TOK_OUT:
2967 cmd->len ^= F_NOT; /* toggle F_NOT */
2968 fill_cmd(cmd, O_IN, 0, 0);
2969 break;
2970
2971 case TOK_FRAG:
2972 fill_cmd(cmd, O_FRAG, 0, 0);
2973 break;
2974
cc9ef3d3
SZ
2975 case TOK_IPFRAG:
2976 fill_cmd(cmd, O_IPFRAG, 0, 0);
2977 break;
2978
984263bc
MD
2979 case TOK_LAYER2:
2980 fill_cmd(cmd, O_LAYER2, 0, 0);
2981 break;
2982
2983 case TOK_XMIT:
2984 case TOK_RECV:
2985 case TOK_VIA:
2986 NEED1("recv, xmit, via require interface name"
2987 " or address");
2988 fill_iface((ipfw_insn_if *)cmd, av[0]);
2989 ac--; av++;
2990 if (F_LEN(cmd) == 0) /* not a valid address */
2991 break;
2992 if (i == TOK_XMIT)
2993 cmd->opcode = O_XMIT;
2994 else if (i == TOK_RECV)
2995 cmd->opcode = O_RECV;
2996 else if (i == TOK_VIA)
2997 cmd->opcode = O_VIA;
2998 break;
2999
3000 case TOK_ICMPTYPES:
3001 NEED1("icmptypes requires list of types");
3002 fill_icmptypes((ipfw_insn_u32 *)cmd, *av);
3003 av++; ac--;
3004 break;
3005
3006 case TOK_IPTTL:
3007 NEED1("ipttl requires TTL");
3008 fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
3009 ac--; av++;
3010 break;
3011
3012 case TOK_IPID:
3013 NEED1("ipid requires length");
3014 fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0));
3015 ac--; av++;
3016 break;
3017
3018 case TOK_IPLEN:
3019 NEED1("iplen requires length");
3020 fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0));
3021 ac--; av++;
3022 break;
3023
3024 case TOK_IPVER:
3025 NEED1("ipver requires version");
3026 fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0));
3027 ac--; av++;
3028 break;
3029
3030 case TOK_IPPRECEDENCE:
3031 NEED1("ipprecedence requires value");
3032 fill_cmd(cmd, O_IPPRECEDENCE, 0,
3033 (strtoul(*av, NULL, 0) & 7) << 5);
3034 ac--; av++;
3035 break;
3036
3037 case TOK_IPOPTS:
3038 NEED1("missing argument for ipoptions");
3039 fill_flags(cmd, O_IPOPT, f_ipopts, *av);
3040 ac--; av++;
3041 break;
3042
3043 case TOK_IPTOS:
3044 NEED1("missing argument for iptos");
3045 fill_flags(cmd, O_IPTOS, f_iptos, *av);
3046 ac--; av++;
3047 break;
3048
3049 case TOK_UID:
3050 NEED1("uid requires argument");
3051 {
3052 char *end;
3053 uid_t uid;
3054 struct passwd *pwd;
3055
3056 cmd->opcode = O_UID;
3057 uid = strtoul(*av, &end, 0);
3058 pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av);
3059 if (pwd == NULL)
3060 errx(EX_DATAERR, "uid \"%s\" nonexistent", *av);
3061 cmd32->d[0] = pwd->pw_uid;
3062 cmd->len = F_INSN_SIZE(ipfw_insn_u32);
3063 ac--; av++;
3064 }
3065 break;
3066
3067 case TOK_GID:
3068 NEED1("gid requires argument");
3069 {
3070 char *end;
3071 gid_t gid;
3072 struct group *grp;
3073
3074 cmd->opcode = O_GID;
3075 gid = strtoul(*av, &end, 0);
3076 grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av);
3077 if (grp == NULL)
3078 errx(EX_DATAERR, "gid \"%s\" nonexistent", *av);
3079 cmd32->d[0] = grp->gr_gid;
3080 cmd->len = F_INSN_SIZE(ipfw_insn_u32);
3081 ac--; av++;
3082 }
3083 break;
3084
3085 case TOK_ESTAB:
3086 fill_cmd(cmd, O_ESTAB, 0, 0);
3087 break;
3088
3089 case TOK_SETUP:
3090 fill_cmd(cmd, O_TCPFLAGS, 0,
3091 (TH_SYN) | ( (TH_ACK) & 0xff) <<8 );
3092 break;
3093
3094 case TOK_TCPOPTS:
3095 NEED1("missing argument for tcpoptions");
3096 fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av);
3097 ac--; av++;
3098 break;
3099
3100 case TOK_TCPSEQ:
3101 case TOK_TCPACK:
3102 NEED1("tcpseq/tcpack requires argument");
3103 cmd->len = F_INSN_SIZE(ipfw_insn_u32);
3104 cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK;
3105 cmd32->d[0] = htonl(strtoul(*av, NULL, 0));
3106 ac--; av++;
3107 break;
3108
3109 case TOK_TCPWIN:
3110 NEED1("tcpwin requires length");
3111 fill_cmd(cmd, O_TCPWIN, 0,
3112 htons(strtoul(*av, NULL, 0)));
3113 ac--; av++;
3114 break;
3115
3116 case TOK_TCPFLAGS:
3117 NEED1("missing argument for tcpflags");
3118 cmd->opcode = O_TCPFLAGS;
3119 fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av);
3120 ac--; av++;
3121 break;
3122
3123 case TOK_KEEPSTATE:
3124 if (open_par)
3125 errx(EX_USAGE, "keep-state cannot be part "
3126 "of an or block");
3127 if (have_state)
3128 errx(EX_USAGE, "only one of keep-state "
3129 "and limit is allowed");
3130 have_state = cmd;
3131 fill_cmd(cmd, O_KEEP_STATE, 0, 0);
3132 break;
3133
3134 case TOK_LIMIT:
3135 if (open_par)
3136 errx(EX_USAGE, "limit cannot be part "
3137 "of an or block");
3138 if (have_state)
3139 errx(EX_USAGE, "only one of keep-state "
3140 "and limit is allowed");
3141 NEED1("limit needs mask and # of connections");
3142 have_state = cmd;
3143 {
3144 ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
3145
3146 cmd->len = F_INSN_SIZE(ipfw_insn_limit);
3147 cmd->opcode = O_LIMIT;
3148 c->limit_mask = 0;
3149 c->conn_limit = 0;
3150 for (; ac >1 ;) {
3151 int val;
3152
3153 val = match_token(limit_masks, *av);
3154 if (val <= 0)
3155 break;
3156 c->limit_mask |= val;
3157 ac--; av++;
3158 }
3159 c->conn_limit = atoi(*av);
3160 if (c->conn_limit == 0)
3161 errx(EX_USAGE, "limit: limit must be >0");
3162 if (c->limit_mask == 0)
3163 errx(EX_USAGE, "missing limit mask");
3164 ac--; av++;
3165 }
3166 break;
3167
3168 case TOK_PROTO:
3169 NEED1("missing protocol");
3170 if (add_proto(cmd, *av)) {
3171 proto = cmd->arg1;
3172 ac--; av++;
3173 } else
7700730e 3174 errx(EX_DATAERR, "invalid protocol ``%s''", *av);
984263bc
MD
3175 break;
3176
3177 case TOK_SRCIP:
3178 NEED1("missing source IP");
3179 if (add_srcip(cmd, *av)) {
3180 ac--; av++;
3181 }
3182 break;
3183
3184 case TOK_DSTIP:
3185 NEED1("missing destination IP");
3186 if (add_dstip(cmd, *av)) {
3187 ac--; av++;
3188 }
3189 break;
3190
3191 case TOK_SRCPORT:
3192 NEED1("missing source port");
3193 if (!strncmp(*av, "any", strlen(*av)) ||
3194 add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
3195 ac--; av++;
3196 } else
3197 errx(EX_DATAERR, "invalid source port %s", *av);
3198 break;
3199
3200 case TOK_DSTPORT:
3201 NEED1("missing destination port");
3202 if (!strncmp(*av, "any", strlen(*av)) ||
3203 add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
3204 ac--; av++;
3205 } else
3206 errx(EX_DATAERR, "invalid destination port %s",
3207 *av);
3208 break;
3209
3210 case TOK_MAC:
3211 if (ac < 2)
3212 errx(EX_USAGE, "MAC dst-mac src-mac");
3213 if (add_mac(cmd, ac, av)) {
3214 ac -= 2; av += 2;
3215 }
3216 break;
3217
3218 case TOK_MACTYPE:
3219 NEED1("missing mac type");
3220 if (!add_mactype(cmd, ac, *av))
7700730e 3221 errx(EX_DATAERR, "invalid mac type %s", *av);
984263bc
MD
3222 ac--; av++;
3223 break;
3224
3225 default:
75a579b7 3226 errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s1);
984263bc
MD
3227 }
3228 if (F_LEN(cmd) > 0) { /* prepare to advance */
3229 prev = cmd;
3230 cmd = next_cmd(cmd);
3231 }
3232 }
3233
3234done:
3235 /*
3236 * Now copy stuff into the rule.
3237 * If we have a keep-state option, the first instruction
3238 * must be a PROBE_STATE (which is generated here).
3239 * If we have a LOG option, it was stored as the first command,
3240 * and now must be moved to the top of the action part.
3241 */
3242 dst = (ipfw_insn *)rule->cmd;
3243
3244 /*
3245 * First thing to write into the command stream is the match probability.
3246 */
3247 if (match_prob != 1) { /* 1 means always match */
3248 dst->opcode = O_PROB;
3249 dst->len = 2;
3250 *((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff);
3251 dst += dst->len;
3252 }
3253
3254 /*
3255 * generate O_PROBE_STATE if necessary
3256 */
3257 if (have_state && have_state->opcode != O_CHECK_STATE) {
3258 fill_cmd(dst, O_PROBE_STATE, 0, 0);
3259 dst = next_cmd(dst);
3260 }
3261 /*
3262 * copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT
3263 */
3264 for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
3265 i = F_LEN(src);
3266
3267 switch (src->opcode) {
3268 case O_LOG:
3269 case O_KEEP_STATE:
3270 case O_LIMIT:
3271 break;
3272 default:
3273 bcopy(src, dst, i * sizeof(u_int32_t));
3274 dst += i;
3275 }
3276 }
3277
3278 /*
3279 * put back the have_state command as last opcode
3280 */
3281 if (have_state && have_state->opcode != O_CHECK_STATE) {
3282 i = F_LEN(have_state);
3283 bcopy(have_state, dst, i * sizeof(u_int32_t));
3284 dst += i;
3285 }
3286 /*
3287 * start action section
3288 */
3289 rule->act_ofs = dst - rule->cmd;
3290
3291 /*
3292 * put back O_LOG if necessary
3293 */
3294 src = (ipfw_insn *)cmdbuf;
3295 if ( src->opcode == O_LOG ) {
3296 i = F_LEN(src);
3297 bcopy(src, dst, i * sizeof(u_int32_t));
3298 dst += i;
3299 }
3300 /*
3301 * copy all other actions
3302 */
3303 for (src = (ipfw_insn *)actbuf; src != action; src += i) {
3304 i = F_LEN(src);
3305 bcopy(src, dst, i * sizeof(u_int32_t));
3306 dst += i;
3307 }
3308
3309 rule->cmd_len = (u_int32_t *)dst - (u_int32_t *)(rule->cmd);
9c2d26d1 3310 i = (uint8_t *)dst - (uint8_t *)rule;
984263bc
MD
3311 if (getsockopt(s, IPPROTO_IP, IP_FW_ADD, rule, &i) == -1)
3312 err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
3313 if (!do_quiet)
3314 show_ipfw(rule, 10, 10);
3315}
3316
3317static void
3318zero(int ac, char *av[])
3319{
3320 int rulenum;
3321 int failed = EX_OK;
3322
3323 av++; ac--;
3324
3325 if (!ac) {
3326 /* clear all entries */
3327 if (setsockopt(s, IPPROTO_IP, IP_FW_ZERO, NULL, 0) < 0)
3328 err(EX_UNAVAILABLE, "setsockopt(%s)", "IP_FW_ZERO");
3329 if (!do_quiet)
3330 printf("Accounting cleared.\n");
3331
3332 return;
3333 }
3334
3335 while (ac) {
3336 /* Rule number */
3337 if (isdigit(**av)) {
3338 rulenum = atoi(*av);
3339 av++;
3340 ac--;
3341 if (setsockopt(s, IPPROTO_IP,
3342 IP_FW_ZERO, &rulenum, sizeof rulenum)) {
3343 warn("rule %u: setsockopt(IP_FW_ZERO)",
3344 rulenum);
3345 failed = EX_UNAVAILABLE;
3346 } else if (!do_quiet)
3347 printf("Entry %d cleared\n", rulenum);
3348 } else {
3349 errx(EX_USAGE, "invalid rule number ``%s''", *av);
3350 }
3351 }
3352 if (failed != EX_OK)
3353 exit(failed);
3354}
3355
3356static void
3357resetlog(int ac, char *av[])
3358{
3359 int rulenum;
3360 int failed = EX_OK;
3361
3362 av++; ac--;
3363
3364 if (!ac) {
3365 /* clear all entries */
3366 if (setsockopt(s, IPPROTO_IP, IP_FW_RESETLOG, NULL, 0) < 0)
3367 err(EX_UNAVAILABLE, "setsockopt(IP_FW_RESETLOG)");
3368 if (!do_quiet)
3369 printf("Logging counts reset.\n");
3370
3371 return;
3372 }
3373
3374 while (ac) {
3375 /* Rule number */
3376 if (isdigit(**av)) {
3377 rulenum = atoi(*av);
3378 av++;
3379 ac--;
3380 if (setsockopt(s, IPPROTO_IP,
3381 IP_FW_RESETLOG, &rulenum, sizeof rulenum)) {
3382 warn("rule %u: setsockopt(IP_FW_RESETLOG)",
3383 rulenum);
3384 failed = EX_UNAVAILABLE;
3385 } else if (!do_quiet)
3386 printf("Entry %d logging count reset\n",
3387 rulenum);
3388 } else {
3389 errx(EX_DATAERR, "invalid rule number ``%s''", *av);
3390 }
3391 }
3392 if (failed != EX_OK)
3393 exit(failed);
3394}
3395
3396static void
1ab50dff 3397flush(void)
984263bc
MD
3398{
3399 int cmd = do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH;
3400
3401 if (!do_force && !do_quiet) { /* need to ask user */
3402 int c;
3403
3404 printf("Are you sure? [yn] ");
3405 fflush(stdout);
3406 do {
3407 c = toupper(getc(stdin));
3408 while (c != '\n' && getc(stdin) != '\n')
3409 if (feof(stdin))
3410 return; /* and do not flush */
3411 } while (c != 'Y' && c != 'N');
3412 printf("\n");
3413 if (c == 'N') /* user said no */
3414 return;
3415 }
3416 if (setsockopt(s, IPPROTO_IP, cmd, NULL, 0) < 0)
3417 err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
3418 do_pipe ? "DUMMYNET" : "FW");
3419 if (!do_quiet)
3420 printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules");
3421}
3422
d938108c
SZ
3423static void
3424table_create(int ac, char **av)
3425{
3426 struct ipfw_ioc_table tbl;
3427 char *eptr;
3428
3429 ac--;
3430 av++;
3431
3432 if (ac == 0)
3433 errx(EX_DATAERR, "missing table id");
3434
3435 memset(&tbl, 0, sizeof(tbl));
3436 tbl.tableid = strtoul(*av, &eptr, 0);
3437 if (*eptr != '\0')
3438 errx(EX_DATAERR, "invalid table id %s", *av);
3439 if (setsockopt(s, IPPROTO_IP, IP_FW_TBL_CREATE, &tbl, sizeof(tbl)) < 0)
3440 err(EX_UNAVAILABLE, "setsockopt(IP_FW_TBL_CREATE)");
3441 if (!do_quiet)
3442 printf("Created table %d\n", tbl.tableid);
3443}
3444
3445static void
3446table_flush(int ac, char **av, int opt)
3447{
3448 struct ipfw_ioc_table tbl;
3449 char *eptr;
3450
3451 ac--;
3452 av++;
3453
3454 memset(&tbl, 0, sizeof(tbl));
3455
3456 if (ac == 0) {
3457 if (opt == IP_FW_TBL_FLUSH) {
3458 /* Flush all tables */
3459 tbl.tableid = -1;
3460 goto flush;
3461 }
3462 errx(EX_DATAERR, "missing table id");
3463 }
3464
3465 tbl.tableid = strtoul(*av, &eptr, 0);
3466 if (*eptr != '\0')
3467 errx(EX_DATAERR, "invalid table id %s", *av);
3468
3469flush:
3470 if (!do_force && !do_quiet) { /* need to ask user */
3471 int c;
3472
3473 printf("Are you sure? [yn] ");
3474 fflush(stdout);
3475 do {
3476 c = toupper(getc(stdin));
3477 while (c != '\n' && getc(stdin) != '\n')
3478 if (feof(stdin))
3479 return; /* and do not flush */
3480 } while (c != 'Y' && c != 'N');
3481 printf("\n");
3482 if (c == 'N') /* user said no */
3483 return;
3484 }
3485
3486 if (setsockopt(s, IPPROTO_IP, opt, &tbl, sizeof(tbl)) < 0) {
3487 err(EX_UNAVAILABLE, "setsockopt(IP_FW_TBL_%s)",
3488 opt == IP_FW_TBL_FLUSH ? "FLUSH" : "DESTROY");
3489 }
3490 if (!do_quiet) {
3491 if (tbl.tableid >= 0) {
3492 printf("%sed table %d\n",
3493 opt == IP_FW_TBL_FLUSH ? "Flush" : "Destroy",
3494 tbl.tableid);
3495 } else {
3496 printf("Flushed all tables\n");
3497 }
3498 }
3499}
3500
3501static void
3502table_list(void)
3503{
3504 struct ipfw_ioc_tbllist *list;
3505 int table_max, i;
3506 size_t len;
3507 socklen_t len1;
3508
3509 len = sizeof(table_max);
3510 if (sysctlbyname("net.inet.ip.fw.table_max", &table_max, &len,
3511 NULL, 0) < 0)
3512 err(EX_UNAVAILABLE, "sysctl net.inet.ip.fw.table_max failed");
3513
3514 len1 = __offsetof(struct ipfw_ioc_tbllist, tables[table_max]);
3515 list = malloc(len1);
3516 list->tableid = -1;
3517
3518 if (getsockopt(s, IPPROTO_IP, IP_FW_TBL_GET, list, &len1) < 0)
3519 err(EX_UNAVAILABLE, "getsockopt(IP_FW_TBL_GET)");
3520
3521 for (i = 0; i < list->tablecnt; ++i)
3522 printf("%u\n", list->tables[i]);
3523}
3524
3525/* XXX copied from route(8) */
3526static void
3527inet_makenetandmask(in_addr_t net, struct sockaddr_in *in,
3528 struct sockaddr_in *in_mask, int bits)
3529{
3530 in_addr_t addr, mask = 0;
3531 char *cp;
3532
3533 /*
3534 * XXX: This approach unable to handle 0.0.0.1/32 correctly
3535 * as inet_network() converts 0.0.0.1 and 1 equally.
3536 */
3537 if (net <= 0xff)
3538 addr = net << IN_CLASSA_NSHIFT;
3539 else if (net <= 0xffff)
3540 addr = net << IN_CLASSB_NSHIFT;
3541 else if (net <= 0xffffff)
3542 addr = net << IN_CLASSC_NSHIFT;
3543 else
3544 addr = net;
3545
3546 if (bits != 0)
3547 mask = 0xffffffff << (32 - bits);
3548 else if (net == 0)
3549 mask = 0;
3550 else if (IN_CLASSA(addr))
3551 mask = IN_CLASSA_NET;
3552 else if (IN_CLASSB(addr))
3553 mask = IN_CLASSB_NET;
3554 else if (IN_CLASSC(addr))
3555 mask = IN_CLASSC_NET;
3556 else if (IN_MULTICAST(addr))
3557 mask = IN_CLASSD_NET;
3558 else
3559 mask = 0xffffffff;
3560
3561 in->sin_family = AF_INET;
3562 in->sin_len = sizeof(struct sockaddr_in);
3563 in->sin_addr.s_addr = htonl(addr);
3564
3565 if (mask != 0xffffffff) {
3566 in_mask->sin_addr.s_addr = htonl(mask);
3567 cp = (char *)(&in_mask->sin_addr + 1);
3568 while (*--cp == 0 && cp > (char *)in_mask)
3569 ;
3570 in_mask->sin_len = 1 + cp - (char *)in_mask;
3571 }
3572}
3573
3574static void
3575table_alt(int ac, char **av, int opt)
3576{
3577 struct ipfw_ioc_tblcont ent;
3578 struct ipfw_ioc_tblent *te;
3579 char *eptr;
3580
3581 --ac;
3582 ++av;
3583 if (ac == 0)
3584 errx(EX_DATAERR, "missing table id");
3585
3586 memset(&ent, 0, sizeof(ent));
3587 ent.tableid = strtoul(*av, &eptr, 0);
3588 ent.entcnt = 1;
3589 te = &ent.ent[0];
3590 if (*eptr != '\0')
3591 errx(EX_DATAERR, "invalid table id %s", *av);
3592
3593 --ac;
3594 ++av;
3595 if (ac == 0)
3596 errx(EX_DATAERR, "missing addresses");
3597
3598 while (ac > 0) {
3599 char *q;
3600
3601 q = strchr(*av, '/');
3602 if (q != NULL) {
3603 in_addr_t val;
3604 int bits;
3605
3606 *q = '\0';
3607 val = inet_network(*av);
3608 *q = '/';
3609 if (val == INADDR_NONE) {
3610 fflush(stdout);
3611 errx(EX_DATAERR, "invalid address %s", *av);
3612 }
3613
3614 bits = strtoul(q + 1, &eptr, 0);
3615 if (*eptr != '\0') {
3616 fflush(stdout);
3617 errx(EX_DATAERR, "invalid address %s", *av);
3618 }
3619 inet_makenetandmask(val, &te->key, &te->netmask, bits);
3620 } else {
3621 int n;
3622
3623 n = inet_pton(AF_INET, *av, &te->key.sin_addr);
3624 if (n == 0) {
3625 fflush(stdout);
3626 errx(EX_DATAERR, "invalid address %s", *av);
3627 } else if (n < 0) {
3628 fflush(stdout);
3629 err(EX_UNAVAILABLE, "inet_pton failed");
3630 }
3631 te->key.sin_family = AF_INET;
3632 te->key.sin_len = sizeof(struct sockaddr_in);
3633 }
3634
3635 if (setsockopt(s, IPPROTO_IP, opt, &ent, sizeof(ent)) < 0) {
3636 if (opt == IP_FW_TBL_ADD && errno == EEXIST) {
3637 printf("Failed to add %s to table %d\n",
3638 *av, ent.tableid);
3639 } else if (opt == IP_FW_TBL_DEL && errno == ESRCH) {
3640 printf("Failed to delete %s from table %d\n",
3641 *av, ent.tableid);
3642 } else {
3643 fflush(stdout);
3644 err(EX_UNAVAILABLE, "setsockopt(IP_FW_TBL_%s)",
3645 opt == IP_FW_TBL_ADD ? "ADD" : "DEL");
3646 }
3647 } else if (!do_quiet) {
3648 printf("%sed %s %s table %d\n",
3649 opt == IP_FW_TBL_ADD ? "Add" : "Delet", *av,
3650 opt == IP_FW_TBL_ADD ? "to" : "from", ent.tableid);
3651 }
3652
3653 --ac;
3654 ++av;
3655 }
3656}
3657
3658static void
3659table_show(int ac, char **av)
3660{
3661 struct ipfw_ioc_tblcont *cont = NULL;
3662 int tableid, count = 128, i, uwidth = 0, lwidth = 0;
3663 char *eptr;
3664
3665 --ac;
3666 ++av;
3667 if (ac == 0)
3668 errx(EX_DATAERR, "missing table id");
3669
3670 tableid = strtoul(*av, &eptr, 0);
3671 if (*eptr != '\0')
3672 errx(EX_DATAERR, "invalid table id %s", *av);
3673
3674 for (;;) {
3675 socklen_t len;
3676
3677 len = __offsetof(struct ipfw_ioc_tblcont, ent[count]);
3678 cont = reallocf(cont, len);
3679 cont->tableid = tableid;
3680
3681 if (getsockopt(s, IPPROTO_IP, IP_FW_TBL_GET, cont, &len) < 0) {
3682 if (errno == E2BIG) {
3683 count *= 2;
3684 continue;
3685 }
3686 err(EX_UNAVAILABLE, "getsockopt(IP_FW_TBL_GET)");
3687 }
3688 break;
3689 }
3690 if (cont->entcnt == 0)
3691 return;
3692
3693 if (do_acct) {
3694 for (i = 0; i < cont->entcnt; ++i) {
3695 int width;
3696
3697 width = snprintf(NULL, 0, "%ju",
3698 (uintmax_t)cont->ent[i].use);
3699 if (width > uwidth)
3700 uwidth = width;
3701 }
3702 }
3703
3704 for (i = 0; i < cont->entcnt; ++i) {
3705 const struct ipfw_ioc_tblent *te = &cont->ent[i];
3706 char addr[INET_ADDRSTRLEN];
3707
3708 if (do_acct)
3709 printf("%*ju ", uwidth, (uintmax_t)te->use);
3710 if (do_time) {
3711 char timestr[30];
3712
3713 if (lwidth == 0) {
3714 time_t t0 = 0;
3715
3716 strcpy(timestr, ctime(&t0));
3717 *strchr(timestr, '\n') = '\0';
3718 lwidth = strlen(timestr);
3719 }
3720 if (te->last_used) {
3721 time_t t = _long_to_time(te->last_used);
3722
3723 strcpy(timestr, ctime(&t));
3724 *strchr(timestr, '\n') = '\0';
3725 printf("%s ", timestr);
3726 } else {
3727 printf("%*s ", lwidth, " ");
3728 }
3729 }
3730
3731 if (te->netmask.sin_len == 0) {
3732 printf("%s\n", inet_ntop(AF_INET,
3733 &te->key.sin_addr, addr, sizeof(addr)));
3734 } else {
3735 struct sockaddr_in mask;
3736 int b;
3737
3738 memset(&mask, 0, sizeof(mask));
3739 memcpy(&mask, &te->netmask,
3740 te->netmask.sin_len);
3741 b = ffs(ntohl(te->netmask.sin_addr.s_addr));
3742 b = 32 - (b - 1);
3743
3744 printf("%s/%d\n", inet_ntop(AF_INET,
3745 &te->key.sin_addr, addr, sizeof(addr)), b);
3746 }
3747 }
3748}
3749
3750static void
3751table_zero(int ac, char **av)
3752{
3753 struct ipfw_ioc_table tbl;
3754
3755 --ac;
3756 ++av;
3757
3758 memset(&tbl, 0, sizeof(tbl));
3759 if (ac == 0) {
3760 tbl.tableid = -1;
3761 } else {
3762 char *eptr;
3763
3764 tbl.tableid = strtoul(*av, &eptr, 0);
3765 if (*eptr != '\0')
3766 errx(EX_DATAERR, "invalid table id %s", *av);
3767 }
3768
3769 if (setsockopt(s, IPPROTO_IP, IP_FW_TBL_ZERO, &tbl, sizeof(tbl)) < 0)
3770 err(EX_UNAVAILABLE, "setsockopt(IP_FW_TBL_ZERO)");
3771 if (!do_quiet)
3772 printf("Accounting cleared\n");
3773}
3774
3775static void
3776table_expire(int ac, char **av)
3777{
3778 struct ipfw_ioc_tblexp tbl;
3779 char *eptr;
3780 socklen_t len;
3781
3782 --ac;
3783 ++av;
3784
3785 memset(&tbl, 0, sizeof(tbl));
3786 if (ac == 0) {
3787 errx(EX_DATAERR, "missing expire time");
3788 } else if (ac == 1) {
3789 tbl.tableid = -1;
3790 } else {
3791 tbl.tableid = strtoul(*av, &eptr, 0);
3792 if (*eptr != '\0')
3793 errx(EX_DATAERR, "invalid table id %s", *av);
3794 --ac;
3795 ++av;
3796 }
3797
3798 tbl.expire = strtoul(*av, &eptr, 0);
3799 if (*eptr != '\0')
3800 errx(EX_DATAERR, "invalid expire timeout %s", *av);
3801
3802 if (!do_force && !do_quiet) { /* need to ask user */
3803 int c;
3804
3805 printf("Are you sure? [yn] ");
3806 fflush(stdout);
3807 do {
3808 c = toupper(getc(stdin));
3809 while (c != '\n' && getc(stdin) != '\n')
3810 if (feof(stdin))
3811 return; /* and do not flush */
3812 } while (c != 'Y' && c != 'N');
3813 printf("\n");
3814 if (c == 'N') /* user said no */
3815 return;
3816 }
3817
3818 len = sizeof(tbl);
3819 if (getsockopt(s, IPPROTO_IP, IP_FW_TBL_EXPIRE, &tbl, &len) < 0)
3820 err(EX_UNAVAILABLE, "getsockopt(IP_FW_TBL_EXPIRE)");
3821 if (!do_quiet) {
3822 printf("Expired %d address%s\n", tbl.expcnt,
3823 (tbl.expcnt == 0 || tbl.expcnt > 1) ? "es" : "");
3824 }
3825}
3826
984263bc
MD
3827static int
3828ipfw_main(int ac, char **av)
3829{
3830 int ch;
3831
3832 if (ac == 1)
3833 show_usage();
3834
3835 /* Set the force flag for non-interactive processes */
3836 do_force = !isatty(STDIN_FILENO);
3837
3838 optind = optreset = 1;
3839 while ((ch = getopt(ac, av, "hs:acdefNqStv")) != -1)
3840 switch (ch) {
3841 case 'h': /* help */
3842 help();
3843 break; /* NOTREACHED */
3844
3845 case 's': /* sort */
3846 do_sort = atoi(optarg);
3847 break;
3848 case 'a':
3849 do_acct = 1;
3850 break;
3851 case 'c':
3852 do_compact = 1;
3853 break;
3854 case 'd':
3855 do_dynamic = 1;
3856 break;
3857 case 'e':
3858 do_expired = 1;
3859 break;
3860 case 'f':
3861 do_force = 1;
3862 break;
3863 case 'N':
3864 do_resolv = 1;
3865 break;
3866 case 'q':
3867 do_quiet = 1;
3868 break;
3869 case 'S':
3870 show_sets = 1;
3871 break;
3872 case 't':
3873 do_time = 1;
3874 break;
3875 case 'v': /* verbose */
3876 verbose++;
3877 break;
3878 default:
3879 show_usage();
3880 }
3881
3882 ac -= optind;
3883 av += optind;
3884 NEED1("bad arguments, for usage summary ``ipfw''");
3885
3886 /*
d938108c 3887 * optional: pipe, queue or table
984263bc
MD
3888 */
3889 if (!strncmp(*av, "pipe", strlen(*av))) {
3890 do_pipe = 1;
3891 ac--;
3892 av++;
3893 } else if (!strncmp(*av, "queue", strlen(*av))) {
3894 do_pipe = 2;
3895 ac--;
3896 av++;
d938108c
SZ
3897 } else if (!strncmp(*av, "table", strlen(*av))) {
3898 do_table = 1;
3899 ac--;
3900 av++;
984263bc
MD
3901 }
3902 NEED1("missing command");
3903
3904 /*
d938108c
SZ
3905 * for pipes, queues and table we normally say 'pipe NN config'
3906 * but the code is easier to parse as 'pipe config NN' so we
3907 * swap the two arguments.
984263bc 3908 */
d938108c
SZ
3909 if ((do_pipe > 0 || do_table > 0) && ac > 1 &&
3910 *av[0] >= '0' && *av[0] <= '9') {
984263bc
MD
3911 char *p = av[0];
3912 av[0] = av[1];
3913 av[1] = p;
3914 }
d938108c
SZ
3915 if (do_table) {
3916 if (!strncmp(*av, "create", strlen(*av)))
3917 table_create(ac, av);
3918 else if (!strncmp(*av, "destroy", strlen(*av)))
3919 table_flush(ac, av, IP_FW_TBL_DESTROY);
3920 else if (!strncmp(*av, "list", strlen(*av)))
3921 table_list();
3922 else if (!strncmp(*av, "add", strlen(*av)))
3923 table_alt(ac, av, IP_FW_TBL_ADD);
3924 else if (!strncmp(*av, "delete", strlen(*av)))
3925 table_alt(ac, av, IP_FW_TBL_DEL);
3926 else if (!strncmp(*av, "flush", strlen(*av)))
3927 table_flush(ac, av, IP_FW_TBL_FLUSH);
3928 else if (!strncmp(*av, "print", strlen(*av)))
3929 table_show(ac, av);
3930 else if (!strncmp(*av, "show", strlen(*av))) {
3931 do_acct++;
3932 table_show(ac, av);
3933 } else if (!strncmp(*av, "zero", strlen(*av)))
3934 table_zero(ac, av);
3935 else if (!strncmp(*av, "expire", strlen(*av)))
3936 table_expire(ac, av);
3937 else
3938 errx(EX_USAGE, "bad command `%s'", *av);
3939 return 0;
3940 }
984263bc
MD
3941 if (!strncmp(*av, "add", strlen(*av)))
3942 add(ac, av);
3943 else if (do_pipe && !strncmp(*av, "config", strlen(*av)))
3944 config_pipe(ac, av);
3945 else if (!strncmp(*av, "delete", strlen(*av)))
3946 delete(ac, av);
3947 else if (!strncmp(*av, "flush", strlen(*av)))
3948 flush();
3949 else if (!strncmp(*av, "zero", strlen(*av)))
3950 zero(ac, av);
3951 else if (!strncmp(*av, "resetlog", strlen(*av)))
3952 resetlog(ac, av);
3953 else if (!strncmp(*av, "print", strlen(*av)) ||
3954 !strncmp(*av, "list", strlen(*av)))
3955 list(ac, av);
3956 else if (!strncmp(*av, "enable", strlen(*av)))
3957 sysctl_handler(ac, av, 1);
3958 else if (!strncmp(*av, "disable", strlen(*av)))
3959 sysctl_handler(ac, av, 0);
3960 else if (!strncmp(*av, "set", strlen(*av)))
3961 sets_handler(ac, av);
3962 else if (!strncmp(*av, "show", strlen(*av))) {
3963 do_acct++;
3964 list(ac, av);
3965 } else
3966 errx(EX_USAGE, "bad command `%s'", *av);
3967 return 0;
3968}
3969
3970
3971static void
3972ipfw_readfile(int ac, char *av[])
3973{
3974#define MAX_ARGS 32
3975#define WHITESP " \t\f\v\n\r"
3976 char buf[BUFSIZ];
3977 char *a, *p, *args[MAX_ARGS], *cmd = NULL;
c36b81f7 3978 char linename[20];
984263bc
MD
3979 int i=0, lineno=0, qflag=0, pflag=0, status;
3980 FILE *f = NULL;
3981 pid_t preproc = 0;
3982 int c;
3983
3984 while ((c = getopt(ac, av, "D:U:p:q")) != -1)
3985 switch(c) {
3986 case 'D':
3987 if (!pflag)
3988 errx(EX_USAGE, "-D requires -p");
3989 if (i > MAX_ARGS - 2)
3990 errx(EX_USAGE,
3991 "too many -D or -U options");
75a579b7 3992 args[i++] = __DECONST(char *, "-D");
984263bc
MD
3993 args[i++] = optarg;
3994 break;
3995
3996 case 'U':
3997 if (!pflag)
3998 errx(EX_USAGE, "-U requires -p");
3999 if (i > MAX_ARGS - 2)
4000 errx(EX_USAGE,
4001 "too many -D or -U options");
75a579b7 4002 args[i++] = __DECONST(char *, "-U");
984263bc
MD
4003 args[i++] = optarg;
4004 break;
4005
4006 case 'p':
4007 pflag = 1;
4008 cmd = optarg;
4009 args[0] = cmd;
4010 i = 1;
4011 break;
4012
4013 case 'q':
4014 qflag = 1;
4015 break;
4016
4017 default:
4018 errx(EX_USAGE, "bad arguments, for usage"
4019 " summary ``ipfw''");
4020 }
4021