diff options
Diffstat (limited to 'iptables.c')
-rw-r--r-- | iptables.c | 1825 |
1 files changed, 644 insertions, 1181 deletions
@@ -29,18 +29,18 @@ #include <string.h> #include <netdb.h> #include <errno.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> -#include <dlfcn.h> #include <ctype.h> #include <stdarg.h> #include <limits.h> #include <unistd.h> #include <iptables.h> +#include <xtables.h> #include <fcntl.h> -#include <sys/wait.h> #include <sys/utsname.h> -#include <netinet/in.h> +#include "xshared.h" #ifndef TRUE #define TRUE 1 @@ -49,10 +49,6 @@ #define FALSE 0 #endif -#ifndef PROC_SYS_MODPROBE -#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" -#endif - #define FMT_NUMERIC 0x0001 #define FMT_NOCOUNTS 0x0002 #define FMT_KILOMEGAGIGA 0x0004 @@ -81,11 +77,11 @@ #define CMD_DELETE_CHAIN 0x0200U #define CMD_SET_POLICY 0x0400U #define CMD_RENAME_CHAIN 0x0800U -#define NUMBER_OF_CMD 13 +#define CMD_LIST_RULES 0x1000U +#define CMD_ZERO_NUM 0x2000U +#define NUMBER_OF_CMD 15 static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z', - 'N', 'X', 'P', 'E' }; - -#define OPTION_OFFSET 256 + 'Z', 'N', 'X', 'P', 'E', 'S' }; #define OPT_NONE 0x00000U #define OPT_NUMERIC 0x00001U @@ -105,38 +101,39 @@ static const char optflags[NUMBER_OF_OPT] = { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', 'f', '0', 'c'}; static struct option original_opts[] = { - { "append", 1, 0, 'A' }, - { "delete", 1, 0, 'D' }, - { "insert", 1, 0, 'I' }, - { "replace", 1, 0, 'R' }, - { "list", 2, 0, 'L' }, - { "flush", 2, 0, 'F' }, - { "zero", 2, 0, 'Z' }, - { "new-chain", 1, 0, 'N' }, - { "delete-chain", 2, 0, 'X' }, - { "rename-chain", 1, 0, 'E' }, - { "policy", 1, 0, 'P' }, - { "source", 1, 0, 's' }, - { "destination", 1, 0, 'd' }, - { "src", 1, 0, 's' }, /* synonym */ - { "dst", 1, 0, 'd' }, /* synonym */ - { "protocol", 1, 0, 'p' }, - { "in-interface", 1, 0, 'i' }, - { "jump", 1, 0, 'j' }, - { "table", 1, 0, 't' }, - { "match", 1, 0, 'm' }, - { "numeric", 0, 0, 'n' }, - { "out-interface", 1, 0, 'o' }, - { "verbose", 0, 0, 'v' }, - { "exact", 0, 0, 'x' }, - { "fragments", 0, 0, 'f' }, - { "version", 0, 0, 'V' }, - { "help", 2, 0, 'h' }, - { "line-numbers", 0, 0, '0' }, - { "modprobe", 1, 0, 'M' }, - { "set-counters", 1, 0, 'c' }, - { "goto", 1, 0, 'g' }, - { 0 } + {.name = "append", .has_arg = 1, .val = 'A'}, + {.name = "delete", .has_arg = 1, .val = 'D'}, + {.name = "insert", .has_arg = 1, .val = 'I'}, + {.name = "replace", .has_arg = 1, .val = 'R'}, + {.name = "list", .has_arg = 2, .val = 'L'}, + {.name = "list-rules", .has_arg = 2, .val = 'S'}, + {.name = "flush", .has_arg = 2, .val = 'F'}, + {.name = "zero", .has_arg = 2, .val = 'Z'}, + {.name = "new-chain", .has_arg = 1, .val = 'N'}, + {.name = "delete-chain", .has_arg = 2, .val = 'X'}, + {.name = "rename-chain", .has_arg = 1, .val = 'E'}, + {.name = "policy", .has_arg = 1, .val = 'P'}, + {.name = "source", .has_arg = 1, .val = 's'}, + {.name = "destination", .has_arg = 1, .val = 'd'}, + {.name = "src", .has_arg = 1, .val = 's'}, /* synonym */ + {.name = "dst", .has_arg = 1, .val = 'd'}, /* synonym */ + {.name = "protocol", .has_arg = 1, .val = 'p'}, + {.name = "in-interface", .has_arg = 1, .val = 'i'}, + {.name = "jump", .has_arg = 1, .val = 'j'}, + {.name = "table", .has_arg = 1, .val = 't'}, + {.name = "match", .has_arg = 1, .val = 'm'}, + {.name = "numeric", .has_arg = 0, .val = 'n'}, + {.name = "out-interface", .has_arg = 1, .val = 'o'}, + {.name = "verbose", .has_arg = 0, .val = 'v'}, + {.name = "exact", .has_arg = 0, .val = 'x'}, + {.name = "fragments", .has_arg = 0, .val = 'f'}, + {.name = "version", .has_arg = 0, .val = 'V'}, + {.name = "help", .has_arg = 2, .val = 'h'}, + {.name = "line-numbers", .has_arg = 0, .val = '0'}, + {.name = "modprobe", .has_arg = 1, .val = 'M'}, + {.name = "set-counters", .has_arg = 1, .val = 'c'}, + {.name = "goto", .has_arg = 1, .val = 'g'}, + {NULL}, }; /* we need this for iptables-restore. iptables-restore.c sets line to the @@ -145,8 +142,15 @@ static struct option original_opts[] = { * magic number of -1 */ int line = -1; -static struct option *opts = original_opts; -static unsigned int global_option_offset = 0; +void iptables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3))); + +struct xtables_globals iptables_globals = { + .option_offset = 0, + .program_version = IPTABLES_VERSION, + .opts = original_opts, + .orig_opts = original_opts, + .exit_err = iptables_exit_error, +}; /* Table of legal combinations of commands and options. If any of the * given commands make an option legal, that option is legal (applies to @@ -160,7 +164,7 @@ static unsigned int global_option_offset = 0; static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = /* Well, it's better than "Re: Linux vs FreeBSD" */ { - /* -n -s -d -p -j -v -x -i -o -f --line -c */ + /* -n -s -d -p -j -v -x -i -o -f --line -c */ /*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x',' '}, /*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x','x'}, /*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x','x'}, @@ -169,10 +173,12 @@ static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = /*LIST*/ {' ','x','x','x','x',' ',' ','x','x','x',' ','x'}, /*FLUSH*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'}, /*ZERO*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'}, +/*ZERO_NUM*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'}, /*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'}, /*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'}, -/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x','x'}, -/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'} +/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x',' '}, +/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'}, +/*LIST_RULES*/{'x','x','x','x','x',' ','x','x','x','x','x','x'} }; static int inverse_for_options[NUMBER_OF_OPT] = @@ -191,29 +197,12 @@ static int inverse_for_options[NUMBER_OF_OPT] = /* -c */ 0, }; -const char *program_version; -const char *program_name; -char *lib_dir; +#define opts iptables_globals.opts +#define prog_name iptables_globals.program_name +#define prog_vers iptables_globals.program_version int kernel_version; -/* the path to command to load kernel module */ -const char *modprobe = NULL; - -/* Keeping track of external matches and targets: linked lists. */ -struct iptables_match *iptables_matches = NULL; -struct iptables_target *iptables_targets = NULL; - -/* Extra debugging from libiptc */ -extern void dump_entries(const iptc_handle_t handle); - -/* A few hardcoded protocols for 'all' and in case the user has no - /etc/protocols */ -struct pprot { - char *name; - u_int8_t num; -}; - /* Primitive headers... */ /* defined in netinet/in.h */ #if 0 @@ -225,17 +214,7 @@ struct pprot { #endif #endif -static const struct pprot chain_protos[] = { - { "tcp", IPPROTO_TCP }, - { "udp", IPPROTO_UDP }, - { "udplite", IPPROTO_UDPLITE }, - { "icmp", IPPROTO_ICMP }, - { "esp", IPPROTO_ESP }, - { "ah", IPPROTO_AH }, - { "sctp", IPPROTO_SCTP }, -}; - -static char * +static const char * proto_to_name(u_int8_t proto, int nolookup) { unsigned int i; @@ -246,185 +225,46 @@ proto_to_name(u_int8_t proto, int nolookup) return pent->p_name; } - for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++) - if (chain_protos[i].num == proto) - return chain_protos[i].name; + for (i = 0; xtables_chain_protos[i].name != NULL; ++i) + if (xtables_chain_protos[i].num == proto) + return xtables_chain_protos[i].name; return NULL; } -int -service_to_port(const char *name, const char *proto) -{ - struct servent *service; - - if ((service = getservbyname(name, proto)) != NULL) - return ntohs((unsigned short) service->s_port); - - return -1; -} - -u_int16_t -parse_port(const char *port, const char *proto) -{ - unsigned int portnum; - - if ((string_to_number(port, 0, 65535, &portnum)) != -1 || - (portnum = service_to_port(port, proto)) != -1) - return (u_int16_t)portnum; - - exit_error(PARAMETER_PROBLEM, - "invalid port/service `%s' specified", port); -} - enum { IPT_DOTTED_ADDR = 0, IPT_DOTTED_MASK }; -static struct in_addr * -__dotted_to_addr(const char *dotted, int type) -{ - static struct in_addr addr; - unsigned char *addrp; - char *p, *q; - unsigned int onebyte; - int i; - char buf[20]; - - /* copy dotted string, because we need to modify it */ - strncpy(buf, dotted, sizeof(buf) - 1); - buf[sizeof(buf) - 1] = '\0'; - addrp = (unsigned char *) &(addr.s_addr); - - p = buf; - for (i = 0; i < 3; i++) { - if ((q = strchr(p, '.')) == NULL) { - if (type == IPT_DOTTED_ADDR) { - /* autocomplete, this is a network address */ - if (string_to_number(p, 0, 255, &onebyte) == -1) - return (struct in_addr *) NULL; - - addrp[i] = (unsigned char) onebyte; - while (i < 3) - addrp[++i] = 0; - - return &addr; - } else - return (struct in_addr *) NULL; - } - - *q = '\0'; - if (string_to_number(p, 0, 255, &onebyte) == -1) - return (struct in_addr *) NULL; - - addrp[i] = (unsigned char) onebyte; - p = q + 1; - } - - /* we've checked 3 bytes, now we check the last one */ - if (string_to_number(p, 0, 255, &onebyte) == -1) - return (struct in_addr *) NULL; - - addrp[3] = (unsigned char) onebyte; - - return &addr; -} - -struct in_addr * -dotted_to_addr(const char *dotted) -{ - return __dotted_to_addr(dotted, IPT_DOTTED_ADDR); -} - -struct in_addr * -dotted_to_mask(const char *dotted) -{ - return __dotted_to_addr(dotted, IPT_DOTTED_MASK); -} - -static struct in_addr * -network_to_addr(const char *name) -{ - struct netent *net; - static struct in_addr addr; - - if ((net = getnetbyname(name)) != NULL) { - if (net->n_addrtype != AF_INET) - return (struct in_addr *) NULL; - addr.s_addr = htonl((unsigned long) net->n_net); - return &addr; - } - - return (struct in_addr *) NULL; -} - static void -inaddrcpy(struct in_addr *dst, struct in_addr *src) -{ - /* memcpy(dst, src, sizeof(struct in_addr)); */ - dst->s_addr = src->s_addr; -} - -static void free_opts(int reset_offset) -{ - if (opts != original_opts) { - free(opts); - opts = original_opts; - if (reset_offset) - global_option_offset = 0; - } -} - -void -exit_error(enum exittype status, char *msg, ...) -{ - va_list args; - - va_start(args, msg); - fprintf(stderr, "%s v%s: ", program_name, program_version); - vfprintf(stderr, msg, args); - va_end(args); - fprintf(stderr, "\n"); - if (status == PARAMETER_PROBLEM) - exit_tryhelp(status); - if (status == VERSION_PROBLEM) - fprintf(stderr, - "Perhaps iptables or your kernel needs to be upgraded.\n"); - /* On error paths, make sure that we don't leak memory */ - free_opts(1); - exit(status); -} - -void exit_tryhelp(int status) { if (line != -1) fprintf(stderr, "Error occurred at line: %d\n", line); fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", - program_name, program_name ); - free_opts(1); + prog_name, prog_name); + xtables_free_opts(1); exit(status); } -void -exit_printhelp(struct iptables_rule_match *matches) +static void +exit_printhelp(struct xtables_rule_match *matches) { - struct iptables_rule_match *matchp = NULL; - struct iptables_target *t = NULL; - printf("%s v%s\n\n" "Usage: %s -[AD] chain rule-specification [options]\n" -" %s -[RI] chain rulenum rule-specification [options]\n" +" %s -I chain [rulenum] rule-specification [options]\n" +" %s -R chain rulenum rule-specification [options]\n" " %s -D chain rulenum [options]\n" -" %s -[LFZ] [chain] [options]\n" +" %s -[LS] [chain [rulenum]] [options]\n" +" %s -[FZ] [chain] [options]\n" " %s -[NX] chain\n" " %s -E old-chain-name new-chain-name\n" " %s -P chain target [options]\n" " %s -h (print this help information)\n\n", - program_name, program_version, program_name, program_name, - program_name, program_name, program_name, program_name, - program_name, program_name); + prog_name, prog_vers, prog_name, prog_name, + prog_name, prog_name, prog_name, prog_name, + prog_name, prog_name, prog_name, prog_name); printf( "Commands:\n" @@ -437,9 +277,13 @@ exit_printhelp(struct iptables_rule_match *matches) " Insert in chain as rulenum (default 1=first)\n" " --replace -R chain rulenum\n" " Replace rule rulenum (1 = first) in chain\n" -" --list -L [chain] List the rules in a chain or all chains\n" +" --list -L [chain [rulenum]]\n" +" List the rules in a chain or all chains\n" +" --list-rules -S [chain [rulenum]]\n" +" Print the rules in a chain or all chains\n" " --flush -F [chain] Delete all rules in chain or all chains\n" -" --zero -Z [chain] Zero counters in chain or all chains\n" +" --zero -Z [chain [rulenum]]\n" +" Zero counters in chain or all chains\n" " --new -N chain Create a new user-defined chain\n" " --delete-chain\n" " -X [chain] Delete a user-defined chain\n" @@ -450,14 +294,14 @@ exit_printhelp(struct iptables_rule_match *matches) " Change chain name, (moving any references)\n" "Options:\n" -" --proto -p [!] proto protocol: by number or name, eg. `tcp'\n" -" --source -s [!] address[/mask]\n" +"[!] --proto -p proto protocol: by number or name, eg. `tcp'\n" +"[!] --source -s address[/mask][...]\n" " source specification\n" -" --destination -d [!] address[/mask]\n" +"[!] --destination -d address[/mask][...]\n" " destination specification\n" -" --in-interface -i [!] input name[+]\n" +"[!] --in-interface -i input name[+]\n" " network interface name ([+] for wildcard)\n" -" --jump -j target\n" +" --jump -j target\n" " target for rule (may load target extension)\n" #ifdef IPT_F_GOTO " --goto -g chain\n" @@ -466,7 +310,7 @@ exit_printhelp(struct iptables_rule_match *matches) " --match -m match\n" " extended match (may load extension)\n" " --numeric -n numeric output of addresses and ports\n" -" --out-interface -o [!] output name[+]\n" +"[!] --out-interface -o output name[+]\n" " network interface name ([+] for wildcard)\n" " --table -t table table to manipulate (default: `filter')\n" " --verbose -v verbose mode\n" @@ -477,22 +321,30 @@ exit_printhelp(struct iptables_rule_match *matches) " --set-counters PKTS BYTES set the counter during insert/append\n" "[!] --version -V print package version.\n"); - /* Print out any special helps. A user might like to be able - to add a --help to the commandline, and see expected - results. So we call help for all specified matches & targets */ - for (t = iptables_targets; t ;t = t->next) { - if (t->used) { - printf("\n"); - t->help(); - } - } - for (matchp = matches; matchp; matchp = matchp->next) { - printf("\n"); - matchp->match->help(); - } + print_extension_helps(xtables_targets, matches); exit(0); } +void +iptables_exit_error(enum xtables_exittype status, const char *msg, ...) +{ + va_list args; + + va_start(args, msg); + fprintf(stderr, "%s v%s: ", prog_name, prog_vers); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, "\n"); + if (status == PARAMETER_PROBLEM) + exit_tryhelp(status); + if (status == VERSION_PROBLEM) + fprintf(stderr, + "Perhaps iptables or your kernel needs to be upgraded.\n"); + /* On error paths, make sure that we don't leak memory */ + xtables_free_opts(1); + exit(status); +} + static void generic_opt_check(int command, int options) { @@ -511,7 +363,7 @@ generic_opt_check(int command, int options) if (!(options & (1<<i))) { if (commands_v_options[j][i] == '+') - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "You need to supply the `-%c' " "option for this command\n", optflags[i]); @@ -523,7 +375,7 @@ generic_opt_check(int command, int options) } } if (legal == -1) - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "Illegal option `-%c' with this command\n", optflags[i]); } @@ -552,94 +404,13 @@ add_command(unsigned int *cmd, const int newcmd, const int othercmds, int invert) { if (invert) - exit_error(PARAMETER_PROBLEM, "unexpected ! flag"); + xtables_error(PARAMETER_PROBLEM, "unexpected ! flag"); if (*cmd & (~othercmds)) - exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n", + xtables_error(PARAMETER_PROBLEM, "Cannot use -%c with -%c\n", cmd2char(newcmd), cmd2char(*cmd & (~othercmds))); *cmd |= newcmd; } -int -check_inverse(const char option[], int *invert, int *optind, int argc) -{ - if (option && strcmp(option, "!") == 0) { - if (*invert) - exit_error(PARAMETER_PROBLEM, - "Multiple `!' flags not allowed"); - *invert = TRUE; - if (optind) { - *optind = *optind+1; - if (argc && *optind > argc) - exit_error(PARAMETER_PROBLEM, - "no argument following `!'"); - } - - return TRUE; - } - return FALSE; -} - -static void * -fw_calloc(size_t count, size_t size) -{ - void *p; - - if ((p = calloc(count, size)) == NULL) { - perror("iptables: calloc failed"); - exit(1); - } - return p; -} - -static void * -fw_malloc(size_t size) -{ - void *p; - - if ((p = malloc(size)) == NULL) { - perror("iptables: malloc failed"); - exit(1); - } - return p; -} - -static struct in_addr * -host_to_addr(const char *name, unsigned int *naddr) -{ - struct hostent *host; - struct in_addr *addr; - unsigned int i; - - *naddr = 0; - if ((host = gethostbyname(name)) != NULL) { - if (host->h_addrtype != AF_INET || - host->h_length != sizeof(struct in_addr)) - return (struct in_addr *) NULL; - - while (host->h_addr_list[*naddr] != (char *) NULL) - (*naddr)++; - addr = fw_calloc(*naddr, sizeof(struct in_addr) * *naddr); - for (i = 0; i < *naddr; i++) - inaddrcpy(&(addr[i]), - (struct in_addr *) host->h_addr_list[i]); - return addr; - } - - return (struct in_addr *) NULL; -} - -static char * -addr_to_host(const struct in_addr *addr) -{ - struct hostent *host; - - if ((host = gethostbyaddr((char *) addr, - sizeof(struct in_addr), AF_INET)) != NULL) - return (char *) host->h_name; - - return (char *) NULL; -} - /* * All functions starting with "parse" should succeed, otherwise * the program fails. @@ -649,262 +420,32 @@ addr_to_host(const struct in_addr *addr) * return global static data. */ -static struct in_addr * -parse_hostnetwork(const char *name, unsigned int *naddrs) -{ - struct in_addr *addrp, *addrptmp; - - if ((addrptmp = dotted_to_addr(name)) != NULL || - (addrptmp = network_to_addr(name)) != NULL) { - addrp = fw_malloc(sizeof(struct in_addr)); - inaddrcpy(addrp, addrptmp); - *naddrs = 1; - return addrp; - } - if ((addrp = host_to_addr(name, naddrs)) != NULL) - return addrp; - - exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name); -} - -static struct in_addr * -parse_mask(char *mask) -{ - static struct in_addr maskaddr; - struct in_addr *addrp; - unsigned int bits; - - if (mask == NULL) { - /* no mask at all defaults to 32 bits */ - maskaddr.s_addr = 0xFFFFFFFF; - return &maskaddr; - } - if ((addrp = dotted_to_mask(mask)) != NULL) - /* dotted_to_addr already returns a network byte order addr */ - return addrp; - if (string_to_number(mask, 0, 32, &bits) == -1) - exit_error(PARAMETER_PROBLEM, - "invalid mask `%s' specified", mask); - if (bits != 0) { - maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits)); - return &maskaddr; - } - - maskaddr.s_addr = 0L; - return &maskaddr; -} - -void -parse_hostnetworkmask(const char *name, struct in_addr **addrpp, - struct in_addr *maskp, unsigned int *naddrs) -{ - struct in_addr *addrp; - char buf[256]; - char *p; - int i, j, k, n; - - strncpy(buf, name, sizeof(buf) - 1); - buf[sizeof(buf) - 1] = '\0'; - if ((p = strrchr(buf, '/')) != NULL) { - *p = '\0'; - addrp = parse_mask(p + 1); - } else - addrp = parse_mask(NULL); - inaddrcpy(maskp, addrp); - - /* if a null mask is given, the name is ignored, like in "any/0" */ - if (maskp->s_addr == 0L) - strcpy(buf, "0.0.0.0"); - - addrp = *addrpp = parse_hostnetwork(buf, naddrs); - n = *naddrs; - for (i = 0, j = 0; i < n; i++) { - addrp[j++].s_addr &= maskp->s_addr; - for (k = 0; k < j - 1; k++) { - if (addrp[k].s_addr == addrp[j - 1].s_addr) { - (*naddrs)--; - j--; - break; - } - } - } -} - -struct iptables_match * -find_match(const char *name, enum ipt_tryload tryload, struct iptables_rule_match **matches) -{ - struct iptables_match *ptr; - - for (ptr = iptables_matches; ptr; ptr = ptr->next) { - if (strcmp(name, ptr->name) == 0) { - struct iptables_match *clone; - - /* First match of this type: */ - if (ptr->m == NULL) - break; - - /* Second and subsequent clones */ - clone = fw_malloc(sizeof(struct iptables_match)); - memcpy(clone, ptr, sizeof(struct iptables_match)); - clone->mflags = 0; - /* This is a clone: */ - clone->next = clone; - - ptr = clone; - break; - } - } - -#ifndef NO_SHARED_LIBS - if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) { - char path[strlen(lib_dir) + sizeof("/libipt_.so") - + strlen(name)]; - sprintf(path, "%s/libipt_%s.so", lib_dir, name); - if (dlopen(path, RTLD_NOW)) { - /* Found library. If it didn't register itself, - maybe they specified target as match. */ - ptr = find_match(name, DONT_LOAD, NULL); - - if (!ptr) - exit_error(PARAMETER_PROBLEM, - "Couldn't load match `%s'\n", - name); - } else if (tryload == LOAD_MUST_SUCCEED) - exit_error(PARAMETER_PROBLEM, - "Couldn't load match `%s':%s\n", - name, dlerror()); - } -#else - if (ptr && !ptr->loaded) { - if (tryload != DONT_LOAD) - ptr->loaded = 1; - else - ptr = NULL; - } - if(!ptr && (tryload == LOAD_MUST_SUCCEED)) { - exit_error(PARAMETER_PROBLEM, - "Couldn't find match `%s'\n", name); - } -#endif - - if (ptr && matches) { - struct iptables_rule_match **i; - struct iptables_rule_match *newentry; - - newentry = fw_malloc(sizeof(struct iptables_rule_match)); - - for (i = matches; *i; i = &(*i)->next) { - if (strcmp(name, (*i)->match->name) == 0) - (*i)->completed = 1; - } - newentry->match = ptr; - newentry->completed = 0; - newentry->next = NULL; - *i = newentry; - } - - return ptr; -} - /* Christophe Burki wants `-p 6' to imply `-m tcp'. */ -static struct iptables_match * -find_proto(const char *pname, enum ipt_tryload tryload, int nolookup, struct iptables_rule_match **matches) +static struct xtables_match * +find_proto(const char *pname, enum xtables_tryload tryload, + int nolookup, struct xtables_rule_match **matches) { unsigned int proto; - if (string_to_number(pname, 0, 255, &proto) != -1) { - char *protoname = proto_to_name(proto, nolookup); + if (xtables_strtoui(pname, NULL, &proto, 0, UINT8_MAX)) { + const char *protoname = proto_to_name(proto, nolookup); if (protoname) - return find_match(protoname, tryload, matches); + return xtables_find_match(protoname, tryload, matches); } else - return find_match(pname, tryload, matches); + return xtables_find_match(pname, tryload, matches); return NULL; } -u_int16_t -parse_protocol(const char *s) -{ - unsigned int proto; - - if (string_to_number(s, 0, 255, &proto) == -1) { - struct protoent *pent; - - /* first deal with the special case of 'all' to prevent - * people from being able to redefine 'all' in nsswitch - * and/or provoke expensive [not working] ldap/nis/... - * lookups */ - if (!strcmp(s, "all")) - return 0; - - if ((pent = getprotobyname(s))) - proto = pent->p_proto; - else { - unsigned int i; - for (i = 0; - i < sizeof(chain_protos)/sizeof(struct pprot); - i++) { - if (strcmp(s, chain_protos[i].name) == 0) { - proto = chain_protos[i].num; - break; - } - } - if (i == sizeof(chain_protos)/sizeof(struct pprot)) - exit_error(PARAMETER_PROBLEM, - "unknown protocol `%s' specified", - s); - } - } - - return (u_int16_t)proto; -} - -void parse_interface(const char *arg, char *vianame, unsigned char *mask) -{ - int vialen = strlen(arg); - unsigned int i; - - memset(mask, 0, IFNAMSIZ); - memset(vianame, 0, IFNAMSIZ); - - if (vialen + 1 > IFNAMSIZ) - exit_error(PARAMETER_PROBLEM, - "interface name `%s' must be shorter than IFNAMSIZ" - " (%i)", arg, IFNAMSIZ-1); - - strcpy(vianame, arg); - if ((vialen == 0) || (vialen == 1 && vianame[0] == '+')) - memset(mask, 0, IFNAMSIZ); - else if (vianame[vialen - 1] == '+') { - memset(mask, 0xFF, vialen - 1); - memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1); - /* Don't remove `+' here! -HW */ - } else { - /* Include nul-terminator in match */ - memset(mask, 0xFF, vialen + 1); - memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1); - for (i = 0; vianame[i]; i++) { - if (vianame[i] == ':' || - vianame[i] == '!' || - vianame[i] == '*') { - printf("Warning: weird character in interface" - " `%s' (No aliases, :, ! or *).\n", - vianame); - break; - } - } - } -} - /* Can't be zero. */ static int parse_rulenumber(const char *rule) { unsigned int rulenum; - if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1) - exit_error(PARAMETER_PROBLEM, + if (!xtables_strtoui(rule, NULL, &rulenum, 1, INT_MAX)) + xtables_error(PARAMETER_PROBLEM, "Invalid rule number `%s'", rule); return rulenum; @@ -916,132 +457,27 @@ parse_target(const char *targetname) const char *ptr; if (strlen(targetname) < 1) - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "Invalid target name (too short)"); if (strlen(targetname)+1 > sizeof(ipt_chainlabel)) - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "Invalid target name `%s' (%u chars max)", targetname, (unsigned int)sizeof(ipt_chainlabel)-1); for (ptr = targetname; *ptr; ptr++) if (isspace(*ptr)) - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "Invalid target name `%s'", targetname); return targetname; } -static char * -addr_to_network(const struct in_addr *addr) -{ - struct netent *net; - - if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL) - return (char *) net->n_name; - - return (char *) NULL; -} - -char * -addr_to_dotted(const struct in_addr *addrp) -{ - static char buf[20]; - const unsigned char *bytep; - - bytep = (const unsigned char *) &(addrp->s_addr); - sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]); - return buf; -} - -char * -addr_to_anyname(const struct in_addr *addr) -{ - char *name; - - if ((name = addr_to_host(addr)) != NULL || - (name = addr_to_network(addr)) != NULL) - return name; - - return addr_to_dotted(addr); -} - -char * -mask_to_dotted(const struct in_addr *mask) -{ - int i; - static char buf[20]; - u_int32_t maskaddr, bits; - - maskaddr = ntohl(mask->s_addr); - - if (maskaddr == 0xFFFFFFFFL) - /* we don't want to see "/32" */ - return ""; - - i = 32; - bits = 0xFFFFFFFEL; - while (--i >= 0 && maskaddr != bits) - bits <<= 1; - if (i >= 0) - sprintf(buf, "/%d", i); - else - /* mask was not a decent combination of 1's and 0's */ - sprintf(buf, "/%s", addr_to_dotted(mask)); - - return buf; -} - -int -string_to_number_ll(const char *s, unsigned long long min, unsigned long long max, - unsigned long long *ret) -{ - unsigned long long number; - char *end; - - /* Handle hex, octal, etc. */ - errno = 0; - number = strtoull(s, &end, 0); - if (*end == '\0' && end != s) { - /* we parsed a number, let's see if we want this */ - if (errno != ERANGE && min <= number && (!max || number <= max)) { - *ret = number; - return 0; - } - } - return -1; -} - -int -string_to_number_l(const char *s, unsigned long min, unsigned long max, - unsigned long *ret) -{ - int result; - unsigned long long number; - - result = string_to_number_ll(s, min, max, &number); - *ret = (unsigned long)number; - - return result; -} - -int string_to_number(const char *s, unsigned int min, unsigned int max, - unsigned int *ret) -{ - int result; - unsigned long number; - - result = string_to_number_l(s, min, max, &number); - *ret = (unsigned int)number; - - return result; -} - static void set_option(unsigned int *options, unsigned int option, u_int8_t *invflg, int invert) { if (*options & option) - exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed", + xtables_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed", opt2char(option)); *options |= option; @@ -1050,254 +486,13 @@ set_option(unsigned int *options, unsigned int option, u_int8_t *invflg, for (i = 0; 1 << i != option; i++); if (!inverse_for_options[i]) - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "cannot have ! before -%c", opt2char(option)); *invflg |= inverse_for_options[i]; } } -struct iptables_target * -find_target(const char *name, enum ipt_tryload tryload) -{ - struct iptables_target *ptr; - - /* Standard target? */ - if (strcmp(name, "") == 0 - || strcmp(name, IPTC_LABEL_ACCEPT) == 0 - || strcmp(name, IPTC_LABEL_DROP) == 0 - || strcmp(name, IPTC_LABEL_QUEUE) == 0 - || strcmp(name, IPTC_LABEL_RETURN) == 0) - name = "standard"; - - for (ptr = iptables_targets; ptr; ptr = ptr->next) { - if (strcmp(name, ptr->name) == 0) - break; - } - -#ifndef NO_SHARED_LIBS - if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) { - char path[strlen(lib_dir) + sizeof("/libipt_.so") - + strlen(name)]; - sprintf(path, "%s/libipt_%s.so", lib_dir, name); - if (dlopen(path, RTLD_NOW)) { - /* Found library. If it didn't register itself, - maybe they specified match as a target. */ - ptr = find_target(name, DONT_LOAD); - if (!ptr) - exit_error(PARAMETER_PROBLEM, - "Couldn't load target `%s'\n", - name); - } else if (tryload == LOAD_MUST_SUCCEED) - exit_error(PARAMETER_PROBLEM, - "Couldn't load target `%s':%s\n", - name, dlerror()); - } -#else - if (ptr && !ptr->loaded) { - if (tryload != DONT_LOAD) - ptr->loaded = 1; - else - ptr = NULL; - } - if(!ptr && (tryload == LOAD_MUST_SUCCEED)) { - exit_error(PARAMETER_PROBLEM, - "Couldn't find target `%s'\n", name); - } -#endif - - if (ptr) - ptr->used = 1; - - return ptr; -} - -static struct option * -merge_options(struct option *oldopts, const struct option *newopts, - unsigned int *option_offset) -{ - unsigned int num_old, num_new, i; - struct option *merge; - - for (num_old = 0; oldopts[num_old].name; num_old++); - for (num_new = 0; newopts[num_new].name; num_new++); - - global_option_offset += OPTION_OFFSET; - *option_offset = global_option_offset; - - merge = malloc(sizeof(struct option) * (num_new + num_old + 1)); - memcpy(merge, oldopts, num_old * sizeof(struct option)); - free_opts(0); /* Release previous options merged if any */ - for (i = 0; i < num_new; i++) { - merge[num_old + i] = newopts[i]; - merge[num_old + i].val += *option_offset; - } - memset(merge + num_old + num_new, 0, sizeof(struct option)); - - return merge; -} - -static int compatible_revision(const char *name, u_int8_t revision, int opt) -{ - struct ipt_get_revision rev; - socklen_t s = sizeof(rev); - int max_rev, sockfd; - - sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); - if (sockfd < 0) { - fprintf(stderr, "Could not open socket to kernel: %s\n", - strerror(errno)); - exit(1); - } - - load_iptables_ko(modprobe); - - strcpy(rev.name, name); - rev.revision = revision; - - max_rev = getsockopt(sockfd, IPPROTO_IP, opt, &rev, &s); - if (max_rev < 0) { - /* Definitely don't support this? */ - if (errno == EPROTONOSUPPORT) { - close(sockfd); - return 0; - } else if (errno == ENOPROTOOPT) { - close(sockfd); - /* Assume only revision 0 support (old kernel) */ - return (revision == 0); - } else { - fprintf(stderr, "getsockopt for %s failed strangely: %s\n", - name, - strerror(errno)); - /* exit(1); */ - } - } - close(sockfd); - return 1; -} - -static int compatible_match_revision(const char *name, u_int8_t revision) -{ - return compatible_revision(name, revision, IPT_SO_GET_REVISION_MATCH); -} - -static int compatible_target_revision(const char *name, u_int8_t revision) -{ - return compatible_revision(name, revision, IPT_SO_GET_REVISION_TARGET); -} - -void -register_match(struct iptables_match *me) -{ - struct iptables_match **i, *old; - - if (strcmp(me->version, program_version) != 0) { - fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n", - program_name, me->name, me->version, program_version); - exit(1); - } - - /* Revision field stole a char from name. */ - if (strlen(me->name) >= IPT_FUNCTION_MAXNAMELEN-1) { - fprintf(stderr, "%s: target `%s' has invalid name\n", - program_name, me->name); - exit(1); - } - - old = find_match(me->name, DURING_LOAD, NULL); - if (old) { - if (old->revision == me->revision) { - fprintf(stderr, - "%s: match `%s' already registered.\n", - program_name, me->name); - exit(1); - } - - /* Now we have two (or more) options, check compatibility. */ - if (compatible_match_revision(old->name, old->revision) - && old->revision > me->revision) - return; - - /* Replace if compatible. */ - if (!compatible_match_revision(me->name, me->revision)) - return; - - /* Delete old one. */ - for (i = &iptables_matches; *i!=old; i = &(*i)->next); - *i = old->next; - } - - if (me->size != IPT_ALIGN(me->size)) { - fprintf(stderr, "%s: match `%s' has invalid size %u.\n", - program_name, me->name, (unsigned int)me->size); - exit(1); - } - - /* Append to list. */ - for (i = &iptables_matches; *i; i = &(*i)->next); - me->next = NULL; - *i = me; - - me->m = NULL; - me->mflags = 0; -} - -void -register_target(struct iptables_target *me) -{ - struct iptables_target *old; - - if (strcmp(me->version, program_version) != 0) { - fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n", - program_name, me->name, me->version, program_version); - exit(1); - } - - /* Revision field stole a char from name. */ - if (strlen(me->name) >= IPT_FUNCTION_MAXNAMELEN-1) { - fprintf(stderr, "%s: target `%s' has invalid name\n", - program_name, me->name); - exit(1); - } - - old = find_target(me->name, DURING_LOAD); - if (old) { - struct iptables_target **i; - - if (old->revision == me->revision) { - fprintf(stderr, - "%s: target `%s' already registered.\n", - program_name, me->name); - exit(1); - } - - /* Now we have two (or more) options, check compatibility. */ - if (compatible_target_revision(old->name, old->revision) - && old->revision > me->revision) - return; - - /* Replace if compatible. */ - if (!compatible_target_revision(me->name, me->revision)) - return; - - /* Delete old one. */ - for (i = &iptables_targets; *i!=old; i = &(*i)->next); - *i = old->next; - } - - if (me->size != IPT_ALIGN(me->size)) { - fprintf(stderr, "%s: target `%s' has invalid size %u.\n", - program_name, me->name, (unsigned int)me->size); - exit(1); - } - - /* Prepend to list. */ - me->next = iptables_targets; - iptables_targets = me; - me->t = NULL; - me->tflags = 0; -} - static void print_num(u_int64_t number, unsigned int format) { @@ -1325,7 +520,7 @@ print_num(u_int64_t number, unsigned int format) static void -print_header(unsigned int format, const char *chain, iptc_handle_t *handle) +print_header(unsigned int format, const char *chain, struct iptc_handle *handle) { struct ipt_counters counters; const char *pol = iptc_get_policy(chain, &counters, handle); @@ -1379,7 +574,8 @@ print_match(const struct ipt_entry_match *m, const struct ipt_ip *ip, int numeric) { - struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD, NULL); + struct xtables_match *match = + xtables_find_match(m->u.user.name, XTF_TRY_LOAD, NULL); if (match) { if (match->print) @@ -1394,29 +590,30 @@ print_match(const struct ipt_entry_match *m, return 0; } -/* e is called `fw' here for hysterical raisins */ +/* e is called `fw' here for historical reasons */ static void print_firewall(const struct ipt_entry *fw, const char *targname, unsigned int num, unsigned int format, - const iptc_handle_t handle) + struct iptc_handle *const handle) { - struct iptables_target *target = NULL; + struct xtables_target *target = NULL; const struct ipt_entry_target *t; u_int8_t flags; char buf[BUFSIZ]; if (!iptc_is_chain(targname, handle)) - target = find_target(targname, TRY_LOAD); + target = xtables_find_target(targname, XTF_TRY_LOAD); else - target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED); + target = xtables_find_target(IPT_STANDARD_TARGET, + XTF_LOAD_MUST_SUCCEED); t = ipt_get_target((struct ipt_entry *)fw); flags = fw->ip.flags; if (format & FMT_LINENUMBERS) - printf(FMT("%-4u ", "%u "), num+1); + printf(FMT("%-4u ", "%u "), num); if (!(format & FMT_NOCOUNTS)) { print_num(fw->counters.pcnt, format); @@ -1428,7 +625,7 @@ print_firewall(const struct ipt_entry *fw, fputc(fw->ip.invflags & IPT_INV_PROTO ? '!' : ' ', stdout); { - char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC); + const char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC); if (pname) printf(FMT("%-5s", "%s "), pname); else @@ -1478,10 +675,10 @@ print_firewall(const struct ipt_entry *fw, printf(FMT("%-19s ","%s "), "anywhere"); else { if (format & FMT_NUMERIC) - sprintf(buf, "%s", addr_to_dotted(&(fw->ip.src))); + strcpy(buf, xtables_ipaddr_to_numeric(&fw->ip.src)); else - sprintf(buf, "%s", addr_to_anyname(&(fw->ip.src))); - strcat(buf, mask_to_dotted(&(fw->ip.smsk))); + strcpy(buf, xtables_ipaddr_to_anyname(&fw->ip.src)); + strcat(buf, xtables_ipmask_to_numeric(&fw->ip.smsk)); printf(FMT("%-19s ","%s "), buf); } @@ -1490,10 +687,10 @@ print_firewall(const struct ipt_entry *fw, printf(FMT("%-19s ","-> %s"), "anywhere"); else { if (format & FMT_NUMERIC) - sprintf(buf, "%s", addr_to_dotted(&(fw->ip.dst))); + strcpy(buf, xtables_ipaddr_to_numeric(&fw->ip.dst)); else - sprintf(buf, "%s", addr_to_anyname(&(fw->ip.dst))); - strcat(buf, mask_to_dotted(&(fw->ip.dmsk))); + strcpy(buf, xtables_ipaddr_to_anyname(&fw->ip.dst)); + strcat(buf, xtables_ipmask_to_numeric(&fw->ip.dmsk)); printf(FMT("%-19s ","-> %s"), buf); } @@ -1521,7 +718,7 @@ print_firewall(const struct ipt_entry *fw, static void print_firewall_line(const struct ipt_entry *fw, - const iptc_handle_t h) + struct iptc_handle *const h) { struct ipt_entry_target *t; @@ -1534,20 +731,24 @@ append_entry(const ipt_chainlabel chain, struct ipt_entry *fw, unsigned int nsaddrs, const struct in_addr saddrs[], + const struct in_addr smasks[], unsigned int ndaddrs, const struct in_addr daddrs[], + const struct in_addr dmasks[], int verbose, - iptc_handle_t *handle) + struct iptc_handle *handle) { unsigned int i, j; int ret = 1; for (i = 0; i < nsaddrs; i++) { fw->ip.src.s_addr = saddrs[i].s_addr; + fw->ip.smsk.s_addr = smasks[i].s_addr; for (j = 0; j < ndaddrs; j++) { fw->ip.dst.s_addr = daddrs[j].s_addr; + fw->ip.dmsk.s_addr = dmasks[j].s_addr; if (verbose) - print_firewall_line(fw, *handle); + print_firewall_line(fw, handle); ret &= iptc_append_entry(chain, fw, handle); } } @@ -1559,16 +760,18 @@ static int replace_entry(const ipt_chainlabel chain, struct ipt_entry *fw, unsigned int rulenum, - const struct in_addr *saddr, - const struct in_addr *daddr, + const struct in_addr *saddr, const struct in_addr *smask, + const struct in_addr *daddr, const struct in_addr *dmask, int verbose, - iptc_handle_t *handle) + struct iptc_handle *handle) { fw->ip.src.s_addr = saddr->s_addr; fw->ip.dst.s_addr = daddr->s_addr; + fw->ip.smsk.s_addr = smask->s_addr; + fw->ip.dmsk.s_addr = dmask->s_addr; if (verbose) - print_firewall_line(fw, *handle); + print_firewall_line(fw, handle); return iptc_replace_entry(chain, fw, rulenum, handle); } @@ -1578,20 +781,24 @@ insert_entry(const ipt_chainlabel chain, unsigned int rulenum, unsigned int nsaddrs, const struct in_addr saddrs[], + const struct in_addr smasks[], unsigned int ndaddrs, const struct in_addr daddrs[], + const struct in_addr dmasks[], int verbose, - iptc_handle_t *handle) + struct iptc_handle *handle) { unsigned int i, j; int ret = 1; for (i = 0; i < nsaddrs; i++) { fw->ip.src.s_addr = saddrs[i].s_addr; + fw->ip.smsk.s_addr = smasks[i].s_addr; for (j = 0; j < ndaddrs; j++) { fw->ip.dst.s_addr = daddrs[j].s_addr; + fw->ip.dmsk.s_addr = dmasks[j].s_addr; if (verbose) - print_firewall_line(fw, *handle); + print_firewall_line(fw, handle); ret &= iptc_insert_entry(chain, fw, rulenum, handle); } } @@ -1600,20 +807,21 @@ insert_entry(const ipt_chainlabel chain, } static unsigned char * -make_delete_mask(struct ipt_entry *fw, struct iptables_rule_match *matches) +make_delete_mask(struct xtables_rule_match *matches, + const struct xtables_target *target) { /* Establish mask for comparison */ unsigned int size; - struct iptables_rule_match *matchp; + struct xtables_rule_match *matchp; unsigned char *mask, *mptr; size = sizeof(struct ipt_entry); for (matchp = matches; matchp; matchp = matchp->next) size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + matchp->match->size; - mask = fw_calloc(1, size + mask = xtables_calloc(1, size + IPT_ALIGN(sizeof(struct ipt_entry_target)) - + iptables_targets->size); + + target->size); memset(mask, 0xFF, sizeof(struct ipt_entry)); mptr = mask + sizeof(struct ipt_entry); @@ -1627,7 +835,7 @@ make_delete_mask(struct ipt_entry *fw, struct iptables_rule_match *matches) memset(mptr, 0xFF, IPT_ALIGN(sizeof(struct ipt_entry_target)) - + iptables_targets->userspacesize); + + target->userspacesize); return mask; } @@ -1637,23 +845,28 @@ delete_entry(const ipt_chainlabel chain, struct ipt_entry *fw, unsigned int nsaddrs, const struct in_addr saddrs[], + const struct in_addr smasks[], unsigned int ndaddrs, const struct in_addr daddrs[], + const struct in_addr dmasks[], int verbose, - iptc_handle_t *handle, - struct iptables_rule_match *matches) + struct iptc_handle *handle, + struct xtables_rule_match *matches, + const struct xtables_target *target) { unsigned int i, j; int ret = 1; unsigned char *mask; - mask = make_delete_mask(fw, matches); + mask = make_delete_mask(matches, target); for (i = 0; i < nsaddrs; i++) { fw->ip.src.s_addr = saddrs[i].s_addr; + fw->ip.smsk.s_addr = smasks[i].s_addr; for (j = 0; j < ndaddrs; j++) { fw->ip.dst.s_addr = daddrs[j].s_addr; + fw->ip.dmsk.s_addr = dmasks[j].s_addr; if (verbose) - print_firewall_line(fw, *handle); + print_firewall_line(fw, handle); ret &= iptc_delete_entry(chain, fw, mask, handle); } } @@ -1663,8 +876,8 @@ delete_entry(const ipt_chainlabel chain, } int -for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *), - int verbose, int builtinstoo, iptc_handle_t *handle) +for_each_chain(int (*fn)(const ipt_chainlabel, int, struct iptc_handle *), + int verbose, int builtinstoo, struct iptc_handle *handle) { int ret = 1; const char *chain; @@ -1677,7 +890,7 @@ for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *), chain = iptc_next_chain(handle); } - chains = fw_malloc(sizeof(ipt_chainlabel) * chaincount); + chains = xtables_malloc(sizeof(ipt_chainlabel) * chaincount); i = 0; chain = iptc_first_chain(handle); while (chain) { @@ -1689,7 +902,7 @@ for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *), for (i = 0; i < chaincount; i++) { if (!builtinstoo && iptc_builtin(chains + i*sizeof(ipt_chainlabel), - *handle) == 1) + handle) == 1) continue; ret &= fn(chains + i*sizeof(ipt_chainlabel), verbose, handle); } @@ -1700,7 +913,7 @@ for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *), int flush_entries(const ipt_chainlabel chain, int verbose, - iptc_handle_t *handle) + struct iptc_handle *handle) { if (!chain) return for_each_chain(flush_entries, verbose, 1, handle); @@ -1712,7 +925,7 @@ flush_entries(const ipt_chainlabel chain, int verbose, static int zero_entries(const ipt_chainlabel chain, int verbose, - iptc_handle_t *handle) + struct iptc_handle *handle) { if (!chain) return for_each_chain(zero_entries, verbose, 1, handle); @@ -1724,19 +937,19 @@ zero_entries(const ipt_chainlabel chain, int verbose, int delete_chain(const ipt_chainlabel chain, int verbose, - iptc_handle_t *handle) + struct iptc_handle *handle) { if (!chain) return for_each_chain(delete_chain, verbose, 0, handle); if (verbose) - fprintf(stdout, "Deleting chain `%s'\n", chain); + fprintf(stdout, "Deleting chain `%s'\n", chain); return iptc_delete_chain(chain, handle); } static int -list_entries(const ipt_chainlabel chain, int verbose, int numeric, - int expanded, int linenumbers, iptc_handle_t *handle) +list_entries(const ipt_chainlabel chain, int rulenum, int verbose, int numeric, + int expanded, int linenumbers, struct iptc_handle *handle) { int found = 0; unsigned int format; @@ -1768,16 +981,19 @@ list_entries(const ipt_chainlabel chain, int verbose, int numeric, if (found) printf("\n"); - print_header(format, this, handle); + if (!rulenum) + print_header(format, this, handle); i = iptc_first_rule(this, handle); num = 0; while (i) { - print_firewall(i, - iptc_get_target(i, handle), - num++, - format, - *handle); + num++; + if (!rulenum || num == rulenum) + print_firewall(i, + iptc_get_target(i, handle), + num, + format, + handle); i = iptc_next_rule(i, handle); } found = 1; @@ -1787,97 +1003,266 @@ list_entries(const ipt_chainlabel chain, int verbose, int numeric, return found; } -static char *get_modprobe(void) +static void print_proto(u_int16_t proto, int invert) { - int procfile; - char *ret; - -#define PROCFILE_BUFSIZ 1024 - procfile = open(PROC_SYS_MODPROBE, O_RDONLY); - if (procfile < 0) - return NULL; - - ret = (char *) malloc(PROCFILE_BUFSIZ); - if (ret) { - memset(ret, 0, PROCFILE_BUFSIZ); - switch (read(procfile, ret, PROCFILE_BUFSIZ)) { - case -1: goto fail; - case PROCFILE_BUFSIZ: goto fail; /* Partial read. Wierd */ + if (proto) { + unsigned int i; + const char *invertstr = invert ? "! " : ""; + + struct protoent *pent = getprotobynumber(proto); + if (pent) { + printf("%s-p %s ", invertstr, pent->p_name); + return; } - if (ret[strlen(ret)-1]=='\n') - ret[strlen(ret)-1]=0; - close(procfile); - return ret; + + for (i = 0; xtables_chain_protos[i].name != NULL; ++i) + if (xtables_chain_protos[i].num == proto) { + printf("%s-p %s ", + invertstr, xtables_chain_protos[i].name); + return; + } + + printf("%s-p %u ", invertstr, proto); } - fail: - free(ret); - close(procfile); - return NULL; } -int iptables_insmod(const char *modname, const char *modprobe) +#define IP_PARTS_NATIVE(n) \ +(unsigned int)((n)>>24)&0xFF, \ +(unsigned int)((n)>>16)&0xFF, \ +(unsigned int)((n)>>8)&0xFF, \ +(unsigned int)((n)&0xFF) + +#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n)) + +/* This assumes that mask is contiguous, and byte-bounded. */ +static void +print_iface(char letter, const char *iface, const unsigned char *mask, + int invert) { - char *buf = NULL; - char *argv[3]; - int status; - - /* If they don't explicitly set it, read out of kernel */ - if (!modprobe) { - buf = get_modprobe(); - if (!buf) - return -1; - modprobe = buf; + unsigned int i; + + if (mask[0] == 0) + return; + + printf("%s-%c ", invert ? "! " : "", letter); + + for (i = 0; i < IFNAMSIZ; i++) { + if (mask[i] != 0) { + if (iface[i] != '\0') + printf("%c", iface[i]); + } else { + /* we can access iface[i-1] here, because + * a few lines above we make sure that mask[0] != 0 */ + if (iface[i-1] != '\0') + printf("+"); + break; + } } - switch (fork()) { - case 0: - argv[0] = (char *)modprobe; - argv[1] = (char *)modname; - argv[2] = NULL; - execv(argv[0], argv); + printf(" "); +} - /* not usually reached */ - exit(1); - case -1: - return -1; +static int print_match_save(const struct ipt_entry_match *e, + const struct ipt_ip *ip) +{ + struct xtables_match *match = + xtables_find_match(e->u.user.name, XTF_TRY_LOAD, NULL); + + if (match) { + printf("-m %s ", e->u.user.name); + + /* some matches don't provide a save function */ + if (match->save) + match->save(ip, e); + } else { + if (e->u.match_size) { + fprintf(stderr, + "Can't find library for match `%s'\n", + e->u.user.name); + exit(1); + } + } + return 0; +} + +/* print a given ip including mask if neccessary */ +static void print_ip(char *prefix, u_int32_t ip, u_int32_t mask, int invert) +{ + u_int32_t bits, hmask = ntohl(mask); + int i; - default: /* parent */ - wait(&status); + if (!mask && !ip && !invert) + return; + + printf("%s%s %u.%u.%u.%u", + invert ? "! " : "", + prefix, + IP_PARTS(ip)); + + if (mask == 0xFFFFFFFFU) { + printf("/32 "); + return; } - free(buf); - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) - return 0; - return -1; + i = 32; + bits = 0xFFFFFFFEU; + while (--i >= 0 && hmask != bits) + bits <<= 1; + if (i >= 0) + printf("/%u ", i); + else + printf("/%u.%u.%u.%u ", IP_PARTS(mask)); } -int load_iptables_ko(const char *modprobe) +/* We want this to be readable, so only print out neccessary fields. + * Because that's the kind of world I want to live in. */ +void print_rule(const struct ipt_entry *e, + struct iptc_handle *h, const char *chain, int counters) { - static int loaded = 0; - static int ret = -1; + struct ipt_entry_target *t; + const char *target_name; + + /* print counters for iptables-save */ + if (counters > 0) + printf("[%llu:%llu] ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt); + + /* print chain name */ + printf("-A %s ", chain); + + /* Print IP part. */ + print_ip("-s", e->ip.src.s_addr,e->ip.smsk.s_addr, + e->ip.invflags & IPT_INV_SRCIP); + + print_ip("-d", e->ip.dst.s_addr, e->ip.dmsk.s_addr, + e->ip.invflags & IPT_INV_DSTIP); + + print_iface('i', e->ip.iniface, e->ip.iniface_mask, + e->ip.invflags & IPT_INV_VIA_IN); + + print_iface('o', e->ip.outiface, e->ip.outiface_mask, + e->ip.invflags & IPT_INV_VIA_OUT); + + print_proto(e->ip.proto, e->ip.invflags & IPT_INV_PROTO); + + if (e->ip.flags & IPT_F_FRAG) + printf("%s-f ", + e->ip.invflags & IPT_INV_FRAG ? "! " : ""); - if (!loaded) { - ret = iptables_insmod("ip_tables", NULL); - loaded = 1; + /* Print matchinfo part */ + if (e->target_offset) { + IPT_MATCH_ITERATE(e, print_match_save, &e->ip); } - return ret; + /* print counters for iptables -R */ + if (counters < 0) + printf("-c %llu %llu ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt); + + /* Print target name */ + target_name = iptc_get_target(e, h); + if (target_name && (*target_name != '\0')) +#ifdef IPT_F_GOTO + printf("-%c %s ", e->ip.flags & IPT_F_GOTO ? 'g' : 'j', target_name); +#else + printf("-j %s ", target_name); +#endif + + /* Print targinfo part */ + t = ipt_get_target((struct ipt_entry *)e); + if (t->u.user.name[0]) { + struct xtables_target *target = + xtables_find_target(t->u.user.name, XTF_TRY_LOAD); + + if (!target) { + fprintf(stderr, "Can't find library for target `%s'\n", + t->u.user.name); + exit(1); + } + + if (target->save) + target->save(&e->ip, t); + else { + /* If the target size is greater than ipt_entry_target + * there is something to be saved, we just don't know + * how to print it */ + if (t->u.target_size != + sizeof(struct ipt_entry_target)) { + fprintf(stderr, "Target `%s' is missing " + "save function\n", + t->u.user.name); + exit(1); + } + } + } + printf("\n"); +} + +static int +list_rules(const ipt_chainlabel chain, int rulenum, int counters, + struct iptc_handle *handle) +{ + const char *this = NULL; + int found = 0; + + if (counters) + counters = -1; /* iptables -c format */ + + /* Dump out chain names first, + * thereby preventing dependency conflicts */ + if (!rulenum) for (this = iptc_first_chain(handle); + this; + this = iptc_next_chain(handle)) { + if (chain && strcmp(this, chain) != 0) + continue; + + if (iptc_builtin(this, handle)) { + struct ipt_counters count; + printf("-P %s %s", this, iptc_get_policy(this, &count, handle)); + if (counters) + printf(" -c %llu %llu", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt); + printf("\n"); + } else { + printf("-N %s\n", this); + } + } + + for (this = iptc_first_chain(handle); + this; + this = iptc_next_chain(handle)) { + const struct ipt_entry *e; + int num = 0; + + if (chain && strcmp(this, chain) != 0) + continue; + + /* Dump out rules */ + e = iptc_first_rule(this, handle); + while(e) { + num++; + if (!rulenum || num == rulenum) + print_rule(e, handle, this, counters); + e = iptc_next_rule(e, handle); + } + found = 1; + } + + errno = ENOENT; + return found; } static struct ipt_entry * generate_entry(const struct ipt_entry *fw, - struct iptables_rule_match *matches, + struct xtables_rule_match *matches, struct ipt_entry_target *target) { unsigned int size; - struct iptables_rule_match *matchp; + struct xtables_rule_match *matchp; struct ipt_entry *e; size = sizeof(struct ipt_entry); for (matchp = matches; matchp; matchp = matchp->next) size += matchp->match->m->u.match_size; - e = fw_malloc(size + target->u.target_size); + e = xtables_malloc(size + target->u.target_size); *e = *fw; e->target_offset = size; e->next_offset = size + target->u.target_size; @@ -1892,9 +1277,9 @@ generate_entry(const struct ipt_entry *fw, return e; } -void clear_rule_matches(struct iptables_rule_match **matches) +static void clear_rule_matches(struct xtables_rule_match **matches) { - struct iptables_rule_match *matchp, *tmp; + struct xtables_rule_match *matchp, *tmp; for (matchp = *matches; matchp;) { tmp = matchp->next; @@ -1913,14 +1298,6 @@ void clear_rule_matches(struct iptables_rule_match **matches) *matches = NULL; } -static void set_revision(char *name, u_int8_t revision) -{ - /* Old kernel sources don't have ".revision" field, - but we stole a byte from name. */ - name[IPT_FUNCTION_MAXNAMELEN - 2] = '\0'; - name[IPT_FUNCTION_MAXNAMELEN - 1] = revision; -} - void get_kernel_version(void) { static struct utsname uts; @@ -1928,20 +1305,21 @@ get_kernel_version(void) { if (uname(&uts) == -1) { fprintf(stderr, "Unable to retrieve kernel version.\n"); - free_opts(1); - exit(1); + xtables_free_opts(1); + exit(1); } sscanf(uts.release, "%d.%d.%d", &x, &y, &z); kernel_version = LINUX_VERSION(x, y, z); } -int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) +int do_command(int argc, char *argv[], char **table, struct iptc_handle **handle) { struct ipt_entry fw, *e = NULL; int invert = 0; unsigned int nsaddrs = 0, ndaddrs = 0; - struct in_addr *saddrs = NULL, *daddrs = NULL; + struct in_addr *saddrs = NULL, *smasks = NULL; + struct in_addr *daddrs = NULL, *dmasks = NULL; int c, verbose = 0; const char *chain = NULL; @@ -1950,14 +1328,15 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) unsigned int rulenum = 0, options = 0, command = 0; const char *pcnt = NULL, *bcnt = NULL; int ret = 1; - struct iptables_match *m; - struct iptables_rule_match *matches = NULL; - struct iptables_rule_match *matchp; - struct iptables_target *target = NULL; - struct iptables_target *t; + struct xtables_match *m; + struct xtables_rule_match *matches = NULL; + struct xtables_rule_match *matchp; + struct xtables_target *target = NULL; + struct xtables_target *t; const char *jumpto = ""; char *protocol = NULL; int proto_used = 0; + unsigned long long cnt; memset(&fw, 0, sizeof(fw)); @@ -1967,10 +1346,10 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) /* clear mflags in case do_command gets called a second time * (we clear the global list of all matches for security)*/ - for (m = iptables_matches; m; m = m->next) + for (m = xtables_matches; m; m = m->next) m->mflags = 0; - for (t = iptables_targets; t; t = t->next) { + for (t = xtables_targets; t; t = t->next) { t->tflags = 0; t->used = 0; } @@ -1980,7 +1359,7 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) opterr = 0; while ((c = getopt_long(argc, argv, - "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:", + "-A:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:", opts, NULL)) != -1) { switch (c) { /* @@ -2011,7 +1390,7 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) && argv[optind][0] != '!') rulenum = parse_rulenumber(argv[optind++]); else - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "-%c requires a rule number", cmd2char(CMD_REPLACE)); break; @@ -2027,12 +1406,27 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) break; case 'L': - add_command(&command, CMD_LIST, CMD_ZERO, - invert); + add_command(&command, CMD_LIST, + CMD_ZERO | CMD_ZERO_NUM, invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + rulenum = parse_rulenumber(argv[optind++]); + break; + + case 'S': + add_command(&command, CMD_LIST_RULES, + CMD_ZERO|CMD_ZERO_NUM, invert); if (optarg) chain = optarg; else if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') chain = argv[optind++]; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + rulenum = parse_rulenumber(argv[optind++]); break; case 'F': @@ -2045,21 +1439,26 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) break; case 'Z': - add_command(&command, CMD_ZERO, CMD_LIST, + add_command(&command, CMD_ZERO, CMD_LIST|CMD_LIST_RULES, invert); if (optarg) chain = optarg; else if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') chain = argv[optind++]; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') { + rulenum = parse_rulenumber(argv[optind++]); + command = CMD_ZERO_NUM; + } break; case 'N': if (optarg && (*optarg == '-' || *optarg == '!')) - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "chain name not allowed to start " "with `%c'\n", *optarg); - if (find_target(optarg, TRY_LOAD)) - exit_error(PARAMETER_PROBLEM, + if (xtables_find_target(optarg, XTF_TRY_LOAD)) + xtables_error(PARAMETER_PROBLEM, "chain name may not clash " "with target name\n"); add_command(&command, CMD_NEW_CHAIN, CMD_NONE, @@ -2084,8 +1483,8 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) && argv[optind][0] != '!') newname = argv[optind++]; else - exit_error(PARAMETER_PROBLEM, - "-%c requires old-chain-name and " + xtables_error(PARAMETER_PROBLEM, + "-%c requires old-chain-name and " "new-chain-name", cmd2char(CMD_RENAME_CHAIN)); break; @@ -2098,7 +1497,7 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) && argv[optind][0] != '!') policy = argv[optind++]; else - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "-%c requires a chain and a policy", cmd2char(CMD_SET_POLICY)); break; @@ -2109,7 +1508,8 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) /* iptables -p icmp -h */ if (!matches && protocol) - find_match(protocol, TRY_LOAD, &matches); + xtables_find_match(protocol, + XTF_TRY_LOAD, &matches); exit_printhelp(matches); @@ -2117,35 +1517,35 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) * Option selection */ case 'p': - check_inverse(optarg, &invert, &optind, argc); + xtables_check_inverse(optarg, &invert, &optind, argc, argv); set_option(&options, OPT_PROTOCOL, &fw.ip.invflags, invert); /* Canonicalize into lower case */ - for (protocol = argv[optind-1]; *protocol; protocol++) + for (protocol = optarg; *protocol; protocol++) *protocol = tolower(*protocol); - protocol = argv[optind-1]; - fw.ip.proto = parse_protocol(protocol); + protocol = optarg; + fw.ip.proto = xtables_parse_protocol(protocol); if (fw.ip.proto == 0 && (fw.ip.invflags & IPT_INV_PROTO)) - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "rule would never match protocol"); break; case 's': - check_inverse(optarg, &invert, &optind, argc); + xtables_check_inverse(optarg, &invert, &optind, argc, argv); set_option(&options, OPT_SOURCE, &fw.ip.invflags, invert); - shostnetworkmask = argv[optind-1]; + shostnetworkmask = optarg; break; case 'd': - check_inverse(optarg, &invert, &optind, argc); + xtables_check_inverse(optarg, &invert, &optind, argc, argv); set_option(&options, OPT_DESTINATION, &fw.ip.invflags, invert); - dhostnetworkmask = argv[optind-1]; + dhostnetworkmask = optarg; break; #ifdef IPT_F_GOTO @@ -2162,7 +1562,7 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) invert); jumpto = parse_target(optarg); /* TRY_LOAD (may be chain name) */ - target = find_target(jumpto, TRY_LOAD); + target = xtables_find_target(jumpto, XTF_TRY_LOAD); if (target) { size_t size; @@ -2170,32 +1570,37 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) size = IPT_ALIGN(sizeof(struct ipt_entry_target)) + target->size; - target->t = fw_calloc(1, size); + target->t = xtables_calloc(1, size); target->t->u.target_size = size; strcpy(target->t->u.user.name, jumpto); - set_revision(target->t->u.user.name, + xtables_set_revision(target->t->u.user.name, target->revision); if (target->init != NULL) - target->init(target->t, &fw.nfcache); - opts = merge_options(opts, target->extra_opts, &target->option_offset); + target->init(target->t); + opts = xtables_merge_options(opts, + target->extra_opts, + &target->option_offset); + if (opts == NULL) + xtables_error(OTHER_PROBLEM, + "can't alloc memory!"); } break; case 'i': - check_inverse(optarg, &invert, &optind, argc); + xtables_check_inverse(optarg, &invert, &optind, argc, argv); set_option(&options, OPT_VIANAMEIN, &fw.ip.invflags, invert); - parse_interface(argv[optind-1], + xtables_parse_interface(optarg, fw.ip.iniface, fw.ip.iniface_mask); break; case 'o': - check_inverse(optarg, &invert, &optind, argc); + xtables_check_inverse(optarg, &invert, &optind, argc, argv); set_option(&options, OPT_VIANAMEOUT, &fw.ip.invflags, invert); - parse_interface(argv[optind-1], + xtables_parse_interface(optarg, fw.ip.outiface, fw.ip.outiface_mask); break; @@ -2217,21 +1622,28 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) size_t size; if (invert) - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "unexpected ! flag before --match"); - m = find_match(optarg, LOAD_MUST_SUCCEED, &matches); + m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, + &matches); size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; - m->m = fw_calloc(1, size); + m->m = xtables_calloc(1, size); m->m->u.match_size = size; strcpy(m->m->u.user.name, m->name); - set_revision(m->m->u.user.name, m->revision); + xtables_set_revision(m->m->u.user.name, m->revision); if (m->init != NULL) - m->init(m->m, &fw.nfcache); - if (m != m->next) + m->init(m->m); + if (m != m->next) { /* Merge options for non-cloned matches */ - opts = merge_options(opts, m->extra_opts, &m->option_offset); + opts = xtables_merge_options(opts, + m->extra_opts, + &m->option_offset); + if (opts == NULL) + xtables_error(OTHER_PROBLEM, + "can't alloc memory!"); + } } break; @@ -2242,9 +1654,9 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) case 't': if (invert) - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "unexpected ! flag before --table"); - *table = argv[optind-1]; + *table = optarg; break; case 'x': @@ -2254,10 +1666,10 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) case 'V': if (invert) - printf("Not %s ;-)\n", program_version); + printf("Not %s ;-)\n", prog_vers); else printf("%s v%s\n", - program_name, program_version); + prog_name, prog_vers); exit(0); case '0': @@ -2266,7 +1678,7 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) break; case 'M': - modprobe = optarg; + xtables_modprobe_program = optarg; break; case 'c': @@ -2274,54 +1686,58 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) set_option(&options, OPT_COUNTERS, &fw.ip.invflags, invert); pcnt = optarg; - if (optind < argc && argv[optind][0] != '-' + bcnt = strchr(pcnt + 1, ','); + if (bcnt) + bcnt++; + if (!bcnt && optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') bcnt = argv[optind++]; - else - exit_error(PARAMETER_PROBLEM, + if (!bcnt) + xtables_error(PARAMETER_PROBLEM, "-%c requires packet and byte counter", opt2char(OPT_COUNTERS)); - if (sscanf(pcnt, "%llu", (unsigned long long *)&fw.counters.pcnt) != 1) - exit_error(PARAMETER_PROBLEM, + if (sscanf(pcnt, "%llu", &cnt) != 1) + xtables_error(PARAMETER_PROBLEM, "-%c packet counter not numeric", opt2char(OPT_COUNTERS)); + fw.counters.pcnt = cnt; - if (sscanf(bcnt, "%llu", (unsigned long long *)&fw.counters.bcnt) != 1) - exit_error(PARAMETER_PROBLEM, + if (sscanf(bcnt, "%llu", &cnt) != 1) + xtables_error(PARAMETER_PROBLEM, "-%c byte counter not numeric", opt2char(OPT_COUNTERS)); - + fw.counters.bcnt = cnt; break; case 1: /* non option */ if (optarg[0] == '!' && optarg[1] == '\0') { if (invert) - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "multiple consecutive ! not" " allowed"); invert = TRUE; optarg[0] = '\0'; continue; } - printf("Bad argument `%s'\n", optarg); + fprintf(stderr, "Bad argument `%s'\n", optarg); exit_tryhelp(2); default: - if (!target - || !(target->parse(c - target->option_offset, + if (target == NULL || target->parse == NULL || + !target->parse(c - target->option_offset, argv, invert, &target->tflags, - &fw, &target->t))) { + &fw, &target->t)) { for (matchp = matches; matchp; matchp = matchp->next) { - if (matchp->completed) + if (matchp->completed || + matchp->match->parse == NULL) continue; if (matchp->match->parse(c - matchp->match->option_offset, argv, invert, &matchp->match->mflags, &fw, - &fw.nfcache, &matchp->match->m)) break; } @@ -2352,60 +1768,88 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) */ if (m == NULL && protocol - && (!find_proto(protocol, DONT_LOAD, - options&OPT_NUMERIC, NULL) - || (find_proto(protocol, DONT_LOAD, + && (!find_proto(protocol, XTF_DONT_LOAD, + options&OPT_NUMERIC, NULL) + || (find_proto(protocol, XTF_DONT_LOAD, options&OPT_NUMERIC, NULL) && (proto_used == 0)) ) - && (m = find_proto(protocol, TRY_LOAD, + && (m = find_proto(protocol, XTF_TRY_LOAD, options&OPT_NUMERIC, &matches))) { /* Try loading protocol */ size_t size; - + proto_used = 1; size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; - m->m = fw_calloc(1, size); + m->m = xtables_calloc(1, size); m->m->u.match_size = size; strcpy(m->m->u.user.name, m->name); - set_revision(m->m->u.user.name, + xtables_set_revision(m->m->u.user.name, m->revision); if (m->init != NULL) - m->init(m->m, &fw.nfcache); + m->init(m->m); - opts = merge_options(opts, - m->extra_opts, &m->option_offset); + opts = xtables_merge_options(opts, + m->extra_opts, + &m->option_offset); + if (opts == NULL) + xtables_error(OTHER_PROBLEM, + "can't alloc memory!"); optind--; continue; } - if (!m) - exit_error(PARAMETER_PROBLEM, - "Unknown arg `%s'", - argv[optind-1]); + if (!m) { + if (c == '?') { + if (optopt) { + xtables_error( + PARAMETER_PROBLEM, + "option `%s' " + "requires an " + "argument", + argv[optind-1]); + } else { + xtables_error( + PARAMETER_PROBLEM, + "unknown option " + "`%s'", + argv[optind-1]); + } + } + xtables_error(PARAMETER_PROBLEM, + "Unknown arg `%s'", optarg); + } } } invert = FALSE; } + if (strcmp(*table, "nat") == 0 && + ((policy != NULL && strcmp(policy, "DROP") == 0) || + (jumpto != NULL && strcmp(jumpto, "DROP") == 0))) + xtables_error(PARAMETER_PROBLEM, + "\nThe \"nat\" table is not intended for filtering, " + "the use of DROP is therefore inhibited.\n\n"); + for (matchp = matches; matchp; matchp = matchp->next) - matchp->match->final_check(matchp->match->mflags); + if (matchp->match->final_check != NULL) + matchp->match->final_check(matchp->match->mflags); - if (target) + if (target != NULL && target->final_check != NULL) target->final_check(target->tflags); /* Fix me: must put inverse options checking here --MN */ if (optind < argc) - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "unknown arguments found on commandline"); if (!command) - exit_error(PARAMETER_PROBLEM, "no command specified"); + xtables_error(PARAMETER_PROBLEM, "no command specified"); if (invert) - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "nothing appropriate following !"); if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) { @@ -2416,26 +1860,26 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) } if (shostnetworkmask) - parse_hostnetworkmask(shostnetworkmask, &saddrs, - &(fw.ip.smsk), &nsaddrs); + xtables_ipparse_multiple(shostnetworkmask, &saddrs, + &smasks, &nsaddrs); if (dhostnetworkmask) - parse_hostnetworkmask(dhostnetworkmask, &daddrs, - &(fw.ip.dmsk), &ndaddrs); + xtables_ipparse_multiple(dhostnetworkmask, &daddrs, + &dmasks, &ndaddrs); if ((nsaddrs > 1 || ndaddrs > 1) && (fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) - exit_error(PARAMETER_PROBLEM, "! not allowed with multiple" + xtables_error(PARAMETER_PROBLEM, "! not allowed with multiple" " source or destination IP addresses"); if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1)) - exit_error(PARAMETER_PROBLEM, "Replacement rule does not " + xtables_error(PARAMETER_PROBLEM, "Replacement rule does not " "specify a unique address"); generic_opt_check(command, options); if (chain && strlen(chain) > IPT_FUNCTION_MAXNAMELEN) - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "chain name `%s' too long (must be under %i chars)", chain, IPT_FUNCTION_MAXNAMELEN); @@ -2444,11 +1888,11 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) *handle = iptc_init(*table); /* try to insmod the module if iptc_init failed */ - if (!*handle && load_iptables_ko(modprobe) != -1) + if (!*handle && xtables_load_ko(xtables_modprobe_program, false) != -1) *handle = iptc_init(*table); if (!*handle) - exit_error(VERSION_PROBLEM, + xtables_error(VERSION_PROBLEM, "can't initialize iptables table `%s': %s", *table, iptc_strerror(errno)); @@ -2460,7 +1904,7 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) || strcmp(chain, "INPUT") == 0) { /* -o not valid with incoming packets. */ if (options & OPT_VIANAMEOUT) - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "Can't use -%c with %s\n", opt2char(OPT_VIANAMEOUT), chain); @@ -2470,15 +1914,16 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) || strcmp(chain, "OUTPUT") == 0) { /* -i not valid with outgoing packets */ if (options & OPT_VIANAMEIN) - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "Can't use -%c with %s\n", opt2char(OPT_VIANAMEIN), chain); } if (target && iptc_is_chain(jumpto, *handle)) { - printf("Warning: using chain %s, not extension\n", - jumpto); + fprintf(stderr, + "Warning: using chain %s, not extension\n", + jumpto); if (target->t) free(target->t); @@ -2493,19 +1938,19 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) || iptc_is_chain(jumpto, *handle))) { size_t size; - target = find_target(IPT_STANDARD_TARGET, - LOAD_MUST_SUCCEED); + target = xtables_find_target(IPT_STANDARD_TARGET, + XTF_LOAD_MUST_SUCCEED); size = sizeof(struct ipt_entry_target) + target->size; - target->t = fw_calloc(1, size); + target->t = xtables_calloc(1, size); target->t->u.target_size = size; strcpy(target->t->u.user.name, jumpto); if (!iptc_is_chain(jumpto, *handle)) - set_revision(target->t->u.user.name, + xtables_set_revision(target->t->u.user.name, target->revision); if (target->init != NULL) - target->init(target->t, &fw.nfcache); + target->init(target->t); } if (!target) { @@ -2515,10 +1960,10 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) * chain. */ #ifdef IPT_F_GOTO if (fw.ip.flags & IPT_F_GOTO) - exit_error(PARAMETER_PROBLEM, + xtables_error(PARAMETER_PROBLEM, "goto '%s' is not a chain\n", jumpto); #endif - find_target(jumpto, LOAD_MUST_SUCCEED); + xtables_find_target(jumpto, XTF_LOAD_MUST_SUCCEED); } else { e = generate_entry(&fw, matches, target->t); free(target->t); @@ -2528,66 +1973,82 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) switch (command) { case CMD_APPEND: ret = append_entry(chain, e, - nsaddrs, saddrs, ndaddrs, daddrs, + nsaddrs, saddrs, smasks, + ndaddrs, daddrs, dmasks, options&OPT_VERBOSE, - handle); + *handle); break; case CMD_DELETE: ret = delete_entry(chain, e, - nsaddrs, saddrs, ndaddrs, daddrs, + nsaddrs, saddrs, smasks, + ndaddrs, daddrs, dmasks, options&OPT_VERBOSE, - handle, matches); + *handle, matches, target); break; case CMD_DELETE_NUM: - ret = iptc_delete_num_entry(chain, rulenum - 1, handle); + ret = iptc_delete_num_entry(chain, rulenum - 1, *handle); break; case CMD_REPLACE: ret = replace_entry(chain, e, rulenum - 1, - saddrs, daddrs, options&OPT_VERBOSE, - handle); + saddrs, smasks, daddrs, dmasks, + options&OPT_VERBOSE, *handle); break; case CMD_INSERT: ret = insert_entry(chain, e, rulenum - 1, - nsaddrs, saddrs, ndaddrs, daddrs, + nsaddrs, saddrs, smasks, + ndaddrs, daddrs, dmasks, options&OPT_VERBOSE, - handle); - break; - case CMD_LIST: - ret = list_entries(chain, - options&OPT_VERBOSE, - options&OPT_NUMERIC, - options&OPT_EXPANDED, - options&OPT_LINENUMBERS, - handle); + *handle); break; case CMD_FLUSH: - ret = flush_entries(chain, options&OPT_VERBOSE, handle); + ret = flush_entries(chain, options&OPT_VERBOSE, *handle); break; case CMD_ZERO: - ret = zero_entries(chain, options&OPT_VERBOSE, handle); + ret = zero_entries(chain, options&OPT_VERBOSE, *handle); break; + case CMD_ZERO_NUM: + ret = iptc_zero_counter(chain, rulenum, *handle); + break; + case CMD_LIST: case CMD_LIST|CMD_ZERO: + case CMD_LIST|CMD_ZERO_NUM: ret = list_entries(chain, + rulenum, options&OPT_VERBOSE, options&OPT_NUMERIC, options&OPT_EXPANDED, options&OPT_LINENUMBERS, - handle); - if (ret) + *handle); + if (ret && (command & CMD_ZERO)) + ret = zero_entries(chain, + options&OPT_VERBOSE, *handle); + if (ret && (command & CMD_ZERO_NUM)) + ret = iptc_zero_counter(chain, rulenum, *handle); + break; + case CMD_LIST_RULES: + case CMD_LIST_RULES|CMD_ZERO: + case CMD_LIST_RULES|CMD_ZERO_NUM: + ret = list_rules(chain, + rulenum, + options&OPT_VERBOSE, + *handle); + if (ret && (command & CMD_ZERO)) ret = zero_entries(chain, - options&OPT_VERBOSE, handle); + options&OPT_VERBOSE, *handle); + if (ret && (command & CMD_ZERO_NUM)) + ret = iptc_zero_counter(chain, rulenum, *handle); break; case CMD_NEW_CHAIN: - ret = iptc_create_chain(chain, handle); + ret = iptc_create_chain(chain, *handle); break; case CMD_DELETE_CHAIN: - ret = delete_chain(chain, options&OPT_VERBOSE, handle); + ret = delete_chain(chain, options&OPT_VERBOSE, *handle); break; case CMD_RENAME_CHAIN: - ret = iptc_rename_chain(chain, newname, handle); + ret = iptc_rename_chain(chain, newname, *handle); break; case CMD_SET_POLICY: - ret = iptc_set_policy(chain, policy, NULL, handle); + ret = iptc_set_policy(chain, policy, options&OPT_COUNTERS ? &fw.counters : NULL, *handle); break; default: /* We should never reach this... */ @@ -2605,8 +2066,10 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) } free(saddrs); + free(smasks); free(daddrs); - free_opts(1); + free(dmasks); + xtables_free_opts(1); return ret; } |