072e5719474223515198f2ac88eb905bf9cc7f0e
[dragonfly.git] / sbin / ipfw3 / ipfw3.c
1 /*
2  * Copyright (c) 2014 - 2018 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_basic/ip_fw3_table.h>
65 #include <net/ipfw3_basic/ip_fw3_state.h>
66 #include <net/ipfw3_basic/ip_fw3_sync.h>
67 #include <net/ipfw3_basic/ip_fw3_basic.h>
68 #include <net/ipfw3_nat/ip_fw3_nat.h>
69 #include <net/dummynet3/ip_dummynet3.h>
70
71 #include "ipfw3.h"
72 #include "ipfw3basic.h"
73 #include "ipfw3log.h"
74 #include "ipfw3set.h"
75 #include "ipfw3table.h"
76 #include "ipfw3dummynet.h"
77 #include "ipfw3state.h"
78 #include "ipfw3sync.h"
79 #include "ipfw3nat.h"
80
81 #define MAX_ARGS        32
82 #define WHITESP         " \t\f\v\n\r"
83 #define IPFW3_LIB_PATH  "/usr/lib/libipfw3%s.so"
84
85 int             fw3_socket = -1;        /* main RAW socket */
86 int             do_acct,                /* Show packet/byte count */
87                 do_time,                /* Show time stamps */
88                 do_quiet = 1,           /* Be quiet , default is quiet*/
89                 do_force,               /* Don't ask for confirmation */
90                 do_pipe,                /* this cmd refers to a pipe */
91                 do_nat,                 /* Nat configuration. */
92                 do_sort,                /* field to sort results (0 = no) */
93                 do_expired,             /* display expired dynamic rules */
94                 do_compact,             /* show rules in compact mode */
95                 show_sets,              /* display rule sets */
96                 verbose;
97
98 struct ipfw3_keyword keywords[KEYWORD_SIZE];
99 struct ipfw3_mapping mappings[MAPPING_SIZE];
100
101 int
102 match_token(struct char_int_map *table, char *string)
103 {
104         while (table->key) {
105                 if (strcmp(table->key, string) == 0) {
106                         return table->val;
107                 }
108                 table++;
109         }
110         return 0;
111 }
112
113 void
114 module_get(char *modules_str, int len)
115 {
116         if (do_get_x(IP_FW_MODULE, modules_str, &len) < 0)
117                 errx(EX_USAGE, "ipfw3 not loaded.");
118 }
119
120 void
121 module_list(int ac, char *av[])
122 {
123         void *module_str = NULL;
124         int len = 1024;
125         if ((module_str = realloc(module_str, len)) == NULL)
126                 err(EX_OSERR, "realloc");
127
128         module_get(module_str, len);
129         printf("%s\n", (char *)module_str);
130 }
131
132 void
133 module_load(void)
134 {
135         const char *error;
136         init_module mod_init_func;
137         void *module_lib;
138         char module_lib_file[50];
139         void *module_str = NULL;
140         int len = 1024;
141
142         if ((module_str = realloc(module_str, len)) == NULL)
143                 err(EX_OSERR, "realloc");
144
145         module_get(module_str, len);
146
147         const char s[2] = ",";
148         char *token;
149         token = strtok(module_str, s);
150         while (token != NULL) {
151                 sprintf(module_lib_file, IPFW3_LIB_PATH, token);
152                 token = strtok(NULL, s);
153                 module_lib = dlopen(module_lib_file, RTLD_LAZY);
154                 if (!module_lib) {
155                         fprintf(stderr, "Couldn't open %s: %s\n",
156                                 module_lib_file, dlerror());
157                         exit(EX_SOFTWARE);
158                 }
159                 mod_init_func = dlsym(module_lib, "load_module");
160                 if ((error = dlerror()))
161                 {
162                         fprintf(stderr, "Couldn't find init function: %s\n", error);
163                         exit(EX_SOFTWARE);
164                 }
165                 (*mod_init_func)((register_func)register_ipfw_func,
166                                 (register_keyword)register_ipfw_keyword);
167         }
168 }
169
170 void
171 register_ipfw_keyword(int module, int opcode, char *word, int type)
172 {
173         struct ipfw3_keyword *tmp;
174
175         tmp=keywords;
176         for (;;) {
177                 if (tmp->type == NONE) {
178                         strcpy(tmp->word, word);
179                         tmp->module = module;
180                         tmp->opcode = opcode;
181                         tmp->type = type;
182                         break;
183                 } else {
184                         if (strcmp(tmp->word, word) == 0)
185                                 errx(EX_USAGE, "keyword `%s' exists", word);
186                         else
187                                 tmp++;
188                 }
189         }
190 }
191
192 void
193 register_ipfw_func(int module, int opcode, parser_func parser, shower_func shower)
194 {
195         struct ipfw3_mapping *tmp;
196
197         tmp = mappings;
198         while (1) {
199                 if (tmp->type == NONE) {
200                         tmp->module = module;
201                         tmp->opcode = opcode;
202                         tmp->parser = parser;
203                         tmp->shower = shower;
204                         tmp->type = IN_USE;
205                         break;
206                 } else {
207                         if (tmp->opcode == opcode && tmp->module == module) {
208                                 errx(EX_USAGE, "func `%d' of module `%d' exists",
209                                         opcode, module);
210                                 break;
211                         } else {
212                                 tmp++;
213                         }
214                 }
215         }
216 }
217
218 /*
219  * this func need to check whether 'or' need to be printed,
220  * when the filter is the first filter with 'or' when dont print
221  * when not first and same as previous, then print or and no filter name
222  * when not first but different from previous, print name without 'or'
223  * show_or = 1: show or and ignore filter name
224  * show_or = 0: show filter name ignore or
225  */
226 void prev_show_chk(ipfw_insn *cmd, uint8_t *prev_module, uint8_t *prev_opcode,
227                 int *show_or)
228 {
229         if (cmd->len & F_OR) {
230                 if (*prev_module == 0 && *prev_opcode == 0) {
231                         /* first cmd with 'or' flag */
232                         *show_or = 0;
233                         *prev_module = cmd->module;
234                         *prev_opcode = cmd->opcode;
235                 } else if (cmd->module == *prev_module &&
236                                 cmd->opcode == *prev_opcode) {
237                         /* cmd same as previous, same module and opcode */
238                         *show_or = 1;
239                 } else {
240                         /* cmd different from prev*/
241                         *show_or = 0;
242                         *prev_module = cmd->module;
243                         *prev_opcode = cmd->opcode;
244
245                 }
246         } else {
247                 *show_or = 0;
248                 *prev_module = 0;
249                 *prev_opcode = 0;
250         }
251 }
252
253 /*
254  * word can be: proto from to other
255  * proto show proto
256  * from show from
257  * to show to
258  * other show all other filters
259  */
260 int show_filter(ipfw_insn *cmd, char *word, int type)
261 {
262         struct ipfw3_keyword *k;
263         struct ipfw3_mapping *m;
264         shower_func fn;
265         int i, j, show_or;
266         uint8_t prev_module, prev_opcode;
267
268         k = keywords;
269         m = mappings;
270         for (i = 1; i < KEYWORD_SIZE; i++, k++) {
271                 if (k->type == type) {
272                         if (k->module == cmd->module &&
273                                         k->opcode == cmd->opcode) {
274                                 for (j = 1; j < MAPPING_SIZE; j++, m++) {
275                                         if (m->type == IN_USE &&
276                                                 k->module == m->module &&
277                                                 k->opcode == m->opcode) {
278                                                 prev_show_chk(cmd, &prev_module,
279                                                         &prev_opcode, &show_or);
280                                                 if (cmd->len & F_NOT)
281                                                         printf(" not");
282
283                                                 fn = m->shower;
284                                                 (*fn)(cmd, show_or);
285                                                 return 1;
286                                         }
287                                 }
288                         }
289                 }
290         }
291         return 0;
292 }
293
294 void
295 help(void)
296 {
297         fprintf(stderr, "usage: ipfw3 [options]\n"
298                         "       ipfw3 add [rulenum] [set id] action filters\n"
299                         "       ipfw3 delete [rulenum]\n"
300                         "       ipfw3 flush\n"
301                         "       ipfw3 list [rulenum]\n"
302                         "       ipfw3 show [rulenum]\n"
303                         "       ipfw3 zero [rulenum]\n"
304                         "       ipfw3 set [show|enable|disable]\n"
305                         "       ipfw3 module\n"
306                         "       ipfw3 [enable|disable]\n"
307                         "       ipfw3 log [reset|off|on]\n"
308                         "       ipfw3 nat [config|show|delete]\n"
309                         "       ipfw3 pipe [config|show|delete]\n"
310                         "       ipfw3 state [add|delete|list|show]\n"
311                         "       ipfw3 nat [config|show]\n"
312                         "\nsee ipfw3 manpage for details\n");
313         exit(EX_USAGE);
314 }
315
316 void
317 rule_delete(int ac, char *av[])
318 {
319         int error, rulenum;
320
321         NEXT_ARG;
322
323         while (ac && isdigit(**av)) {
324                 rulenum = atoi(*av);
325                 error = do_set_x(IP_FW_DEL, &rulenum, sizeof(int));
326                 if (error) {
327                         err(EX_OSERR, "do_get_x(IP_FW_DEL)");
328                 }
329                 NEXT_ARG;
330         }
331 }
332
333 /*
334  * helper function, updates the pointer to cmd with the length
335  * of the current command, and also cleans up the first word of
336  * the new command in case it has been clobbered before.
337  */
338 ipfw_insn*
339 next_cmd(ipfw_insn *cmd)
340 {
341         cmd += F_LEN(cmd);
342         bzero(cmd, sizeof(*cmd));
343         return cmd;
344 }
345
346 /*
347  * Parse arguments and assemble the microinstructions which make up a rule.
348  * Rules are added into the 'rulebuf' and then copied in the correct order
349  * into the actual rule.
350  *
351  *
352  */
353 void
354 rule_add(int ac, char *av[], uint8_t insert)
355 {
356         /*
357          * rules are added into the 'rulebuf' and then copied in
358          * the correct order into the actual rule.
359          * Some things that need to go out of order (prob, action etc.)
360          * go into actbuf[].
361          */
362         static uint32_t rulebuf[IPFW_RULE_SIZE_MAX];
363         static uint32_t actbuf[IPFW_RULE_SIZE_MAX];
364         static uint32_t othbuf[IPFW_RULE_SIZE_MAX];
365         static uint32_t cmdbuf[IPFW_RULE_SIZE_MAX];
366
367         ipfw_insn *src, *dst, *cmd, *action, *other;
368         ipfw_insn *prev;
369         char *prev_av;
370         ipfw_insn *the_comment = NULL;
371         struct ipfw_ioc_rule *rule;
372         struct ipfw3_keyword *key;
373         struct ipfw3_mapping *map;
374         parser_func fn;
375         int i, j;
376
377         bzero(actbuf, sizeof(actbuf));          /* actions go here */
378         bzero(othbuf, sizeof(actbuf));          /* others */
379         bzero(cmdbuf, sizeof(cmdbuf));          /* filters */
380         bzero(rulebuf, sizeof(rulebuf));
381
382         rule = (struct ipfw_ioc_rule *)rulebuf;
383         cmd = (ipfw_insn *)cmdbuf;
384         action = (ipfw_insn *)actbuf;
385         other = (ipfw_insn *)othbuf;
386
387         rule->insert = insert;
388
389         NEED2("need more parameters");
390         NEXT_ARG;
391
392         /* [rule N]     -- Rule number optional */
393         if (ac && isdigit(**av)) {
394                 rule->rulenum = atoi(*av);
395                 NEXT_ARG;
396         }
397
398         /* [set N]      -- set number (0..30), optional */
399         if (ac > 1 && !strncmp(*av, "set", strlen(*av))) {
400                 int set = strtoul(av[1], NULL, 10);
401                 if (set < 0 || set > 30)
402                         errx(EX_DATAERR, "illegal set %s", av[1]);
403                 rule->set = set;
404                 av += 2; ac -= 2;
405         }
406
407         /*
408          * parse before
409          */
410         for (;;) {
411                 for (i = 0, key = keywords; i < KEYWORD_SIZE; i++, key++) {
412                         if (key->type == BEFORE &&
413                                 strcmp(key->word, *av) == 0) {
414                                 for (j = 0, map = mappings;
415                                         j < MAPPING_SIZE; j++, map++) {
416                                         if (map->type == IN_USE &&
417                                                 map->module == key->module &&
418                                                 map->opcode == key->opcode ) {
419                                                 fn = map->parser;
420                                                 (*fn)(&other, &ac, &av);
421                                                 break;
422                                         }
423                                 }
424                                 break;
425                         }
426                 }
427                 if (i >= KEYWORD_SIZE) {
428                         break;
429                 } else if (F_LEN(other) > 0) {
430                         if (other->module == MODULE_BASIC_ID &&
431                                 other->opcode == O_BASIC_CHECK_STATE) {
432                                 other = next_cmd(other);
433                                 goto done;
434                         }
435                         other = next_cmd(other);
436                 }
437         }
438
439         /*
440          * parse actions
441          *
442          * only accept 1 action
443          */
444         NEED1("missing action");
445         for (i = 0, key = keywords; i < KEYWORD_SIZE; i++, key++) {
446                 if (ac > 0 && key->type == ACTION &&
447                         strcmp(key->word, *av) == 0) {
448                         for (j = 0, map = mappings;
449                                         j < MAPPING_SIZE; j++, map++) {
450                                 if (map->type == IN_USE &&
451                                         map->module == key->module &&
452                                         map->opcode == key->opcode) {
453                                         fn = map->parser;
454                                         (*fn)(&action, &ac, &av);
455                                         break;
456                                 }
457                         }
458                         break;
459                 }
460         }
461         if (F_LEN(action) > 0)
462                 action = next_cmd(action);
463
464         /*
465          * parse protocol
466          */
467         if (strcmp(*av, "proto") == 0){
468                 NEXT_ARG;
469         }
470
471         NEED1("missing protocol");
472         for (i = 0, key = keywords; i < KEYWORD_SIZE; i++, key++) {
473                 if (key->type == PROTO &&
474                         strcmp(key->word, "proto") == 0) {
475                         for (j = 0, map = mappings;
476                                         j < MAPPING_SIZE; j++, map++) {
477                                 if (map->type == IN_USE &&
478                                         map->module == key->module &&
479                                         map->opcode == key->opcode ) {
480                                         fn = map->parser;
481                                         (*fn)(&cmd, &ac, &av);
482                                         break;
483                                 }
484                         }
485                         break;
486                 }
487         }
488         if (F_LEN(cmd) > 0)
489                 cmd = next_cmd(cmd);
490
491         /*
492          * other filters
493          */
494         while (ac > 0) {
495                 char *s, *cur;          /* current filter */
496                 ipfw_insn_u32 *cmd32;   /* alias for cmd */
497
498                 s = *av;
499                 cmd32 = (ipfw_insn_u32 *)cmd;
500                 if (strcmp(*av, "or") == 0) {
501                         if (prev == NULL)
502                                 errx(EX_USAGE, "'or' should"
503                                                 "between two filters\n");
504                         prev->len |= F_OR;
505                         cmd->len = F_OR;
506                         *av = prev_av;
507                 }
508                 if (strcmp(*av, "not") == 0) {
509                         if (cmd->len & F_NOT)
510                                 errx(EX_USAGE, "double \"not\" not allowed\n");
511                         cmd->len = F_NOT;
512                         NEXT_ARG;
513                         continue;
514                 }
515                 cur = *av;
516                 for (i = 0, key = keywords; i < KEYWORD_SIZE; i++, key++) {
517                         if ((key->type == FILTER ||
518                                 key->type == AFTER ||
519                                 key->type == FROM ||
520                                 key->type == TO) &&
521                                 strcmp(key->word, cur) == 0) {
522                                 for (j = 0, map = mappings;
523                                         j< MAPPING_SIZE; j++, map++) {
524                                         if (map->type == IN_USE &&
525                                                 map->module == key->module &&
526                                                 map->opcode == key->opcode ) {
527                                                 fn = map->parser;
528                                                 (*fn)(&cmd, &ac, &av);
529                                                 break;
530                                         }
531                                 }
532                                 break;
533                         } else if (i == KEYWORD_SIZE - 1) {
534                                 errx(EX_USAGE, "bad command `%s'", cur);
535                         }
536                 }
537                 if (i >= KEYWORD_SIZE) {
538                         break;
539                 } else if (F_LEN(cmd) > 0) {
540                         prev = cmd;
541                         prev_av = cur;
542                         cmd = next_cmd(cmd);
543                 }
544         }
545
546 done:
547         if (ac>0)
548                 errx(EX_USAGE, "bad command `%s'", *av);
549
550         /*
551          * Now copy stuff into the rule.
552          * [filters][others][action][comment]
553          */
554         dst = (ipfw_insn *)rule->cmd;
555         /*
556          * copy all filters, except comment
557          */
558         src = (ipfw_insn *)cmdbuf;
559         for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
560                 /* pick comment out */
561                 i = F_LEN(src);
562                 if (src->module == MODULE_BASIC_ID &&
563                                 src->opcode == O_BASIC_COMMENT) {
564                         the_comment=src;
565                 } else {
566                         bcopy(src, dst, i * sizeof(u_int32_t));
567                         dst = (ipfw_insn *)((uint32_t *)dst + i);
568                 }
569         }
570
571         /*
572          * start action section, it begin with others
573          */
574         rule->act_ofs = (uint32_t *)dst - (uint32_t *)(rule->cmd);
575
576         /*
577          * copy all other others
578          */
579         for (src = (ipfw_insn *)othbuf; src != other; src += i) {
580                 i = F_LEN(src);
581                 bcopy(src, dst, i * sizeof(u_int32_t));
582                 dst = (ipfw_insn *)((uint32_t *)dst + i);
583         }
584
585         /* copy the action to the end of rule */
586         src = (ipfw_insn *)actbuf;
587         i = F_LEN(src);
588         bcopy(src, dst, i * sizeof(u_int32_t));
589         dst = (ipfw_insn *)((uint32_t *)dst + i);
590
591         /*
592          * comment place behind the action
593          */
594         if (the_comment != NULL) {
595                 i = F_LEN(the_comment);
596                 bcopy(the_comment, dst, i * sizeof(u_int32_t));
597                 dst = (ipfw_insn *)((uint32_t *)dst + i);
598         }
599
600         rule->cmd_len = (u_int32_t *)dst - (u_int32_t *)(rule->cmd);
601         i = (void *)dst - (void *)rule;
602         if (do_set_x(IP_FW_ADD, (void *)rule, i) == -1) {
603                 err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
604         }
605         if (!do_quiet)
606                 rule_show(rule, 10, 10);
607 }
608
609 void
610 rule_zero(int ac, char *av[])
611 {
612         int rulenum;
613         int failed = EX_OK;
614
615         NEXT_ARG;
616
617         if (!ac) {
618                 /* clear all entries */
619                 if (do_set_x(IP_FW_ZERO, NULL, 0) < 0)
620                         err(EX_UNAVAILABLE, "do_set_x(IP_FW_ZERO)");
621                 if (!do_quiet)
622                         printf("Accounting cleared.\n");
623                 return;
624         }
625
626         while (ac) {
627                 /* Rule number */
628                 if (isdigit(**av)) {
629                         rulenum = atoi(*av);
630                         NEXT_ARG;
631                         if (do_set_x(IP_FW_ZERO, &rulenum, sizeof rulenum)) {
632                                 warn("rule %u: do_set_x(IP_FW_ZERO)", rulenum);
633                                 failed = EX_UNAVAILABLE;
634                         } else if (!do_quiet)
635                                 printf("Entry %d cleared\n", rulenum);
636                 } else {
637                         errx(EX_USAGE, "invalid rule number ``%s''", *av);
638                 }
639         }
640         if (failed != EX_OK)
641                 exit(failed);
642 }
643
644 void
645 rule_flush(void)
646 {
647         int cmd = IP_FW_FLUSH;
648         if (do_pipe) {
649                 cmd = IP_DUMMYNET_FLUSH;
650         }
651         if (!do_force) {
652                 int c;
653
654                 printf("Are you sure? [yn] ");
655                 fflush(stdout);
656                 do {
657                         c = toupper(getc(stdin));
658                         while (c != '\n' && getc(stdin) != '\n')
659                                 if (feof(stdin))
660                                         return; /* and do not flush */
661                 } while (c != 'Y' && c != 'N');
662                 if (c == 'N')   /* user said no */
663                         return;
664         }
665         if (do_set_x(cmd, NULL, 0) < 0 ) {
666                 if (do_pipe)
667                         errx(EX_USAGE, "pipe/queue in use");
668                 else
669                         errx(EX_USAGE, "do_set_x(IP_FW_FLUSH) failed");
670         }
671         if (!do_quiet) {
672                 printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules");
673         }
674 }
675
676 void
677 rule_list(int ac, char *av[])
678 {
679         struct ipfw_ioc_rule *rule;
680
681         void *data = NULL;
682         int bcwidth, nbytes, pcwidth, width;
683         int nalloc = 1024;
684         int the_rule_num = 0;
685         int total_len;
686
687         NEXT_ARG;
688
689         /* get rules or pipes from kernel, resizing array as necessary */
690         nbytes = nalloc;
691
692         while (nbytes >= nalloc) {
693                 nalloc = nalloc * 2 ;
694                 nbytes = nalloc;
695                 if ((data = realloc(data, nbytes)) == NULL)
696                         err(EX_OSERR, "realloc");
697                 if (do_get_x(IP_FW_GET, data, &nbytes) < 0)
698                         err(EX_OSERR, "do_get_x(IP_FW_GET)");
699         }
700
701         /*
702          * Count static rules.
703          */
704         rule = data;
705         bcwidth = pcwidth = 0;
706         if (do_acct) {
707                 total_len = 0;
708                 for (rule = data; rule != NULL; rule = (void *)rule + IOC_RULESIZE(rule)) {
709                         /* packet counter */
710                         width = snprintf(NULL, 0, "%ju", (uintmax_t)rule->pcnt);
711                         if (width > pcwidth)
712                                 pcwidth = width;
713
714                         /* byte counter */
715                         width = snprintf(NULL, 0, "%ju", (uintmax_t)rule->bcnt);
716                         if (width > bcwidth)
717                                 bcwidth = width;
718
719                         total_len += IOC_RULESIZE(rule);
720                         if (total_len == nbytes) {
721                                 break;
722                         }
723                 }
724         }
725
726
727         if (ac == 1) {
728                 the_rule_num = atoi(*av);
729         }
730
731         total_len = 0;
732         for (rule = data; rule != NULL; rule = (void *)rule + IOC_RULESIZE(rule)) {
733                 if(the_rule_num == 0 || rule->rulenum == the_rule_num) {
734                         rule_show(rule, pcwidth, bcwidth);
735                 }
736                 total_len += IOC_RULESIZE(rule);
737                 if (total_len == nbytes) {
738                         break;
739                 }
740         }
741
742 }
743
744 void
745 rule_show(struct ipfw_ioc_rule *rule, int pcwidth, int bcwidth)
746 {
747         static int twidth = 0;
748         ipfw_insn *cmd;
749         int l;
750
751         u_int32_t set_disable = rule->sets;
752
753         if (set_disable & (1 << rule->set)) { /* disabled */
754                 if (!show_sets)
755                         return;
756                 else
757                         printf("# DISABLED ");
758         }
759         if (do_compact) {
760                 printf("%u", rule->rulenum);
761         } else {
762                 printf("%05u", rule->rulenum);
763         }
764
765         if (do_acct) {
766                 if (do_compact) {
767                         printf(" %ju %ju", (uintmax_t)rule->pcnt,
768                                                 (uintmax_t)rule->bcnt);
769                 } else {
770                         printf(" %*ju %*ju", pcwidth, (uintmax_t)rule->pcnt,
771                                 bcwidth, (uintmax_t)rule->bcnt);
772                 }
773         }
774
775         if (do_time == 1) {
776                 char timestr[30];
777
778                 if (twidth == 0) {
779                         strcpy(timestr, ctime((time_t *)&twidth));
780                         *strchr(timestr, '\n') = '\0';
781                         twidth = strlen(timestr);
782                 }
783                 if (rule->timestamp) {
784                         time_t t = _long_to_time(rule->timestamp);
785
786                         strcpy(timestr, ctime(&t));
787                         *strchr(timestr, '\n') = '\0';
788                         printf(" %s", timestr);
789                 } else {
790                         printf(" %*s", twidth, " ");
791                 }
792         } else if (do_time == 2) {
793                 printf( " %10u", rule->timestamp);
794         }
795
796         if (show_sets)
797                 printf(" set %d", rule->set);
798
799
800         struct ipfw3_keyword *k;
801         struct ipfw3_mapping *m;
802         shower_func fn, comment_fn = NULL;
803         ipfw_insn *comment_cmd;
804         int i, j, changed;
805
806         /*
807          * show others and actions
808          */
809         for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule);
810                 l > 0; l -= F_LEN(cmd),
811                 cmd = (ipfw_insn *)((uint32_t *)cmd + F_LEN(cmd))) {
812                 k = keywords;
813                 m = mappings;
814                 for (i = 1; i< KEYWORD_SIZE; i++, k++) {
815                         if ( k->module == cmd->module && k->opcode == cmd->opcode ) {
816                                 for (j = 1; j< MAPPING_SIZE; j++, m++) {
817                                         if (m->type == IN_USE &&
818                                                 m->module == cmd->module &&
819                                                 m->opcode == cmd->opcode) {
820                                                 if (cmd->module == MODULE_BASIC_ID &&
821                                                         cmd->opcode == O_BASIC_COMMENT) {
822                                                         comment_fn = m->shower;
823                                                         comment_cmd = cmd;
824                                                 } else {
825                                                         fn = m->shower;
826                                                         (*fn)(cmd, 0);
827                                                 }
828                                                 if (cmd->module == MODULE_BASIC_ID &&
829                                                         cmd->opcode ==
830                                                                 O_BASIC_CHECK_STATE) {
831                                                         goto done;
832                                                 }
833                                                 break;
834                                         }
835                                 }
836                                 break;
837                         }
838                 }
839         }
840
841         /*
842          * show proto
843          */
844         changed=0;
845         for (l = rule->act_ofs, cmd = rule->cmd; l > 0; l -= F_LEN(cmd),
846                         cmd = (ipfw_insn *)((uint32_t *)cmd + F_LEN(cmd))) {
847                 changed = show_filter(cmd, "proto", PROTO);
848         }
849         if (!changed && !do_quiet)
850                 printf(" ip");
851
852         /*
853          * show from
854          */
855         changed = 0;
856         for (l = rule->act_ofs, cmd = rule->cmd; l > 0; l -= F_LEN(cmd),
857                         cmd = (ipfw_insn *)((uint32_t *)cmd + F_LEN(cmd))) {
858                 changed = show_filter(cmd, "from", FROM);
859         }
860         if (!changed && !do_quiet)
861                 printf(" from any");
862
863         /*
864          * show to
865          */
866         changed = 0;
867         for (l = rule->act_ofs, cmd = rule->cmd; l > 0; l -= F_LEN(cmd),
868                         cmd = (ipfw_insn *)((uint32_t *)cmd + F_LEN(cmd))) {
869                 changed = show_filter(cmd, "to", TO);
870         }
871         if (!changed && !do_quiet)
872                 printf(" to any");
873
874         /*
875          * show other filters
876          */
877         l = rule->act_ofs;
878         cmd = rule->cmd;
879         m = mappings;
880         for ( ; l > 0; ) {
881                 show_filter(cmd, "other", FILTER);
882                 l -= F_LEN(cmd);
883                 cmd=(ipfw_insn *)((uint32_t *)cmd + F_LEN(cmd));
884         }
885
886         /* show the comment in the end */
887         if (comment_fn != NULL) {
888                 (*comment_fn)(comment_cmd, 0);
889         }
890 done:
891         printf("\n");
892 }
893
894 /*
895  * do_set_x - extended version og do_set
896  * insert a x_header in the beginning of the rule buf
897  * and call setsockopt() with IP_FW_X.
898  */
899 int
900 do_set_x(int optname, void *rule, int optlen)
901 {
902         int len, *newbuf, retval;
903         ip_fw_x_header *x_header;
904
905         if (fw3_socket < 0)
906                 err(EX_UNAVAILABLE, "socket not avaialble");
907
908         len = optlen + sizeof(ip_fw_x_header);
909         newbuf = malloc(len);
910         if (newbuf == NULL)
911                 err(EX_OSERR, "malloc newbuf in do_set_x");
912
913         bzero(newbuf, len);
914         x_header = (ip_fw_x_header *)newbuf;
915         x_header->opcode = optname;
916         /* copy the rule into the newbuf, just after the x_header*/
917         bcopy(rule, ++x_header, optlen);
918         retval = setsockopt(fw3_socket, IPPROTO_IP, IP_FW_X, newbuf, len);
919         free(newbuf);
920         return retval;
921 }
922
923 /*
924  * same as do_set_x
925  */
926 int
927 do_get_x(int optname, void *rule, int *optlen)
928 {
929         int len, *newbuf, retval;
930         ip_fw_x_header *x_header;
931
932         if (fw3_socket < 0)
933                 err(EX_UNAVAILABLE, "socket not avaialble");
934
935         len = *optlen + sizeof(ip_fw_x_header);
936         newbuf = malloc(len);
937         if (newbuf == NULL)
938                 err(EX_OSERR, "malloc newbuf in do_get_x");
939
940         bzero(newbuf, len);
941         x_header = (ip_fw_x_header *)newbuf;
942         x_header->opcode = optname;
943         /* copy the rule into the newbuf, just after the x_header*/
944         bcopy(rule, ++x_header, *optlen);
945         retval = getsockopt(fw3_socket, IPPROTO_IP, IP_FW_X, newbuf, &len);
946         bcopy(newbuf, rule, len);
947         free(newbuf);
948         *optlen = len;
949         return retval;
950 }
951
952 int
953 ipfw3_main(int ac, char **av)
954 {
955         int ch;
956
957         if (ac == 1)
958                 help();
959
960         /* Set the force flag for non-interactive processes */
961         do_force = !isatty(STDIN_FILENO);
962
963         optind = optreset = 1;
964         while ((ch = getopt(ac, av, "hs:acefStTv")) != -1)
965                 switch (ch) {
966                 case 'h': /* help */
967                         help();
968                         break;  /* NOTREACHED */
969
970                 case 's': /* sort */
971                         do_sort = atoi(optarg);
972                         break;
973                 case 'a':
974                         do_acct = 1;
975                         break;
976                 case 'c':
977                         do_compact = 1;
978                         break;
979                 case 'e':
980                         do_expired = 1;
981                         break;
982                 case 'f':
983                         do_force = 1;
984                         break;
985                 case 'S':
986                         show_sets = 1;
987                         break;
988                 case 't':
989                         do_time = 1;
990                         break;
991                 case 'T':
992                         do_time = 2;
993                         break;
994                 case 'v':
995                         do_quiet = 0;
996                         verbose++;
997                         break;
998                 default:
999                         help();
1000                 }
1001
1002         ac -= optind;
1003         av += optind;
1004         NEED1("bad arguments, for usage summary ``ipfw3''");
1005
1006         /*
1007          * optional: pipe or queue or nat
1008          */
1009         do_nat = 0;
1010         do_pipe = 0;
1011         if (!strncmp(*av, "nat", strlen(*av)))
1012                 do_nat = 1;
1013         else if (!strncmp(*av, "pipe", strlen(*av))) {
1014                 do_pipe = 1;
1015         } else if (!strncmp(*av, "queue", strlen(*av))) {
1016                 do_pipe = 2;
1017         }
1018         NEED1("missing command");
1019
1020         /*
1021          * for pipes and queues and nat we normally say 'pipe NN config'
1022          * but the code is easier to parse as 'pipe config NN'
1023          * so we swap the two arguments.
1024          */
1025         if ((do_pipe || do_nat) && ac > 2 && isdigit(*(av[1]))) {
1026                 char *p = av[1];
1027                 av[1] = av[2];
1028                 av[2] = p;
1029         }
1030
1031         if (!strncmp(*av, "add", strlen(*av))) {
1032                 module_load();
1033                 rule_add(ac, av, 0);
1034         } else if (!strncmp(*av, "insert", strlen(*av))) {
1035                 module_load();
1036                 rule_add(ac, av, 1);
1037         } else if (!strncmp(*av, "delete", strlen(*av))) {
1038                 rule_delete(ac, av);
1039         } else if (!strncmp(*av, "flush", strlen(*av))) {
1040                 rule_flush();
1041         } else if (!strncmp(*av, "list", strlen(*av))) {
1042                 module_load();
1043                 rule_list(ac, av);
1044         } else if (!strncmp(*av, "show", strlen(*av))) {
1045                 do_acct++;
1046                 module_load();
1047                 rule_list(ac, av);
1048         } else if (!strncmp(*av, "zero", strlen(*av))) {
1049                 rule_zero(ac, av);
1050         } else if (!strncmp(*av, "set", strlen(*av))) {
1051                 set_main(ac, av);
1052         } else if (!strncmp(*av, "module", strlen(*av))) {
1053                 NEXT_ARG;
1054                 if (!strncmp(*av, "list", strlen(*av))) {
1055                         module_list(ac, av);
1056                 } else {
1057                         errx(EX_USAGE, "bad ipfw3 module command `%s'", *av);
1058                 }
1059         } else if (!strncmp(*av, "log", strlen(*av))) {
1060                 NEXT_ARG;
1061                 log_main(ac, av);
1062         } else if (!strncmp(*av, "nat", strlen(*av))) {
1063                 NEXT_ARG;
1064                 nat_main(ac, av);
1065         } else if (!strncmp(*av, "pipe", strlen(*av)) ||
1066                 !strncmp(*av, "queue", strlen(*av))) {
1067                 NEXT_ARG;
1068                 dummynet_main(ac, av);
1069         } else if (!strncmp(*av, "state", strlen(*av))) {
1070                 NEXT_ARG;
1071                 state_main(ac, av);
1072         } else if (!strncmp(*av, "table", strlen(*av))) {
1073                 if (ac > 2 && isdigit(*(av[1]))) {
1074                         char *p = av[1];
1075                         av[1] = av[2];
1076                         av[2] = p;
1077                 }
1078                 NEXT_ARG;
1079                 table_main(ac, av);
1080         } else if (!strncmp(*av, "sync", strlen(*av))) {
1081                 NEXT_ARG;
1082                 sync_main(ac, av);
1083         } else {
1084                 errx(EX_USAGE, "bad ipfw3 command `%s'", *av);
1085         }
1086         return 0;
1087 }
1088
1089 void
1090 ipfw3_readfile(int ac, char *av[])
1091 {
1092         char    buf[BUFSIZ];
1093         char    *a, *p, *args[MAX_ARGS], *cmd = NULL;
1094         char    linename[17];
1095         int     i=0, lineno=0, qflag=0, pflag=0, status;
1096         FILE    *f = NULL;
1097         pid_t   preproc = 0;
1098         int     c;
1099
1100         while ((c = getopt(ac, av, "D:U:p:q")) != -1) {
1101                 switch (c) {
1102                 case 'D':
1103                         if (!pflag)
1104                                 errx(EX_USAGE, "-D requires -p");
1105                         if (i > MAX_ARGS - 2)
1106                                 errx(EX_USAGE, "too many -D or -U options");
1107                         args[i++] = "-D";
1108                         args[i++] = optarg;
1109                         break;
1110
1111                 case 'U':
1112                         if (!pflag)
1113                                 errx(EX_USAGE, "-U requires -p");
1114                         if (i > MAX_ARGS - 2)
1115                                 errx(EX_USAGE, "too many -D or -U options");
1116                         args[i++] = "-U";
1117                         args[i++] = optarg;
1118                         break;
1119
1120                 case 'p':
1121                         pflag = 1;
1122                         cmd = optarg;
1123                         args[0] = cmd;
1124                         i = 1;
1125                         break;
1126
1127                 case 'q':
1128                         qflag = 1;
1129                         break;
1130
1131                 default:
1132                         errx(EX_USAGE, "bad arguments, for usage"
1133                             " summary ``ipfw''");
1134                 }
1135         }
1136
1137         av += optind;
1138         ac -= optind;
1139         if (ac != 1)
1140                 errx(EX_USAGE, "extraneous filename arguments");
1141
1142         if ((f = fopen(av[0], "r")) == NULL)
1143                 err(EX_UNAVAILABLE, "fopen: %s", av[0]);
1144
1145         if (pflag) {
1146                 /* pipe through preprocessor (cpp or m4) */
1147                 int pipedes[2];
1148
1149                 args[i] = NULL;
1150
1151                 if (pipe(pipedes) == -1)
1152                         err(EX_OSERR, "cannot create pipe");
1153
1154                 switch ((preproc = fork())) {
1155                 case -1:
1156                         err(EX_OSERR, "cannot fork");
1157
1158                 case 0:
1159                         /* child */
1160                         if (dup2(fileno(f), 0) == -1 ||
1161                             dup2(pipedes[1], 1) == -1) {
1162                                 err(EX_OSERR, "dup2()");
1163                         }
1164                         fclose(f);
1165                         close(pipedes[1]);
1166                         close(pipedes[0]);
1167                         execvp(cmd, args);
1168                         err(EX_OSERR, "execvp(%s) failed", cmd);
1169
1170                 default:
1171                         /* parent */
1172                         fclose(f);
1173                         close(pipedes[1]);
1174                         if ((f = fdopen(pipedes[0], "r")) == NULL) {
1175                                 int savederrno = errno;
1176
1177                                 kill(preproc, SIGTERM);
1178                                 errno = savederrno;
1179                                 err(EX_OSERR, "fdopen()");
1180                         }
1181                 }
1182         }
1183
1184         while (fgets(buf, BUFSIZ, f)) {
1185                 lineno++;
1186                 sprintf(linename, "Line %d", lineno);
1187                 args[0] = linename;
1188
1189                 if (*buf == '#')
1190                         continue;
1191                 if ((p = strchr(buf, '#')) != NULL)
1192                         *p = '\0';
1193                 i = 1;
1194                 if (qflag)
1195                         args[i++] = "-q";
1196                 for (a = strtok(buf, WHITESP); a && i < MAX_ARGS;
1197                         a = strtok(NULL, WHITESP), i++) {
1198                         args[i] = a;
1199                 }
1200
1201                 if (i == (qflag? 2: 1))
1202                         continue;
1203                 if (i == MAX_ARGS)
1204                         errx(EX_USAGE, "%s: too many arguments", linename);
1205
1206                 args[i] = NULL;
1207                 ipfw3_main(i, args);
1208         }
1209         fclose(f);
1210         if (pflag) {
1211                 if (waitpid(preproc, &status, 0) == -1)
1212                         errx(EX_OSERR, "waitpid()");
1213                 if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
1214                         errx(EX_UNAVAILABLE, "preprocessor exited with status %d",
1215                                 WEXITSTATUS(status));
1216                 else if (WIFSIGNALED(status))
1217                         errx(EX_UNAVAILABLE, "preprocessor exited with signal %d",
1218                                 WTERMSIG(status));
1219         }
1220 }
1221
1222 int
1223 main(int ac, char *av[])
1224 {
1225         fw3_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
1226         if (fw3_socket < 0)
1227                 err(EX_UNAVAILABLE, "socket");
1228
1229         memset(keywords, 0, LEN_FW3_KEYWORD * KEYWORD_SIZE);
1230         memset(mappings, 0, LEN_FW3_MAPPING * MAPPING_SIZE);
1231
1232         prepare_default_funcs();
1233
1234         if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0)
1235                 ipfw3_readfile(ac, av);
1236         else
1237                 ipfw3_main(ac, av);
1238         return EX_OK;
1239 }