text: /*
   * Copyright (c) 2014 - 2018 The DragonFly Project.  All rights reserved.
   *
   * This code is derived from software contributed to The DragonFly Project
   * by Bill Yuan <bycn82@dragonflybsd.org>
   *
   * Copyright (c) 2002 Luigi Rizzo
   * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
   * Copyright (c) 1994 Ugen J.S.Antsilevich
   *
   * Idea and grammar partially left from:
   * Copyright (c) 1993 Daniel Boulet
   *
   *
   * Redistribution and use in source forms, with and without modification,
   * are permitted provided that this entire comment appears intact.
   *
   * Redistribution in binary form may occur without any restrictions.
   * Obviously, it would be nice if you gave credit where credit is due
   * but requiring it would be too onerous.
   *
   * This software is provided ``AS IS'' without any warranties of any kind.
   *
   */
  
  #include <sys/param.h>
  #include <sys/mbuf.h>
  #include <sys/socket.h>
  #include <sys/sockio.h>
  #include <sys/sysctl.h>
  #include <sys/time.h>
  #include <sys/wait.h>
  
  #include <arpa/inet.h>
  #include <ctype.h>
  #include <dlfcn.h>
  #include <err.h>
  #include <errno.h>
  #include <grp.h>
  #include <limits.h>
  #include <netdb.h>
  #include <pwd.h>
  #include <sysexits.h>
  #include <signal.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <stdarg.h>
  #include <string.h>
  #include <timeconv.h>
  #include <unistd.h>
  
  #include <netinet/in.h>
  #include <netinet/in_systm.h>
  #include <netinet/ip.h>
  #include <netinet/ip_icmp.h>
  #include <netinet/tcp.h>
  #include <net/if.h>
  #include <net/if_dl.h>
  #include <net/route.h>
  #include <net/ethernet.h>
  
  
  #include <net/ipfw3/ip_fw3.h>
  #include <net/ipfw3_basic/ip_fw3_table.h>
  #include <net/ipfw3_basic/ip_fw3_state.h>
  #include <net/ipfw3_basic/ip_fw3_sync.h>
  #include <net/ipfw3_basic/ip_fw3_basic.h>
  #include <net/ipfw3_nat/ip_fw3_nat.h>
  #include <net/dummynet3/ip_dummynet3.h>
  
  #include "ipfw3.h"
  #include "ipfw3basic.h"
  #include "ipfw3log.h"
  #include "ipfw3set.h"
  #include "ipfw3table.h"
  #include "ipfw3dummynet.h"
  #include "ipfw3state.h"
  #include "ipfw3sync.h"
  #include "ipfw3nat.h"
  
  #define MAX_ARGS	32
  #define WHITESP		" \t\f\v\n\r"
  #define IPFW3_LIB_PATH	"/usr/lib/libipfw3%s.so"
  
  int		fw3_socket = -1;	/* main RAW socket */
  int		do_acct, 		/* Show packet/byte count */
  		do_time, 		/* Show time stamps */
  		do_quiet = 1,		/* Be quiet , default is quiet*/
  		do_force, 		/* Don't ask for confirmation */
  		do_pipe, 		/* this cmd refers to a pipe */
  		do_nat, 		/* Nat configuration. */
  		do_sort, 		/* field to sort results (0 = no) */
  		do_expired, 		/* display expired dynamic rules */
  		do_compact, 		/* show rules in compact mode */
  		show_sets, 		/* display rule sets */
  		verbose;
  
  struct ipfw3_keyword keywords[KEYWORD_SIZE];
  struct ipfw3_mapping mappings[MAPPING_SIZE];
  
  int
  match_token(struct char_int_map *table, char *string)
  {
  	while (table->key) {
  		if (strcmp(table->key, string) == 0) {
  			return table->val;
  		}
  		table++;
  	}
  	return 0;
  }
  
  void
  module_get(char *modules_str, int len)
  {
  	if (do_get_x(IP_FW_MODULE, modules_str, &len) < 0)
  		errx(EX_USAGE, "ipfw3 not loaded.");
  }
  
  void
  module_list(int ac, char *av[])
  {
  	void *module_str = NULL;
  	int len = 1024;
  	if ((module_str = realloc(module_str, len)) == NULL)
  		err(EX_OSERR, "realloc");
  
  	module_get(module_str, len);
  	printf("%s\n", (char *)module_str);
  }
  
  void
  module_load(void)
  {
  	const char *error;
  	init_module mod_init_func;
  	void *module_lib;
  	char module_lib_file[50];
  	void *module_str = NULL;
  	int len = 1024;
  
  	if ((module_str = realloc(module_str, len)) == NULL)
  		err(EX_OSERR, "realloc");
  
  	module_get(module_str, len);
  
  	const char s[2] = ",";
  	char *token;
  	token = strtok(module_str, s);
  	while (token != NULL) {
  		sprintf(module_lib_file, IPFW3_LIB_PATH, token);
  		token = strtok(NULL, s);
  		module_lib = dlopen(module_lib_file, RTLD_LAZY);
  		if (!module_lib) {
  			fprintf(stderr, "Couldn't open %s: %s\n",
  				module_lib_file, dlerror());
  			exit(EX_SOFTWARE);
  		}
  		mod_init_func = dlsym(module_lib, "load_module");
  		if ((error = dlerror()))
  		{
  			fprintf(stderr, "Couldn't find init function: %s\n", error);
  			exit(EX_SOFTWARE);
  		}
  		(*mod_init_func)((register_func)register_ipfw_func,
  				(register_keyword)register_ipfw_keyword);
  	}
  }
  
  void
  register_ipfw_keyword(int module, int opcode, char *word, int type)
  {
  	struct ipfw3_keyword *tmp;
  
  	tmp=keywords;
  	for (;;) {
  		if (tmp->type == NONE) {
  			strcpy(tmp->word, word);
  			tmp->module = module;
  			tmp->opcode = opcode;
  			tmp->type = type;
  			break;
  		} else {
  			if (strcmp(tmp->word, word) == 0)
  				errx(EX_USAGE, "keyword `%s' exists", word);
  			else
  				tmp++;
  		}
  	}
  }
  
  void
  register_ipfw_func(int module, int opcode, parser_func parser, shower_func shower)
  {
  	struct ipfw3_mapping *tmp;
  
  	tmp = mappings;
  	while (1) {
  		if (tmp->type == NONE) {
  			tmp->module = module;
  			tmp->opcode = opcode;
  			tmp->parser = parser;
  			tmp->shower = shower;
  			tmp->type = IN_USE;
  			break;
  		} else {
  			if (tmp->opcode == opcode && tmp->module == module) {
  				errx(EX_USAGE, "func `%d' of module `%d' exists",
  					opcode, module);
  				break;
  			} else {
  				tmp++;
  			}
  		}
  	}
  }
  
  /*
   * this func need to check whether 'or' need to be printed,
   * when the filter is the first filter with 'or' when dont print
   * when not first and same as previous, then print or and no filter name
   * when not first but different from previous, print name without 'or'
   * show_or = 1: show or and ignore filter name
   * show_or = 0: show filter name ignore or
   */
  void prev_show_chk(ipfw_insn *cmd, uint8_t *prev_module, uint8_t *prev_opcode,
  		int *show_or)
  {
  	if (cmd->len & F_OR) {
  		if (*prev_module == 0 && *prev_opcode == 0) {
  			/* first cmd with 'or' flag */
  			*show_or = 0;
  			*prev_module = cmd->module;
  			*prev_opcode = cmd->opcode;
  		} else if (cmd->module == *prev_module &&
  				cmd->opcode == *prev_opcode) {
  			/* cmd same as previous, same module and opcode */
  			*show_or = 1;
  		} else {
  			/* cmd different from prev*/
  			*show_or = 0;
  			*prev_module = cmd->module;
  			*prev_opcode = cmd->opcode;
  
  		}
  	} else {
  		*show_or = 0;
  		*prev_module = 0;
  		*prev_opcode = 0;
  	}
  }
  
  /*
   * word can be: proto from to other
   * proto show proto
   * from show from
   * to show to
   * other show all other filters
   */
  int show_filter(ipfw_insn *cmd, char *word, int type)
  {
  	struct ipfw3_keyword *k;
  	struct ipfw3_mapping *m;
  	shower_func fn;
  	int i, j, show_or;
  	uint8_t prev_module, prev_opcode;
  
  	k = keywords;
  	m = mappings;
  	for (i = 1; i < KEYWORD_SIZE; i++, k++) {
  		if (k->type == type) {
  			if (k->module == cmd->module &&
  					k->opcode == cmd->opcode) {
  				for (j = 1; j < MAPPING_SIZE; j++, m++) {
  					if (m->type == IN_USE &&
  						k->module == m->module &&
  						k->opcode == m->opcode) {
  						prev_show_chk(cmd, &prev_module,
  							&prev_opcode, &show_or);
  						if (cmd->len & F_NOT)
  							printf(" not");
  
  						fn = m->shower;
  						(*fn)(cmd, show_or);
  						return 1;
  					}
  				}
  			}
  		}
  	}
  	return 0;
  }
  
  void
  help(void)
  {
  	fprintf(stderr, "usage: ipfw3 [options]\n"
  			"	ipfw3 add [rulenum] [set id] action filters\n"
  			"	ipfw3 delete [rulenum]\n"
  			"	ipfw3 flush\n"
  			"	ipfw3 list [rulenum]\n"
  			"	ipfw3 show [rulenum]\n"
  			"	ipfw3 zero [rulenum]\n"
  			"	ipfw3 set [show|enable|disable]\n"
  			"	ipfw3 module\n"
  			"	ipfw3 [enable|disable]\n"
  			"	ipfw3 log [reset|off|on]\n"
  			"	ipfw3 nat [config|show|delete]\n"
  			"	ipfw3 pipe [config|show|delete]\n"
  			"	ipfw3 state [add|delete|list|show]\n"
  			"	ipfw3 nat [config|show]\n"
  			"\nsee ipfw3 manpage for details\n");
  	exit(EX_USAGE);
  }
  
  void
  rule_delete(int ac, char *av[])
  {
  	int error, rulenum;
  
  	NEXT_ARG;
  
  	while (ac && isdigit(**av)) {
  		rulenum = atoi(*av);
  		error = do_set_x(IP_FW_DEL, &rulenum, sizeof(int));
  		if (error) {
  			err(EX_OSERR, "do_get_x(IP_FW_DEL)");
  		}
  		NEXT_ARG;
  	}
  }
  
  /*
   * helper function, updates the pointer to cmd with the length
   * of the current command, and also cleans up the first word of
   * the new command in case it has been clobbered before.
   */
  ipfw_insn*
  next_cmd(ipfw_insn *cmd)
  {
  	cmd += F_LEN(cmd);
  	bzero(cmd, sizeof(*cmd));
  	return cmd;
  }
  
  /*
   * Parse arguments and assemble the microinstructions which make up a rule.
   * Rules are added into the 'rulebuf' and then copied in the correct order
   * into the actual rule.
   *
   *
   */
  void
  rule_add(int ac, char *av[])
  {
  	/*
  	 * rules are added into the 'rulebuf' and then copied in
  	 * the correct order into the actual rule.
  	 * Some things that need to go out of order (prob, action etc.)
  	 * go into actbuf[].
  	 */
  	static uint32_t rulebuf[IPFW_RULE_SIZE_MAX];
  	static uint32_t actbuf[IPFW_RULE_SIZE_MAX];
  	static uint32_t othbuf[IPFW_RULE_SIZE_MAX];
  	static uint32_t cmdbuf[IPFW_RULE_SIZE_MAX];
  
  	ipfw_insn *src, *dst, *cmd, *action, *other;
  	ipfw_insn *prev;
  	char *prev_av;
  	ipfw_insn *the_comment = NULL;
  	struct ipfw_ioc_rule *rule;
  	struct ipfw3_keyword *key;
  	struct ipfw3_mapping *map;
  	parser_func fn;
  	int i, j;
  
  	bzero(actbuf, sizeof(actbuf)); 		/* actions go here */
  	bzero(othbuf, sizeof(actbuf)); 		/* others */
  	bzero(cmdbuf, sizeof(cmdbuf)); 		/* filters */
  	bzero(rulebuf, sizeof(rulebuf));
  
  	rule = (struct ipfw_ioc_rule *)rulebuf;
  	cmd = (ipfw_insn *)cmdbuf;
  	action = (ipfw_insn *)actbuf;
  	other = (ipfw_insn *)othbuf;
  
  	NEED2("need more parameters");
  	NEXT_ARG;
  
  	/* [rule N]	-- Rule number optional */
  	if (ac && isdigit(**av)) {
  		rule->rulenum = atoi(*av);
  		NEXT_ARG;
  	}
  
  	/* [set N]	-- set number (0..30), optional */
  	if (ac > 1 && !strncmp(*av, "set", strlen(*av))) {
  		int set = strtoul(av[1], NULL, 10);
  		if (set < 0 || set > 30)
  			errx(EX_DATAERR, "illegal set %s", av[1]);
  		rule->set = set;
  		av += 2; ac -= 2;
  	}
  
  	/*
  	 * parse before
  	 */
  	for (;;) {
  		for (i = 0, key = keywords; i < KEYWORD_SIZE; i++, key++) {
  			if (key->type == BEFORE &&
  				strcmp(key->word, *av) == 0) {
  				for (j = 0, map = mappings;
  					j < MAPPING_SIZE; j++, map++) {
  					if (map->type == IN_USE &&
  						map->module == key->module &&
  						map->opcode == key->opcode ) {
  						fn = map->parser;
  						(*fn)(&other, &ac, &av);
  						break;
  					}
  				}
  				break;
  			}
  		}
  		if (i >= KEYWORD_SIZE) {
  			break;
  		} else if (F_LEN(other) > 0) {
  			if (other->module == MODULE_BASIC_ID &&
  				other->opcode == O_BASIC_CHECK_STATE) {
  				other = next_cmd(other);
  				goto done;
  			}
  			other = next_cmd(other);
  		}
  	}
  
  	/*
  	 * parse actions
  	 *
  	 * only accept 1 action
  	 */
  	NEED1("missing action");
  	for (i = 0, key = keywords; i < KEYWORD_SIZE; i++, key++) {
  		if (ac > 0 && key->type == ACTION &&
  			strcmp(key->word, *av) == 0) {
  			for (j = 0, map = mappings;
  					j < MAPPING_SIZE; j++, map++) {
  				if (map->type == IN_USE &&
  					map->module == key->module &&
  					map->opcode == key->opcode) {
  					fn = map->parser;
  					(*fn)(&action, &ac, &av);
  					break;
  				}
  			}
  			break;
  		}
  	}
  	if (F_LEN(action) > 0)
  		action = next_cmd(action);
  
  	/*
  	 * parse protocol
  	 */
  	if (strcmp(*av, "proto") == 0){
  		NEXT_ARG;
  	}
  
  	NEED1("missing protocol");
  	for (i = 0, key = keywords; i < KEYWORD_SIZE; i++, key++) {
  		if (key->type == PROTO &&
  			strcmp(key->word, "proto") == 0) {
  			for (j = 0, map = mappings;
  					j < MAPPING_SIZE; j++, map++) {
  				if (map->type == IN_USE &&
  					map->module == key->module &&
  					map->opcode == key->opcode ) {
  					fn = map->parser;
  					(*fn)(&cmd, &ac, &av);
  					break;
  				}
  			}
  			break;
  		}
  	}
  	if (F_LEN(cmd) > 0)
  		cmd = next_cmd(cmd);
  
  	/*
  	 * other filters
  	 */
  	while (ac > 0) {
  		char *s, *cur;		/* current filter */
  		ipfw_insn_u32 *cmd32; 	/* alias for cmd */
  
  		s = *av;
  		cmd32 = (ipfw_insn_u32 *)cmd;
  		if (strcmp(*av, "or") == 0) {
  			if (prev == NULL)
  				errx(EX_USAGE, "'or' should"
  						"between two filters\n");
  			prev->len |= F_OR;
  			cmd->len = F_OR;
  			*av = prev_av;
  		}
  		if (strcmp(*av, "not") == 0) {
  			if (cmd->len & F_NOT)
  				errx(EX_USAGE, "double \"not\" not allowed\n");
  			cmd->len = F_NOT;
  			NEXT_ARG;
  			continue;
  		}
  		cur = *av;
  		for (i = 0, key = keywords; i < KEYWORD_SIZE; i++, key++) {
  			if ((key->type == FILTER ||
                                  key->type == AFTER ||
                                  key->type == FROM ||
                                  key->type == TO) &&
  				strcmp(key->word, cur) == 0) {
  				for (j = 0, map = mappings;
  					j< MAPPING_SIZE; j++, map++) {
  					if (map->type == IN_USE &&
  						map->module == key->module &&
  						map->opcode == key->opcode ) {
  						fn = map->parser;
  						(*fn)(&cmd, &ac, &av);
  						break;
  					}
  				}
  				break;
  			} else if (i == KEYWORD_SIZE - 1) {
  				errx(EX_USAGE, "bad command `%s'", cur);
  			}
  		}
  		if (i >= KEYWORD_SIZE) {
  			break;
  		} else if (F_LEN(cmd) > 0) {
  			prev = cmd;
  			prev_av = cur;
  			cmd = next_cmd(cmd);
  		}
  	}
  
  done:
  	if (ac>0)
  		errx(EX_USAGE, "bad command `%s'", *av);
  
  	/*
  	 * Now copy stuff into the rule.
  	 * [filters][others][action][comment]
  	 */
  	dst = (ipfw_insn *)rule->cmd;
  	/*
  	 * copy all filters, except comment
  	 */
  	src = (ipfw_insn *)cmdbuf;
  	for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
  		/* pick comment out */
  		i = F_LEN(src);
  		if (src->module == MODULE_BASIC_ID &&
  				src->opcode == O_BASIC_COMMENT) {
  			the_comment=src;
  		} else {
  			bcopy(src, dst, i * sizeof(u_int32_t));
  			dst = (ipfw_insn *)((uint32_t *)dst + i);
  		}
  	}
  
  	/*
  	 * start action section, it begin with others
  	 */
  	rule->act_ofs = (uint32_t *)dst - (uint32_t *)(rule->cmd);
  
  	/*
  	 * copy all other others
  	 */
  	for (src = (ipfw_insn *)othbuf; src != other; src += i) {
  		i = F_LEN(src);
  		bcopy(src, dst, i * sizeof(u_int32_t));
  		dst = (ipfw_insn *)((uint32_t *)dst + i);
  	}
  
  	/* copy the action to the end of rule */
  	src = (ipfw_insn *)actbuf;
  	i = F_LEN(src);
  	bcopy(src, dst, i * sizeof(u_int32_t));
  	dst = (ipfw_insn *)((uint32_t *)dst + i);
  
  	/*
  	 * comment place behind the action
  	 */
  	if (the_comment != NULL) {
  		i = F_LEN(the_comment);
  		bcopy(the_comment, dst, i * sizeof(u_int32_t));
  		dst = (ipfw_insn *)((uint32_t *)dst + i);
  	}
  
  	rule->cmd_len = (u_int32_t *)dst - (u_int32_t *)(rule->cmd);
  	i = (void *)dst - (void *)rule;
  	if (do_set_x(IP_FW_ADD, (void *)rule, i) == -1) {
  		err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
  	}
  	if (!do_quiet)
  		rule_show(rule, 10, 10);
  }
  
  void
  rule_zero(int ac, char *av[])
  {
  	int rulenum;
  	int failed = EX_OK;
  
  	NEXT_ARG;
  
  	if (!ac) {
  		/* clear all entries */
  		if (do_set_x(IP_FW_ZERO, NULL, 0) < 0)
  			err(EX_UNAVAILABLE, "do_set_x(IP_FW_ZERO)");
  		if (!do_quiet)
  			printf("Accounting cleared.\n");
  		return;
  	}
  
  	while (ac) {
  		/* Rule number */
  		if (isdigit(**av)) {
  			rulenum = atoi(*av);
  			NEXT_ARG;
  			if (do_set_x(IP_FW_ZERO, &rulenum, sizeof rulenum)) {
  				warn("rule %u: do_set_x(IP_FW_ZERO)", rulenum);
  				failed = EX_UNAVAILABLE;
  			} else if (!do_quiet)
  				printf("Entry %d cleared\n", rulenum);
  		} else {
  			errx(EX_USAGE, "invalid rule number ``%s''", *av);
  		}
  	}
  	if (failed != EX_OK)
  		exit(failed);
  }
  
  void
  rule_flush(void)
  {
  	int cmd = IP_FW_FLUSH;
  	if (do_pipe) {
  		cmd = IP_DUMMYNET_FLUSH;
  	}
  	if (!do_force) {
  		int c;
  
  		printf("Are you sure? [yn] ");
  		fflush(stdout);
  		do {
  			c = toupper(getc(stdin));
  			while (c != '\n' && getc(stdin) != '\n')
  				if (feof(stdin))
  					return; /* and do not flush */
  		} while (c != 'Y' && c != 'N');
  		if (c == 'N')	/* user said no */
  			return;
  	}
  	if (do_set_x(cmd, NULL, 0) < 0 ) {
  		if (do_pipe)
  			errx(EX_USAGE, "pipe/queue in use");
  		else
  			errx(EX_USAGE, "do_set_x(IP_FW_FLUSH) failed");
  	}
  	if (!do_quiet) {
  		printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules");
  	}
  }
  
  void
  rule_list(int ac, char *av[])
  {
  	struct ipfw_ioc_rule *rule;
  
  	void *data = NULL;
  	int bcwidth, nbytes, pcwidth, width;
  	int nalloc = 1024;
  	int the_rule_num = 0;
  	int total_len;
  
  	NEXT_ARG;
  
  	/* get rules or pipes from kernel, resizing array as necessary */
  	nbytes = nalloc;
  
  	while (nbytes >= nalloc) {
  		nalloc = nalloc * 2 ;
  		nbytes = nalloc;
  		if ((data = realloc(data, nbytes)) == NULL)
  			err(EX_OSERR, "realloc");
  		if (do_get_x(IP_FW_GET, data, &nbytes) < 0)
  			err(EX_OSERR, "do_get_x(IP_FW_GET)");
  	}
  
  	/*
  	 * Count static rules.
  	 */
  	rule = data;
  	bcwidth = pcwidth = 0;
  	if (do_acct) {
  		total_len = 0;
  		for (rule = data; rule != NULL; rule = (void *)rule + IOC_RULESIZE(rule)) {
  			/* packet counter */
  			width = snprintf(NULL, 0, "%ju", (uintmax_t)rule->pcnt);
  			if (width > pcwidth)
  				pcwidth = width;
  
  			/* byte counter */
  			width = snprintf(NULL, 0, "%ju", (uintmax_t)rule->bcnt);
  			if (width > bcwidth)
  				bcwidth = width;
  
  			total_len += IOC_RULESIZE(rule);
  			if (total_len == nbytes) {
  				break;
  			}
  		}
  	}
  
  
  	if (ac == 1) {
  		the_rule_num = atoi(*av);
  	}
  
  	total_len = 0;
  	for (rule = data; rule != NULL; rule = (void *)rule + IOC_RULESIZE(rule)) {
  		if(the_rule_num == 0 || rule->rulenum == the_rule_num) {
  			rule_show(rule, pcwidth, bcwidth);
  		}
  		total_len += IOC_RULESIZE(rule);
  		if (total_len == nbytes) {
  			break;
  		}
  	}
  
  }
  
  void
  rule_show(struct ipfw_ioc_rule *rule, int pcwidth, int bcwidth)
  {
  	static int twidth = 0;
  	ipfw_insn *cmd;
  	int l;
  
  	u_int32_t set_disable = rule->sets;
  
  	if (set_disable & (1 << rule->set)) { /* disabled */
  		if (!show_sets)
  			return;
  		else
  			printf("# DISABLED ");
  	}
  	if (do_compact) {
  		printf("%u", rule->rulenum);
  	} else {
  		printf("%05u", rule->rulenum);
  	}
  
  	if (do_acct) {
  		if (do_compact) {
  			printf(" %ju %ju", (uintmax_t)rule->pcnt,
  						(uintmax_t)rule->bcnt);
  		} else {
  			printf(" %*ju %*ju", pcwidth, (uintmax_t)rule->pcnt,
  				bcwidth, (uintmax_t)rule->bcnt);
  		}
  	}
  
  	if (do_time == 1) {
  		char timestr[30];
  
  		if (twidth == 0) {
  			strcpy(timestr, ctime((time_t *)&twidth));
  			*strchr(timestr, '\n') = '\0';
  			twidth = strlen(timestr);
  		}
  		if (rule->timestamp) {
  			time_t t = _long_to_time(rule->timestamp);
  
  			strcpy(timestr, ctime(&t));
  			*strchr(timestr, '\n') = '\0';
  			printf(" %s", timestr);
  		} else {
  			printf(" %*s", twidth, " ");
  		}
  	} else if (do_time == 2) {
  		printf( " %10u", rule->timestamp);
  	}
  
  	if (show_sets)
  		printf(" set %d", rule->set);
  
  
  	struct ipfw3_keyword *k;
  	struct ipfw3_mapping *m;
  	shower_func fn, comment_fn = NULL;
  	ipfw_insn *comment_cmd;
  	int i, j, changed;
  
  	/*
  	 * show others and actions
  	 */
  	for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule);
  		l > 0; l -= F_LEN(cmd),
  		cmd = (ipfw_insn *)((uint32_t *)cmd + F_LEN(cmd))) {
  		k = keywords;
  		m = mappings;
  		for (i = 1; i< KEYWORD_SIZE; i++, k++) {
  			if ( k->module == cmd->module && k->opcode == cmd->opcode ) {
  				for (j = 1; j< MAPPING_SIZE; j++, m++) {
  					if (m->type == IN_USE &&
  						m->module == cmd->module &&
  						m->opcode == cmd->opcode) {
  						if (cmd->module == MODULE_BASIC_ID &&
  							cmd->opcode == O_BASIC_COMMENT) {
  							comment_fn = m->shower;
  							comment_cmd = cmd;
  						} else {
  							fn = m->shower;
  							(*fn)(cmd, 0);
  						}
  						if (cmd->module == MODULE_BASIC_ID &&
  							cmd->opcode ==
  								O_BASIC_CHECK_STATE) {
  							goto done;
  						}
  						break;
  					}
  				}
  				break;
  			}
  		}
  	}
  
  	/*
  	 * show proto
  	 */
  	changed=0;
  	for (l = rule->act_ofs, cmd = rule->cmd; l > 0; l -= F_LEN(cmd),
  			cmd = (ipfw_insn *)((uint32_t *)cmd + F_LEN(cmd))) {
  		changed = show_filter(cmd, "proto", PROTO);
  	}
  	if (!changed && !do_quiet)
  		printf(" ip");
  
  	/*
  	 * show from
  	 */
  	changed = 0;
  	for (l = rule->act_ofs, cmd = rule->cmd; l > 0; l -= F_LEN(cmd),
  			cmd = (ipfw_insn *)((uint32_t *)cmd + F_LEN(cmd))) {
  		changed = show_filter(cmd, "from", FROM);
  	}
  	if (!changed && !do_quiet)
  		printf(" from any");
  
  	/*
  	 * show to
  	 */
  	changed = 0;
  	for (l = rule->act_ofs, cmd = rule->cmd; l > 0; l -= F_LEN(cmd),
  			cmd = (ipfw_insn *)((uint32_t *)cmd + F_LEN(cmd))) {
  		changed = show_filter(cmd, "to", TO);
  	}
  	if (!changed && !do_quiet)
  		printf(" to any");
  
  	/*
  	 * show other filters
  	 */
  	l = rule->act_ofs;
  	cmd = rule->cmd;
  	m = mappings;
  	for ( ; l > 0; ) {
  		show_filter(cmd, "other", FILTER);
  		l -= F_LEN(cmd);
  		cmd=(ipfw_insn *)((uint32_t *)cmd + F_LEN(cmd));
  	}
  
  	/* show the comment in the end */
  	if (comment_fn != NULL) {
  		(*comment_fn)(comment_cmd, 0);
  	}
  done:
  	printf("\n");
  }
  
  /*
   * do_set_x - extended version og do_set
   * insert a x_header in the beginning of the rule buf
   * and call setsockopt() with IP_FW_X.
   */
  int
  do_set_x(int optname, void *rule, int optlen)
  {
  	int len, *newbuf, retval;
  	ip_fw_x_header *x_header;
  
  	if (fw3_socket < 0)
  		err(EX_UNAVAILABLE, "socket not avaialble");
  
  	len = optlen + sizeof(ip_fw_x_header);
  	newbuf = malloc(len);
  	if (newbuf == NULL)
  		err(EX_OSERR, "malloc newbuf in do_set_x");
  
  	bzero(newbuf, len);
  	x_header = (ip_fw_x_header *)newbuf;
  	x_header->opcode = optname;
  	/* copy the rule into the newbuf, just after the x_header*/
  	bcopy(rule, ++x_header, optlen);
  	retval = setsockopt(fw3_socket, IPPROTO_IP, IP_FW_X, newbuf, len);
  	free(newbuf);
  	return retval;
  }
  
  /*
   * same as do_set_x
   */
  int
  do_get_x(int optname, void *rule, int *optlen)
  {
  	int len, *newbuf, retval;
  	ip_fw_x_header *x_header;
  
  	if (fw3_socket < 0)
  		err(EX_UNAVAILABLE, "socket not avaialble");
  
  	len = *optlen + sizeof(ip_fw_x_header);
  	newbuf = malloc(len);
  	if (newbuf == NULL)
  		err(EX_OSERR, "malloc newbuf in do_get_x");
  
  	bzero(newbuf, len);
  	x_header = (ip_fw_x_header *)newbuf;
  	x_header->opcode = optname;
  	/* copy the rule into the newbuf, just after the x_header*/
  	bcopy(rule, ++x_header, *optlen);
  	retval = getsockopt(fw3_socket, IPPROTO_IP, IP_FW_X, newbuf, &len);
  	bcopy(newbuf, rule, len);
  	free(newbuf);
  	*optlen = len;
  	return retval;
  }
  
  int
  ipfw3_main(int ac, char **av)
  {
  	int ch;
  
  	if (ac == 1)
  		help();
  
  	/* Set the force flag for non-interactive processes */
  	do_force = !isatty(STDIN_FILENO);
  
  	optind = optreset = 1;
  	while ((ch = getopt(ac, av, "hs:acefStTv")) != -1)
  		switch (ch) {
  		case 'h': /* help */
  			help();
  			break; 	/* NOTREACHED */
  
  		case 's': /* sort */
  			do_sort = atoi(optarg);
  			break;
  		case 'a':
  			do_acct = 1;
  			break;
  		case 'c':
  			do_compact = 1;
  			break;
  		case 'e':
  			do_expired = 1;
  			break;
  		case 'f':
  			do_force = 1;
  			break;
  		case 'S':
  			show_sets = 1;
  			break;
  		case 't':
  			do_time = 1;
  			break;
  		case 'T':
  			do_time = 2;
  			break;
  		case 'v':
  			do_quiet = 0;
  			verbose++;
  			break;
  		default:
  			help();
  		}
  
  	ac -= optind;
  	av += optind;
  	NEED1("bad arguments, for usage summary ``ipfw3''");
  
  	/*
  	 * optional: pipe or queue or nat
  	 */
  	do_nat = 0;
  	do_pipe = 0;
  	if (!strncmp(*av, "nat", strlen(*av)))
  		do_nat = 1;
  	else if (!strncmp(*av, "pipe", strlen(*av))) {
  		do_pipe = 1;
  	} else if (!strncmp(*av, "queue", strlen(*av))) {
  		do_pipe = 2;
  	}
  	NEED1("missing command");
  
  	/*
  	 * for pipes and queues and nat we normally say 'pipe NN config'
  	 * but the code is easier to parse as 'pipe config NN'
  	 * so we swap the two arguments.
  	 */
  	if ((do_pipe || do_nat) && ac > 2 && isdigit(*(av[1]))) {
  		char *p = av[1];
  		av[1] = av[2];
  		av[2] = p;
  	}
  
  	if (!strncmp(*av, "add", strlen(*av))) {
  		module_load();
  		rule_add(ac, av);
  	} else if (!strncmp(*av, "delete", strlen(*av))) {
  		rule_delete(ac, av);
  	} else if (!strncmp(*av, "flush", strlen(*av))) {
  		rule_flush();
  	} else if (!strncmp(*av, "list", strlen(*av))) {
  		module_load();
  		rule_list(ac, av);
  	} else if (!strncmp(*av, "show", strlen(*av))) {
  		do_acct++;
  		module_load();
  		rule_list(ac, av);
  	} else if (!strncmp(*av, "zero", strlen(*av))) {
  		rule_zero(ac, av);
  	} else if (!strncmp(*av, "set", strlen(*av))) {
  		set_main(ac, av);
  	} else if (!strncmp(*av, "module", strlen(*av))) {
  		NEXT_ARG;
  		if (!strncmp(*av, "list", strlen(*av))) {
  			module_list(ac, av);
  		} else {
  			errx(EX_USAGE, "bad ipfw3 module command `%s'", *av);
  		}
  	} else if (!strncmp(*av, "log", strlen(*av))) {
  		NEXT_ARG;
  		log_main(ac, av);
  	} else if (!strncmp(*av, "nat", strlen(*av))) {
  		NEXT_ARG;
  		nat_main(ac, av);
  	} else if (!strncmp(*av, "pipe", strlen(*av)) ||
  		!strncmp(*av, "queue", strlen(*av))) {
  		NEXT_ARG;
  		dummynet_main(ac, av);
  	} else if (!strncmp(*av, "state", strlen(*av))) {
  		NEXT_ARG;
  		state_main(ac, av);
  	} else if (!strncmp(*av, "table", strlen(*av))) {
  		if (ac > 2 && isdigit(*(av[1]))) {
  			char *p = av[1];
  			av[1] = av[2];
  			av[2] = p;
  		}
  		NEXT_ARG;
  		table_main(ac, av);
  	} else if (!strncmp(*av, "sync", strlen(*av))) {
  		NEXT_ARG;
  		sync_main(ac, av);
  	} else {
  		errx(EX_USAGE, "bad ipfw3 command `%s'", *av);
  	}
  	return 0;
  }
  
  void
  ipfw3_readfile(int ac, char *av[])
  {
  	char	buf[BUFSIZ];
  	char	*a, *p, *args[MAX_ARGS], *cmd = NULL;
  	char	linename[17];
  	int	i=0, lineno=0, qflag=0, pflag=0, status;
  	FILE	*f = NULL;
  	pid_t	preproc = 0;
  	int	c;
  
  	while ((c = getopt(ac, av, "D:U:p:q")) != -1) {
  		switch (c) {
  		case 'D':
  			if (!pflag)
  				errx(EX_USAGE, "-D requires -p");
  			if (i > MAX_ARGS - 2)
  				errx(EX_USAGE, "too many -D or -U options");
  			args[i++] = "-D";
  			args[i++] = optarg;
  			break;
  
  		case 'U':
  			if (!pflag)
  				errx(EX_USAGE, "-U requires -p");
  			if (i > MAX_ARGS - 2)
  				errx(EX_USAGE, "too many -D or -U options");
  			args[i++] = "-U";
  			args[i++] = optarg;
  			break;
  
  		case 'p':
  			pflag = 1;
  			cmd = optarg;
  			args[0] = cmd;
  			i = 1;
  			break;
  
  		case 'q':
  			qflag = 1;
  			break;
  
  		default:
  			errx(EX_USAGE, "bad arguments, for usage"
  			    " summary ``ipfw''");
  		}
  	}
  
  	av += optind;
  	ac -= optind;
  	if (ac != 1)
  		errx(EX_USAGE, "extraneous filename arguments");
  
  	if ((f = fopen(av[0], "r")) == NULL)
  		err(EX_UNAVAILABLE, "fopen: %s", av[0]);
  
  	if (pflag) {
  		/* pipe through preprocessor (cpp or m4) */
  		int pipedes[2];
  
  		args[i] = NULL;
  
  		if (pipe(pipedes) == -1)
  			err(EX_OSERR, "cannot create pipe");
  
  		switch ((preproc = fork())) {
  		case -1:
  			err(EX_OSERR, "cannot fork");
  
  		case 0:
  			/* child */
  			if (dup2(fileno(f), 0) == -1 ||
  			    dup2(pipedes[1], 1) == -1) {
  				err(EX_OSERR, "dup2()");
  			}
  			fclose(f);
  			close(pipedes[1]);
  			close(pipedes[0]);
  			execvp(cmd, args);
  			err(EX_OSERR, "execvp(%s) failed", cmd);
  
  		default:
  			/* parent */
  			fclose(f);
  			close(pipedes[1]);
  			if ((f = fdopen(pipedes[0], "r")) == NULL) {
  				int savederrno = errno;
  
  				kill(preproc, SIGTERM);
  				errno = savederrno;
  				err(EX_OSERR, "fdopen()");
  			}
  		}
  	}
  
  	while (fgets(buf, BUFSIZ, f)) {
  		lineno++;
  		sprintf(linename, "Line %d", lineno);
  		args[0] = linename;
  
  		if (*buf == '#')
  			continue;
  		if ((p = strchr(buf, '#')) != NULL)
  			*p = '\0';
  		i = 1;
  		if (qflag)
  			args[i++] = "-q";
  		for (a = strtok(buf, WHITESP); a && i < MAX_ARGS;
  			a = strtok(NULL, WHITESP), i++) {
  			args[i] = a;
  		}
  
  		if (i == (qflag? 2: 1))
  			continue;
  		if (i == MAX_ARGS)
  			errx(EX_USAGE, "%s: too many arguments", linename);
  
  		args[i] = NULL;
  		ipfw3_main(i, args);
  	}
  	fclose(f);
  	if (pflag) {
  		if (waitpid(preproc, &status, 0) == -1)
  			errx(EX_OSERR, "waitpid()");
  		if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
  			errx(EX_UNAVAILABLE, "preprocessor exited with status %d",
  				WEXITSTATUS(status));
  		else if (WIFSIGNALED(status))
  			errx(EX_UNAVAILABLE, "preprocessor exited with signal %d",
  				WTERMSIG(status));
  	}
  }
  
  int
  main(int ac, char *av[])
  {
  	fw3_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
  	if (fw3_socket < 0)
  		err(EX_UNAVAILABLE, "socket");
  
  	memset(keywords, 0, LEN_FW3_KEYWORD * KEYWORD_SIZE);
  	memset(mappings, 0, LEN_FW3_MAPPING * MAPPING_SIZE);
  
  	prepare_default_funcs();
  
  	if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0)
  		ipfw3_readfile(ac, av);
  	else
  		ipfw3_main(ac, av);
  	return EX_OK;
  }
  