ipfw3: insert the new rule in the beginning
[dragonfly.git] / sbin / ipfw3 / ipfw3.c
CommitLineData
105c533e 1/*
4408d548 2 * Copyright (c) 2014 - 2018 The DragonFly Project. All rights reserved.
9187b359
BY
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Bill Yuan <bycn82@dragonflybsd.org>
6 *
105c533e
BY
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 *
9187b359 14 *
105c533e
BY
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 *
105c533e
BY
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>
105c533e 61
37cedf07 62
59ea0e34 63#include <net/ipfw3/ip_fw3.h>
4408d548
BY
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>
59ea0e34
BY
67#include <net/ipfw3_basic/ip_fw3_basic.h>
68#include <net/ipfw3_nat/ip_fw3_nat.h>
4408d548 69#include <net/dummynet3/ip_dummynet3.h>
9187b359
BY
70
71#include "ipfw3.h"
4408d548
BY
72#include "ipfw3basic.h"
73#include "ipfw3log.h"
74#include "ipfw3set.h"
75#include "ipfw3table.h"
76#include "ipfw3dummynet.h"
77#include "ipfw3state.h"
e2124e7d 78#include "ipfw3sync.h"
9187b359 79#include "ipfw3nat.h"
105c533e 80
105c533e
BY
81#define MAX_ARGS 32
82#define WHITESP " \t\f\v\n\r"
4408d548 83#define IPFW3_LIB_PATH "/usr/lib/libipfw3%s.so"
105c533e 84
4408d548
BY
85int fw3_socket = -1; /* main RAW socket */
86int do_acct, /* Show packet/byte count */
105c533e
BY
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) */
105c533e
BY
93 do_expired, /* display expired dynamic rules */
94 do_compact, /* show rules in compact mode */
95 show_sets, /* display rule sets */
96 verbose;
97
4408d548
BY
98struct ipfw3_keyword keywords[KEYWORD_SIZE];
99struct ipfw3_mapping mappings[MAPPING_SIZE];
105c533e 100
9187b359 101int
105c533e
BY
102match_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
4408d548
BY
113void
114module_get(char *modules_str, int len)
105c533e
BY
115{
116 if (do_get_x(IP_FW_MODULE, modules_str, &len) < 0)
6a03354e 117 errx(EX_USAGE, "ipfw3 not loaded.");
105c533e
BY
118}
119
4408d548
BY
120void
121module_list(int ac, char *av[])
105c533e
BY
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
4408d548 128 module_get(module_str, len);
992b3001 129 printf("%s\n", (char *)module_str);
105c533e 130}
105c533e
BY
131
132void
4408d548 133module_load(void)
105c533e
BY
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
4408d548 145 module_get(module_str, len);
105c533e
BY
146
147 const char s[2] = ",";
148 char *token;
149 token = strtok(module_str, s);
150 while (token != NULL) {
4408d548 151 sprintf(module_lib_file, IPFW3_LIB_PATH, token);
105c533e
BY
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
105c533e
BY
170void
171register_ipfw_keyword(int module, int opcode, char *word, int type)
172{
4408d548 173 struct ipfw3_keyword *tmp;
105c533e
BY
174
175 tmp=keywords;
2490fdca 176 for (;;) {
3b6ebdc3 177 if (tmp->type == NONE) {
105c533e
BY
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
192void
193register_ipfw_func(int module, int opcode, parser_func parser, shower_func shower)
194{
4408d548 195 struct ipfw3_mapping *tmp;
105c533e
BY
196
197 tmp = mappings;
198 while (1) {
3b6ebdc3 199 if (tmp->type == NONE) {
105c533e
BY
200 tmp->module = module;
201 tmp->opcode = opcode;
202 tmp->parser = parser;
203 tmp->shower = shower;
3b6ebdc3 204 tmp->type = IN_USE;
105c533e
BY
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
6ce8c93f 218/*
3b6ebdc3
BY
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'
6ce8c93f
BY
223 * show_or = 1: show or and ignore filter name
224 * show_or = 0: show filter name ignore or
225 */
226void 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) {
3b6ebdc3 231 /* first cmd with 'or' flag */
6ce8c93f
BY
232 *show_or = 0;
233 *prev_module = cmd->module;
234 *prev_opcode = cmd->opcode;
3b6ebdc3
BY
235 } else if (cmd->module == *prev_module &&
236 cmd->opcode == *prev_opcode) {
237 /* cmd same as previous, same module and opcode */
6ce8c93f
BY
238 *show_or = 1;
239 } else {
3b6ebdc3 240 /* cmd different from prev*/
6ce8c93f
BY
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}
3b6ebdc3 252
6ce8c93f
BY
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 */
260int show_filter(ipfw_insn *cmd, char *word, int type)
261{
4408d548
BY
262 struct ipfw3_keyword *k;
263 struct ipfw3_mapping *m;
6ce8c93f 264 shower_func fn;
3b6ebdc3 265 int i, j, show_or;
6ce8c93f
BY
266 uint8_t prev_module, prev_opcode;
267
268 k = keywords;
269 m = mappings;
3b6ebdc3
BY
270 for (i = 1; i < KEYWORD_SIZE; i++, k++) {
271 if (k->type == type) {
6ce8c93f
BY
272 if (k->module == cmd->module &&
273 k->opcode == cmd->opcode) {
3b6ebdc3
BY
274 for (j = 1; j < MAPPING_SIZE; j++, m++) {
275 if (m->type == IN_USE &&
6ce8c93f
BY
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
4408d548
BY
294void
295help(void)
105c533e 296{
4408d548
BY
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);
390a3a0e
BY
314}
315
316void
4408d548
BY
317rule_delete(int ac, char *av[])
318{
319 int error, rulenum;
105c533e 320
4408d548
BY
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;
105c533e 330 }
105c533e
BY
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 */
4408d548 338ipfw_insn*
105c533e
BY
339next_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 */
4408d548 353void
372a54ac 354rule_add(int ac, char *av[], uint8_t insert)
105c533e
BY
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;
6ce8c93f
BY
368 ipfw_insn *prev;
369 char *prev_av;
105c533e
BY
370 ipfw_insn *the_comment = NULL;
371 struct ipfw_ioc_rule *rule;
4408d548
BY
372 struct ipfw3_keyword *key;
373 struct ipfw3_mapping *map;
105c533e
BY
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
372a54ac
YN
387 rule->insert = insert;
388
105c533e
BY
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 /*
3b6ebdc3 408 * parse before
105c533e
BY
409 */
410 for (;;) {
411 for (i = 0, key = keywords; i < KEYWORD_SIZE; i++, key++) {
3b6ebdc3 412 if (key->type == BEFORE &&
105c533e
BY
413 strcmp(key->word, *av) == 0) {
414 for (j = 0, map = mappings;
415 j < MAPPING_SIZE; j++, map++) {
3b6ebdc3 416 if (map->type == IN_USE &&
105c533e
BY
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++) {
3b6ebdc3 446 if (ac > 0 && key->type == ACTION &&
105c533e 447 strcmp(key->word, *av) == 0) {
9187b359
BY
448 for (j = 0, map = mappings;
449 j < MAPPING_SIZE; j++, map++) {
3b6ebdc3 450 if (map->type == IN_USE &&
105c533e
BY
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++) {
3b6ebdc3 473 if (key->type == PROTO &&
105c533e 474 strcmp(key->word, "proto") == 0) {
9187b359
BY
475 for (j = 0, map = mappings;
476 j < MAPPING_SIZE; j++, map++) {
3b6ebdc3 477 if (map->type == IN_USE &&
105c533e
BY
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) {
6ce8c93f 495 char *s, *cur; /* current filter */
105c533e
BY
496 ipfw_insn_u32 *cmd32; /* alias for cmd */
497
498 s = *av;
499 cmd32 = (ipfw_insn_u32 *)cmd;
6ce8c93f
BY
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 }
105c533e
BY
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 }
6ce8c93f 515 cur = *av;
105c533e 516 for (i = 0, key = keywords; i < KEYWORD_SIZE; i++, key++) {
3b6ebdc3
BY
517 if ((key->type == FILTER ||
518 key->type == AFTER ||
519 key->type == FROM ||
520 key->type == TO) &&
6ce8c93f 521 strcmp(key->word, cur) == 0) {
105c533e
BY
522 for (j = 0, map = mappings;
523 j< MAPPING_SIZE; j++, map++) {
3b6ebdc3 524 if (map->type == IN_USE &&
105c533e
BY
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;
3b6ebdc3 533 } else if (i == KEYWORD_SIZE - 1) {
6ce8c93f 534 errx(EX_USAGE, "bad command `%s'", cur);
105c533e
BY
535 }
536 }
537 if (i >= KEYWORD_SIZE) {
538 break;
539 } else if (F_LEN(cmd) > 0) {
6ce8c93f
BY
540 prev = cmd;
541 prev_av = cur;
105c533e
BY
542 cmd = next_cmd(cmd);
543 }
544 }
545
546done:
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);
9187b359
BY
562 if (src->module == MODULE_BASIC_ID &&
563 src->opcode == O_BASIC_COMMENT) {
105c533e
BY
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)
4408d548 606 rule_show(rule, 10, 10);
105c533e
BY
607}
608
4408d548
BY
609void
610rule_zero(int ac, char *av[])
105c533e
BY
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
4408d548
BY
644void
645rule_flush(void)
105c533e
BY
646{
647 int cmd = IP_FW_FLUSH;
648 if (do_pipe) {
649 cmd = IP_DUMMYNET_FLUSH;
105c533e
BY
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 ) {
b34cca6d
BY
666 if (do_pipe)
667 errx(EX_USAGE, "pipe/queue in use");
b34cca6d 668 else
9187b359 669 errx(EX_USAGE, "do_set_x(IP_FW_FLUSH) failed");
105c533e
BY
670 }
671 if (!do_quiet) {
9187b359 672 printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules");
0fe3a974
BY
673 }
674}
675
4408d548
BY
676void
677rule_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
744void
745rule_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 }
890done:
891 printf("\n");
892}
893
105c533e
BY
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 */
899int
900do_set_x(int optname, void *rule, int optlen)
901{
4408d548 902 int len, *newbuf, retval;
105c533e 903 ip_fw_x_header *x_header;
4408d548
BY
904
905 if (fw3_socket < 0)
5ee21f45 906 err(EX_UNAVAILABLE, "socket not avaialble");
4408d548 907
5ee21f45 908 len = optlen + sizeof(ip_fw_x_header);
49b8c4c8
SW
909 newbuf = malloc(len);
910 if (newbuf == NULL)
911 err(EX_OSERR, "malloc newbuf in do_set_x");
4408d548 912
49b8c4c8 913 bzero(newbuf, len);
5ee21f45 914 x_header = (ip_fw_x_header *)newbuf;
105c533e
BY
915 x_header->opcode = optname;
916 /* copy the rule into the newbuf, just after the x_header*/
917 bcopy(rule, ++x_header, optlen);
4408d548
BY
918 retval = setsockopt(fw3_socket, IPPROTO_IP, IP_FW_X, newbuf, len);
919 free(newbuf);
920 return retval;
105c533e
BY
921}
922
923/*
924 * same as do_set_x
925 */
926int
927do_get_x(int optname, void *rule, int *optlen)
928{
5ee21f45 929 int len, *newbuf, retval;
105c533e 930 ip_fw_x_header *x_header;
4408d548
BY
931
932 if (fw3_socket < 0)
5ee21f45 933 err(EX_UNAVAILABLE, "socket not avaialble");
4408d548 934
5ee21f45 935 len = *optlen + sizeof(ip_fw_x_header);
49b8c4c8
SW
936 newbuf = malloc(len);
937 if (newbuf == NULL)
938 err(EX_OSERR, "malloc newbuf in do_get_x");
4408d548 939
5ee21f45
BY
940 bzero(newbuf, len);
941 x_header = (ip_fw_x_header *)newbuf;
105c533e
BY
942 x_header->opcode = optname;
943 /* copy the rule into the newbuf, just after the x_header*/
944 bcopy(rule, ++x_header, *optlen);
4408d548 945 retval = getsockopt(fw3_socket, IPPROTO_IP, IP_FW_X, newbuf, &len);
5ee21f45 946 bcopy(newbuf, rule, len);
4408d548
BY
947 free(newbuf);
948 *optlen = len;
105c533e
BY
949 return retval;
950}
951
4408d548
BY
952int
953ipfw3_main(int ac, char **av)
105c533e
BY
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;
4408d548 964 while ((ch = getopt(ac, av, "hs:acefStTv")) != -1)
105c533e
BY
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;
105c533e
BY
979 case 'e':
980 do_expired = 1;
981 break;
982 case 'f':
983 do_force = 1;
984 break;
105c533e
BY
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;
4408d548 1004 NEED1("bad arguments, for usage summary ``ipfw3''");
105c533e
BY
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))) {
4408d548 1032 module_load();
372a54ac
YN
1033 rule_add(ac, av, 0);
1034 } else if (!strncmp(*av, "insert", strlen(*av))) {
1035 module_load();
1036 rule_add(ac, av, 1);
105c533e 1037 } else if (!strncmp(*av, "delete", strlen(*av))) {
4408d548 1038 rule_delete(ac, av);
105c533e 1039 } else if (!strncmp(*av, "flush", strlen(*av))) {
4408d548 1040 rule_flush();
105c533e 1041 } else if (!strncmp(*av, "list", strlen(*av))) {
4408d548
BY
1042 module_load();
1043 rule_list(ac, av);
105c533e
BY
1044 } else if (!strncmp(*av, "show", strlen(*av))) {
1045 do_acct++;
4408d548
BY
1046 module_load();
1047 rule_list(ac, av);
105c533e 1048 } else if (!strncmp(*av, "zero", strlen(*av))) {
4408d548 1049 rule_zero(ac, av);
105c533e 1050 } else if (!strncmp(*av, "set", strlen(*av))) {
4408d548 1051 set_main(ac, av);
105c533e
BY
1052 } else if (!strncmp(*av, "module", strlen(*av))) {
1053 NEXT_ARG;
992b3001 1054 if (!strncmp(*av, "list", strlen(*av))) {
4408d548 1055 module_list(ac, av);
105c533e 1056 } else {
4408d548 1057 errx(EX_USAGE, "bad ipfw3 module command `%s'", *av);
105c533e 1058 }
105c533e
BY
1059 } else if (!strncmp(*av, "log", strlen(*av))) {
1060 NEXT_ARG;
4408d548 1061 log_main(ac, av);
105c533e
BY
1062 } else if (!strncmp(*av, "nat", strlen(*av))) {
1063 NEXT_ARG;
9187b359 1064 nat_main(ac, av);
105c533e
BY
1065 } else if (!strncmp(*av, "pipe", strlen(*av)) ||
1066 !strncmp(*av, "queue", strlen(*av))) {
1067 NEXT_ARG;
4408d548 1068 dummynet_main(ac, av);
105c533e
BY
1069 } else if (!strncmp(*av, "state", strlen(*av))) {
1070 NEXT_ARG;
4408d548 1071 state_main(ac, av);
390a3a0e
BY
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;
4408d548 1079 table_main(ac, av);
e2124e7d
BY
1080 } else if (!strncmp(*av, "sync", strlen(*av))) {
1081 NEXT_ARG;
4408d548 1082 sync_main(ac, av);
105c533e 1083 } else {
4408d548 1084 errx(EX_USAGE, "bad ipfw3 command `%s'", *av);
105c533e
BY
1085 }
1086 return 0;
1087}
1088
4408d548
BY
1089void
1090ipfw3_readfile(int ac, char *av[])
105c533e
BY
1091{
1092 char buf[BUFSIZ];
1093 char *a, *p, *args[MAX_ARGS], *cmd = NULL;
d0937b0e 1094 char linename[17];
105c533e
BY
1095 int i=0, lineno=0, qflag=0, pflag=0, status;
1096 FILE *f = NULL;
1097 pid_t preproc = 0;
1098 int c;
1099
1782f138 1100 while ((c = getopt(ac, av, "D:U:p:q")) != -1) {
105c533e 1101 switch (c) {
1782f138
SW
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;
105c533e 1110
1782f138
SW
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;
105c533e 1119
1782f138
SW
1120 case 'p':
1121 pflag = 1;
1122 cmd = optarg;
1123 args[0] = cmd;
1124 i = 1;
1125 break;
105c533e 1126
1782f138
SW
1127 case 'q':
1128 qflag = 1;
1129 break;
105c533e 1130
1782f138
SW
1131 default:
1132 errx(EX_USAGE, "bad arguments, for usage"
1133 " summary ``ipfw''");
105c533e 1134 }
1782f138 1135 }
105c533e
BY
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())) {
1782f138
SW
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 }
105c533e
BY
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;
4408d548 1207 ipfw3_main(i, args);
105c533e
BY
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
1222int
1223main(int ac, char *av[])
1224{
4408d548
BY
1225 fw3_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
1226 if (fw3_socket < 0)
105c533e
BY
1227 err(EX_UNAVAILABLE, "socket");
1228
4408d548
BY
1229 memset(keywords, 0, LEN_FW3_KEYWORD * KEYWORD_SIZE);
1230 memset(mappings, 0, LEN_FW3_MAPPING * MAPPING_SIZE);
105c533e
BY
1231
1232 prepare_default_funcs();
1233
1234 if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0)
4408d548 1235 ipfw3_readfile(ac, av);
105c533e 1236 else
4408d548 1237 ipfw3_main(ac, av);
105c533e
BY
1238 return EX_OK;
1239}