xref: /dragonfly/sbin/ipfw3/ipfw3.c (revision 0de61e28)
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, "setsockopt(%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 of 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 }
1240