6c8d6487891c4191774fa2adb79b412bd7737722
[dragonfly.git] / sbin / ipfw3 / ipfw3.c
1 /*
2  * Copyright (c) 2014 - 2016 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Bill Yuan <bycn82@dragonflybsd.org>
6  *
7  * Copyright (c) 2002 Luigi Rizzo
8  * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
9  * Copyright (c) 1994 Ugen J.S.Antsilevich
10  *
11  * Idea and grammar partially left from:
12  * Copyright (c) 1993 Daniel Boulet
13  *
14  *
15  * Redistribution and use in source forms, with and without modification,
16  * are permitted provided that this entire comment appears intact.
17  *
18  * Redistribution in binary form may occur without any restrictions.
19  * Obviously, it would be nice if you gave credit where credit is due
20  * but requiring it would be too onerous.
21  *
22  * This software is provided ``AS IS'' without any warranties of any kind.
23  *
24  */
25
26 #include <sys/param.h>
27 #include <sys/mbuf.h>
28 #include <sys/socket.h>
29 #include <sys/sockio.h>
30 #include <sys/sysctl.h>
31 #include <sys/time.h>
32 #include <sys/wait.h>
33
34 #include <arpa/inet.h>
35 #include <ctype.h>
36 #include <dlfcn.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <grp.h>
40 #include <limits.h>
41 #include <netdb.h>
42 #include <pwd.h>
43 #include <sysexits.h>
44 #include <signal.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <stdarg.h>
48 #include <string.h>
49 #include <timeconv.h>
50 #include <unistd.h>
51
52 #include <netinet/in.h>
53 #include <netinet/in_systm.h>
54 #include <netinet/ip.h>
55 #include <netinet/ip_icmp.h>
56 #include <netinet/tcp.h>
57 #include <net/if.h>
58 #include <net/if_dl.h>
59 #include <net/route.h>
60 #include <net/ethernet.h>
61
62
63 #include "../../sys/net/ipfw3/ip_fw3.h"
64 #include "../../sys/net/ipfw3/ip_fw3_table.h"
65 #include "../../sys/net/ipfw3/ip_fw3_sync.h"
66 #include "../../sys/net/dummynet3/ip_dummynet3.h"
67 #include "../../sys/net/libalias/alias.h"
68 #include "../../sys/net/ipfw3_basic/ip_fw3_basic.h"
69 #include "../../sys/net/ipfw3_nat/ip_fw3_nat.h"
70
71 #include "ipfw3.h"
72 #include "ipfw3sync.h"
73 #include "ipfw3nat.h"
74
75
76 #define KEYWORD_SIZE    256
77 #define MAPPING_SIZE    256
78
79 #define MAX_KEYWORD_LEN 20
80 #define MAX_ARGS        32
81 #define WHITESP         " \t\f\v\n\r"
82 #define IPFW_LIB_PATH   "/usr/lib/libipfw3%s.so"
83 #define IP_MASK_ALL     0xffffffff
84 /*
85  * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines
86  * This is only used in this code.
87  */
88 #define IPPROTO_ETHERTYPE       0x1000
89
90 /*
91  * show_rules() prints the body of an ipfw rule.
92  * Because the standard rule has at least proto src_ip dst_ip, we use
93  * a helper function to produce these entries if not provided explicitly.
94  * The first argument is the list of fields we have, the second is
95  * the list of fields we want to be printed.
96  *
97  * Special cases if we have provided a MAC header:
98  * + if the rule does not contain IP addresses/ports, do not print them;
99  * + if the rule does not contain an IP proto, print "all" instead of "ip";
100  *
101  */
102 #define HAVE_PROTO      0x0001
103 #define HAVE_SRCIP      0x0002
104 #define HAVE_DSTIP      0x0004
105 #define HAVE_MAC        0x0008
106 #define HAVE_MACTYPE    0x0010
107 #define HAVE_OPTIONS    0x8000
108
109 #define HAVE_IP         (HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP)
110
111
112 int             ipfw_socket = -1;       /* main RAW socket */
113 int             do_resolv,              /* Would try to resolve all */
114                 do_acct,                /* Show packet/byte count */
115                 do_time,                /* Show time stamps */
116                 do_quiet = 1,           /* Be quiet , default is quiet*/
117                 do_force,               /* Don't ask for confirmation */
118                 do_pipe,                /* this cmd refers to a pipe */
119                 do_nat,                 /* Nat configuration. */
120                 do_sort,                /* field to sort results (0 = no) */
121                 do_dynamic,             /* display dynamic rules */
122                 do_expired,             /* display expired dynamic rules */
123                 do_compact,             /* show rules in compact mode */
124                 show_sets,              /* display rule sets */
125                 verbose;
126
127 struct char_int_map dummynet_params[] = {
128         { "plr",                TOK_PLR },
129         { "noerror",            TOK_NOERROR },
130         { "buckets",            TOK_BUCKETS },
131         { "dst-ip",             TOK_DSTIP },
132         { "src-ip",             TOK_SRCIP },
133         { "dst-port",           TOK_DSTPORT },
134         { "src-port",           TOK_SRCPORT },
135         { "proto",              TOK_PROTO },
136         { "weight",             TOK_WEIGHT },
137         { "all",                TOK_ALL },
138         { "mask",               TOK_MASK },
139         { "droptail",           TOK_DROPTAIL },
140         { "red",                TOK_RED },
141         { "gred",               TOK_GRED },
142         { "bw",                 TOK_BW },
143         { "bandwidth",          TOK_BW },
144         { "delay",              TOK_DELAY },
145         { "pipe",               TOK_PIPE },
146         { "queue",              TOK_QUEUE },
147         { "dummynet-params",    TOK_NULL },
148         { NULL, 0 }
149 };
150
151 struct ipfw_keyword {
152         int type;
153         char word[MAX_KEYWORD_LEN];
154         int module;
155         int opcode;
156 };
157
158 struct ipfw_mapping {
159         int type;
160         int module;
161         int opcode;
162         parser_func parser;
163         shower_func shower;
164 };
165
166 struct ipfw_keyword keywords[KEYWORD_SIZE];
167 struct ipfw_mapping mappings[MAPPING_SIZE];
168
169 int
170 match_token(struct char_int_map *table, char *string)
171 {
172         while (table->key) {
173                 if (strcmp(table->key, string) == 0) {
174                         return table->val;
175                 }
176                 table++;
177         }
178         return 0;
179 }
180
181 static void
182 get_modules(char *modules_str, int len)
183 {
184         if (do_get_x(IP_FW_MODULE, modules_str, &len) < 0)
185                 errx(EX_USAGE, "ipfw3 not loaded.");
186 }
187
188 static void
189 list_modules(int ac, char *av[])
190 {
191         void *module_str = NULL;
192         int len = 1024;
193         if ((module_str = realloc(module_str, len)) == NULL)
194                 err(EX_OSERR, "realloc");
195
196         get_modules(module_str, len);
197         printf("%s", (char *)module_str);
198 }
199 void
200 parse_accept(ipfw_insn **cmd, int *ac, char **av[])
201 {
202         (*cmd)->opcode = O_BASIC_ACCEPT;
203         (*cmd)->module = MODULE_BASIC_ID;
204         (*cmd)->len = (*cmd)->len|LEN_OF_IPFWINSN;
205         NEXT_ARG1;
206         if (!strncmp(**av, "log", strlen(**av))) {
207                 (*cmd)->arg3 = 1;
208                 NEXT_ARG1;
209                 if (isdigit(***av)) {
210                         (*cmd)->arg1 = strtoul(**av, NULL, 10);
211                         NEXT_ARG1;
212                 }
213         }
214 }
215
216 void
217 parse_deny(ipfw_insn **cmd, int *ac, char **av[])
218 {
219         (*cmd)->opcode = O_BASIC_DENY;
220         (*cmd)->module = MODULE_BASIC_ID;
221         (*cmd)->len = (*cmd)->len|LEN_OF_IPFWINSN;
222         NEXT_ARG1;
223         if (!strncmp(**av, "log", strlen(**av))) {
224                 (*cmd)->arg3 = 1;
225                 NEXT_ARG1;
226                 if (isdigit(***av)) {
227                         (*cmd)->arg1 = strtoul(**av, NULL, 10);
228                         NEXT_ARG1;
229                 }
230         }
231 }
232
233 void
234 show_accept(ipfw_insn *cmd, int show_or)
235 {
236         printf(" allow");
237         if (cmd->arg3) {
238                 printf(" log %d", cmd->arg1);
239         }
240 }
241
242 void
243 show_deny(ipfw_insn *cmd, int show_or)
244 {
245         printf(" deny");
246         if (cmd->arg3) {
247                 printf(" log %d", cmd->arg1);
248         }
249 }
250
251 static void
252 load_modules(void)
253 {
254         const char *error;
255         init_module mod_init_func;
256         void *module_lib;
257         char module_lib_file[50];
258         void *module_str = NULL;
259         int len = 1024;
260
261         if ((module_str = realloc(module_str, len)) == NULL)
262                 err(EX_OSERR, "realloc");
263
264         get_modules(module_str, len);
265
266         const char s[2] = ",";
267         char *token;
268         token = strtok(module_str, s);
269         while (token != NULL) {
270                 sprintf(module_lib_file, IPFW_LIB_PATH, token);
271                 token = strtok(NULL, s);
272                 module_lib = dlopen(module_lib_file, RTLD_LAZY);
273                 if (!module_lib) {
274                         fprintf(stderr, "Couldn't open %s: %s\n",
275                                 module_lib_file, dlerror());
276                         exit(EX_SOFTWARE);
277                 }
278                 mod_init_func = dlsym(module_lib, "load_module");
279                 if ((error = dlerror()))
280                 {
281                         fprintf(stderr, "Couldn't find init function: %s\n", error);
282                         exit(EX_SOFTWARE);
283                 }
284                 (*mod_init_func)((register_func)register_ipfw_func,
285                                 (register_keyword)register_ipfw_keyword);
286         }
287 }
288
289 void
290 prepare_default_funcs(void)
291 {
292         /* register allow*/
293         register_ipfw_keyword(MODULE_BASIC_ID, O_BASIC_ACCEPT, "allow", ACTION);
294         register_ipfw_keyword(MODULE_BASIC_ID, O_BASIC_ACCEPT, "accept", ACTION);
295         register_ipfw_func(MODULE_BASIC_ID, O_BASIC_ACCEPT,
296                         (parser_func)parse_accept, (shower_func)show_accept);
297         /* register deny*/
298         register_ipfw_keyword(MODULE_BASIC_ID, O_BASIC_DENY, "deny", ACTION);
299         register_ipfw_keyword(MODULE_BASIC_ID, O_BASIC_DENY, "reject", ACTION);
300         register_ipfw_func(MODULE_BASIC_ID, O_BASIC_DENY,
301                         (parser_func)parse_deny, (shower_func)show_deny);
302 }
303
304 void
305 register_ipfw_keyword(int module, int opcode, char *word, int type)
306 {
307         struct ipfw_keyword *tmp;
308
309         tmp=keywords;
310         for(;;) {
311                 if (tmp->type == NONE) {
312                         strcpy(tmp->word, word);
313                         tmp->module = module;
314                         tmp->opcode = opcode;
315                         tmp->type = type;
316                         break;
317                 } else {
318                         if (strcmp(tmp->word, word) == 0)
319                                 errx(EX_USAGE, "keyword `%s' exists", word);
320                         else
321                                 tmp++;
322                 }
323         }
324 }
325
326 void
327 register_ipfw_func(int module, int opcode, parser_func parser, shower_func shower)
328 {
329         struct ipfw_mapping *tmp;
330
331         tmp = mappings;
332         while (1) {
333                 if (tmp->type == NONE) {
334                         tmp->module = module;
335                         tmp->opcode = opcode;
336                         tmp->parser = parser;
337                         tmp->shower = shower;
338                         tmp->type = IN_USE;
339                         break;
340                 } else {
341                         if (tmp->opcode == opcode && tmp->module == module) {
342                                 errx(EX_USAGE, "func `%d' of module `%d' exists",
343                                         opcode, module);
344                                 break;
345                         } else {
346                                 tmp++;
347                         }
348                 }
349         }
350 }
351
352 /*
353  * this func need to check whether 'or' need to be printed,
354  * when the filter is the first filter with 'or' when dont print
355  * when not first and same as previous, then print or and no filter name
356  * when not first but different from previous, print name without 'or'
357  * show_or = 1: show or and ignore filter name
358  * show_or = 0: show filter name ignore or
359  */
360 void prev_show_chk(ipfw_insn *cmd, uint8_t *prev_module, uint8_t *prev_opcode,
361                 int *show_or)
362 {
363         if (cmd->len & F_OR) {
364                 if (*prev_module == 0 && *prev_opcode == 0) {
365                         /* first cmd with 'or' flag */
366                         *show_or = 0;
367                         *prev_module = cmd->module;
368                         *prev_opcode = cmd->opcode;
369                 } else if (cmd->module == *prev_module &&
370                                 cmd->opcode == *prev_opcode) {
371                         /* cmd same as previous, same module and opcode */
372                         *show_or = 1;
373                 } else {
374                         /* cmd different from prev*/
375                         *show_or = 0;
376                         *prev_module = cmd->module;
377                         *prev_opcode = cmd->opcode;
378
379                 }
380         } else {
381                 *show_or = 0;
382                 *prev_module = 0;
383                 *prev_opcode = 0;
384         }
385 }
386
387 /*
388  * word can be: proto from to other
389  * proto show proto
390  * from show from
391  * to show to
392  * other show all other filters
393  */
394 int show_filter(ipfw_insn *cmd, char *word, int type)
395 {
396         struct ipfw_keyword *k;
397         struct ipfw_mapping *m;
398         shower_func fn;
399         int i, j, show_or;
400         uint8_t prev_module, prev_opcode;
401
402         k = keywords;
403         m = mappings;
404         for (i = 1; i < KEYWORD_SIZE; i++, k++) {
405                 if (k->type == type) {
406                         if (k->module == cmd->module &&
407                                         k->opcode == cmd->opcode) {
408                                 for (j = 1; j < MAPPING_SIZE; j++, m++) {
409                                         if (m->type == IN_USE &&
410                                                 k->module == m->module &&
411                                                 k->opcode == m->opcode) {
412                                                 prev_show_chk(cmd, &prev_module,
413                                                         &prev_opcode, &show_or);
414                                                 if (cmd->len & F_NOT)
415                                                         printf(" not");
416
417                                                 fn = m->shower;
418                                                 (*fn)(cmd, show_or);
419                                                 return 1;
420                                         }
421                                 }
422                         }
423                 }
424         }
425         return 0;
426 }
427
428 static void
429 show_rules(struct ipfw_ioc_rule *rule, int pcwidth, int bcwidth)
430 {
431         static int twidth = 0;
432         ipfw_insn *cmd;
433         int l;
434
435         u_int32_t set_disable = rule->set_disable;
436
437         if (set_disable & (1 << rule->set)) { /* disabled */
438                 if (!show_sets)
439                         return;
440                 else
441                         printf("# DISABLED ");
442         }
443         printf("%05u ", rule->rulenum);
444
445         if (do_acct)
446                 printf("%*ju %*ju ", pcwidth, (uintmax_t)rule->pcnt, bcwidth,
447                         (uintmax_t)rule->bcnt);
448
449         if (do_time == 1) {
450                 char timestr[30];
451
452                 if (twidth == 0) {
453                         strcpy(timestr, ctime((time_t *)&twidth));
454                         *strchr(timestr, '\n') = '\0';
455                         twidth = strlen(timestr);
456                 }
457                 if (rule->timestamp) {
458                         time_t t = _long_to_time(rule->timestamp);
459
460                         strcpy(timestr, ctime(&t));
461                         *strchr(timestr, '\n') = '\0';
462                         printf("%s ", timestr);
463                 } else {
464                         printf("%*s ", twidth, " ");
465                 }
466         } else if (do_time == 2) {
467                 printf( "%10u ", rule->timestamp);
468         }
469
470         if (show_sets)
471                 printf("set %d ", rule->set);
472
473
474         struct ipfw_keyword *k;
475         struct ipfw_mapping *m;
476         shower_func fn, comment_fn = NULL;
477         ipfw_insn *comment_cmd;
478         int i, j, changed;
479
480         /*
481          * show others and actions
482          */
483         for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule);
484                 l > 0; l -= F_LEN(cmd),
485                 cmd = (ipfw_insn *)((uint32_t *)cmd + F_LEN(cmd))) {
486                 k = keywords;
487                 m = mappings;
488                 for (i = 1; i< KEYWORD_SIZE; i++, k++) {
489                         if ( k->module == cmd->module && k->opcode == cmd->opcode ) {
490                                 for (j = 1; j< MAPPING_SIZE; j++, m++) {
491                                         if (m->type == IN_USE &&
492                                                 m->module == cmd->module &&
493                                                 m->opcode == cmd->opcode) {
494                                                 if (cmd->module == MODULE_BASIC_ID &&
495                                                         cmd->opcode == O_BASIC_COMMENT) {
496                                                         comment_fn = m->shower;
497                                                         comment_cmd = cmd;
498                                                 } else {
499                                                         fn = m->shower;
500                                                         (*fn)(cmd, 0);
501                                                 }
502                                                 if (cmd->module == MODULE_BASIC_ID &&
503                                                         cmd->opcode ==
504                                                                 O_BASIC_CHECK_STATE) {
505                                                         goto done;
506                                                 }
507                                                 break;
508                                         }
509                                 }
510                                 break;
511                         }
512                 }
513         }
514
515         /*
516          * show proto
517          */
518         changed=0;
519         for (l = rule->act_ofs, cmd = rule->cmd; l > 0; l -= F_LEN(cmd),
520                         cmd = (ipfw_insn *)((uint32_t *)cmd + F_LEN(cmd))) {
521                 changed = show_filter(cmd, "proto", PROTO);
522         }
523         if (!changed && !do_quiet)
524                 printf(" ip");
525
526         /*
527          * show from
528          */
529         changed = 0;
530         for (l = rule->act_ofs, cmd = rule->cmd; l > 0; l -= F_LEN(cmd),
531                         cmd = (ipfw_insn *)((uint32_t *)cmd + F_LEN(cmd))) {
532                 changed = show_filter(cmd, "from", FROM);
533         }
534         if (!changed && !do_quiet)
535                 printf(" from any");
536
537         /*
538          * show to
539          */
540         changed = 0;
541         for (l = rule->act_ofs, cmd = rule->cmd; l > 0; l -= F_LEN(cmd),
542                         cmd = (ipfw_insn *)((uint32_t *)cmd + F_LEN(cmd))) {
543                 changed = show_filter(cmd, "to", TO);
544         }
545         if (!changed && !do_quiet)
546                 printf(" to any");
547
548         /*
549          * show other filters
550          */
551         for (l = rule->act_ofs, cmd = rule->cmd, m = mappings;
552                         l > 0; l -= F_LEN(cmd),
553                         cmd=(ipfw_insn *)((uint32_t *)cmd + F_LEN(cmd))) {
554                 show_filter(cmd, "other", FILTER);
555         }
556
557         /* show the comment in the end */
558         if (comment_fn != NULL) {
559                 (*comment_fn)(comment_cmd, 0);
560         }
561 done:
562         printf("\n");
563 }
564
565 static void
566 show_states(struct ipfw_ioc_state *d, int pcwidth, int bcwidth)
567 {
568         struct protoent *pe;
569         struct in_addr a;
570
571         printf("%05u ", d->rulenum);
572         if (do_acct) {
573                 printf("%*ju %*ju ", pcwidth, (uintmax_t)d->pcnt,
574                                 bcwidth, (uintmax_t)d->bcnt);
575         }
576
577         if (do_time == 1) {
578                 /* state->timestamp */
579                 char timestr[30];
580                 time_t t = _long_to_time(d->timestamp);
581                 strcpy(timestr, ctime(&t));
582                 *strchr(timestr, '\n') = '\0';
583                 printf(" (%s", timestr);
584
585                 /* state->lifetime */
586                 printf(" %ds", d->lifetime);
587
588                 /* state->expiry */
589                 if (d->expiry !=0) {
590                         t = _long_to_time(d->expiry);
591                         strcpy(timestr, ctime(&t));
592                         *strchr(timestr, '\n') = '\0';
593                         printf(" %s)", timestr);
594                 } else {
595                         printf(" 0)");
596                 }
597
598         } else if (do_time == 2) {
599                 printf("(%u %ds %u) ", d->timestamp, d->lifetime, d->expiry);
600         }
601
602         if ((pe = getprotobynumber(d->flow_id.proto)) != NULL)
603                 printf(" %s", pe->p_name);
604         else
605                 printf(" proto %u", d->flow_id.proto);
606
607         a.s_addr = htonl(d->flow_id.src_ip);
608         printf(" %s %d", inet_ntoa(a), d->flow_id.src_port);
609
610         a.s_addr = htonl(d->flow_id.dst_ip);
611         printf(" <-> %s %d", inet_ntoa(a), d->flow_id.dst_port);
612         printf(" CPU %d", d->cpuid);
613         printf("\n");
614 }
615
616 int
617 sort_q(const void *pa, const void *pb)
618 {
619         int rev = (do_sort < 0);
620         int field = rev ? -do_sort : do_sort;
621         long long res = 0;
622         const struct dn_ioc_flowqueue *a = pa;
623         const struct dn_ioc_flowqueue *b = pb;
624
625         switch(field) {
626         case 1: /* pkts */
627                 res = a->len - b->len;
628                 break;
629         case 2: /* bytes */
630                 res = a->len_bytes - b->len_bytes;
631                 break;
632
633         case 3: /* tot pkts */
634                 res = a->tot_pkts - b->tot_pkts;
635                 break;
636
637         case 4: /* tot bytes */
638                 res = a->tot_bytes - b->tot_bytes;
639                 break;
640         }
641         if (res < 0)
642                 res = -1;
643         if (res > 0)
644                 res = 1;
645         return (int)(rev ? res : -res);
646 }
647
648 static void
649 show_queues(struct dn_ioc_flowset *fs, struct dn_ioc_flowqueue *q)
650 {
651         int l;
652
653         printf("mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
654                 fs->flow_mask.u.ip.proto,
655                 fs->flow_mask.u.ip.src_ip, fs->flow_mask.u.ip.src_port,
656                 fs->flow_mask.u.ip.dst_ip, fs->flow_mask.u.ip.dst_port);
657         if (fs->rq_elements == 0)
658                 return;
659
660         printf("BKT Prot ___Source IP/port____ "
661                 "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n");
662         if (do_sort != 0)
663                 heapsort(q, fs->rq_elements, sizeof(*q), sort_q);
664         for (l = 0; l < fs->rq_elements; l++) {
665                 struct in_addr ina;
666                 struct protoent *pe;
667
668                 ina.s_addr = htonl(q[l].id.u.ip.src_ip);
669                 printf("%3d ", q[l].hash_slot);
670                 pe = getprotobynumber(q[l].id.u.ip.proto);
671                 if (pe)
672                         printf("%-4s ", pe->p_name);
673                 else
674                         printf("%4u ", q[l].id.u.ip.proto);
675                 printf("%15s/%-5d ",
676                         inet_ntoa(ina), q[l].id.u.ip.src_port);
677                 ina.s_addr = htonl(q[l].id.u.ip.dst_ip);
678                 printf("%15s/%-5d ",
679                         inet_ntoa(ina), q[l].id.u.ip.dst_port);
680                 printf("%4ju %8ju %2u %4u %3u\n",
681                         (uintmax_t)q[l].tot_pkts, (uintmax_t)q[l].tot_bytes,
682                         q[l].len, q[l].len_bytes, q[l].drops);
683                 if (verbose)
684                         printf(" S %20ju F %20ju\n",
685                                 (uintmax_t)q[l].S, (uintmax_t)q[l].F);
686         }
687 }
688
689 static void
690 show_flowset_parms(struct dn_ioc_flowset *fs, char *prefix)
691 {
692         char qs[30];
693         char plr[30];
694         char red[90];   /* Display RED parameters */
695         int l;
696
697         l = fs->qsize;
698         if (fs->flags_fs & DN_QSIZE_IS_BYTES) {
699                 if (l >= 8192)
700                         sprintf(qs, "%d KB", l / 1024);
701                 else
702                         sprintf(qs, "%d B", l);
703         } else
704                 sprintf(qs, "%3d sl.", l);
705         if (fs->plr)
706                 sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
707         else
708                 plr[0] = '\0';
709         if (fs->flags_fs & DN_IS_RED)   /* RED parameters */
710                 sprintf(red,
711                         "\n\t %cRED w_q %f min_th %d max_th %d max_p %f",
712                         (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ',
713                         1.0 * fs->w_q / (double)(1 << SCALE_RED),
714                         SCALE_VAL(fs->min_th),
715                         SCALE_VAL(fs->max_th),
716                         1.0 * fs->max_p / (double)(1 << SCALE_RED));
717         else
718                 sprintf(red, "droptail");
719
720         printf("%s %s%s %d queues (%d buckets) %s\n",
721                 prefix, qs, plr, fs->rq_elements, fs->rq_size, red);
722 }
723
724 static void
725 show_pipes(void *data, int nbytes, int ac, char *av[])
726 {
727         u_long rulenum;
728         void *next = data;
729         struct dn_ioc_pipe *p = (struct dn_ioc_pipe *)data;
730         struct dn_ioc_flowset *fs;
731         struct dn_ioc_flowqueue *q;
732         int l;
733
734         if (ac > 0)
735                 rulenum = strtoul(*av++, NULL, 10);
736         else
737                 rulenum = 0;
738         for (; nbytes >= sizeof(*p); p = (struct dn_ioc_pipe *)next) {
739                 double b = p->bandwidth;
740                 char buf[30];
741                 char prefix[80];
742
743                 if (p->fs.fs_type != DN_IS_PIPE)
744                         break;  /* done with pipes, now queues */
745
746                 /*
747                  * compute length, as pipe have variable size
748                  */
749                 l = sizeof(*p) + p->fs.rq_elements * sizeof(*q);
750                 next = (void *)p + l;
751                 nbytes -= l;
752
753                 if (rulenum != 0 && rulenum != p->pipe_nr)
754                         continue;
755
756                 /*
757                  * Print rate
758                  */
759                 if (b == 0)
760                         sprintf(buf, "unlimited");
761                 else if (b >= 1000000)
762                         sprintf(buf, "%7.3f Mbit/s", b/1000000);
763                 else if (b >= 1000)
764                         sprintf(buf, "%7.3f Kbit/s", b/1000);
765                 else
766                         sprintf(buf, "%7.3f bit/s ", b);
767
768                 sprintf(prefix, "%05d: %s %4d ms ",
769                         p->pipe_nr, buf, p->delay);
770                 show_flowset_parms(&p->fs, prefix);
771                 if (verbose)
772                         printf(" V %20ju\n", (uintmax_t)p->V >> MY_M);
773
774                 q = (struct dn_ioc_flowqueue *)(p+1);
775                 show_queues(&p->fs, q);
776         }
777
778         for (fs = next; nbytes >= sizeof(*fs); fs = next) {
779                 char prefix[80];
780
781                 if (fs->fs_type != DN_IS_QUEUE)
782                         break;
783                 l = sizeof(*fs) + fs->rq_elements * sizeof(*q);
784                 next = (void *)fs + l;
785                 nbytes -= l;
786                 q = (struct dn_ioc_flowqueue *)(fs+1);
787                 sprintf(prefix, "q%05d: weight %d pipe %d ",
788                         fs->fs_nr, fs->weight, fs->parent_nr);
789                 show_flowset_parms(fs, prefix);
790                 show_queues(fs, q);
791         }
792 }
793
794 /*
795  * This one handles all set-related commands
796  *      ipfw set { show | enable | disable }
797  *      ipfw set swap X Y
798  *      ipfw set move X to Y
799  *      ipfw set move rule X to Y
800  */
801 static void
802 sets_handler(int ac, char *av[])
803 {
804         u_int32_t set_disable, masks[2];
805         u_int16_t rulenum;
806         u_int8_t cmd, new_set;
807         int i, nbytes;
808
809         NEXT_ARG;
810         if (!ac)
811                 errx(EX_USAGE, "set needs command");
812         if (!strncmp(*av, "show", strlen(*av)) ) {
813                 void *data = NULL;
814                 char *msg;
815                 int nalloc=1000;
816                 nbytes = nalloc;
817
818                 while (nbytes >= nalloc) {
819                         nalloc = nalloc * 2+321;
820                         nbytes = nalloc;
821                         if (data == NULL) {
822                                 if ((data = malloc(nbytes)) == NULL) {
823                                         err(EX_OSERR, "malloc");
824                                 }
825                         } else if ((data = realloc(data, nbytes)) == NULL) {
826                                 err(EX_OSERR, "realloc");
827                         }
828                         if (do_get_x(IP_FW_GET, data, &nbytes) < 0) {
829                                 err(EX_OSERR, "getsockopt(IP_FW_GET)");
830                         }
831                 }
832                 set_disable = ((struct ipfw_ioc_rule *)data)->set_disable;
833                 for (i = 0, msg = "disable" ; i < 31; i++)
834                         if ( (set_disable & (1<<i))) {
835                                 printf("%s %d", msg, i);
836                                 msg = "";
837                         }
838                 msg = (set_disable) ? " enable" : "enable";
839                 for (i = 0; i < 31; i++)
840                         if ( !(set_disable & (1<<i))) {
841                                 printf("%s %d", msg, i);
842                                 msg = "";
843                         }
844                 printf("\n");
845         } else if (!strncmp(*av, "swap", strlen(*av))) {
846                 NEXT_ARG;
847                 if (ac != 2)
848                         errx(EX_USAGE, "set swap needs 2 set numbers\n");
849                 rulenum = atoi(av[0]);
850                 new_set = atoi(av[1]);
851                 if (!isdigit(*(av[0])) || rulenum > 30)
852                         errx(EX_DATAERR, "invalid set number %s\n", av[0]);
853                 if (!isdigit(*(av[1])) || new_set > 30)
854                         errx(EX_DATAERR, "invalid set number %s\n", av[1]);
855                 masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
856                 i = do_set_x(IP_FW_DEL, masks, sizeof(u_int32_t));
857         } else if (!strncmp(*av, "move", strlen(*av))) {
858                 NEXT_ARG;
859                 if (ac && !strncmp(*av, "rule", strlen(*av))) {
860                         cmd = 2;
861                         NEXT_ARG;
862                 } else
863                         cmd = 3;
864                 if (ac != 3 || strncmp(av[1], "to", strlen(*av)))
865                         errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
866                 rulenum = atoi(av[0]);
867                 new_set = atoi(av[2]);
868                 if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > 30) ||
869                                 (cmd == 2 && rulenum == 65535) )
870                         errx(EX_DATAERR, "invalid source number %s\n", av[0]);
871                 if (!isdigit(*(av[2])) || new_set > 30)
872                         errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
873                 masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
874                 i = do_set_x(IP_FW_DEL, masks, sizeof(u_int32_t));
875         } else if (!strncmp(*av, "disable", strlen(*av)) ||
876                         !strncmp(*av, "enable", strlen(*av)) ) {
877                 int which = !strncmp(*av, "enable", strlen(*av)) ? 1 : 0;
878
879                 NEXT_ARG;
880                 masks[0] = masks[1] = 0;
881
882                 while (ac) {
883                         if (isdigit(**av)) {
884                                 i = atoi(*av);
885                                 if (i < 0 || i > 30)
886                                         errx(EX_DATAERR, "invalid set number %d\n", i);
887                                 masks[which] |= (1<<i);
888                         } else if (!strncmp(*av, "disable", strlen(*av)))
889                                 which = 0;
890                         else if (!strncmp(*av, "enable", strlen(*av)))
891                                 which = 1;
892                         else
893                                 errx(EX_DATAERR, "invalid set command %s\n", *av);
894                         NEXT_ARG;
895                 }
896                 if ( (masks[0] & masks[1]) != 0 )
897                         errx(EX_DATAERR, "cannot enable and disable the same set\n");
898                 i = do_set_x(IP_FW_DEL, masks, sizeof(masks));
899                 if (i)
900                         warn("set enable/disable: setsockopt(IP_FW_DEL)");
901         } else
902                 errx(EX_USAGE, "invalid set command %s\n", *av);
903 }
904
905 static void
906 add_state(int ac, char *av[])
907 {
908         struct ipfw_ioc_state ioc_state;
909         ioc_state.expiry = 0;
910         ioc_state.lifetime = 0;
911         NEXT_ARG;
912         if (strcmp(*av, "rulenum") == 0) {
913                 NEXT_ARG;
914                 ioc_state.rulenum = atoi(*av);
915         } else {
916                 errx(EX_USAGE, "ipfw state add rule");
917         }
918         NEXT_ARG;
919         struct protoent *pe;
920         pe = getprotobyname(*av);
921         ioc_state.flow_id.proto = pe->p_proto;
922
923         NEXT_ARG;
924         ioc_state.flow_id.src_ip = inet_addr(*av);
925
926         NEXT_ARG;
927         ioc_state.flow_id.src_port = atoi(*av);
928
929         NEXT_ARG;
930         ioc_state.flow_id.dst_ip = inet_addr(*av);
931
932         NEXT_ARG;
933         ioc_state.flow_id.dst_port = atoi(*av);
934
935         NEXT_ARG;
936         if (strcmp(*av, "live") == 0) {
937                 NEXT_ARG;
938                 ioc_state.lifetime = atoi(*av);
939                 NEXT_ARG;
940         }
941
942         if (strcmp(*av, "expiry") == 0) {
943                 NEXT_ARG;
944                 ioc_state.expiry = strtoul(*av, NULL, 10);
945                 printf("ioc_state.expiry=%d\n", ioc_state.expiry);
946         }
947
948         if (do_set_x(IP_FW_STATE_ADD, &ioc_state, sizeof(struct ipfw_ioc_state)) < 0 ) {
949                 err(EX_UNAVAILABLE, "do_set_x(IP_FW_STATE_ADD)");
950         }
951         if (!do_quiet) {
952                 printf("Flushed all states.\n");
953         }
954 }
955
956 static void
957 delete_state(int ac, char *av[])
958 {
959         int rulenum;
960         NEXT_ARG;
961         if (ac == 1 && isdigit(**av))
962                 rulenum = atoi(*av);
963         if (do_set_x(IP_FW_STATE_DEL, &rulenum, sizeof(int)) < 0 )
964                 err(EX_UNAVAILABLE, "do_set_x(IP_FW_STATE_DEL)");
965         if (!do_quiet)
966                 printf("Flushed all states.\n");
967 }
968
969 static void
970 flush_state(int ac, char *av[])
971 {
972         if (!do_force) {
973                 int c;
974
975                 printf("Are you sure? [yn] ");
976                 fflush(stdout);
977                 do {
978                         c = toupper(getc(stdin));
979                         while (c != '\n' && getc(stdin) != '\n')
980                                 if (feof(stdin))
981                                         return; /* and do not flush */
982                 } while (c != 'Y' && c != 'N');
983                 if (c == 'N')   /* user said no */
984                         return;
985         }
986         if (do_set_x(IP_FW_STATE_FLUSH, NULL, 0) < 0 )
987                 err(EX_UNAVAILABLE, "do_set_x(IP_FW_STATE_FLUSH)");
988         if (!do_quiet)
989                 printf("Flushed all states.\n");
990 }
991
992 static int
993 lookup_host (char *host, struct in_addr *ipaddr)
994 {
995         struct hostent *he;
996
997         if (!inet_aton(host, ipaddr)) {
998                 if ((he = gethostbyname(host)) == NULL)
999                         return(-1);
1000                 *ipaddr = *(struct in_addr *)he->h_addr_list[0];
1001         }
1002         return(0);
1003 }
1004
1005 static void
1006 table_append(int ac, char *av[])
1007 {
1008         struct ipfw_ioc_table tbl;
1009         char *p;
1010         int size;
1011
1012         NEXT_ARG;
1013         if (isdigit(**av))
1014                 tbl.id = atoi(*av);
1015         else
1016                 errx(EX_USAGE, "table id `%s' invalid", *av);
1017
1018         if (tbl.id < 0 || tbl.id > IPFW_TABLES_MAX - 1)
1019                 errx(EX_USAGE, "table id `%d' invalid", tbl.id);
1020
1021         NEXT_ARG;
1022         if (strcmp(*av, "ip") == 0)
1023                 tbl.type = 1;
1024         else if (strcmp(*av, "mac") == 0)
1025                 tbl.type = 2;
1026         else
1027                 errx(EX_USAGE, "table type `%s' not supported", *av);
1028
1029         NEXT_ARG;
1030         if (tbl.type == 1) { /* table type ipv4 */
1031                 struct ipfw_ioc_table_ip_entry ip_ent;
1032                 if (!ac)
1033                         errx(EX_USAGE, "IP address required");
1034
1035                 p = strchr(*av, '/');
1036                 if (p) {
1037                         *p++ = '\0';
1038                         ip_ent.masklen = atoi(p);
1039                         if (ip_ent.masklen > 32)
1040                                 errx(EX_DATAERR, "bad width ``%s''", p);
1041                 } else {
1042                         ip_ent.masklen = 32;
1043                 }
1044
1045                 if (lookup_host(*av, (struct in_addr *)&ip_ent.addr) != 0)
1046                         errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
1047
1048                 tbl.ip_ent[0] = ip_ent;
1049                 size = sizeof(tbl) + sizeof(ip_ent);
1050         } else if (tbl.type == 2) { /* table type mac */
1051                 struct ipfw_ioc_table_mac_entry mac_ent;
1052                 if (!ac)
1053                         errx(EX_USAGE, "MAC address required");
1054
1055                 mac_ent.addr = *ether_aton(*av);
1056                 tbl.mac_ent[0] = mac_ent;
1057                 size = sizeof(tbl) + sizeof(mac_ent);
1058         }
1059         if (do_set_x(IP_FW_TABLE_APPEND, &tbl, size) < 0 )
1060                 errx(EX_USAGE, "do_set_x(IP_FW_TABLE_APPEND) "
1061                         "table `%d' append `%s' failed", tbl.id, *av);
1062 }
1063
1064 static void
1065 table_remove(int ac, char *av[])
1066 {
1067         struct ipfw_ioc_table tbl;
1068         struct ipfw_ioc_table_ip_entry ip_ent;
1069         char *p;
1070         int size;
1071
1072         NEXT_ARG;
1073         if (isdigit(**av))
1074                 tbl.id = atoi(*av);
1075         else
1076                 errx(EX_USAGE, "table id `%s' invalid", *av);
1077
1078         if (tbl.id < 0 || tbl.id > IPFW_TABLES_MAX - 1)
1079                 errx(EX_USAGE, "table id `%d' invalid", tbl.id);
1080
1081         NEXT_ARG;
1082         if (strcmp(*av, "ip") == 0)
1083                 tbl.type = 1;
1084         else if (strcmp(*av, "mac") == 0)
1085                 tbl.type = 2;
1086         else
1087                 errx(EX_USAGE, "table type `%s' not supported", *av);
1088
1089         NEXT_ARG;
1090         if (!ac)
1091                 errx(EX_USAGE, "IP address required");
1092         p = strchr(*av, '/');
1093         if (p) {
1094                 *p++ = '\0';
1095                 ip_ent.masklen = atoi(p);
1096                 if (ip_ent.masklen > 32)
1097                         errx(EX_DATAERR, "bad width ``%s''", p);
1098         } else {
1099                 ip_ent.masklen = 32;
1100         }
1101
1102         if (lookup_host(*av, (struct in_addr *)&ip_ent.addr) != 0)
1103                 errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
1104
1105         tbl.ip_ent[0] = ip_ent;
1106         size = sizeof(tbl) + sizeof(ip_ent);
1107         if (do_set_x(IP_FW_TABLE_REMOVE, &tbl, size) < 0 ) {
1108                 errx(EX_USAGE, "do_set_x(IP_FW_TABLE_REMOVE) "
1109                         "table `%d' append `%s' failed", tbl.id, *av);
1110         }
1111 }
1112
1113 static void
1114 table_flush(int ac, char *av[])
1115 {
1116         struct ipfw_ioc_table ioc_table;
1117         struct ipfw_ioc_table *t = &ioc_table;
1118
1119         NEXT_ARG;
1120         if (isdigit(**av)) {
1121                 t->id = atoi(*av);
1122                 if (t->id < 0 || t->id > IPFW_TABLES_MAX - 1)
1123                         errx(EX_USAGE, "table id `%d' invalid", t->id);
1124         } else {
1125                 errx(EX_USAGE, "table id `%s' invalid", *av);
1126         }
1127         if (do_set_x(IP_FW_TABLE_FLUSH, t, sizeof(struct ipfw_ioc_table)) < 0 )
1128                 errx(EX_USAGE, "do_set_x(IP_FW_TABLE_FLUSH) "
1129                                         "table `%s' flush failed", *av);
1130 }
1131
1132 static void
1133 table_list(int ac, char *av[])
1134 {
1135         struct ipfw_ioc_table *ioc_table;
1136         int i, count, nbytes, nalloc = 1024;
1137         void *data = NULL;
1138         NEXT_ARG;
1139         if (strcmp(*av, "list") == 0) {
1140                 nbytes = nalloc;
1141                 while (nbytes >= nalloc) {
1142                         nalloc = nalloc * 2 ;
1143                         nbytes = nalloc;
1144                         if ((data = realloc(data, nbytes)) == NULL)
1145                                 err(EX_OSERR, "realloc");
1146                         if (do_get_x(IP_FW_TABLE_LIST, data, &nbytes) < 0)
1147                                 err(EX_OSERR, "do_get_x(IP_FW_TABLE_LIST)");
1148                 }
1149                 ioc_table = (struct ipfw_ioc_table *)data;
1150                 count = nbytes / sizeof(struct ipfw_ioc_table);
1151                 for (i = 0; i < count; i++, ioc_table++) {
1152                         if (ioc_table->type > 0) {
1153                                 printf("table %d",ioc_table->id);
1154                                 if (ioc_table->type == 1)
1155                                         printf(" type ip");
1156                                 else if (ioc_table->type == 2)
1157                                         printf(" type mac");
1158                                 printf(" count %d",ioc_table->count);
1159                                 if (strlen(ioc_table->name) > 0)
1160                                         printf(" name %s",ioc_table->name);
1161                                 printf("\n");
1162
1163                         }
1164                 }
1165         } else {
1166                 errx(EX_USAGE, "ipfw3 table `%s' delete invalid", *av);
1167         }
1168 }
1169
1170 void
1171 print_table(struct ipfw_ioc_table * tbl)
1172 {
1173         int i;
1174         if (tbl->type == 0)
1175                 errx(EX_USAGE, "table %d is not in use", tbl->id);
1176
1177         printf("table %d", tbl->id);
1178         if (tbl->type == 1)
1179                 printf(" type ip");
1180         else if (tbl->type == 2)
1181                 printf(" type mac");
1182
1183         printf(" count %d", tbl->count);
1184         if (strlen(tbl->name) > 0)
1185                 printf(" name %s", tbl->name);
1186
1187         printf("\n");
1188
1189         if (tbl->type == 1) {
1190                 struct ipfw_ioc_table_ip_entry *ip_ent;
1191                 ip_ent = tbl->ip_ent;
1192                 for (i = 0; i < tbl->count; i++) {
1193                         printf("%s", inet_ntoa(*(struct in_addr *)&ip_ent->addr));
1194                         printf("/%d ", ip_ent->masklen);
1195                         printf("\n");
1196                         ip_ent++;
1197                 }
1198         } else if (tbl->type == 2) {
1199                 struct ipfw_ioc_table_mac_entry *mac_ent;
1200                 mac_ent = tbl->mac_ent;
1201                 for (i = 0; i < tbl->count; i++) {
1202                         printf("%s", ether_ntoa(&mac_ent->addr));
1203                         printf("\n");
1204                         mac_ent++;
1205                 }
1206         }
1207 }
1208
1209 static void
1210 table_show(int ac, char *av[])
1211 {
1212         int nbytes, nalloc = 1024;
1213         void *data = NULL;
1214         NEXT_ARG;
1215         if (isdigit(**av)) {
1216                 nbytes = nalloc;
1217                 while (nbytes >= nalloc) {
1218                         nalloc = nalloc * 2 + 256;
1219                         nbytes = nalloc;
1220                         if (data == NULL) {
1221                                 if ((data = malloc(nbytes)) == NULL) {
1222                                         err(EX_OSERR, "malloc");
1223                                 }
1224                         } else if ((data = realloc(data, nbytes)) == NULL) {
1225                                 err(EX_OSERR, "realloc");
1226                         }
1227                         /* store table id in the header of data */
1228                         int *head = (int *)data;
1229                         *head = atoi(*av);
1230                         if (*head < 0 || *head > IPFW_TABLES_MAX - 1)
1231                                 errx(EX_USAGE, "table id `%d' invalid", *head);
1232                         if (do_get_x(IP_FW_TABLE_SHOW, data, &nbytes) < 0)
1233                                 err(EX_OSERR, "do_get_x(IP_FW_TABLE_LIST)");
1234                         struct ipfw_ioc_table *tbl;
1235                         tbl = (struct ipfw_ioc_table *)data;
1236                         print_table(tbl);
1237                 }
1238         } else {
1239                 errx(EX_USAGE, "ipfw3 table `%s' show invalid", *av);
1240         }
1241 }
1242
1243 static void
1244 table_create(int ac, char *av[])
1245 {
1246         struct ipfw_ioc_table ioc_table;
1247         struct ipfw_ioc_table *t = &ioc_table;
1248
1249         NEXT_ARG;
1250         if (ac < 2)
1251                 errx(EX_USAGE, "table parameters invalid");
1252         if (isdigit(**av)) {
1253                 t->id = atoi(*av);
1254                 if (t->id < 0 || t->id > IPFW_TABLES_MAX - 1)
1255                         errx(EX_USAGE, "table id `%d' invalid", t->id);
1256         } else {
1257                 errx(EX_USAGE, "table id `%s' invalid", *av);
1258         }
1259         NEXT_ARG;
1260         if (strcmp(*av, "ip") == 0)
1261                 t->type = 1;
1262         else if (strcmp(*av, "mac") == 0)
1263                 t->type = 2;
1264         else
1265                 errx(EX_USAGE, "table type `%s' not supported", *av);
1266
1267         NEXT_ARG;
1268         memset(t->name, 0, IPFW_TABLE_NAME_LEN);
1269         if (ac == 2 && strcmp(*av, "name") == 0) {
1270                 NEXT_ARG;
1271                 if (strlen(*av) < IPFW_TABLE_NAME_LEN) {
1272                         strncpy(t->name, *av, strlen(*av));
1273                 } else {
1274                         errx(EX_USAGE, "table name `%s' too long", *av);
1275                 }
1276         } else if (ac == 1) {
1277                 errx(EX_USAGE, "table `%s' invalid", *av);
1278         }
1279
1280         if (do_set_x(IP_FW_TABLE_CREATE, t, sizeof(struct ipfw_ioc_table)) < 0)
1281                 errx(EX_USAGE, "do_set_x(IP_FW_TABLE_CREATE) "
1282                                         "table `%d' in use", t->id);
1283 }
1284
1285 static void
1286 table_delete(int ac, char *av[])
1287 {
1288         struct ipfw_ioc_table ioc_table;
1289         struct ipfw_ioc_table *t = &ioc_table;
1290
1291         NEXT_ARG;
1292         if (isdigit(**av)) {
1293                 t->id = atoi(*av);
1294                 if (t->id < 0 || t->id > IPFW_TABLES_MAX - 1)
1295                         errx(EX_USAGE, "table id `%d' invalid", t->id);
1296         } else {
1297                 errx(EX_USAGE, "table id `%s' invalid", *av);
1298         }
1299         if (t->id < 0 || t->id > IPFW_TABLES_MAX - 1)
1300                 errx(EX_USAGE, "table id `%d' invalid", t->id);
1301
1302         if (do_set_x(IP_FW_TABLE_DELETE, t, sizeof(struct ipfw_ioc_table)) < 0)
1303                 errx(EX_USAGE, "do_set_x(IP_FW_TABLE_DELETE) "
1304                                         "table `%s' delete failed", *av);
1305 }
1306
1307 static void
1308 table_test(int ac, char *av[])
1309 {
1310         struct ipfw_ioc_table tbl;
1311         int size;
1312
1313         NEXT_ARG;
1314         if (isdigit(**av))
1315                 tbl.id = atoi(*av);
1316         else
1317                 errx(EX_USAGE, "table id `%s' invalid", *av);
1318
1319         if (tbl.id < 0 || tbl.id > IPFW_TABLES_MAX - 1)
1320                 errx(EX_USAGE, "table id `%d' invalid", tbl.id);
1321
1322         NEXT_ARG;
1323         if (strcmp(*av, "ip") == 0)
1324                 tbl.type = 1;
1325         else if (strcmp(*av, "mac") == 0)
1326                 tbl.type = 2;
1327         else
1328                 errx(EX_USAGE, "table type `%s' not supported", *av);
1329
1330         NEXT_ARG;
1331         if (tbl.type == 1) { /* table type ipv4 */
1332                 struct ipfw_ioc_table_ip_entry ip_ent;
1333                 if (lookup_host(*av, (struct in_addr *)&ip_ent.addr) != 0)
1334                         errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
1335
1336                 tbl.ip_ent[0] = ip_ent;
1337                 size = sizeof(tbl) + sizeof(ip_ent);
1338         } else if (tbl.type == 2) { /* table type mac */
1339                 struct ipfw_ioc_table_mac_entry mac_ent;
1340                 if (!ac)
1341                         errx(EX_USAGE, "MAC address required");
1342
1343                 mac_ent.addr = *ether_aton(*av);
1344                 tbl.mac_ent[0] = mac_ent;
1345                 size = sizeof(tbl) + sizeof(mac_ent);
1346         }
1347         if (do_set_x(IP_FW_TABLE_TEST, &tbl, size) < 0 ) {
1348                 printf("NO, %s not exists in table %d\n", *av, tbl.id);
1349         } else {
1350                 printf("YES, %s exists in table %d\n", *av, tbl.id);
1351         }
1352 }
1353
1354 static void
1355 table_rename(int ac, char *av[])
1356 {
1357         struct ipfw_ioc_table tbl;
1358         int size;
1359
1360         bzero(&tbl, sizeof(tbl));
1361         NEXT_ARG;
1362         if (isdigit(**av))
1363                 tbl.id = atoi(*av);
1364         else
1365                 errx(EX_USAGE, "table id `%s' invalid", *av);
1366
1367         if (tbl.id < 0 || tbl.id > IPFW_TABLES_MAX - 1)
1368                 errx(EX_USAGE, "table id `%d' invalid", tbl.id);
1369
1370         NEXT_ARG;
1371         strlcpy(tbl.name, *av, IPFW_TABLE_NAME_LEN);
1372         size = sizeof(tbl);
1373         if (do_set_x(IP_FW_TABLE_RENAME, &tbl, size) < 0 )
1374                 errx(EX_USAGE, "do_set_x(IP_FW_TABLE_RENAME) "
1375                                         "table `%d' not in use", tbl.id);
1376 }
1377
1378 static void
1379 list(int ac, char *av[])
1380 {
1381         struct ipfw_ioc_state *dynrules, *d;
1382         struct ipfw_ioc_rule *r;
1383
1384         u_long rnum;
1385         void *data = NULL;
1386         int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width;
1387         int exitval = EX_OK, lac;
1388         char **lav, *endptr;
1389         int seen = 0;
1390         int nalloc = 1024;
1391
1392         NEXT_ARG;
1393
1394         /* get rules or pipes from kernel, resizing array as necessary */
1395         nbytes = nalloc;
1396
1397         while (nbytes >= nalloc) {
1398                 nalloc = nalloc * 2 ;
1399                 nbytes = nalloc;
1400                 if ((data = realloc(data, nbytes)) == NULL)
1401                         err(EX_OSERR, "realloc");
1402                 if (do_get_x(IP_FW_GET, data, &nbytes) < 0)
1403                         err(EX_OSERR, "do_get_x(IP_FW_GET)");
1404         }
1405
1406         /*
1407          * Count static rules.
1408          */
1409         r = data;
1410         nstat = r->static_count;
1411
1412         /*
1413          * Count dynamic rules. This is easier as they have
1414          * fixed size.
1415          */
1416         dynrules = (struct ipfw_ioc_state *)((void *)r + r->static_len);
1417         ndyn = (nbytes - r->static_len) / sizeof(*dynrules);
1418
1419         /* if showing stats, figure out column widths ahead of time */
1420         bcwidth = pcwidth = 0;
1421         if (do_acct) {
1422                 for (n = 0, r = data; n < nstat;
1423                         n++, r = (void *)r + IOC_RULESIZE(r)) {
1424                         /* packet counter */
1425                         width = snprintf(NULL, 0, "%ju", (uintmax_t)r->pcnt);
1426                         if (width > pcwidth)
1427                                 pcwidth = width;
1428
1429                         /* byte counter */
1430                         width = snprintf(NULL, 0, "%ju", (uintmax_t)r->bcnt);
1431                         if (width > bcwidth)
1432                                 bcwidth = width;
1433                 }
1434         }
1435         if (do_dynamic && ndyn) {
1436                 for (n = 0, d = dynrules; n < ndyn; n++, d++) {
1437                         width = snprintf(NULL, 0, "%ju", (uintmax_t)d->pcnt);
1438                         if (width > pcwidth)
1439                                 pcwidth = width;
1440
1441                         width = snprintf(NULL, 0, "%ju", (uintmax_t)d->bcnt);
1442                         if (width > bcwidth)
1443                                 bcwidth = width;
1444                 }
1445         }
1446
1447         /* if no rule numbers were specified, list all rules */
1448         if (ac == 0) {
1449                 if (do_dynamic != 2) {
1450                         for (n = 0, r = data; n < nstat; n++,
1451                                 r = (void *)r + IOC_RULESIZE(r)) {
1452                                 show_rules(r, pcwidth, bcwidth);
1453                         }
1454                 }
1455                 if (do_dynamic && ndyn) {
1456                         if (do_dynamic != 2) {
1457                                 printf("## States (%d):\n", ndyn);
1458                         }
1459                         for (n = 0, d = dynrules; n < ndyn; n++, d++)
1460                                 show_states(d, pcwidth, bcwidth);
1461                 }
1462                 goto done;
1463         }
1464
1465         /* display specific rules requested on command line */
1466
1467         if (do_dynamic != 2) {
1468                 for (lac = ac, lav = av; lac != 0; lac--) {
1469                         /* convert command line rule # */
1470                         rnum = strtoul(*lav++, &endptr, 10);
1471                         if (*endptr) {
1472                                 exitval = EX_USAGE;
1473                                 warnx("invalid rule number: %s", *(lav - 1));
1474                                 continue;
1475                         }
1476                         for (n = seen = 0, r = data; n < nstat;
1477                                 n++, r = (void *)r + IOC_RULESIZE(r) ) {
1478                                 if (r->rulenum > rnum)
1479                                         break;
1480                                 if (r->rulenum == rnum) {
1481                                         show_rules(r, pcwidth, bcwidth);
1482                                         seen = 1;
1483                                 }
1484                         }
1485                         if (!seen) {
1486                                 /* give precedence to other error(s) */
1487                                 if (exitval == EX_OK)
1488                                         exitval = EX_UNAVAILABLE;
1489                                 warnx("rule %lu does not exist", rnum);
1490                         }
1491                 }
1492         }
1493
1494         if (do_dynamic && ndyn) {
1495                 if (do_dynamic != 2) {
1496                         printf("## States (%d):\n", ndyn);
1497                 }
1498                 for (lac = ac, lav = av; lac != 0; lac--) {
1499                         rnum = strtoul(*lav++, &endptr, 10);
1500                         if (*endptr)
1501                                 /* already warned */
1502                                 continue;
1503                         for (n = 0, d = dynrules; n < ndyn; n++, d++) {
1504                                 if (d->rulenum > rnum)
1505                                         break;
1506                                 if (d->rulenum == rnum)
1507                                         show_states(d, pcwidth, bcwidth);
1508                         }
1509                 }
1510         }
1511
1512         ac = 0;
1513
1514 done:
1515         free(data);
1516
1517         if (exitval != EX_OK)
1518                 exit(exitval);
1519 }
1520
1521 static void
1522 show_dummynet(int ac, char *av[])
1523 {
1524         void *data = NULL;
1525         int nbytes;
1526         int nalloc = 1024;      /* start somewhere... */
1527
1528         NEXT_ARG;
1529
1530         nbytes = nalloc;
1531         while (nbytes >= nalloc) {
1532                 nalloc = nalloc * 2 + 200;
1533                 nbytes = nalloc;
1534                 if ((data = realloc(data, nbytes)) == NULL)
1535                         err(EX_OSERR, "realloc");
1536                 if (do_get_x(IP_DUMMYNET_GET, data, &nbytes) < 0) {
1537                         err(EX_OSERR, "do_get_x(IP_%s_GET)",
1538                                 do_pipe ? "DUMMYNET" : "FW");
1539                 }
1540         }
1541
1542         show_pipes(data, nbytes, ac, av);
1543         free(data);
1544 }
1545
1546 static void
1547 help(void)
1548 {
1549         fprintf(stderr, "usage: ipfw [options]\n"
1550                         "       ipfw add [rulenum] [set id] action filters\n"
1551                         "       ipfw delete [rulenum]\n"
1552                         "       ipfw flush\n"
1553                         "       ipfw list [rulenum]\n"
1554                         "       ipfw show [rulenum]\n"
1555                         "       ipfw zero [rulenum]\n"
1556                         "       ipfw set [show|enable|disable]\n"
1557                         "       ipfw module\n"
1558                         "       ipfw [enable|disable]\n"
1559                         "       ipfw log [reset|off|on]\n"
1560                         "       ipfw nat [config|show|delete]\n"
1561                         "       ipfw pipe [config|show|delete]\n"
1562                         "       ipfw state [add|delete|list|show]"
1563                         "\nsee ipfw manpage for details\n");
1564         exit(EX_USAGE);
1565 }
1566
1567
1568 static void
1569 delete_rules(int ac, char *av[])
1570 {
1571         struct dn_ioc_pipe pipe;
1572         u_int32_t rulenum;
1573         int exitval = EX_OK;
1574         int do_set = 0;
1575         int i;
1576
1577         memset(&pipe, 0, sizeof pipe);
1578
1579         NEXT_ARG;
1580         if (ac > 0 && !strncmp(*av, "set", strlen(*av))) {
1581                 do_set = 1;     /* delete set */
1582                 NEXT_ARG;
1583         }
1584
1585         /* Rule number */
1586         while (ac && isdigit(**av)) {
1587                 i = atoi(*av);
1588                 NEXT_ARG;
1589                 if (do_pipe) {
1590                         if (do_pipe == 1)
1591                                 pipe.pipe_nr = i;
1592                         else
1593                                 pipe.fs.fs_nr = i;
1594
1595                         i = do_set_x(IP_DUMMYNET_DEL, &pipe, sizeof pipe);
1596                         if (i) {
1597                                 exitval = 1;
1598                                 warn("rule %u: setsockopt(IP_DUMMYNET_DEL)",
1599                                         do_pipe == 1 ? pipe.pipe_nr :
1600                                         pipe.fs.fs_nr);
1601                         }
1602                 } else {
1603                         rulenum = (i & 0xffff) | (do_set << 24);
1604                         i = do_set_x(IP_FW_DEL, &rulenum, sizeof rulenum);
1605                         if (i) {
1606                                 exitval = EX_UNAVAILABLE;
1607                                 warn("rule %u: setsockopt(IP_FW_DEL)",
1608                                         rulenum);
1609                         }
1610                 }
1611         }
1612         if (exitval != EX_OK)
1613                 exit(exitval);
1614 }
1615
1616
1617 static unsigned long
1618 getbw(const char *str, u_short *flags, int kb)
1619 {
1620         unsigned long val;
1621         int inbytes = 0;
1622         char *end;
1623
1624         val = strtoul(str, &end, 0);
1625         if (*end == 'k' || *end == 'K') {
1626                 ++end;
1627                 val *= kb;
1628         } else if (*end == 'm' || *end == 'M') {
1629                 ++end;
1630                 val *= kb * kb;
1631         }
1632
1633         /*
1634          * Deal with bits or bytes or b(bits) or B(bytes). If there is no
1635          * trailer assume bits.
1636          */
1637         if (strncasecmp(end, "bit", 3) == 0) {
1638                 ;
1639         } else if (strncasecmp(end, "byte", 4) == 0) {
1640                 inbytes = 1;
1641         } else if (*end == 'b') {
1642                 ;
1643         } else if (*end == 'B') {
1644                 inbytes = 1;
1645         }
1646
1647         /*
1648          * Return in bits if flags is NULL, else flag bits
1649          * or bytes in flags and return the unconverted value.
1650          */
1651         if (inbytes && flags)
1652                 *flags |= DN_QSIZE_IS_BYTES;
1653         else if (inbytes && flags == NULL)
1654                 val *= 8;
1655
1656         return(val);
1657 }
1658
1659 /*
1660  * config dummynet pipe/queue
1661  */
1662 static void
1663 config_dummynet(int ac, char **av)
1664 {
1665         struct dn_ioc_pipe pipe;
1666         u_int32_t a;
1667         void *par = NULL;
1668         int i;
1669         char *end;
1670
1671         NEXT_ARG;
1672         memset(&pipe, 0, sizeof pipe);
1673         /* Pipe number */
1674         if (ac && isdigit(**av)) {
1675                 i = atoi(*av);
1676                 NEXT_ARG;
1677                 if (do_pipe == 1)
1678                         pipe.pipe_nr = i;
1679                 else
1680                         pipe.fs.fs_nr = i;
1681         }
1682
1683         while (ac > 0) {
1684                 double d;
1685
1686                 int tok = match_token(dummynet_params, *av);
1687                 NEXT_ARG;
1688
1689                 switch(tok) {
1690                 case TOK_NOERROR:
1691                         pipe.fs.flags_fs |= DN_NOERROR;
1692                         break;
1693
1694                 case TOK_PLR:
1695                         NEED1("plr needs argument 0..1\n");
1696                         d = strtod(av[0], NULL);
1697                         if (d > 1)
1698                                 d = 1;
1699                         else if (d < 0)
1700                                 d = 0;
1701                         pipe.fs.plr = (int)(d*0x7fffffff);
1702                         NEXT_ARG;
1703                         break;
1704
1705                 case TOK_QUEUE:
1706                         NEED1("queue needs queue size\n");
1707                         end = NULL;
1708                         pipe.fs.qsize = getbw(av[0], &pipe.fs.flags_fs, 1024);
1709                         NEXT_ARG;
1710                         break;
1711
1712                 case TOK_BUCKETS:
1713                         NEED1("buckets needs argument\n");
1714                         pipe.fs.rq_size = strtoul(av[0], NULL, 0);
1715                         NEXT_ARG;
1716                         break;
1717
1718                 case TOK_MASK:
1719                         NEED1("mask needs mask specifier\n");
1720                         /*
1721                          * per-flow queue, mask is dst_ip, dst_port,
1722                          * src_ip, src_port, proto measured in bits
1723                          */
1724                         par = NULL;
1725
1726                         pipe.fs.flow_mask.type = ETHERTYPE_IP;
1727                         pipe.fs.flow_mask.u.ip.dst_ip = 0;
1728                         pipe.fs.flow_mask.u.ip.src_ip = 0;
1729                         pipe.fs.flow_mask.u.ip.dst_port = 0;
1730                         pipe.fs.flow_mask.u.ip.src_port = 0;
1731                         pipe.fs.flow_mask.u.ip.proto = 0;
1732                         end = NULL;
1733
1734                         while (ac >= 1) {
1735                                 u_int32_t *p32 = NULL;
1736                                 u_int16_t *p16 = NULL;
1737
1738                                 tok = match_token(dummynet_params, *av);
1739                                 NEXT_ARG;
1740                                 switch(tok) {
1741                                 case TOK_ALL:
1742                                         /*
1743                                          * special case, all bits significant
1744                                          */
1745                                         pipe.fs.flow_mask.u.ip.dst_ip = ~0;
1746                                         pipe.fs.flow_mask.u.ip.src_ip = ~0;
1747                                         pipe.fs.flow_mask.u.ip.dst_port = ~0;
1748                                         pipe.fs.flow_mask.u.ip.src_port = ~0;
1749                                         pipe.fs.flow_mask.u.ip.proto = ~0;
1750                                         pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK;
1751                                         goto end_mask;
1752
1753                                 case TOK_DSTIP:
1754                                         p32 = &pipe.fs.flow_mask.u.ip.dst_ip;
1755                                         break;
1756
1757                                 case TOK_SRCIP:
1758                                         p32 = &pipe.fs.flow_mask.u.ip.src_ip;
1759                                         break;
1760
1761                                 case TOK_DSTPORT:
1762                                         p16 = &pipe.fs.flow_mask.u.ip.dst_port;
1763                                         break;
1764
1765                                 case TOK_SRCPORT:
1766                                         p16 = &pipe.fs.flow_mask.u.ip.src_port;
1767                                         break;
1768
1769                                 case TOK_PROTO:
1770                                         break;
1771
1772                                 default:
1773                                         NEXT_ARG;
1774                                         goto end_mask;
1775                                 }
1776                                 if (ac < 1)
1777                                         errx(EX_USAGE, "mask: value missing");
1778                                 if (*av[0] == '/') {
1779                                         a = strtoul(av[0]+1, &end, 0);
1780                                         a = (a == 32) ? ~0 : (1 << a) - 1;
1781                                 } else
1782                                         a = strtoul(av[0], &end, 0);
1783                                 if (p32 != NULL)
1784                                         *p32 = a;
1785                                 else if (p16 != NULL) {
1786                                         if (a > 65535)
1787                                                 errx(EX_DATAERR,
1788                                                 "mask: must be 16 bit");
1789                                         *p16 = (u_int16_t)a;
1790                                 } else {
1791                                         if (a > 255)
1792                                                 errx(EX_DATAERR,
1793                                                 "mask: must be 8 bit");
1794                                         pipe.fs.flow_mask.u.ip.proto =
1795                                                 (uint8_t)a;
1796                                 }
1797                                 if (a != 0)
1798                                         pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK;
1799                                 NEXT_ARG;
1800                         } /* end while, config masks */
1801
1802 end_mask:
1803                         break;
1804
1805                 case TOK_RED:
1806                 case TOK_GRED:
1807                         NEED1("red/gred needs w_q/min_th/max_th/max_p\n");
1808                         pipe.fs.flags_fs |= DN_IS_RED;
1809                         if (tok == TOK_GRED)
1810                                 pipe.fs.flags_fs |= DN_IS_GENTLE_RED;
1811                         /*
1812                          * the format for parameters is w_q/min_th/max_th/max_p
1813                          */
1814                         if ((end = strsep(&av[0], "/"))) {
1815                                 double w_q = strtod(end, NULL);
1816                                 if (w_q > 1 || w_q <= 0)
1817                                         errx(EX_DATAERR, "0 < w_q <= 1");
1818                                 pipe.fs.w_q = (int) (w_q * (1 << SCALE_RED));
1819                         }
1820                         if ((end = strsep(&av[0], "/"))) {
1821                                 pipe.fs.min_th = strtoul(end, &end, 0);
1822                                 if (*end == 'K' || *end == 'k')
1823                                         pipe.fs.min_th *= 1024;
1824                         }
1825                         if ((end = strsep(&av[0], "/"))) {
1826                                 pipe.fs.max_th = strtoul(end, &end, 0);
1827                                 if (*end == 'K' || *end == 'k')
1828                                         pipe.fs.max_th *= 1024;
1829                         }
1830                         if ((end = strsep(&av[0], "/"))) {
1831                                 double max_p = strtod(end, NULL);
1832                                 if (max_p > 1 || max_p <= 0)
1833                                         errx(EX_DATAERR, "0 < max_p <= 1");
1834                                 pipe.fs.max_p = (int)(max_p * (1 << SCALE_RED));
1835                         }
1836                         NEXT_ARG;
1837                         break;
1838
1839                 case TOK_DROPTAIL:
1840                         pipe.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
1841                         break;
1842
1843                 case TOK_BW:
1844                         NEED1("bw needs bandwidth\n");
1845                         if (do_pipe != 1)
1846                                 errx(EX_DATAERR,
1847                                         "bandwidth only valid for pipes");
1848                         /*
1849                          * set bandwidth value
1850                          */
1851                         pipe.bandwidth = getbw(av[0], NULL, 1000);
1852                         if (pipe.bandwidth < 0)
1853                                 errx(EX_DATAERR, "bandwidth too large");
1854                         NEXT_ARG;
1855                         break;
1856
1857                 case TOK_DELAY:
1858                         if (do_pipe != 1)
1859                                 errx(EX_DATAERR, "delay only valid for pipes");
1860                         NEED1("delay needs argument 0..10000ms\n");
1861                         pipe.delay = strtoul(av[0], NULL, 0);
1862                         NEXT_ARG;
1863                         break;
1864
1865                 case TOK_WEIGHT:
1866                         if (do_pipe == 1)
1867                                 errx(EX_DATAERR,
1868                                         "weight only valid for queues");
1869                         NEED1("weight needs argument 0..100\n");
1870                         pipe.fs.weight = strtoul(av[0], &end, 0);
1871                         NEXT_ARG;
1872                         break;
1873
1874                 case TOK_PIPE:
1875                         if (do_pipe == 1)
1876                                 errx(EX_DATAERR, "pipe only valid for queues");
1877                         NEED1("pipe needs pipe_number\n");
1878                         pipe.fs.parent_nr = strtoul(av[0], &end, 0);
1879                         NEXT_ARG;
1880                         break;
1881
1882                 default:
1883                         errx(EX_DATAERR, "unrecognised option ``%s''", *av);
1884                 }
1885         }
1886         if (do_pipe == 1) {
1887                 if (pipe.pipe_nr == 0)
1888                         errx(EX_DATAERR, "pipe_nr must be > 0");
1889                 if (pipe.delay > 10000)
1890                         errx(EX_DATAERR, "delay must be < 10000");
1891         } else { /* do_pipe == 2, queue */
1892                 if (pipe.fs.parent_nr == 0)
1893                         errx(EX_DATAERR, "pipe must be > 0");
1894                 if (pipe.fs.weight >100)
1895                         errx(EX_DATAERR, "weight must be <= 100");
1896         }
1897         if (pipe.fs.flags_fs & DN_QSIZE_IS_BYTES) {
1898                 if (pipe.fs.qsize > 1024*1024)
1899                         errx(EX_DATAERR, "queue size must be < 1MB");
1900         } else {
1901                 if (pipe.fs.qsize > 100)
1902                         errx(EX_DATAERR, "2 <= queue size <= 100");
1903         }
1904         if (pipe.fs.flags_fs & DN_IS_RED) {
1905                 size_t len;
1906                 int lookup_depth, avg_pkt_size;
1907                 double s, idle, weight, w_q;
1908                 int clock_hz;
1909                 int t;
1910
1911                 if (pipe.fs.min_th >= pipe.fs.max_th)
1912                         errx(EX_DATAERR, "min_th %d must be < than max_th %d",
1913                         pipe.fs.min_th, pipe.fs.max_th);
1914                 if (pipe.fs.max_th == 0)
1915                         errx(EX_DATAERR, "max_th must be > 0");
1916
1917                 len = sizeof(int);
1918                 if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
1919                         &lookup_depth, &len, NULL, 0) == -1)
1920
1921                         errx(1, "sysctlbyname(\"%s\")",
1922                                 "net.inet.ip.dummynet.red_lookup_depth");
1923                 if (lookup_depth == 0)
1924                         errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth"
1925                                 " must be greater than zero");
1926
1927                 len = sizeof(int);
1928                 if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
1929                         &avg_pkt_size, &len, NULL, 0) == -1)
1930
1931                         errx(1, "sysctlbyname(\"%s\")",
1932                                 "net.inet.ip.dummynet.red_avg_pkt_size");
1933                 if (avg_pkt_size == 0)
1934                         errx(EX_DATAERR,
1935                                 "net.inet.ip.dummynet.red_avg_pkt_size must"
1936                                 " be greater than zero");
1937
1938                 len = sizeof(clock_hz);
1939                 if (sysctlbyname("net.inet.ip.dummynet.hz", &clock_hz, &len,
1940                                  NULL, 0) == -1) {
1941                         errx(1, "sysctlbyname(\"%s\")",
1942                                  "net.inet.ip.dummynet.hz");
1943                 }
1944
1945                 /*
1946                  * Ticks needed for sending a medium-sized packet.
1947                  * Unfortunately, when we are configuring a WF2Q+ queue, we
1948                  * do not have bandwidth information, because that is stored
1949                  * in the parent pipe, and also we have multiple queues
1950                  * competing for it. So we set s=0, which is not very
1951                  * correct. But on the other hand, why do we want RED with
1952                  * WF2Q+ ?
1953                  */
1954                 if (pipe.bandwidth == 0) /* this is a WF2Q+ queue */
1955                         s = 0;
1956                 else
1957                         s = clock_hz * avg_pkt_size * 8 / pipe.bandwidth;
1958
1959                 /*
1960                  * max idle time (in ticks) before avg queue size becomes 0.
1961                  * NOTA: (3/w_q) is approx the value x so that
1962                  * (1-w_q)^x < 10^-3.
1963                  */
1964                 w_q = ((double)pipe.fs.w_q) / (1 << SCALE_RED);
1965                 idle = s * 3. / w_q;
1966                 pipe.fs.lookup_step = (int)idle / lookup_depth;
1967                 if (!pipe.fs.lookup_step)
1968                         pipe.fs.lookup_step = 1;
1969                 weight = 1 - w_q;
1970                 for (t = pipe.fs.lookup_step; t > 0; --t)
1971                         weight *= weight;
1972                 pipe.fs.lookup_weight = (int)(weight * (1 << SCALE_RED));
1973         }
1974         i = do_set_x(IP_DUMMYNET_CONFIGURE, &pipe, sizeof pipe);
1975         if (i)
1976                 err(1, "do_set_x(%s)", "IP_DUMMYNET_CONFIGURE");
1977 }
1978
1979 /*
1980  * helper function, updates the pointer to cmd with the length
1981  * of the current command, and also cleans up the first word of
1982  * the new command in case it has been clobbered before.
1983  */
1984 static ipfw_insn*
1985 next_cmd(ipfw_insn *cmd)
1986 {
1987         cmd += F_LEN(cmd);
1988         bzero(cmd, sizeof(*cmd));
1989         return cmd;
1990 }
1991
1992 /*
1993  * Parse arguments and assemble the microinstructions which make up a rule.
1994  * Rules are added into the 'rulebuf' and then copied in the correct order
1995  * into the actual rule.
1996  *
1997  *
1998  */
1999 static void
2000 add(int ac, char *av[])
2001 {
2002         /*
2003          * rules are added into the 'rulebuf' and then copied in
2004          * the correct order into the actual rule.
2005          * Some things that need to go out of order (prob, action etc.)
2006          * go into actbuf[].
2007          */
2008         static uint32_t rulebuf[IPFW_RULE_SIZE_MAX];
2009         static uint32_t actbuf[IPFW_RULE_SIZE_MAX];
2010         static uint32_t othbuf[IPFW_RULE_SIZE_MAX];
2011         static uint32_t cmdbuf[IPFW_RULE_SIZE_MAX];
2012
2013         ipfw_insn *src, *dst, *cmd, *action, *other;
2014         ipfw_insn *prev;
2015         char *prev_av;
2016         ipfw_insn *the_comment = NULL;
2017         struct ipfw_ioc_rule *rule;
2018         struct ipfw_keyword *key;
2019         struct ipfw_mapping *map;
2020         parser_func fn;
2021         int i, j;
2022
2023         bzero(actbuf, sizeof(actbuf));          /* actions go here */
2024         bzero(othbuf, sizeof(actbuf));          /* others */
2025         bzero(cmdbuf, sizeof(cmdbuf));          /* filters */
2026         bzero(rulebuf, sizeof(rulebuf));
2027
2028         rule = (struct ipfw_ioc_rule *)rulebuf;
2029         cmd = (ipfw_insn *)cmdbuf;
2030         action = (ipfw_insn *)actbuf;
2031         other = (ipfw_insn *)othbuf;
2032
2033         NEED2("need more parameters");
2034         NEXT_ARG;
2035
2036         /* [rule N]     -- Rule number optional */
2037         if (ac && isdigit(**av)) {
2038                 rule->rulenum = atoi(*av);
2039                 NEXT_ARG;
2040         }
2041
2042         /* [set N]      -- set number (0..30), optional */
2043         if (ac > 1 && !strncmp(*av, "set", strlen(*av))) {
2044                 int set = strtoul(av[1], NULL, 10);
2045                 if (set < 0 || set > 30)
2046                         errx(EX_DATAERR, "illegal set %s", av[1]);
2047                 rule->set = set;
2048                 av += 2; ac -= 2;
2049         }
2050
2051         /*
2052          * parse before
2053          */
2054         for (;;) {
2055                 for (i = 0, key = keywords; i < KEYWORD_SIZE; i++, key++) {
2056                         if (key->type == BEFORE &&
2057                                 strcmp(key->word, *av) == 0) {
2058                                 for (j = 0, map = mappings;
2059                                         j < MAPPING_SIZE; j++, map++) {
2060                                         if (map->type == IN_USE &&
2061                                                 map->module == key->module &&
2062                                                 map->opcode == key->opcode ) {
2063                                                 fn = map->parser;
2064                                                 (*fn)(&other, &ac, &av);
2065                                                 break;
2066                                         }
2067                                 }
2068                                 break;
2069                         }
2070                 }
2071                 if (i >= KEYWORD_SIZE) {
2072                         break;
2073                 } else if (F_LEN(other) > 0) {
2074                         if (other->module == MODULE_BASIC_ID &&
2075                                 other->opcode == O_BASIC_CHECK_STATE) {
2076                                 other = next_cmd(other);
2077                                 goto done;
2078                         }
2079                         other = next_cmd(other);
2080                 }
2081         }
2082
2083         /*
2084          * parse actions
2085          *
2086          * only accept 1 action
2087          */
2088         NEED1("missing action");
2089         for (i = 0, key = keywords; i < KEYWORD_SIZE; i++, key++) {
2090                 if (ac > 0 && key->type == ACTION &&
2091                         strcmp(key->word, *av) == 0) {
2092                         for (j = 0, map = mappings;
2093                                         j < MAPPING_SIZE; j++, map++) {
2094                                 if (map->type == IN_USE &&
2095                                         map->module == key->module &&
2096                                         map->opcode == key->opcode) {
2097                                         fn = map->parser;
2098                                         (*fn)(&action, &ac, &av);
2099                                         break;
2100                                 }
2101                         }
2102                         break;
2103                 }
2104         }
2105         if (F_LEN(action) > 0)
2106                 action = next_cmd(action);
2107
2108         /*
2109          * parse protocol
2110          */
2111         if (strcmp(*av, "proto") == 0){
2112                 NEXT_ARG;
2113         }
2114
2115         NEED1("missing protocol");
2116         for (i = 0, key = keywords; i < KEYWORD_SIZE; i++, key++) {
2117                 if (key->type == PROTO &&
2118                         strcmp(key->word, "proto") == 0) {
2119                         for (j = 0, map = mappings;
2120                                         j < MAPPING_SIZE; j++, map++) {
2121                                 if (map->type == IN_USE &&
2122                                         map->module == key->module &&
2123                                         map->opcode == key->opcode ) {
2124                                         fn = map->parser;
2125                                         (*fn)(&cmd, &ac, &av);
2126                                         break;
2127                                 }
2128                         }
2129                         break;
2130                 }
2131         }
2132         if (F_LEN(cmd) > 0)
2133                 cmd = next_cmd(cmd);
2134
2135         /*
2136          * other filters
2137          */
2138         while (ac > 0) {
2139                 char *s, *cur;          /* current filter */
2140                 ipfw_insn_u32 *cmd32;   /* alias for cmd */
2141
2142                 s = *av;
2143                 cmd32 = (ipfw_insn_u32 *)cmd;
2144                 if (strcmp(*av, "or") == 0) {
2145                         if (prev == NULL)
2146                                 errx(EX_USAGE, "'or' should"
2147                                                 "between two filters\n");
2148                         prev->len |= F_OR;
2149                         cmd->len = F_OR;
2150                         *av = prev_av;
2151                 }
2152                 if (strcmp(*av, "not") == 0) {
2153                         if (cmd->len & F_NOT)
2154                                 errx(EX_USAGE, "double \"not\" not allowed\n");
2155                         cmd->len = F_NOT;
2156                         NEXT_ARG;
2157                         continue;
2158                 }
2159                 cur = *av;
2160                 for (i = 0, key = keywords; i < KEYWORD_SIZE; i++, key++) {
2161                         if ((key->type == FILTER ||
2162                                 key->type == AFTER ||
2163                                 key->type == FROM ||
2164                                 key->type == TO) &&
2165                                 strcmp(key->word, cur) == 0) {
2166                                 for (j = 0, map = mappings;
2167                                         j< MAPPING_SIZE; j++, map++) {
2168                                         if (map->type == IN_USE &&
2169                                                 map->module == key->module &&
2170                                                 map->opcode == key->opcode ) {
2171                                                 fn = map->parser;
2172                                                 (*fn)(&cmd, &ac, &av);
2173                                                 break;
2174                                         }
2175                                 }
2176                                 break;
2177                         } else if (i == KEYWORD_SIZE - 1) {
2178                                 errx(EX_USAGE, "bad command `%s'", cur);
2179                         }
2180                 }
2181                 if (i >= KEYWORD_SIZE) {
2182                         break;
2183                 } else if (F_LEN(cmd) > 0) {
2184                         prev = cmd;
2185                         prev_av = cur;
2186                         cmd = next_cmd(cmd);
2187                 }
2188         }
2189
2190 done:
2191         if (ac>0)
2192                 errx(EX_USAGE, "bad command `%s'", *av);
2193
2194         /*
2195          * Now copy stuff into the rule.
2196          * [filters][others][action][comment]
2197          */
2198         dst = (ipfw_insn *)rule->cmd;
2199         /*
2200          * copy all filters, except comment
2201          */
2202         src = (ipfw_insn *)cmdbuf;
2203         for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
2204                 /* pick comment out */
2205                 i = F_LEN(src);
2206                 if (src->module == MODULE_BASIC_ID &&
2207                                 src->opcode == O_BASIC_COMMENT) {
2208                         the_comment=src;
2209                 } else {
2210                         bcopy(src, dst, i * sizeof(u_int32_t));
2211                         dst = (ipfw_insn *)((uint32_t *)dst + i);
2212                 }
2213         }
2214
2215         /*
2216          * start action section, it begin with others
2217          */
2218         rule->act_ofs = (uint32_t *)dst - (uint32_t *)(rule->cmd);
2219
2220         /*
2221          * copy all other others
2222          */
2223         for (src = (ipfw_insn *)othbuf; src != other; src += i) {
2224                 i = F_LEN(src);
2225                 bcopy(src, dst, i * sizeof(u_int32_t));
2226                 dst = (ipfw_insn *)((uint32_t *)dst + i);
2227         }
2228
2229         /* copy the action to the end of rule */
2230         src = (ipfw_insn *)actbuf;
2231         i = F_LEN(src);
2232         bcopy(src, dst, i * sizeof(u_int32_t));
2233         dst = (ipfw_insn *)((uint32_t *)dst + i);
2234
2235         /*
2236          * comment place behind the action
2237          */
2238         if (the_comment != NULL) {
2239                 i = F_LEN(the_comment);
2240                 bcopy(the_comment, dst, i * sizeof(u_int32_t));
2241                 dst = (ipfw_insn *)((uint32_t *)dst + i);
2242         }
2243
2244         rule->cmd_len = (u_int32_t *)dst - (u_int32_t *)(rule->cmd);
2245         i = (void *)dst - (void *)rule;
2246         if (do_set_x(IP_FW_ADD, (void *)rule, i) == -1) {
2247                 err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
2248         }
2249         if (!do_quiet)
2250                 show_rules(rule, 10, 10);
2251 }
2252
2253 static void
2254 zero(int ac, char *av[])
2255 {
2256         int rulenum;
2257         int failed = EX_OK;
2258
2259         NEXT_ARG;
2260
2261         if (!ac) {
2262                 /* clear all entries */
2263                 if (do_set_x(IP_FW_ZERO, NULL, 0) < 0)
2264                         err(EX_UNAVAILABLE, "do_set_x(IP_FW_ZERO)");
2265                 if (!do_quiet)
2266                         printf("Accounting cleared.\n");
2267                 return;
2268         }
2269
2270         while (ac) {
2271                 /* Rule number */
2272                 if (isdigit(**av)) {
2273                         rulenum = atoi(*av);
2274                         NEXT_ARG;
2275                         if (do_set_x(IP_FW_ZERO, &rulenum, sizeof rulenum)) {
2276                                 warn("rule %u: do_set_x(IP_FW_ZERO)", rulenum);
2277                                 failed = EX_UNAVAILABLE;
2278                         } else if (!do_quiet)
2279                                 printf("Entry %d cleared\n", rulenum);
2280                 } else {
2281                         errx(EX_USAGE, "invalid rule number ``%s''", *av);
2282                 }
2283         }
2284         if (failed != EX_OK)
2285                 exit(failed);
2286 }
2287
2288 static void
2289 resetlog(int ac, char *av[])
2290 {
2291         int rulenum;
2292         int failed = EX_OK;
2293
2294         NEXT_ARG;
2295
2296         if (!ac) {
2297                 /* clear all entries */
2298                 if (setsockopt(ipfw_socket, IPPROTO_IP,
2299                                         IP_FW_RESETLOG, NULL, 0) < 0)
2300                         err(EX_UNAVAILABLE, "setsockopt(IP_FW_RESETLOG)");
2301                 if (!do_quiet)
2302                         printf("Logging counts reset.\n");
2303
2304                 return;
2305         }
2306
2307         while (ac) {
2308                 /* Rule number */
2309                 if (isdigit(**av)) {
2310                         rulenum = atoi(*av);
2311                         NEXT_ARG;
2312                         if (setsockopt(ipfw_socket, IPPROTO_IP,
2313                                 IP_FW_RESETLOG, &rulenum, sizeof rulenum)) {
2314                                 warn("rule %u: setsockopt(IP_FW_RESETLOG)",
2315                                                 rulenum);
2316                                 failed = EX_UNAVAILABLE;
2317                         } else if (!do_quiet)
2318                                 printf("Entry %d logging count reset\n",
2319                                                 rulenum);
2320                 } else {
2321                         errx(EX_DATAERR, "invalid rule number ``%s''", *av);
2322                 }
2323         }
2324         if (failed != EX_OK)
2325                 exit(failed);
2326 }
2327
2328 static void
2329 flush(void)
2330 {
2331         int cmd = IP_FW_FLUSH;
2332         if (do_pipe) {
2333                 cmd = IP_DUMMYNET_FLUSH;
2334         }
2335         if (!do_force) {
2336                 int c;
2337
2338                 printf("Are you sure? [yn] ");
2339                 fflush(stdout);
2340                 do {
2341                         c = toupper(getc(stdin));
2342                         while (c != '\n' && getc(stdin) != '\n')
2343                                 if (feof(stdin))
2344                                         return; /* and do not flush */
2345                 } while (c != 'Y' && c != 'N');
2346                 if (c == 'N')   /* user said no */
2347                         return;
2348         }
2349         if (do_set_x(cmd, NULL, 0) < 0 ) {
2350                 if (do_pipe)
2351                         errx(EX_USAGE, "pipe/queue in use");
2352                 else
2353                         errx(EX_USAGE, "do_set_x(IP_FW_FLUSH) failed");
2354         }
2355         if (!do_quiet) {
2356                 printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules");
2357         }
2358 }
2359
2360 /*
2361  * do_set_x - extended version og do_set
2362  * insert a x_header in the beginning of the rule buf
2363  * and call setsockopt() with IP_FW_X.
2364  */
2365 int
2366 do_set_x(int optname, void *rule, int optlen)
2367 {
2368         int len, *newbuf;
2369
2370         ip_fw_x_header *x_header;
2371         if (ipfw_socket < 0)
2372                 err(EX_UNAVAILABLE, "socket not avaialble");
2373         len = optlen + sizeof(ip_fw_x_header);
2374         newbuf = malloc(len);
2375         if (newbuf == NULL)
2376                 err(EX_OSERR, "malloc newbuf in do_set_x");
2377         bzero(newbuf, len);
2378         x_header = (ip_fw_x_header *)newbuf;
2379         x_header->opcode = optname;
2380         /* copy the rule into the newbuf, just after the x_header*/
2381         bcopy(rule, ++x_header, optlen);
2382         return setsockopt(ipfw_socket, IPPROTO_IP, IP_FW_X, newbuf, len);
2383 }
2384
2385 /*
2386  * same as do_set_x
2387  */
2388 int
2389 do_get_x(int optname, void *rule, int *optlen)
2390 {
2391         int len, *newbuf, retval;
2392
2393         ip_fw_x_header *x_header;
2394         if (ipfw_socket < 0)
2395                 err(EX_UNAVAILABLE, "socket not avaialble");
2396         len = *optlen + sizeof(ip_fw_x_header);
2397         newbuf = malloc(len);
2398         if (newbuf == NULL)
2399                 err(EX_OSERR, "malloc newbuf in do_get_x");
2400         bzero(newbuf, len);
2401         x_header = (ip_fw_x_header *)newbuf;
2402         x_header->opcode = optname;
2403         /* copy the rule into the newbuf, just after the x_header*/
2404         bcopy(rule, ++x_header, *optlen);
2405         retval = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW_X, newbuf, &len);
2406         bcopy(newbuf, rule, len);
2407         *optlen=len;
2408         return retval;
2409 }
2410
2411 static int
2412 ipfw_main(int ac, char **av)
2413 {
2414         int ch;
2415
2416         if (ac == 1)
2417                 help();
2418
2419         /* Set the force flag for non-interactive processes */
2420         do_force = !isatty(STDIN_FILENO);
2421
2422         optind = optreset = 1;
2423         while ((ch = getopt(ac, av, "hs:acdDefNStTv")) != -1)
2424                 switch (ch) {
2425                 case 'h': /* help */
2426                         help();
2427                         break;  /* NOTREACHED */
2428
2429                 case 's': /* sort */
2430                         do_sort = atoi(optarg);
2431                         break;
2432                 case 'a':
2433                         do_acct = 1;
2434                         break;
2435                 case 'c':
2436                         do_compact = 1;
2437                         break;
2438                 case 'd':
2439                         do_dynamic = 1;
2440                         break;
2441                 case 'D':
2442                         do_dynamic = 2;
2443                         break;
2444                 case 'e':
2445                         do_expired = 1;
2446                         break;
2447                 case 'f':
2448                         do_force = 1;
2449                         break;
2450                 case 'N':
2451                         do_resolv = 1;
2452                         break;
2453                 case 'S':
2454                         show_sets = 1;
2455                         break;
2456                 case 't':
2457                         do_time = 1;
2458                         break;
2459                 case 'T':
2460                         do_time = 2;
2461                         break;
2462                 case 'v':
2463                         do_quiet = 0;
2464                         verbose++;
2465                         break;
2466                 default:
2467                         help();
2468                 }
2469
2470         ac -= optind;
2471         av += optind;
2472         NEED1("bad arguments, for usage summary ``ipfw''");
2473
2474         /*
2475          * optional: pipe or queue or nat
2476          */
2477         do_nat = 0;
2478         do_pipe = 0;
2479         if (!strncmp(*av, "nat", strlen(*av)))
2480                 do_nat = 1;
2481         else if (!strncmp(*av, "pipe", strlen(*av))) {
2482                 do_pipe = 1;
2483         } else if (!strncmp(*av, "queue", strlen(*av))) {
2484                 do_pipe = 2;
2485         }
2486         NEED1("missing command");
2487
2488         /*
2489          * for pipes and queues and nat we normally say 'pipe NN config'
2490          * but the code is easier to parse as 'pipe config NN'
2491          * so we swap the two arguments.
2492          */
2493         if ((do_pipe || do_nat) && ac > 2 && isdigit(*(av[1]))) {
2494                 char *p = av[1];
2495                 av[1] = av[2];
2496                 av[2] = p;
2497         }
2498
2499         if (!strncmp(*av, "add", strlen(*av))) {
2500                 load_modules();
2501                 add(ac, av);
2502         } else if (!strncmp(*av, "delete", strlen(*av))) {
2503                 delete_rules(ac, av);
2504         } else if (!strncmp(*av, "flush", strlen(*av))) {
2505                 flush();
2506         } else if (!strncmp(*av, "list", strlen(*av))) {
2507                 load_modules();
2508                 list(ac, av);
2509         } else if (!strncmp(*av, "show", strlen(*av))) {
2510                 do_acct++;
2511                 load_modules();
2512                 list(ac, av);
2513         } else if (!strncmp(*av, "zero", strlen(*av))) {
2514                 zero(ac, av);
2515         } else if (!strncmp(*av, "set", strlen(*av))) {
2516                 sets_handler(ac, av);
2517         } else if (!strncmp(*av, "module", strlen(*av))) {
2518                 NEXT_ARG;
2519                 if (!strncmp(*av, "show", strlen(*av)) ||
2520                         !strncmp(*av, "show", strlen(*av))) {
2521                         list_modules(ac, av);
2522                 } else {
2523                         errx(EX_USAGE, "bad ipfw module command `%s'", *av);
2524                 }
2525         } else if (!strncmp(*av, "resetlog", strlen(*av))) {
2526                 resetlog(ac, av);
2527         } else if (!strncmp(*av, "log", strlen(*av))) {
2528                 NEXT_ARG;
2529                 if (!strncmp(*av, "reset", strlen(*av))) {
2530                         resetlog(ac, av);
2531                 } else if (!strncmp(*av, "off", strlen(*av))) {
2532
2533                 } else if (!strncmp(*av, "on", strlen(*av))) {
2534
2535                 } else {
2536                         errx(EX_USAGE, "bad command `%s'", *av);
2537                 }
2538         } else if (!strncmp(*av, "nat", strlen(*av))) {
2539                 NEXT_ARG;
2540                 nat_main(ac, av);
2541         } else if (!strncmp(*av, "pipe", strlen(*av)) ||
2542                 !strncmp(*av, "queue", strlen(*av))) {
2543                 NEXT_ARG;
2544                 if (!strncmp(*av, "config", strlen(*av))) {
2545                         config_dummynet(ac, av);
2546                 } else if (!strncmp(*av, "flush", strlen(*av))) {
2547                         flush();
2548                 } else if (!strncmp(*av, "show", strlen(*av))) {
2549                         show_dummynet(ac, av);
2550                 } else {
2551                         errx(EX_USAGE, "bad ipfw pipe command `%s'", *av);
2552                 }
2553         } else if (!strncmp(*av, "state", strlen(*av))) {
2554                 NEXT_ARG;
2555                 if (!strncmp(*av, "add", strlen(*av))) {
2556                         add_state(ac, av);
2557                 } else if (!strncmp(*av, "delete", strlen(*av))) {
2558                         delete_state(ac, av);
2559                 } else if (!strncmp(*av, "flush", strlen(*av))) {
2560                         flush_state(ac, av);
2561                 } else if (!strncmp(*av, "list", strlen(*av))) {
2562                         do_dynamic = 2;
2563                         list(ac, av);
2564                 } else if (!strncmp(*av, "show", strlen(*av))) {
2565                         do_acct = 1;
2566                         do_dynamic =2;
2567                         list(ac, av);
2568                 } else {
2569                         errx(EX_USAGE, "bad ipfw state command `%s'", *av);
2570                 }
2571         } else if (!strncmp(*av, "table", strlen(*av))) {
2572                 if (ac > 2 && isdigit(*(av[1]))) {
2573                         char *p = av[1];
2574                         av[1] = av[2];
2575                         av[2] = p;
2576                 }
2577                 NEXT_ARG;
2578                 if (!strncmp(*av, "append", strlen(*av))) {
2579                         table_append(ac, av);
2580                 } else if (!strncmp(*av, "remove", strlen(*av))) {
2581                         table_remove(ac, av);
2582                 } else if (!strncmp(*av, "flush", strlen(*av))) {
2583                         table_flush(ac, av);
2584                 } else if (!strncmp(*av, "list", strlen(*av))) {
2585                         table_list(ac, av);
2586                 } else if (!strncmp(*av, "show", strlen(*av))) {
2587                         table_show(ac, av);
2588                 } else if (!strncmp(*av, "type", strlen(*av))) {
2589                         table_create(ac, av);
2590                 } else if (!strncmp(*av, "delete", strlen(*av))) {
2591                         table_delete(ac, av);
2592                 } else if (!strncmp(*av, "test", strlen(*av))) {
2593                         table_test(ac,av);
2594                 } else if (!strncmp(*av, "name", strlen(*av))) {
2595                         table_rename(ac, av);
2596                 } else {
2597                         errx(EX_USAGE, "bad ipfw table command `%s'", *av);
2598                 }
2599         } else if (!strncmp(*av, "sync", strlen(*av))) {
2600                 NEXT_ARG;
2601                 if (!strncmp(*av, "edge", strlen(*av))) {
2602                         sync_config_edge(ac, av);
2603                 } else if (!strncmp(*av, "centre", strlen(*av))) {
2604                         sync_config_centre(ac, av);
2605                 } else if (!strncmp(*av, "show", strlen(*av))) {
2606                         NEXT_ARG;
2607                         if (!strncmp(*av, "config", strlen(*av))) {
2608                                 sync_show_config(ac, av);
2609                         } else if (!strncmp(*av, "status", strlen(*av))) {
2610                                 sync_show_status(ac, av);
2611                         } else {
2612                                 errx(EX_USAGE, "bad show command `%s'", *av);
2613                         }
2614                 } else if (!strncmp(*av, "start", strlen(*av))) {
2615                         NEXT_ARG;
2616                         if (!strncmp(*av, "edge", strlen(*av))) {
2617                                 sync_edge_start(ac, av);
2618                         } else if (!strncmp(*av, "centre", strlen(*av))) {
2619                                 sync_centre_start(ac, av);
2620                         }
2621                 } else if (!strncmp(*av, "stop", strlen(*av))) {
2622                         NEXT_ARG;
2623                         if (!strncmp(*av, "edge", strlen(*av))) {
2624                                 sync_edge_stop(ac, av);
2625                         } else if (!strncmp(*av, "centre", strlen(*av))) {
2626                                 sync_centre_stop(ac, av);
2627                         }
2628                 } else if (!strncmp(*av, "clear", strlen(*av))) {
2629                         NEXT_ARG;
2630                         if (!strncmp(*av, "edge", strlen(*av))) {
2631                                 sync_edge_clear(ac, av);
2632                         } else if (!strncmp(*av, "centre", strlen(*av))) {
2633                                 sync_centre_clear(ac, av);
2634                         }
2635                 } else if (!strncmp(*av, "test", strlen(*av))) {
2636                         NEXT_ARG;
2637                         if (!strncmp(*av, "edge", strlen(*av))) {
2638                                 sync_edge_test(ac, av);
2639                         } else if (!strncmp(*av, "centre", strlen(*av))) {
2640                                 sync_centre_test(ac, av);
2641                         }
2642                 } else {
2643                         errx(EX_USAGE, "bad ipfw sync command `%s'", *av);
2644                 }
2645         } else {
2646                 errx(EX_USAGE, "bad ipfw command `%s'", *av);
2647         }
2648         return 0;
2649 }
2650
2651 static void
2652 ipfw_readfile(int ac, char *av[])
2653 {
2654         char    buf[BUFSIZ];
2655         char    *a, *p, *args[MAX_ARGS], *cmd = NULL;
2656         char    linename[10];
2657         int     i=0, lineno=0, qflag=0, pflag=0, status;
2658         FILE    *f = NULL;
2659         pid_t   preproc = 0;
2660         int     c;
2661
2662         while ((c = getopt(ac, av, "D:U:p:q")) != -1) {
2663                 switch (c) {
2664                 case 'D':
2665                         if (!pflag)
2666                                 errx(EX_USAGE, "-D requires -p");
2667                         if (i > MAX_ARGS - 2)
2668                                 errx(EX_USAGE, "too many -D or -U options");
2669                         args[i++] = "-D";
2670                         args[i++] = optarg;
2671                         break;
2672
2673                 case 'U':
2674                         if (!pflag)
2675                                 errx(EX_USAGE, "-U requires -p");
2676                         if (i > MAX_ARGS - 2)
2677                                 errx(EX_USAGE, "too many -D or -U options");
2678                         args[i++] = "-U";
2679                         args[i++] = optarg;
2680                         break;
2681
2682                 case 'p':
2683                         pflag = 1;
2684                         cmd = optarg;
2685                         args[0] = cmd;
2686                         i = 1;
2687                         break;
2688
2689                 case 'q':
2690                         qflag = 1;
2691                         break;
2692
2693                 default:
2694                         errx(EX_USAGE, "bad arguments, for usage"
2695                             " summary ``ipfw''");
2696                 }
2697         }
2698
2699         av += optind;
2700         ac -= optind;
2701         if (ac != 1)
2702                 errx(EX_USAGE, "extraneous filename arguments");
2703
2704         if ((f = fopen(av[0], "r")) == NULL)
2705                 err(EX_UNAVAILABLE, "fopen: %s", av[0]);
2706
2707         if (pflag) {
2708                 /* pipe through preprocessor (cpp or m4) */
2709                 int pipedes[2];
2710
2711                 args[i] = NULL;
2712
2713                 if (pipe(pipedes) == -1)
2714                         err(EX_OSERR, "cannot create pipe");
2715
2716                 switch ((preproc = fork())) {
2717                 case -1:
2718                         err(EX_OSERR, "cannot fork");
2719
2720                 case 0:
2721                         /* child */
2722                         if (dup2(fileno(f), 0) == -1 ||
2723                             dup2(pipedes[1], 1) == -1) {
2724                                 err(EX_OSERR, "dup2()");
2725                         }
2726                         fclose(f);
2727                         close(pipedes[1]);
2728                         close(pipedes[0]);
2729                         execvp(cmd, args);
2730                         err(EX_OSERR, "execvp(%s) failed", cmd);
2731
2732                 default:
2733                         /* parent */
2734                         fclose(f);
2735                         close(pipedes[1]);
2736                         if ((f = fdopen(pipedes[0], "r")) == NULL) {
2737                                 int savederrno = errno;
2738
2739                                 kill(preproc, SIGTERM);
2740                                 errno = savederrno;
2741                                 err(EX_OSERR, "fdopen()");
2742                         }
2743                 }
2744         }
2745
2746         while (fgets(buf, BUFSIZ, f)) {
2747                 lineno++;
2748                 sprintf(linename, "Line %d", lineno);
2749                 args[0] = linename;
2750
2751                 if (*buf == '#')
2752                         continue;
2753                 if ((p = strchr(buf, '#')) != NULL)
2754                         *p = '\0';
2755                 i = 1;
2756                 if (qflag)
2757                         args[i++] = "-q";
2758                 for (a = strtok(buf, WHITESP); a && i < MAX_ARGS;
2759                         a = strtok(NULL, WHITESP), i++) {
2760                         args[i] = a;
2761                 }
2762
2763                 if (i == (qflag? 2: 1))
2764                         continue;
2765                 if (i == MAX_ARGS)
2766                         errx(EX_USAGE, "%s: too many arguments", linename);
2767
2768                 args[i] = NULL;
2769                 ipfw_main(i, args);
2770         }
2771         fclose(f);
2772         if (pflag) {
2773                 if (waitpid(preproc, &status, 0) == -1)
2774                         errx(EX_OSERR, "waitpid()");
2775                 if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
2776                         errx(EX_UNAVAILABLE, "preprocessor exited with status %d",
2777                                 WEXITSTATUS(status));
2778                 else if (WIFSIGNALED(status))
2779                         errx(EX_UNAVAILABLE, "preprocessor exited with signal %d",
2780                                 WTERMSIG(status));
2781         }
2782 }
2783
2784 int
2785 main(int ac, char *av[])
2786 {
2787         ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
2788         if (ipfw_socket < 0)
2789                 err(EX_UNAVAILABLE, "socket");
2790
2791         memset(keywords, 0, sizeof(struct ipfw_keyword) * KEYWORD_SIZE);
2792         memset(mappings, 0, sizeof(struct ipfw_mapping) * MAPPING_SIZE);
2793
2794         prepare_default_funcs();
2795
2796         if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0)
2797                 ipfw_readfile(ac, av);
2798         else
2799                 ipfw_main(ac, av);
2800         return EX_OK;
2801 }