diff options
author | luigi <luigi@FreeBSD.org> | 2009-01-27 10:18:55 +0000 |
---|---|---|
committer | luigi <luigi@FreeBSD.org> | 2009-01-27 10:18:55 +0000 |
commit | 80a7476516826d36a4d351787cfe847b666aa55a (patch) | |
tree | a1a40a84f3213075ec4e957b2dfc8b2f1ee5a02e /sbin | |
parent | 5b66cc0d53742e78e265b5587f7841f9c899d031 (diff) | |
download | FreeBSD-src-80a7476516826d36a4d351787cfe847b666aa55a.zip FreeBSD-src-80a7476516826d36a4d351787cfe847b666aa55a.tar.gz |
Start splitting the monster file in smaller blocks.
In this episode:
- introduce a common header with a minimal set of common definitions;
- bring the main() function and options parser in main.c
- rename the main functions with an ipfw_ prefix
No code changes except for the introduction of a global variable,
resvd_set_number, which stores the RESVD_SET value from ip_fw.h
and is used to remove the dependency of main.c from ip_fw.h
(and the subtree of dependencies) for just a single constant.
Diffstat (limited to 'sbin')
-rw-r--r-- | sbin/ipfw/Makefile | 2 | ||||
-rw-r--r-- | sbin/ipfw/ipfw2.c | 624 | ||||
-rw-r--r-- | sbin/ipfw/ipfw2.h | 105 | ||||
-rw-r--r-- | sbin/ipfw/main.c | 539 |
4 files changed, 681 insertions, 589 deletions
diff --git a/sbin/ipfw/Makefile b/sbin/ipfw/Makefile index 06dd40f..34e850b 100644 --- a/sbin/ipfw/Makefile +++ b/sbin/ipfw/Makefile @@ -1,7 +1,7 @@ # $FreeBSD$ PROG= ipfw -SRCS= ipfw2.c +SRCS= ipfw2.c main.c WARNS?= 2 MAN= ipfw.8 diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index 4744966..e457328 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -24,7 +24,8 @@ #include <sys/socket.h> #include <sys/sockio.h> #include <sys/sysctl.h> -#include <sys/wait.h> + +#include "ipfw2.h" #include <ctype.h> #include <err.h> @@ -32,7 +33,6 @@ #include <grp.h> #include <netdb.h> #include <pwd.h> -#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -59,49 +59,9 @@ #include <arpa/inet.h> #include <alias.h> -/* - * Options that can be set on the command line. - * When reading commands from a file, a subset of the options can also - * be applied globally by specifying them before the file name. - * After that, each line can contain its own option that changes - * the global value. - * XXX The context is not restored after each line. - */ - -struct cmdline_opts { - /* boolean options: */ - int do_value_as_ip; /* show table value as IP */ - int do_resolv; /* try to resolve all ip to names */ - int do_time; /* Show time stamps */ - int do_quiet; /* Be quiet in add and flush */ - int do_pipe; /* this cmd refers to a pipe */ - int do_nat; /* this cmd refers to a nat config */ - int do_dynamic; /* display dynamic rules */ - int do_expired; /* display expired dynamic rules */ - int do_compact; /* show rules in compact mode */ - int do_force; /* do not ask for confirmation */ - int show_sets; /* display the set each rule belongs to */ - int test_only; /* only check syntax */ - int comment_only; /* only print action and comment */ - int verbose; /* be verbose on some commands */ - - /* The options below can have multiple values. */ - - int do_sort; /* field to sort results (0 = no) */ - /* valid fields are 1 and above */ - - int use_set; /* work with specified set number */ - /* 0 means all sets, otherwise apply to set use_set - 1 */ - -}; - -struct cmdline_opts co; +struct cmdline_opts co; /* global options */ -/* - * the following macro returns an error message if we run out of - * arguments. - */ -#define NEED1(msg) {if (!ac) errx(EX_USAGE, msg);} +int resvd_set_number = RESVD_SET; #define GET_UINT_ARG(arg, min, max, tok, s_x) do { \ if (!ac) \ @@ -143,19 +103,6 @@ PRINT_UINT_ARG(const char *str, uint32_t arg) printf("%u", arg); } -/* - * _s_x is a structure that stores a string <-> token pairs, used in - * various places in the parser. Entries are stored in arrays, - * with an entry with s=NULL as terminator. - * The search routines are match_token() and match_value(). - * Often, an element with x=0 contains an error string. - * - */ -struct _s_x { - char const *s; - int x; -}; - static struct _s_x f_tcpflags[] = { { "syn", TH_SYN }, { "fin", TH_FIN }, @@ -528,7 +475,7 @@ align_uint64(uint64_t *pll) { return ret; } -static void * +void * safe_calloc(size_t number, size_t size) { void *ret = calloc(number, size); @@ -538,7 +485,7 @@ safe_calloc(size_t number, size_t size) return ret; } -static void * +void * safe_realloc(void *ptr, size_t size) { void *ret = realloc(ptr, size); @@ -615,7 +562,7 @@ match_value(struct _s_x *p, int value) * This function will be removed in the future through the usual * deprecation process. */ -static int +int _substrcmp(const char *str1, const char* str2) { @@ -2396,8 +2343,8 @@ list_pipes(void *data, uint nbytes, int ac, char *av[]) * ipfw set move X to Y * ipfw set move rule X to Y */ -static void -sets_handler(int ac, char *av[]) +void +ipfw_sets_handler(int ac, char *av[]) { uint32_t set_disable, masks[2]; int i, nbytes; @@ -2496,8 +2443,8 @@ sets_handler(int ac, char *av[]) errx(EX_USAGE, "invalid set command %s\n", *av); } -static void -sysctl_handler(int ac, char *av[], int which) +void +ipfw_sysctl_handler(int ac, char *av[], int which) { ac--; av++; @@ -2526,8 +2473,8 @@ sysctl_handler(int ac, char *av[], int which) } } -static void -list(int ac, char *av[], int show_counters) +void +ipfw_list(int ac, char *av[], int show_counters) { struct ip_fw *r; ipfw_dyn_rule *dynrules, *d; @@ -2723,52 +2670,6 @@ done: #undef NEXT } -static void -help(void) -{ - fprintf(stderr, -"ipfw syntax summary (but please do read the ipfw(8) manpage):\n\n" -"\tipfw [-abcdefhnNqStTv] <command>\n\n" -"where <command> is one of the following:\n\n" -"add [num] [set N] [prob x] RULE-BODY\n" -"{pipe|queue} N config PIPE-BODY\n" -"[pipe|queue] {zero|delete|show} [N{,N}]\n" -"nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|reset|\n" -" reverse|proxy_only|redirect_addr linkspec|\n" -" redirect_port linkspec|redirect_proto linkspec}\n" -"set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n" -"set N {show|list|zero|resetlog|delete} [N{,N}] | flush\n" -"table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n" -"table all {flush | list}\n" -"\n" -"RULE-BODY: check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n" -"ACTION: check-state | allow | count | deny | unreach{,6} CODE |\n" -" skipto N | {divert|tee} PORT | forward ADDR |\n" -" pipe N | queue N | nat N | setfib FIB\n" -"PARAMS: [log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n" -"ADDR: [ MAC dst src ether_type ] \n" -" [ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n" -" [ ipv6|ip6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n" -"IPADDR: [not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n" -"IP6ADDR: [not] { any | me | me6 | ip6/bits | IP6LIST }\n" -"IP6LIST: { ip6 | ip6/bits }[,IP6LIST]\n" -"IPLIST: { ip | ip/bits | ip:mask }[,IPLIST]\n" -"OPTION_LIST: OPTION [OPTION_LIST]\n" -"OPTION: bridged | diverted | diverted-loopback | diverted-output |\n" -" {dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n" -" {dst-port|src-port} LIST |\n" -" estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n" -" iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n" -" ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n" -" icmp6types LIST | ext6hdr LIST | flow-id N[,N] | fib FIB |\n" -" mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n" -" setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n" -" tcpdatalen LIST | verrevpath | versrcreach | antispoof\n" -); -exit(0); -} - - static int lookup_host (char *host, struct in_addr *ipaddr) { @@ -3240,8 +3141,8 @@ fill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode, } -static void -delete(int ac, char *av[]) +void +ipfw_delete(int ac, char *av[]) { uint32_t rulenum; struct dn_pipe p; @@ -3871,9 +3772,6 @@ nospace: } static void -show_nat(int ac, char **av); - -static void print_nat_config(unsigned char *buf) { struct cfg_nat *n; @@ -3988,8 +3886,8 @@ print_nat_config(unsigned char *buf) printf("\n"); } -static void -config_nat(int ac, char **av) +void +ipfw_config_nat(int ac, char **av) { struct cfg_nat *n; /* Nat instance configuration. */ int i, len, off, tok; @@ -4087,12 +3985,12 @@ config_nat(int ac, char **av) /* After every modification, we show the resultant rule. */ int _ac = 3; char *_av[] = {"show", "config", id}; - show_nat(_ac, _av); + ipfw_show_nat(_ac, _av); } } -static void -config_pipe(int ac, char **av) +void +ipfw_config_pipe(int ac, char **av) { struct dn_pipe p; int i; @@ -4772,8 +4670,8 @@ add_dst(ipfw_insn *cmd, char *av, u_char proto) * various match patterns, log/altq actions, and the actual action. * */ -static void -add(int ac, char *av[]) +void +ipfw_add(int ac, char *av[]) { /* * rules are added into the 'rulebuf' and then copied in @@ -5781,13 +5679,18 @@ done: show_ipfw(rule, 0, 0); } -static void -zero(int ac, char *av[], int optname /* IP_FW_ZERO or IP_FW_RESETLOG */) +/* + * clear the counters or the log counters. + */ +void +ipfw_zero(int ac, char *av[], int optname /* 0 = IP_FW_ZERO, 1 = IP_FW_RESETLOG */) { uint32_t arg, saved_arg; int failed = EX_OK; - char const *name = optname == IP_FW_ZERO ? "ZERO" : "RESETLOG"; char const *errstr; + char const *name = optname ? "RESETLOG" : "ZERO"; + + optname = optname ? IP_FW_RESETLOG : IP_FW_ZERO; av++; ac--; @@ -5830,8 +5733,8 @@ zero(int ac, char *av[], int optname /* IP_FW_ZERO or IP_FW_RESETLOG */) exit(failed); } -static void -flush(int force) +void +ipfw_flush(int force) { int cmd = co.do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH; @@ -5862,18 +5765,6 @@ flush(int force) printf("Flushed all %s.\n", co.do_pipe ? "pipes" : "rules"); } -/* - * Free a the (locally allocated) copy of command line arguments. - */ -static void -free_args(int ac, char **av) -{ - int i; - - for (i=0; i < ac; i++) - free(av[i]); - free(av); -} static void table_list(ipfw_table_entry ent, int need_header); @@ -5884,8 +5775,8 @@ static void table_list(ipfw_table_entry ent, int need_header); * ipfw table {N | all} flush * ipfw table {N | all} list */ -static void -table_handler(int ac, char *av[]) +void +ipfw_table_handler(int ac, char *av[]) { ipfw_table_entry ent; int do_add; @@ -6033,8 +5924,8 @@ table_list(ipfw_table_entry ent, int need_header) free(tbl); } -static void -show_nat(int ac, char **av) +void +ipfw_show_nat(int ac, char **av) { struct cfg_nat *n; struct cfg_redir *e; @@ -6107,446 +5998,3 @@ show_nat(int ac, char **av) } } } - -/* - * Called with the arguments, including program name because getopt - * wants it to be present. - * Returns 0 if successful, 1 if empty command, errx() in case of errors. - */ -static int -ipfw_main(int oldac, char **oldav) -{ - int ch, ac, save_ac; - const char *errstr; - char **av, **save_av; - int do_acct = 0; /* Show packet/byte count */ - -#define WHITESP " \t\f\v\n\r" - if (oldac < 2) - return 1; /* need at least one argument */ - if (oldac == 2) { - /* - * If we are called with a single string, try to split it into - * arguments for subsequent parsing. - * But first, remove spaces after a ',', by copying the string - * in-place. - */ - char *arg = oldav[1]; /* The string is the first arg. */ - int l = strlen(arg); - int copy = 0; /* 1 if we need to copy, 0 otherwise */ - int i, j; - for (i = j = 0; i < l; i++) { - if (arg[i] == '#') /* comment marker */ - break; - if (copy) { - arg[j++] = arg[i]; - copy = !index("," WHITESP, arg[i]); - } else { - copy = !index(WHITESP, arg[i]); - if (copy) - arg[j++] = arg[i]; - } - } - if (!copy && j > 0) /* last char was a 'blank', remove it */ - j--; - l = j; /* the new argument length */ - arg[j++] = '\0'; - if (l == 0) /* empty string! */ - return 1; - - /* - * First, count number of arguments. Because of the previous - * processing, this is just the number of blanks plus 1. - */ - for (i = 0, ac = 1; i < l; i++) - if (index(WHITESP, arg[i]) != NULL) - ac++; - - /* - * Allocate the argument list, including one entry for - * the program name because getopt expects it. - */ - av = safe_calloc(ac + 1, sizeof(char *)); - - /* - * Second, copy arguments from arg[] to av[]. For each one, - * j is the initial character, i is the one past the end. - */ - for (ac = 1, i = j = 0; i < l; i++) - if (index(WHITESP, arg[i]) != NULL || i == l-1) { - if (i == l-1) - i++; - av[ac] = safe_calloc(i-j+1, 1); - bcopy(arg+j, av[ac], i-j); - ac++; - j = i + 1; - } - } else { - /* - * If an argument ends with ',' join with the next one. - */ - int first, i, l; - - av = safe_calloc(oldac, sizeof(char *)); - for (first = i = ac = 1, l = 0; i < oldac; i++) { - char *arg = oldav[i]; - int k = strlen(arg); - - l += k; - if (arg[k-1] != ',' || i == oldac-1) { - /* Time to copy. */ - av[ac] = safe_calloc(l+1, 1); - for (l=0; first <= i; first++) { - strcat(av[ac]+l, oldav[first]); - l += strlen(oldav[first]); - } - ac++; - l = 0; - first = i+1; - } - } - } - - av[0] = strdup(oldav[0]); /* copy progname from the caller */ - /* Set the force flag for non-interactive processes */ - if (!co.do_force) - co.do_force = !isatty(STDIN_FILENO); - - /* Save arguments for final freeing of memory. */ - save_ac = ac; - save_av = av; - - optind = optreset = 1; /* restart getopt() */ - while ((ch = getopt(ac, av, "abcdefhinNqs:STtv")) != -1) - switch (ch) { - case 'a': - do_acct = 1; - break; - - case 'b': - co.comment_only = 1; - co.do_compact = 1; - break; - - case 'c': - co.do_compact = 1; - break; - - case 'd': - co.do_dynamic = 1; - break; - - case 'e': - co.do_expired = 1; - break; - - case 'f': - co.do_force = 1; - break; - - case 'h': /* help */ - free_args(save_ac, save_av); - help(); - break; /* NOTREACHED */ - - case 'i': - co.do_value_as_ip = 1; - break; - - case 'n': - co.test_only = 1; - break; - - case 'N': - co.do_resolv = 1; - break; - - case 'q': - co.do_quiet = 1; - break; - - case 's': /* sort */ - co.do_sort = atoi(optarg); - break; - - case 'S': - co.show_sets = 1; - break; - - case 't': - co.do_time = 1; - break; - - case 'T': - co.do_time = 2; /* numeric timestamp */ - break; - - case 'v': /* verbose */ - co.verbose = 1; - break; - - default: - free_args(save_ac, save_av); - return 1; - } - - ac -= optind; - av += optind; - NEED1("bad arguments, for usage summary ``ipfw''"); - - /* - * An undocumented behaviour of ipfw1 was to allow rule numbers first, - * e.g. "100 add allow ..." instead of "add 100 allow ...". - * In case, swap first and second argument to get the normal form. - */ - if (ac > 1 && isdigit(*av[0])) { - char *p = av[0]; - - av[0] = av[1]; - av[1] = p; - } - - /* - * Optional: pipe, queue or nat. - */ - co.do_nat = 0; - co.do_pipe = 0; - if (!strncmp(*av, "nat", strlen(*av))) - co.do_nat = 1; - else if (!strncmp(*av, "pipe", strlen(*av))) - co.do_pipe = 1; - else if (_substrcmp(*av, "queue") == 0) - co.do_pipe = 2; - else if (!strncmp(*av, "set", strlen(*av))) { - if (ac > 1 && isdigit(av[1][0])) { - co.use_set = strtonum(av[1], 0, RESVD_SET, &errstr); - if (errstr) - errx(EX_DATAERR, - "invalid set number %s\n", av[1]); - ac -= 2; av += 2; co.use_set++; - } - } - - if (co.do_pipe || co.do_nat) { - ac--; - av++; - } - NEED1("missing command"); - - /* - * For pipes, queues and nats we normally say 'nat|pipe NN config' - * but the code is easier to parse as 'nat|pipe config NN' - * so we swap the two arguments. - */ - if ((co.do_pipe || co.do_nat) && ac > 1 && isdigit(*av[0])) { - char *p = av[0]; - - av[0] = av[1]; - av[1] = p; - } - - int try_next = 0; - if (co.use_set == 0) { - if (_substrcmp(*av, "add") == 0) - add(ac, av); - else if (co.do_nat && _substrcmp(*av, "show") == 0) - show_nat(ac, av); - else if (co.do_pipe && _substrcmp(*av, "config") == 0) - config_pipe(ac, av); - else if (co.do_nat && _substrcmp(*av, "config") == 0) - config_nat(ac, av); - else if (_substrcmp(*av, "set") == 0) - sets_handler(ac, av); - else if (_substrcmp(*av, "table") == 0) - table_handler(ac, av); - else if (_substrcmp(*av, "enable") == 0) - sysctl_handler(ac, av, 1); - else if (_substrcmp(*av, "disable") == 0) - sysctl_handler(ac, av, 0); - else - try_next = 1; - } - - if (co.use_set || try_next) { - if (_substrcmp(*av, "delete") == 0) - delete(ac, av); - else if (_substrcmp(*av, "flush") == 0) - flush(co.do_force); - else if (_substrcmp(*av, "zero") == 0) - zero(ac, av, IP_FW_ZERO); - else if (_substrcmp(*av, "resetlog") == 0) - zero(ac, av, IP_FW_RESETLOG); - else if (_substrcmp(*av, "print") == 0 || - _substrcmp(*av, "list") == 0) - list(ac, av, do_acct); - else if (_substrcmp(*av, "show") == 0) - list(ac, av, 1 /* show counters */); - else - errx(EX_USAGE, "bad command `%s'", *av); - } - - /* Free memory allocated in the argument parsing. */ - free_args(save_ac, save_av); - return 0; -} - - -static void -ipfw_readfile(int ac, char *av[]) -{ -#define MAX_ARGS 32 - char buf[BUFSIZ]; - char *progname = av[0]; /* original program name */ - const char *cmd = NULL; /* preprocessor name, if any */ - const char *filename = av[ac-1]; /* file to read */ - int c, lineno=0; - FILE *f = NULL; - pid_t preproc = 0; - - while ((c = getopt(ac, av, "cfNnp:qS")) != -1) { - switch(c) { - case 'c': - co.do_compact = 1; - break; - - case 'f': - co.do_force = 1; - break; - - case 'N': - co.do_resolv = 1; - break; - - case 'n': - co.test_only = 1; - break; - - case 'p': - /* - * ipfw -p cmd [args] filename - * - * We are done with getopt(). All arguments - * except the filename go to the preprocessor, - * so we need to do the following: - * - check that a filename is actually present; - * - advance av by optind-1 to skip arguments - * already processed; - * - decrease ac by optind, to remove the args - * already processed and the final filename; - * - set the last entry in av[] to NULL so - * popen() can detect the end of the array; - * - set optind=ac to let getopt() terminate. - */ - if (optind == ac) - errx(EX_USAGE, "no filename argument"); - cmd = optarg; - av[ac-1] = NULL; - av += optind - 1; - ac -= optind; - optind = ac; - break; - - case 'q': - co.do_quiet = 1; - break; - - case 'S': - co.show_sets = 1; - break; - - default: - errx(EX_USAGE, "bad arguments, for usage" - " summary ``ipfw''"); - } - - } - - if (cmd == NULL && ac != optind + 1) - errx(EX_USAGE, "extraneous filename arguments %s", av[ac-1]); - - if ((f = fopen(filename, "r")) == NULL) - err(EX_UNAVAILABLE, "fopen: %s", filename); - - if (cmd != NULL) { /* pipe through preprocessor */ - int pipedes[2]; - - if (pipe(pipedes) == -1) - err(EX_OSERR, "cannot create pipe"); - - preproc = fork(); - if (preproc == -1) - err(EX_OSERR, "cannot fork"); - - if (preproc == 0) { - /* - * Child, will run the preprocessor with the - * file on stdin and the pipe on stdout. - */ - 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, av); - err(EX_OSERR, "execvp(%s) failed", cmd); - } else { /* parent, will reopen f as the pipe */ - fclose(f); - close(pipedes[1]); - if ((f = fdopen(pipedes[0], "r")) == NULL) { - int savederrno = errno; - - (void)kill(preproc, SIGTERM); - errno = savederrno; - err(EX_OSERR, "fdopen()"); - } - } - } - - while (fgets(buf, BUFSIZ, f)) { /* read commands */ - char linename[10]; - char *args[2]; - - lineno++; - sprintf(linename, "Line %d", lineno); - setprogname(linename); /* XXX */ - args[0] = progname; - args[1] = buf; - ipfw_main(2, args); - } - fclose(f); - if (cmd != NULL) { - int status; - - 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[]) -{ - /* - * If the last argument is an absolute pathname, interpret it - * as a file to be preprocessed. - */ - - if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0) - ipfw_readfile(ac, av); - else { - if (ipfw_main(ac, av)) { - errx(EX_USAGE, - "usage: ipfw [options]\n" - "do \"ipfw -h\" or \"man ipfw\" for details"); - } - } - return EX_OK; -} diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h new file mode 100644 index 0000000..0184e44 --- /dev/null +++ b/sbin/ipfw/ipfw2.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2002-2003 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. + * + * NEW command line interface for IP firewall facility + * + * $FreeBSD$ + */ + +/* + * Options that can be set on the command line. + * When reading commands from a file, a subset of the options can also + * be applied globally by specifying them before the file name. + * After that, each line can contain its own option that changes + * the global value. + * XXX The context is not restored after each line. + */ + +struct cmdline_opts { + /* boolean options: */ + int do_value_as_ip; /* show table value as IP */ + int do_resolv; /* try to resolve all ip to names */ + int do_time; /* Show time stamps */ + int do_quiet; /* Be quiet in add and flush */ + int do_pipe; /* this cmd refers to a pipe */ + int do_nat; /* this cmd refers to a nat config */ + int do_dynamic; /* display dynamic rules */ + int do_expired; /* display expired dynamic rules */ + int do_compact; /* show rules in compact mode */ + int do_force; /* do not ask for confirmation */ + int show_sets; /* display the set each rule belongs to */ + int test_only; /* only check syntax */ + int comment_only; /* only print action and comment */ + int verbose; /* be verbose on some commands */ + + /* The options below can have multiple values. */ + + int do_sort; /* field to sort results (0 = no) */ + /* valid fields are 1 and above */ + + int use_set; /* work with specified set number */ + /* 0 means all sets, otherwise apply to set use_set - 1 */ + +}; + +extern struct cmdline_opts co; + +/* + * _s_x is a structure that stores a string <-> token pairs, used in + * various places in the parser. Entries are stored in arrays, + * with an entry with s=NULL as terminator. + * The search routines are match_token() and match_value(). + * Often, an element with x=0 contains an error string. + * + */ +struct _s_x { + char const *s; + int x; +}; + +/* + * the following macro returns an error message if we run out of + * arguments. + */ +#define NEED1(msg) {if (!ac) errx(EX_USAGE, msg);} + +/* memory allocation support */ +void *safe_calloc(size_t number, size_t size); +void *safe_realloc(void *ptr, size_t size); + +/* a string comparison function used for historical compatibility */ +int _substrcmp(const char *str1, const char* str2); + +/* + * The reserved set numer. This is a constant in ip_fw.h + * but we store it in a variable so other files do not depend + * in that header just for one constant. + */ +extern int resvd_set_number; + +void ipfw_add(int ac, char *av[]); +void ipfw_show_nat(int ac, char **av); +void ipfw_config_pipe(int ac, char **av); +void ipfw_config_nat(int ac, char **av); +void ipfw_sets_handler(int ac, char *av[]); +void ipfw_table_handler(int ac, char *av[]); +void ipfw_sysctl_handler(int ac, char *av[], int which); +void ipfw_delete(int ac, char *av[]); +void ipfw_flush(int force); +void ipfw_zero(int ac, char *av[], int optname); +void ipfw_list(int ac, char *av[], int show_counters); + diff --git a/sbin/ipfw/main.c b/sbin/ipfw/main.c new file mode 100644 index 0000000..5cb5a42 --- /dev/null +++ b/sbin/ipfw/main.c @@ -0,0 +1,539 @@ +/* + * Copyright (c) 2002-2003 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. + * + * Command line interface for IP firewall facility + * + * $FreeBSD$ + */ + +#include <sys/wait.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +#include "ipfw2.h" + +static void +help(void) +{ + fprintf(stderr, +"ipfw syntax summary (but please do read the ipfw(8) manpage):\n\n" +"\tipfw [-abcdefhnNqStTv] <command>\n\n" +"where <command> is one of the following:\n\n" +"add [num] [set N] [prob x] RULE-BODY\n" +"{pipe|queue} N config PIPE-BODY\n" +"[pipe|queue] {zero|delete|show} [N{,N}]\n" +"nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|reset|\n" +" reverse|proxy_only|redirect_addr linkspec|\n" +" redirect_port linkspec|redirect_proto linkspec}\n" +"set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n" +"set N {show|list|zero|resetlog|delete} [N{,N}] | flush\n" +"table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n" +"table all {flush | list}\n" +"\n" +"RULE-BODY: check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n" +"ACTION: check-state | allow | count | deny | unreach{,6} CODE |\n" +" skipto N | {divert|tee} PORT | forward ADDR |\n" +" pipe N | queue N | nat N | setfib FIB\n" +"PARAMS: [log [logamount LOGLIMIT]] [altq QUEUE_NAME]\n" +"ADDR: [ MAC dst src ether_type ] \n" +" [ ip from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n" +" [ ipv6|ip6 from IP6ADDR [ PORT ] to IP6ADDR [ PORTLIST ] ]\n" +"IPADDR: [not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n" +"IP6ADDR: [not] { any | me | me6 | ip6/bits | IP6LIST }\n" +"IP6LIST: { ip6 | ip6/bits }[,IP6LIST]\n" +"IPLIST: { ip | ip/bits | ip:mask }[,IPLIST]\n" +"OPTION_LIST: OPTION [OPTION_LIST]\n" +"OPTION: bridged | diverted | diverted-loopback | diverted-output |\n" +" {dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n" +" {dst-port|src-port} LIST |\n" +" estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n" +" iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n" +" ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n" +" icmp6types LIST | ext6hdr LIST | flow-id N[,N] | fib FIB |\n" +" mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n" +" setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n" +" tcpdatalen LIST | verrevpath | versrcreach | antispoof\n" +); + + exit(0); +} + +/* + * Free a the (locally allocated) copy of command line arguments. + */ +static void +free_args(int ac, char **av) +{ + int i; + + for (i=0; i < ac; i++) + free(av[i]); + free(av); +} + +/* + * Called with the arguments, including program name because getopt + * wants it to be present. + * Returns 0 if successful, 1 if empty command, errx() in case of errors. + */ +static int +ipfw_main(int oldac, char **oldav) +{ + int ch, ac, save_ac; + const char *errstr; + char **av, **save_av; + int do_acct = 0; /* Show packet/byte count */ + +#define WHITESP " \t\f\v\n\r" + if (oldac < 2) + return 1; /* need at least one argument */ + + if (oldac == 2) { + /* + * If we are called with a single string, try to split it into + * arguments for subsequent parsing. + * But first, remove spaces after a ',', by copying the string + * in-place. + */ + char *arg = oldav[1]; /* The string is the first arg. */ + int l = strlen(arg); + int copy = 0; /* 1 if we need to copy, 0 otherwise */ + int i, j; + + for (i = j = 0; i < l; i++) { + if (arg[i] == '#') /* comment marker */ + break; + if (copy) { + arg[j++] = arg[i]; + copy = !index("," WHITESP, arg[i]); + } else { + copy = !index(WHITESP, arg[i]); + if (copy) + arg[j++] = arg[i]; + } + } + if (!copy && j > 0) /* last char was a 'blank', remove it */ + j--; + l = j; /* the new argument length */ + arg[j++] = '\0'; + if (l == 0) /* empty string! */ + return 1; + + /* + * First, count number of arguments. Because of the previous + * processing, this is just the number of blanks plus 1. + */ + for (i = 0, ac = 1; i < l; i++) + if (index(WHITESP, arg[i]) != NULL) + ac++; + + /* + * Allocate the argument list, including one entry for + * the program name because getopt expects it. + */ + av = safe_calloc(ac + 1, sizeof(char *)); + + /* + * Second, copy arguments from arg[] to av[]. For each one, + * j is the initial character, i is the one past the end. + */ + for (ac = 1, i = j = 0; i < l; i++) + if (index(WHITESP, arg[i]) != NULL || i == l-1) { + if (i == l-1) + i++; + av[ac] = safe_calloc(i-j+1, 1); + bcopy(arg+j, av[ac], i-j); + ac++; + j = i + 1; + } + } else { + /* + * If an argument ends with ',' join with the next one. + */ + int first, i, l; + + av = safe_calloc(oldac, sizeof(char *)); + for (first = i = ac = 1, l = 0; i < oldac; i++) { + char *arg = oldav[i]; + int k = strlen(arg); + + l += k; + if (arg[k-1] != ',' || i == oldac-1) { + /* Time to copy. */ + av[ac] = safe_calloc(l+1, 1); + for (l=0; first <= i; first++) { + strcat(av[ac]+l, oldav[first]); + l += strlen(oldav[first]); + } + ac++; + l = 0; + first = i+1; + } + } + } + + av[0] = strdup(oldav[0]); /* copy progname from the caller */ + /* Set the force flag for non-interactive processes */ + if (!co.do_force) + co.do_force = !isatty(STDIN_FILENO); + + /* Save arguments for final freeing of memory. */ + save_ac = ac; + save_av = av; + + optind = optreset = 1; /* restart getopt() */ + while ((ch = getopt(ac, av, "abcdefhinNqs:STtv")) != -1) + switch (ch) { + case 'a': + do_acct = 1; + break; + + case 'b': + co.comment_only = 1; + co.do_compact = 1; + break; + + case 'c': + co.do_compact = 1; + break; + + case 'd': + co.do_dynamic = 1; + break; + + case 'e': + co.do_expired = 1; + break; + + case 'f': + co.do_force = 1; + break; + + case 'h': /* help */ + free_args(save_ac, save_av); + help(); + break; /* NOTREACHED */ + + case 'i': + co.do_value_as_ip = 1; + break; + + case 'n': + co.test_only = 1; + break; + + case 'N': + co.do_resolv = 1; + break; + + case 'q': + co.do_quiet = 1; + break; + + case 's': /* sort */ + co.do_sort = atoi(optarg); + break; + + case 'S': + co.show_sets = 1; + break; + + case 't': + co.do_time = 1; + break; + + case 'T': + co.do_time = 2; /* numeric timestamp */ + break; + + case 'v': /* verbose */ + co.verbose = 1; + break; + + default: + free_args(save_ac, save_av); + return 1; + } + + ac -= optind; + av += optind; + NEED1("bad arguments, for usage summary ``ipfw''"); + + /* + * An undocumented behaviour of ipfw1 was to allow rule numbers first, + * e.g. "100 add allow ..." instead of "add 100 allow ...". + * In case, swap first and second argument to get the normal form. + */ + if (ac > 1 && isdigit(*av[0])) { + char *p = av[0]; + + av[0] = av[1]; + av[1] = p; + } + + /* + * Optional: pipe, queue or nat. + */ + co.do_nat = 0; + co.do_pipe = 0; + if (!strncmp(*av, "nat", strlen(*av))) + co.do_nat = 1; + else if (!strncmp(*av, "pipe", strlen(*av))) + co.do_pipe = 1; + else if (_substrcmp(*av, "queue") == 0) + co.do_pipe = 2; + else if (!strncmp(*av, "set", strlen(*av))) { + if (ac > 1 && isdigit(av[1][0])) { + co.use_set = strtonum(av[1], 0, resvd_set_number, + &errstr); + if (errstr) + errx(EX_DATAERR, + "invalid set number %s\n", av[1]); + ac -= 2; av += 2; co.use_set++; + } + } + + if (co.do_pipe || co.do_nat) { + ac--; + av++; + } + NEED1("missing command"); + + /* + * For pipes, queues and nats we normally say 'nat|pipe NN config' + * but the code is easier to parse as 'nat|pipe config NN' + * so we swap the two arguments. + */ + if ((co.do_pipe || co.do_nat) && ac > 1 && isdigit(*av[0])) { + char *p = av[0]; + + av[0] = av[1]; + av[1] = p; + } + + int try_next = 0; + if (co.use_set == 0) { + if (_substrcmp(*av, "add") == 0) + ipfw_add(ac, av); + else if (co.do_nat && _substrcmp(*av, "show") == 0) + ipfw_show_nat(ac, av); + else if (co.do_pipe && _substrcmp(*av, "config") == 0) + ipfw_config_pipe(ac, av); + else if (co.do_nat && _substrcmp(*av, "config") == 0) + ipfw_config_nat(ac, av); + else if (_substrcmp(*av, "set") == 0) + ipfw_sets_handler(ac, av); + else if (_substrcmp(*av, "table") == 0) + ipfw_table_handler(ac, av); + else if (_substrcmp(*av, "enable") == 0) + ipfw_sysctl_handler(ac, av, 1); + else if (_substrcmp(*av, "disable") == 0) + ipfw_sysctl_handler(ac, av, 0); + else + try_next = 1; + } + + if (co.use_set || try_next) { + if (_substrcmp(*av, "delete") == 0) + ipfw_delete(ac, av); + else if (_substrcmp(*av, "flush") == 0) + ipfw_flush(co.do_force); + else if (_substrcmp(*av, "zero") == 0) + ipfw_zero(ac, av, 0 /* IP_FW_ZERO */); + else if (_substrcmp(*av, "resetlog") == 0) + ipfw_zero(ac, av, 1 /* IP_FW_RESETLOG */); + else if (_substrcmp(*av, "print") == 0 || + _substrcmp(*av, "list") == 0) + ipfw_list(ac, av, do_acct); + else if (_substrcmp(*av, "show") == 0) + ipfw_list(ac, av, 1 /* show counters */); + else + errx(EX_USAGE, "bad command `%s'", *av); + } + + /* Free memory allocated in the argument parsing. */ + free_args(save_ac, save_av); + return 0; +} + + +static void +ipfw_readfile(int ac, char *av[]) +{ +#define MAX_ARGS 32 + char buf[BUFSIZ]; + char *progname = av[0]; /* original program name */ + const char *cmd = NULL; /* preprocessor name, if any */ + const char *filename = av[ac-1]; /* file to read */ + int c, lineno=0; + FILE *f = NULL; + pid_t preproc = 0; + + while ((c = getopt(ac, av, "cfNnp:qS")) != -1) { + switch(c) { + case 'c': + co.do_compact = 1; + break; + + case 'f': + co.do_force = 1; + break; + + case 'N': + co.do_resolv = 1; + break; + + case 'n': + co.test_only = 1; + break; + + case 'p': + /* + * ipfw -p cmd [args] filename + * + * We are done with getopt(). All arguments + * except the filename go to the preprocessor, + * so we need to do the following: + * - check that a filename is actually present; + * - advance av by optind-1 to skip arguments + * already processed; + * - decrease ac by optind, to remove the args + * already processed and the final filename; + * - set the last entry in av[] to NULL so + * popen() can detect the end of the array; + * - set optind=ac to let getopt() terminate. + */ + if (optind == ac) + errx(EX_USAGE, "no filename argument"); + cmd = optarg; + av[ac-1] = NULL; + av += optind - 1; + ac -= optind; + optind = ac; + break; + + case 'q': + co.do_quiet = 1; + break; + + case 'S': + co.show_sets = 1; + break; + + default: + errx(EX_USAGE, "bad arguments, for usage" + " summary ``ipfw''"); + } + + } + + if (cmd == NULL && ac != optind + 1) + errx(EX_USAGE, "extraneous filename arguments %s", av[ac-1]); + + if ((f = fopen(filename, "r")) == NULL) + err(EX_UNAVAILABLE, "fopen: %s", filename); + + if (cmd != NULL) { /* pipe through preprocessor */ + int pipedes[2]; + + if (pipe(pipedes) == -1) + err(EX_OSERR, "cannot create pipe"); + + preproc = fork(); + if (preproc == -1) + err(EX_OSERR, "cannot fork"); + + if (preproc == 0) { + /* + * Child, will run the preprocessor with the + * file on stdin and the pipe on stdout. + */ + 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, av); + err(EX_OSERR, "execvp(%s) failed", cmd); + } else { /* parent, will reopen f as the pipe */ + fclose(f); + close(pipedes[1]); + if ((f = fdopen(pipedes[0], "r")) == NULL) { + int savederrno = errno; + + (void)kill(preproc, SIGTERM); + errno = savederrno; + err(EX_OSERR, "fdopen()"); + } + } + } + + while (fgets(buf, BUFSIZ, f)) { /* read commands */ + char linename[10]; + char *args[2]; + + lineno++; + sprintf(linename, "Line %d", lineno); + setprogname(linename); /* XXX */ + args[0] = progname; + args[1] = buf; + ipfw_main(2, args); + } + fclose(f); + if (cmd != NULL) { + int status; + + 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[]) +{ + /* + * If the last argument is an absolute pathname, interpret it + * as a file to be preprocessed. + */ + + if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0) + ipfw_readfile(ac, av); + else { + if (ipfw_main(ac, av)) { + errx(EX_USAGE, + "usage: ipfw [options]\n" + "do \"ipfw -h\" or \"man ipfw\" for details"); + } + } + return EX_OK; +} |