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