diff options
Diffstat (limited to 'iptables/xshared.c')
-rw-r--r-- | iptables/xshared.c | 1596 |
1 files changed, 1507 insertions, 89 deletions
diff --git a/iptables/xshared.c b/iptables/xshared.c index 16c58914..b1997ea3 100644 --- a/iptables/xshared.c +++ b/iptables/xshared.c @@ -9,23 +9,38 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <arpa/inet.h> #include <sys/file.h> #include <sys/socket.h> #include <sys/un.h> -#include <sys/time.h> #include <unistd.h> #include <fcntl.h> #include <xtables.h> #include <math.h> +#include <signal.h> #include "xshared.h" +/* a few arp opcode names */ +char *arp_opcodes[] = +{ + "Request", + "Reply", + "Request_Reverse", + "Reply_Reverse", + "DRARP_Request", + "DRARP_Reply", + "DRARP_Error", + "InARP_Request", + "ARP_NAK", +}; + /* * 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 and targets. */ -void print_extension_helps(const struct xtables_target *t, - const struct xtables_rule_match *m) +static void print_extension_helps(const struct xtables_target *t, + const struct xtables_rule_match *m) { for (; t != NULL; t = t->next) { if (t->used) { @@ -48,20 +63,20 @@ void print_extension_helps(const struct xtables_target *t, } const char * -proto_to_name(uint8_t proto, int nolookup) +proto_to_name(uint16_t proto, int nolookup) { unsigned int i; + for (i = 0; xtables_chain_protos[i].name != NULL; ++i) + if (xtables_chain_protos[i].num == proto) + return xtables_chain_protos[i].name; + if (proto && !nolookup) { struct protoent *pent = getprotobynumber(proto); if (pent) return pent->p_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; } @@ -96,26 +111,19 @@ find_proto(const char *pname, enum xtables_tryload tryload, * [think of ip6tables-restore!] * - the protocol extension can be successively loaded */ -static bool should_load_proto(struct iptables_command_state *cs) +static struct xtables_match *load_proto(struct iptables_command_state *cs) { if (cs->protocol == NULL) - return false; - if (find_proto(cs->protocol, XTF_DONT_LOAD, - cs->options & OPT_NUMERIC, NULL) == NULL) - return true; - return !cs->proto_used; -} - -struct xtables_match *load_proto(struct iptables_command_state *cs) -{ - if (!should_load_proto(cs)) return NULL; + if (cs->proto_used) + return NULL; + cs->proto_used = true; return find_proto(cs->protocol, XTF_TRY_LOAD, cs->options & OPT_NUMERIC, &cs->matches); } int command_default(struct iptables_command_state *cs, - struct xtables_globals *gl) + struct xtables_globals *gl, bool invert) { struct xtables_rule_match *matchp; struct xtables_match *m; @@ -124,7 +132,7 @@ int command_default(struct iptables_command_state *cs, (cs->target->parse != NULL || cs->target->x6_parse != NULL) && cs->c >= cs->target->option_offset && cs->c < cs->target->option_offset + XT_OPTION_OFFSET_SCALE) { - xtables_option_tpcall(cs->c, cs->argv, cs->invert, + xtables_option_tpcall(cs->c, cs->argv, invert, cs->target, &cs->fw); return 0; } @@ -138,17 +146,14 @@ int command_default(struct iptables_command_state *cs, if (cs->c < matchp->match->option_offset || cs->c >= matchp->match->option_offset + XT_OPTION_OFFSET_SCALE) continue; - xtables_option_mpcall(cs->c, cs->argv, cs->invert, m, &cs->fw); + xtables_option_mpcall(cs->c, cs->argv, invert, m, &cs->fw); return 0; } - /* Try loading protocol */ m = load_proto(cs); if (m != NULL) { size_t size; - cs->proto_used = 1; - size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size; m->m = xtables_calloc(1, size); @@ -177,9 +182,12 @@ int command_default(struct iptables_command_state *cs, if (cs->c == ':') xtables_error(PARAMETER_PROBLEM, "option \"%s\" " "requires an argument", cs->argv[optind-1]); - if (cs->c == '?') - xtables_error(PARAMETER_PROBLEM, "unknown option " - "\"%s\"", cs->argv[optind-1]); + if (cs->c == '?') { + char optoptstr[3] = {'-', optopt, '\0'}; + + xtables_error(PARAMETER_PROBLEM, "unknown option \"%s\"", + optopt ? optoptstr : cs->argv[optind - 1]); + } xtables_error(PARAMETER_PROBLEM, "Unknown arg \"%s\"", optarg); } @@ -220,9 +228,7 @@ void xs_init_target(struct xtables_target *target) { if (target->udata_size != 0) { free(target->udata); - target->udata = calloc(1, target->udata_size); - if (target->udata == NULL) - xtables_error(RESOURCE_PROBLEM, "malloc"); + target->udata = xtables_calloc(1, target->udata_size); } if (target->init != NULL) target->init(target->t); @@ -238,54 +244,50 @@ void xs_init_match(struct xtables_match *match) * Same goes for target. */ free(match->udata); - match->udata = calloc(1, match->udata_size); - if (match->udata == NULL) - xtables_error(RESOURCE_PROBLEM, "malloc"); + match->udata = xtables_calloc(1, match->udata_size); } if (match->init != NULL) match->init(match->m); } -static int xtables_lock(int wait, struct timeval *wait_interval) +static void alarm_ignore(int i) { +} + +static int xtables_lock(int wait) { - struct timeval time_left, wait_time; - int fd, i = 0; + struct sigaction sigact_alarm; + const char *lock_file; + int fd; - time_left.tv_sec = wait; - time_left.tv_usec = 0; + lock_file = getenv("XTABLES_LOCKFILE"); + if (lock_file == NULL || lock_file[0] == '\0') + lock_file = XT_LOCK_NAME; - fd = open(XT_LOCK_NAME, O_CREAT, 0600); + fd = open(lock_file, O_CREAT, 0600); if (fd < 0) { fprintf(stderr, "Fatal: can't open lock file %s: %s\n", - XT_LOCK_NAME, strerror(errno)); + lock_file, strerror(errno)); return XT_LOCK_FAILED; } - if (wait == -1) { - if (flock(fd, LOCK_EX) == 0) - return fd; - - fprintf(stderr, "Can't lock %s: %s\n", XT_LOCK_NAME, - strerror(errno)); - return XT_LOCK_BUSY; + if (wait > 0) { + sigact_alarm.sa_handler = alarm_ignore; + sigact_alarm.sa_flags = SA_RESETHAND; + sigemptyset(&sigact_alarm.sa_mask); + sigaction(SIGALRM, &sigact_alarm, NULL); + alarm(wait); } - while (1) { - if (flock(fd, LOCK_EX | LOCK_NB) == 0) - return fd; - else if (timercmp(&time_left, wait_interval, <)) - return XT_LOCK_BUSY; - - if (++i % 10 == 0) { - fprintf(stderr, "Another app is currently holding the xtables lock; " - "still %lds %ldus time ahead to have a chance to grab the lock...\n", - time_left.tv_sec, time_left.tv_usec); - } + if (flock(fd, LOCK_EX | (wait ? 0 : LOCK_NB)) == 0) + return fd; - wait_time = *wait_interval; - select(0, NULL, NULL, NULL, &wait_time); - timersub(&time_left, wait_interval, &time_left); + if (errno == EINTR) { + errno = EWOULDBLOCK; } + + fprintf(stderr, "Can't lock %s: %s\n", lock_file, + strerror(errno)); + return XT_LOCK_BUSY; } void xtables_unlock(int lock) @@ -294,9 +296,9 @@ void xtables_unlock(int lock) close(lock); } -int xtables_lock_or_exit(int wait, struct timeval *wait_interval) +int xtables_lock_or_exit(int wait) { - int lock = xtables_lock(wait, wait_interval); + int lock = xtables_lock(wait); if (lock == XT_LOCK_FAILED) { xtables_free_opts(1); @@ -332,7 +334,7 @@ int parse_wait_time(int argc, char *argv[]) return wait; } -void parse_wait_interval(int argc, char *argv[], struct timeval *wait_interval) +void parse_wait_interval(int argc, char *argv[]) { const char *arg; unsigned int usec; @@ -352,8 +354,7 @@ void parse_wait_interval(int argc, char *argv[], struct timeval *wait_interval) "too long usec wait %u > 999999 usec", usec); - wait_interval->tv_sec = 0; - wait_interval->tv_usec = usec; + fprintf(stderr, "Ignoring deprecated --wait-interval option.\n"); return; } xtables_error(PARAMETER_PROBLEM, "wait interval not numeric"); @@ -392,15 +393,15 @@ bool tokenize_rule_counters(char **bufferp, char **pcntp, char **bcntp, int line ptr = strchr(buffer, ']'); if (!ptr) - xtables_error(PARAMETER_PROBLEM, "Bad line %u: need ]\n", line); + xtables_error(PARAMETER_PROBLEM, "Bad line %u: need ]", line); pcnt = strtok(buffer+1, ":"); if (!pcnt) - xtables_error(PARAMETER_PROBLEM, "Bad line %u: need :\n", line); + xtables_error(PARAMETER_PROBLEM, "Bad line %u: need :", line); bcnt = strtok(NULL, "]"); if (!bcnt) - xtables_error(PARAMETER_PROBLEM, "Bad line %u: need ]\n", line); + xtables_error(PARAMETER_PROBLEM, "Bad line %u: need ]", line); *pcntp = pcnt; *bcntp = bcnt; @@ -425,12 +426,12 @@ void add_argv(struct argv_store *store, const char *what, int quoted) if (store->argc + 1 >= MAX_ARGC) xtables_error(PARAMETER_PROBLEM, - "Parser cannot handle more arguments\n"); + "Parser cannot handle more arguments"); if (!what) xtables_error(PARAMETER_PROBLEM, - "Trying to store NULL argument\n"); + "Trying to store NULL argument"); - store->argv[store->argc] = strdup(what); + store->argv[store->argc] = xtables_strdup(what); store->argvattr[store->argc] = quoted; store->argv[++store->argc] = NULL; } @@ -495,7 +496,6 @@ void add_param_to_argv(struct argv_store *store, char *parsestart, int line) continue; } else if (*curchar == '"') { quote_open = 0; - *curchar = '"'; } else { add_param(¶m, curchar); continue; @@ -546,9 +546,55 @@ void debug_print_argv(struct argv_store *store) } #endif -static const char *ipv4_addr_to_string(const struct in_addr *addr, - const struct in_addr *mask, - unsigned int format) +void print_header(unsigned int format, const char *chain, const char *pol, + const struct xt_counters *counters, + int refs, uint32_t entries) +{ + printf("Chain %s", chain); + if (pol) { + printf(" (policy %s", pol); + if (!(format & FMT_NOCOUNTS)) { + fputc(' ', stdout); + xtables_print_num(counters->pcnt, (format|FMT_NOTABLE)); + fputs("packets, ", stdout); + xtables_print_num(counters->bcnt, (format|FMT_NOTABLE)); + fputs("bytes", stdout); + } + printf(")\n"); + } else if (refs < 0) { + printf(" (ERROR obtaining refs)\n"); + } else { + printf(" (%d references)\n", refs); + } + + if (format & FMT_LINENUMBERS) + printf(FMT("%-4s ", "%s "), "num"); + if (!(format & FMT_NOCOUNTS)) { + if (format & FMT_KILOMEGAGIGA) { + printf(FMT("%5s ","%s "), "pkts"); + printf(FMT("%5s ","%s "), "bytes"); + } else { + printf(FMT("%8s ","%s "), "pkts"); + printf(FMT("%10s ","%s "), "bytes"); + } + } + if (!(format & FMT_NOTARGET)) + printf(FMT("%-9s ","%s "), "target"); + fputs(" prot ", stdout); + if (format & FMT_OPTIONS) + fputs("opt", stdout); + if (format & FMT_VIA) { + printf(FMT(" %-6s ","%s "), "in"); + printf(FMT("%-6s ","%s "), "out"); + } + printf(FMT(" %-19s ","%s "), "source"); + printf(FMT(" %-19s "," %s "), "destination"); + printf("\n"); +} + +const char *ipv4_addr_to_string(const struct in_addr *addr, + const struct in_addr *mask, + unsigned int format) { static char buf[BUFSIZ]; @@ -578,6 +624,42 @@ void print_ipv4_addresses(const struct ipt_entry *fw, unsigned int format) ipv4_addr_to_string(&fw->ip.dst, &fw->ip.dmsk, format)); } +static const char *mask_to_str(const struct in_addr *mask) +{ + uint32_t bits, hmask = ntohl(mask->s_addr); + static char mask_str[INET_ADDRSTRLEN]; + int i; + + if (mask->s_addr == 0xFFFFFFFFU) { + sprintf(mask_str, "32"); + return mask_str; + } + + i = 32; + bits = 0xFFFFFFFEU; + while (--i >= 0 && hmask != bits) + bits <<= 1; + if (i >= 0) + sprintf(mask_str, "%u", i); + else + inet_ntop(AF_INET, mask, mask_str, sizeof(mask_str)); + + return mask_str; +} + +void save_ipv4_addr(char letter, const struct in_addr *addr, + const struct in_addr *mask, int invert) +{ + char addrbuf[INET_ADDRSTRLEN]; + + if (!mask->s_addr && !invert && !addr->s_addr) + return; + + printf("%s -%c %s/%s", invert ? " !" : "", letter, + inet_ntop(AF_INET, addr, addrbuf, sizeof(addrbuf)), + mask_to_str(mask)); +} + static const char *ipv6_addr_to_string(const struct in6_addr *addr, const struct in6_addr *mask, unsigned int format) @@ -612,6 +694,44 @@ void print_ipv6_addresses(const struct ip6t_entry *fw6, unsigned int format) &fw6->ipv6.dmsk, format)); } +void save_ipv6_addr(char letter, const struct in6_addr *addr, + const struct in6_addr *mask, int invert) +{ + int l = xtables_ip6mask_to_cidr(mask); + char addr_str[INET6_ADDRSTRLEN]; + + if (!invert && l == 0) + return; + + printf("%s -%c %s", + invert ? " !" : "", letter, + inet_ntop(AF_INET6, addr, addr_str, sizeof(addr_str))); + + if (l == -1) + printf("/%s", inet_ntop(AF_INET6, mask, + addr_str, sizeof(addr_str))); + else + printf("/%d", l); +} + +void print_fragment(unsigned int flags, unsigned int invflags, + unsigned int format, bool fake) +{ + if (!(format & FMT_OPTIONS)) + return; + + if (format & FMT_NOTABLE) + fputs("opt ", stdout); + + if (fake) { + fputs("--", stdout); + } else { + fputc(invflags & IPT_INV_FRAG ? '!' : '-', stdout); + fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout); + } + fputc(' ', stdout); +} + /* Luckily, IPT_INV_VIA_IN and IPT_INV_VIA_OUT * have the same values as IP6T_INV_VIA_IN and IP6T_INV_VIA_OUT * so this function serves for both iptables and ip6tables */ @@ -637,13 +757,21 @@ void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags, printf(FMT("%-6s ", "out %s "), iface); } -void command_match(struct iptables_command_state *cs) +void save_iface(char letter, const char *iface, int invert) +{ + if (!strlen(iface) || !strcmp(iface, "+")) + return; + + printf("%s -%c %s", invert ? " !" : "", letter, iface); +} + +static void command_match(struct iptables_command_state *cs, bool invert) { struct option *opts = xt_params->opts; struct xtables_match *m; size_t size; - if (cs->invert) + if (invert) xtables_error(PARAMETER_PROBLEM, "unexpected ! flag before --match"); @@ -670,12 +798,15 @@ void command_match(struct iptables_command_state *cs) else if (m->extra_opts != NULL) opts = xtables_merge_options(xt_params->orig_opts, opts, m->extra_opts, &m->option_offset); + else + return; + if (opts == NULL) xtables_error(OTHER_PROBLEM, "can't alloc memory!"); xt_params->opts = opts; } -const char *xt_parse_target(const char *targetname) +static const char *xt_parse_target(const char *targetname) { const char *ptr; @@ -728,16 +859,19 @@ void command_jump(struct iptables_command_state *cs, const char *jumpto) opts = xtables_options_xfrm(xt_params->orig_opts, opts, cs->target->x6_options, &cs->target->option_offset); - else + else if (cs->target->extra_opts != NULL) opts = xtables_merge_options(xt_params->orig_opts, opts, cs->target->extra_opts, &cs->target->option_offset); + else + return; + if (opts == NULL) xtables_error(OTHER_PROBLEM, "can't alloc memory!"); xt_params->opts = opts; } -char cmd2char(int option) +static char cmd2char(int option) { /* cmdflags index corresponds with position of bit in CMD_* values */ static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z', @@ -748,24 +882,23 @@ char cmd2char(int option) ; if (i >= ARRAY_SIZE(cmdflags)) xtables_error(OTHER_PROBLEM, - "cmd2char(): Invalid command number %u.\n", - 1 << i); + "cmd2char(): Invalid command number %u.", 1 << i); return cmdflags[i]; } -void add_command(unsigned int *cmd, const int newcmd, - const int othercmds, int invert) +static void add_command(unsigned int *cmd, const int newcmd, + const int othercmds, int invert) { if (invert) xtables_error(PARAMETER_PROBLEM, "unexpected '!' flag"); if (*cmd & (~othercmds)) - xtables_error(PARAMETER_PROBLEM, "Cannot use -%c with -%c\n", - cmd2char(newcmd), cmd2char(*cmd & (~othercmds))); + xtables_error(PARAMETER_PROBLEM, "Cannot use -%c with -%c", + cmd2char(newcmd), cmd2char(*cmd & (~othercmds))); *cmd |= newcmd; } /* Can't be zero. */ -int parse_rulenumber(const char *rule) +static int parse_rulenumber(const char *rule) { unsigned int rulenum; @@ -775,3 +908,1288 @@ int parse_rulenumber(const char *rule) return rulenum; } + +static void parse_rule_range(struct xt_cmd_parse *p, const char *argv) +{ + char *colon = strchr(argv, ':'), *buffer; + + if (colon) { + if (!p->rule_ranges) + xtables_error(PARAMETER_PROBLEM, + "Rule ranges are not supported"); + + *colon = '\0'; + if (*(colon + 1) == '\0') + p->rulenum_end = -1; /* Until the last rule */ + else { + p->rulenum_end = strtol(colon + 1, &buffer, 10); + if (*buffer != '\0' || p->rulenum_end == 0) + xtables_error(PARAMETER_PROBLEM, + "Invalid rule range end`%s'", + colon + 1); + } + } + if (colon == argv) + p->rulenum = 1; /* Beginning with the first rule */ + else { + p->rulenum = strtol(argv, &buffer, 10); + if (*buffer != '\0' || p->rulenum == 0) + xtables_error(PARAMETER_PROBLEM, + "Invalid rule number `%s'", argv); + } + if (!colon) + p->rulenum_end = p->rulenum; +} + +/* list the commands an option is allowed with */ +#define CMD_IDRAC CMD_INSERT | CMD_DELETE | CMD_REPLACE | \ + CMD_APPEND | CMD_CHECK | CMD_CHANGE_COUNTERS +static const unsigned int options_v_commands[NUMBER_OF_OPT] = { +/*OPT_NUMERIC*/ CMD_LIST, +/*OPT_SOURCE*/ CMD_IDRAC, +/*OPT_DESTINATION*/ CMD_IDRAC, +/*OPT_PROTOCOL*/ CMD_IDRAC, +/*OPT_JUMP*/ CMD_IDRAC, +/*OPT_VERBOSE*/ UINT_MAX, +/*OPT_EXPANDED*/ CMD_LIST, +/*OPT_VIANAMEIN*/ CMD_IDRAC, +/*OPT_VIANAMEOUT*/ CMD_IDRAC, +/*OPT_LINENUMBERS*/ CMD_LIST, +/*OPT_COUNTERS*/ CMD_INSERT | CMD_REPLACE | CMD_APPEND | CMD_SET_POLICY, +/*OPT_FRAGMENT*/ CMD_IDRAC, +/*OPT_S_MAC*/ CMD_IDRAC, +/*OPT_D_MAC*/ CMD_IDRAC, +/*OPT_H_LENGTH*/ CMD_IDRAC, +/*OPT_OPCODE*/ CMD_IDRAC, +/*OPT_H_TYPE*/ CMD_IDRAC, +/*OPT_P_TYPE*/ CMD_IDRAC, +/*OPT_LOGICALIN*/ CMD_IDRAC, +/*OPT_LOGICALOUT*/ CMD_IDRAC, +/*OPT_LIST_C*/ CMD_LIST, +/*OPT_LIST_X*/ CMD_LIST, +/*OPT_LIST_MAC2*/ CMD_LIST, +}; +#undef CMD_IDRAC + +static void generic_opt_check(struct xt_cmd_parse_ops *ops, + int command, int options) +{ + int i, optval; + + /* Check that commands are valid with options. Complicated by the + * fact that if an option is legal with *any* command given, it is + * legal overall (ie. -z and -l). + */ + for (i = 0, optval = 1; i < NUMBER_OF_OPT; optval = (1 << ++i)) { + if ((options & optval) && + (options_v_commands[i] & command) != command) + xtables_error(PARAMETER_PROBLEM, + "Illegal option `%s' with this command", + ops->option_name(optval)); + } +} + +const char *ip46t_option_name(int option) +{ + switch (option) { + case OPT_NUMERIC: return "--numeric"; + case OPT_SOURCE: return "--source"; + case OPT_DESTINATION: return "--destination"; + case OPT_PROTOCOL: return "--protocol"; + case OPT_JUMP: return "--jump"; + case OPT_VERBOSE: return "--verbose"; + case OPT_EXPANDED: return "--exact"; + case OPT_VIANAMEIN: return "--in-interface"; + case OPT_VIANAMEOUT: return "--out-interface"; + case OPT_LINENUMBERS: return "--line-numbers"; + case OPT_COUNTERS: return "--set-counters"; + case OPT_FRAGMENT: return "--fragments"; + default: return "unknown option"; + } +} + +int ip46t_option_invert(int option) +{ + switch (option) { + case OPT_SOURCE: return IPT_INV_SRCIP; + case OPT_DESTINATION: return IPT_INV_DSTIP; + case OPT_PROTOCOL: return XT_INV_PROTO; + case OPT_VIANAMEIN: return IPT_INV_VIA_IN; + case OPT_VIANAMEOUT: return IPT_INV_VIA_OUT; + case OPT_FRAGMENT: return IPT_INV_FRAG; + default: return -1; + } +} + +static void +set_option(struct xt_cmd_parse_ops *ops, + unsigned int *options, unsigned int option, + uint16_t *invflg, bool invert) +{ + if (*options & option) + xtables_error(PARAMETER_PROBLEM, + "multiple %s options not allowed", + ops->option_name(option)); + *options |= option; + + if (invert) { + int invopt = ops->option_invert(option); + + if (invopt < 0) + xtables_error(PARAMETER_PROBLEM, + "cannot have ! before %s", + ops->option_name(option)); + *invflg |= invopt; + } +} + +void assert_valid_chain_name(const char *chainname) +{ + const char *ptr; + + if (strlen(chainname) >= XT_EXTENSION_MAXNAMELEN) + xtables_error(PARAMETER_PROBLEM, + "chain name `%s' too long (must be under %u chars)", + chainname, XT_EXTENSION_MAXNAMELEN); + + if (*chainname == '-' || *chainname == '!') + xtables_error(PARAMETER_PROBLEM, + "chain name not allowed to start with `%c'", + *chainname); + + if (xtables_find_target(chainname, XTF_TRY_LOAD)) + xtables_error(PARAMETER_PROBLEM, + "chain name may not clash with target name"); + + for (ptr = chainname; *ptr; ptr++) + if (isspace(*ptr)) + xtables_error(PARAMETER_PROBLEM, + "Invalid chain name `%s'", chainname); +} + +void print_rule_details(unsigned int linenum, const struct xt_counters *ctrs, + const char *targname, uint8_t proto, uint8_t flags, + uint8_t invflags, unsigned int format) +{ + const char *pname = proto_to_name(proto, format&FMT_NUMERIC); + + if (format & FMT_LINENUMBERS) + printf(FMT("%-4u ", "%u "), linenum); + + if (!(format & FMT_NOCOUNTS)) { + xtables_print_num(ctrs->pcnt, format); + xtables_print_num(ctrs->bcnt, format); + } + + if (!(format & FMT_NOTARGET)) + printf(FMT("%-9s ", "%s "), targname ? targname : ""); + + fputc(invflags & XT_INV_PROTO ? '!' : ' ', stdout); + + if (pname) + printf(FMT("%-4s ", "%s "), pname); + else + printf(FMT("%-4hu ", "%hu "), proto); +} + +void save_rule_details(const char *iniface, const char *outiface, + uint16_t proto, int frag, uint8_t invflags) +{ + if (iniface != NULL) { + save_iface('i', iniface, invflags & IPT_INV_VIA_IN); + } + if (outiface != NULL) { + save_iface('o', outiface, invflags & IPT_INV_VIA_OUT); + } + + if (proto > 0) { + const char *pname = proto_to_name(proto, true); + + if (invflags & XT_INV_PROTO) + printf(" !"); + + if (pname) + printf(" -p %s", pname); + else + printf(" -p %u", proto); + } + + if (frag) { + if (invflags & IPT_INV_FRAG) + printf(" !"); + printf(" -f"); + } +} + +int print_match_save(const struct xt_entry_match *e, const void *ip) +{ + const char *name = e->u.user.name; + const int revision = e->u.user.revision; + struct xtables_match *match, *mt, *mt2; + + match = xtables_find_match(name, XTF_TRY_LOAD, NULL); + if (match) { + mt = mt2 = xtables_find_match_revision(name, XTF_TRY_LOAD, + match, revision); + if (!mt2) + mt2 = match; + printf(" -m %s", mt2->alias ? mt2->alias(e) : name); + + /* some matches don't provide a save function */ + if (mt && mt->save) + mt->save(ip, e); + else if (match->save) + printf(" [unsupported revision]"); + } else { + if (e->u.match_size) { + fprintf(stderr, + "Can't find library for match `%s'\n", + name); + exit(1); + } + } + return 0; +} + +void xtables_printhelp(struct iptables_command_state *cs) +{ + const struct xtables_rule_match *matches = cs->matches; + const char *prog_name = xt_params->program_name; + const char *prog_vers = xt_params->program_version; + + printf("%s v%s\n\n" +"Usage: %s -[ACD] chain 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 -[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", + 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" +"Either long or short options are allowed.\n" +" --append -A chain Append to chain\n" +" --check -C chain Check for the existence of a rule\n" +" --delete -D chain Delete matching rule from chain\n" +" --delete -D chain rulenum\n" +" Delete rule rulenum (1 = first) from chain\n" +" --insert -I chain [rulenum]\n" +" 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 [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 [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" +" --policy -P chain target\n" +" Change policy on chain to target\n" +" --rename-chain\n" +" -E old-chain new-chain\n" +" Change chain name, (moving any references)\n" +"\n" +"Options:\n"); + + if (afinfo->family == NFPROTO_ARP) { + printf( +"[!] --source-ip -s address[/mask]\n" +" source specification\n" +"[!] --destination-ip -d address[/mask]\n" +" destination specification\n" +"[!] --source-mac address[/mask]\n" +"[!] --destination-mac address[/mask]\n" +" --h-length -l length[/mask] hardware length (nr of bytes)\n" +" --opcode code[/mask] operation code (2 bytes)\n" +" --h-type type[/mask] hardware type (2 bytes, hexadecimal)\n" +" --proto-type type[/mask] protocol type (2 bytes)\n"); + } else { + printf( +" --ipv4 -4 %s (line is ignored by ip6tables-restore)\n" +" --ipv6 -6 %s (line is ignored by iptables-restore)\n" +"[!] --protocol -p proto protocol: by number or name, eg. `tcp'\n" +"[!] --source -s address[/mask][...]\n" +" source specification\n" +"[!] --destination -d address[/mask][...]\n" +" destination specification\n", + afinfo->family == NFPROTO_IPV4 ? "Nothing" : "Error", + afinfo->family == NFPROTO_IPV4 ? "Error" : "Nothing"); + } + + printf( +"[!] --in-interface -i input name[+]\n" +" network interface name ([+] for wildcard)\n" +" --jump -j target\n" +" target for rule (may load target extension)\n"); + + if (0 +#ifdef IPT_F_GOTO + || afinfo->family == NFPROTO_IPV4 +#endif +#ifdef IP6T_F_GOTO + || afinfo->family == NFPROTO_IPV6 +#endif + ) + printf( +" --goto -g chain\n" +" jump to chain with no return\n"); + printf( +" --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" +" network interface name ([+] for wildcard)\n" +" --table -t table table to manipulate (default: `filter')\n" +" --verbose -v verbose mode\n" +" --wait -w [seconds] maximum wait to acquire xtables lock before give up\n" +" --line-numbers print line numbers when listing\n" +" --exact -x expand numbers (display exact values)\n"); + + if (afinfo->family == NFPROTO_IPV4) + printf( +"[!] --fragment -f match second or further fragments only\n"); + + printf( +" --modprobe=<command> try to insert modules using this command\n" +" --set-counters -c PKTS BYTES set the counter during insert/append\n" +"[!] --version -V print package version.\n"); + + if (afinfo->family == NFPROTO_ARP) { + int i; + + printf(" opcode strings: \n"); + for (i = 0; i < ARP_NUMOPCODES; i++) + printf(" %d = %s\n", i + 1, arp_opcodes[i]); + printf( + " hardware type string: 1 = Ethernet\n" + " protocol type string: 0x800 = IPv4\n"); + + xtables_find_target("standard", XTF_TRY_LOAD); + xtables_find_target("mangle", XTF_TRY_LOAD); + xtables_find_target("CLASSIFY", XTF_TRY_LOAD); + xtables_find_target("MARK", XTF_TRY_LOAD); + } + + print_extension_helps(xtables_targets, matches); +} + +void exit_tryhelp(int status, int line) +{ + if (line != -1) + fprintf(stderr, "Error occurred at line: %d\n", line); + fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", + xt_params->program_name, xt_params->program_name); + xtables_free_opts(1); + exit(status); +} + +static void check_empty_interface(struct xtables_args *args, const char *arg) +{ + const char *msg = "Empty interface is likely to be undesired"; + + if (*arg != '\0') + return; + + if (args->family != NFPROTO_ARP) + xtables_error(PARAMETER_PROBLEM, "%s", msg); + + fprintf(stderr, "%s", msg); +} + +static void check_inverse(struct xtables_args *args, const char option[], + bool *invert, int argc, char **argv) +{ + switch (args->family) { + case NFPROTO_ARP: + case NFPROTO_BRIDGE: + break; + default: + return; + } + + if (!option || strcmp(option, "!")) + return; + + fprintf(stderr, "Using intrapositioned negation (`--option ! this`) " + "is deprecated in favor of extrapositioned (`! --option this`).\n"); + + if (*invert) + xtables_error(PARAMETER_PROBLEM, + "Multiple `!' flags not allowed"); + *invert = true; + optind++; + if (optind > argc) + xtables_error(PARAMETER_PROBLEM, "no argument following `!'"); + + optarg = argv[optind - 1]; +} + +static const char *optstring_lookup(int family) +{ + switch (family) { + case AF_INET: + case AF_INET6: + return IPT_OPTSTRING; + case NFPROTO_ARP: + return ARPT_OPTSTRING; + case NFPROTO_BRIDGE: + return EBT_OPTSTRING; + } + return ""; +} + +void xtables_clear_iptables_command_state(struct iptables_command_state *cs) +{ + xtables_rule_matches_free(&cs->matches); + if (cs->target) { + free(cs->target->t); + cs->target->t = NULL; + + free(cs->target->udata); + cs->target->udata = NULL; + + if (cs->target == cs->target->next) { + free(cs->target); + cs->target = NULL; + } + } +} + +void iface_to_mask(const char *iface, unsigned char *mask) +{ + unsigned int len = strlen(iface); + + memset(mask, 0, IFNAMSIZ); + + if (!len) { + return; + } else if (iface[len - 1] == '+') { + memset(mask, 0xff, len - 1); + /* Don't remove `+' here! -HW */ + } else { + /* Include nul-terminator in match */ + memset(mask, 0xff, len + 1); + } +} + +static void parse_interface(const char *arg, char *iface) +{ + unsigned int len = strlen(arg); + + memset(iface, 0, IFNAMSIZ); + + if (!len) + return; + if (len >= IFNAMSIZ) + xtables_error(PARAMETER_PROBLEM, + "interface name `%s' must be shorter than %d characters", + arg, IFNAMSIZ); + + if (strchr(arg, '/') || strchr(arg, ' ')) + fprintf(stderr, + "Warning: weird character in interface `%s' ('/' and ' ' are not allowed by the kernel).\n", + arg); + + strcpy(iface, arg); +} + +static bool +parse_signed_counter(char *argv, unsigned long long *val, uint8_t *ctr_op, + uint8_t flag_inc, uint8_t flag_dec) +{ + char *endptr, *p = argv; + + switch (*p) { + case '+': + *ctr_op |= flag_inc; + p++; + break; + case '-': + *ctr_op |= flag_dec; + p++; + break; + } + *val = strtoull(p, &endptr, 10); + return *endptr == '\0'; +} + +static void parse_change_counters_rule(int argc, char **argv, + struct xt_cmd_parse *p, + struct xtables_args *args) +{ + if (optind + 1 >= argc || + (argv[optind][0] == '-' && !isdigit(argv[optind][1])) || + (argv[optind + 1][0] == '-' && !isdigit(argv[optind + 1][1]))) + xtables_error(PARAMETER_PROBLEM, + "The command -C needs at least 2 arguments"); + if (optind + 2 < argc && + (argv[optind + 2][0] != '-' || isdigit(argv[optind + 2][1]))) { + if (optind + 3 != argc) + xtables_error(PARAMETER_PROBLEM, + "No extra options allowed with -C start_nr[:end_nr] pcnt bcnt"); + parse_rule_range(p, argv[optind++]); + } + + if (!parse_signed_counter(argv[optind++], &args->pcnt_cnt, + &args->counter_op, + CTR_OP_INC_PKTS, CTR_OP_DEC_PKTS) || + !parse_signed_counter(argv[optind++], &args->bcnt_cnt, + &args->counter_op, + CTR_OP_INC_BYTES, CTR_OP_DEC_BYTES)) + xtables_error(PARAMETER_PROBLEM, + "Packet counter '%s' invalid", argv[optind - 1]); +} + +static void option_test_and_reject(struct xt_cmd_parse *p, + struct iptables_command_state *cs, + unsigned int option) +{ + if (cs->options & option) + xtables_error(PARAMETER_PROBLEM, "Can't use %s with %s", + p->ops->option_name(option), p->chain); +} + +void do_parse(int argc, char *argv[], + struct xt_cmd_parse *p, struct iptables_command_state *cs, + struct xtables_args *args) +{ + bool family_is_bridge = args->family == NFPROTO_BRIDGE; + struct xtables_match *m; + struct xtables_rule_match *matchp; + bool wait_interval_set = false; + struct xtables_target *t; + bool table_set = false; + bool invert = false; + + /* re-set optind to 0 in case do_command4 gets called + * a second time */ + optind = 0; + + /* clear mflags in case do_command4 gets called a second time + * (we clear the global list of all matches for security)*/ + for (m = xtables_matches; m; m = m->next) + m->mflags = 0; + + for (t = xtables_targets; t; t = t->next) { + t->tflags = 0; + t->used = 0; + } + + /* Suppress error messages: we may add new options if we + demand-load a protocol. */ + opterr = 0; + + while ((cs->c = getopt_long(argc, argv, + optstring_lookup(afinfo->family), + xt_params->opts ?: xt_params->orig_opts, + NULL)) != -1) { + switch (cs->c) { + /* + * Command selection + */ + case 'A': + add_command(&p->command, CMD_APPEND, CMD_NONE, invert); + p->chain = optarg; + break; + + case 'C': + if (family_is_bridge) { + add_command(&p->command, CMD_CHANGE_COUNTERS, + CMD_NONE, invert); + p->chain = optarg; + parse_change_counters_rule(argc, argv, p, args); + break; + } + /* fall through */ + case 14: /* ebtables --check */ + add_command(&p->command, CMD_CHECK, CMD_NONE, invert); + p->chain = optarg; + break; + + case 'D': + add_command(&p->command, CMD_DELETE, CMD_NONE, invert); + p->chain = optarg; + if (xs_has_arg(argc, argv)) { + parse_rule_range(p, argv[optind++]); + p->command = CMD_DELETE_NUM; + } + break; + + case 'R': + add_command(&p->command, CMD_REPLACE, CMD_NONE, invert); + p->chain = optarg; + if (xs_has_arg(argc, argv)) + p->rulenum = parse_rulenumber(argv[optind++]); + else + xtables_error(PARAMETER_PROBLEM, + "-%c requires a rule number", + cmd2char(CMD_REPLACE)); + break; + + case 'I': + add_command(&p->command, CMD_INSERT, CMD_NONE, invert); + p->chain = optarg; + if (xs_has_arg(argc, argv)) + p->rulenum = parse_rulenumber(argv[optind++]); + else + p->rulenum = 1; + break; + + case 'L': + add_command(&p->command, CMD_LIST, + CMD_ZERO | CMD_ZERO_NUM, invert); + if (optarg) + p->chain = optarg; + else if (xs_has_arg(argc, argv)) + p->chain = argv[optind++]; + if (xs_has_arg(argc, argv)) + p->rulenum = parse_rulenumber(argv[optind++]); + break; + + case 'S': + add_command(&p->command, CMD_LIST_RULES, + CMD_ZERO|CMD_ZERO_NUM, invert); + if (optarg) + p->chain = optarg; + else if (xs_has_arg(argc, argv)) + p->chain = argv[optind++]; + if (xs_has_arg(argc, argv)) + p->rulenum = parse_rulenumber(argv[optind++]); + break; + + case 'F': + add_command(&p->command, CMD_FLUSH, CMD_NONE, invert); + if (optarg) + p->chain = optarg; + else if (xs_has_arg(argc, argv)) + p->chain = argv[optind++]; + break; + + case 'Z': + add_command(&p->command, CMD_ZERO, + CMD_LIST|CMD_LIST_RULES, invert); + if (optarg) + p->chain = optarg; + else if (xs_has_arg(argc, argv)) + p->chain = argv[optind++]; + if (xs_has_arg(argc, argv)) { + p->rulenum = parse_rulenumber(argv[optind++]); + p->command = CMD_ZERO_NUM; + } + break; + + case 'N': + assert_valid_chain_name(optarg); + add_command(&p->command, CMD_NEW_CHAIN, CMD_NONE, + invert); + p->chain = optarg; + break; + + case 'X': + add_command(&p->command, CMD_DELETE_CHAIN, CMD_NONE, + invert); + if (optarg) + p->chain = optarg; + else if (xs_has_arg(argc, argv)) + p->chain = argv[optind++]; + break; + + case 'E': + add_command(&p->command, CMD_RENAME_CHAIN, CMD_NONE, + invert); + p->chain = optarg; + if (xs_has_arg(argc, argv)) + p->newname = argv[optind++]; + else + xtables_error(PARAMETER_PROBLEM, + "-%c requires old-chain-name and " + "new-chain-name", + cmd2char(CMD_RENAME_CHAIN)); + assert_valid_chain_name(p->newname); + break; + + case 'P': + add_command(&p->command, CMD_SET_POLICY, + family_is_bridge ? CMD_NEW_CHAIN : CMD_NONE, + invert); + if (p->command & CMD_NEW_CHAIN) { + p->policy = optarg; + } else if (xs_has_arg(argc, argv)) { + p->chain = optarg; + p->policy = argv[optind++]; + } else { + xtables_error(PARAMETER_PROBLEM, + "-%c requires a chain and a policy", + cmd2char(CMD_SET_POLICY)); + } + break; + + case 'h': + /* iptables -p icmp -h */ + if (!cs->matches && cs->protocol) + xtables_find_match(cs->protocol, + XTF_TRY_LOAD, &cs->matches); + + p->ops->print_help(cs); + xtables_clear_iptables_command_state(cs); + xtables_free_opts(1); + xtables_fini(); + exit(0); + + /* + * Option selection + */ + case 'p': + check_inverse(args, optarg, &invert, argc, argv); + set_option(p->ops, &cs->options, OPT_PROTOCOL, + &args->invflags, invert); + + /* Canonicalize into lower case */ + for (cs->protocol = optarg; + *cs->protocol; cs->protocol++) + *cs->protocol = tolower(*cs->protocol); + + cs->protocol = optarg; + + /* This needs to happen here to parse extensions */ + if (p->ops->proto_parse) + p->ops->proto_parse(cs, args); + break; + + case 's': + check_inverse(args, optarg, &invert, argc, argv); + set_option(p->ops, &cs->options, OPT_SOURCE, + &args->invflags, invert); + args->shostnetworkmask = optarg; + break; + + case 'd': + check_inverse(args, optarg, &invert, argc, argv); + set_option(p->ops, &cs->options, OPT_DESTINATION, + &args->invflags, invert); + args->dhostnetworkmask = optarg; + break; + +#ifdef IPT_F_GOTO + case 'g': + set_option(p->ops, &cs->options, OPT_JUMP, + &args->invflags, invert); + args->goto_set = true; + cs->jumpto = xt_parse_target(optarg); + break; +#endif + + case 2:/* src-mac */ + check_inverse(args, optarg, &invert, argc, argv); + set_option(p->ops, &cs->options, OPT_S_MAC, + &args->invflags, invert); + args->src_mac = optarg; + break; + + case 3:/* dst-mac */ + check_inverse(args, optarg, &invert, argc, argv); + set_option(p->ops, &cs->options, OPT_D_MAC, + &args->invflags, invert); + args->dst_mac = optarg; + break; + + case 'l':/* hardware length */ + check_inverse(args, optarg, &invert, argc, argv); + set_option(p->ops, &cs->options, OPT_H_LENGTH, + &args->invflags, invert); + args->arp_hlen = optarg; + break; + + case 8: /* was never supported, not even in arptables-legacy */ + xtables_error(PARAMETER_PROBLEM, "not supported"); + case 4:/* opcode */ + check_inverse(args, optarg, &invert, argc, argv); + set_option(p->ops, &cs->options, OPT_OPCODE, + &args->invflags, invert); + args->arp_opcode = optarg; + break; + + case 5:/* h-type */ + check_inverse(args, optarg, &invert, argc, argv); + set_option(p->ops, &cs->options, OPT_H_TYPE, + &args->invflags, invert); + args->arp_htype = optarg; + break; + + case 6:/* proto-type */ + check_inverse(args, optarg, &invert, argc, argv); + set_option(p->ops, &cs->options, OPT_P_TYPE, + &args->invflags, invert); + args->arp_ptype = optarg; + break; + + case 11: /* ebtables --init-table */ + if (p->restore) + xtables_error(PARAMETER_PROBLEM, + "--init-table is not supported in daemon mode"); + add_command(&p->command, CMD_INIT_TABLE, CMD_NONE, invert); + break; + + case 12 : /* ebtables --Lmac2 */ + set_option(p->ops, &cs->options, OPT_LIST_MAC2, + &args->invflags, invert); + break; + + case 13 : /* ebtables --concurrent */ + break; + + case 15 : /* ebtables --logical-in */ + check_inverse(args, optarg, &invert, argc, argv); + set_option(p->ops, &cs->options, OPT_LOGICALIN, + &args->invflags, invert); + parse_interface(optarg, args->bri_iniface); + break; + + case 16 : /* ebtables --logical-out */ + check_inverse(args, optarg, &invert, argc, argv); + set_option(p->ops, &cs->options, OPT_LOGICALOUT, + &args->invflags, invert); + parse_interface(optarg, args->bri_outiface); + break; + + case 17 : /* ebtables --Lc */ + set_option(p->ops, &cs->options, OPT_LIST_C, + &args->invflags, invert); + break; + + case 19 : /* ebtables --Lx */ + set_option(p->ops, &cs->options, OPT_LIST_X, + &args->invflags, invert); + break; + + case 'j': + set_option(p->ops, &cs->options, OPT_JUMP, + &args->invflags, invert); + if (strcmp(optarg, "CONTINUE")) + command_jump(cs, optarg); + break; + + case 'i': + check_empty_interface(args, optarg); + check_inverse(args, optarg, &invert, argc, argv); + set_option(p->ops, &cs->options, OPT_VIANAMEIN, + &args->invflags, invert); + parse_interface(optarg, args->iniface); + break; + + case 'o': + check_empty_interface(args, optarg); + check_inverse(args, optarg, &invert, argc, argv); + set_option(p->ops, &cs->options, OPT_VIANAMEOUT, + &args->invflags, invert); + parse_interface(optarg, args->outiface); + break; + + case 'f': + if (args->family == AF_INET6) { + xtables_error(PARAMETER_PROBLEM, + "`-f' is not supported in IPv6, " + "use -m frag instead"); + } + set_option(p->ops, &cs->options, OPT_FRAGMENT, + &args->invflags, invert); + args->flags |= IPT_F_FRAG; + break; + + case 'v': + if (!p->verbose) + set_option(p->ops, &cs->options, OPT_VERBOSE, + &args->invflags, invert); + p->verbose++; + break; + + case 'm': + command_match(cs, invert); + break; + + case 'n': + set_option(p->ops, &cs->options, OPT_NUMERIC, + &args->invflags, invert); + break; + + case 't': + if (invert) + xtables_error(PARAMETER_PROBLEM, + "unexpected ! flag before --table"); + if (p->restore && table_set) + xtables_error(PARAMETER_PROBLEM, + "The -t option cannot be used in %s.\n", + xt_params->program_name); + p->table = optarg; + table_set = true; + break; + + case 'x': + set_option(p->ops, &cs->options, OPT_EXPANDED, + &args->invflags, invert); + break; + + case 'V': + if (invert) + printf("Not %s ;-)\n", + xt_params->program_version); + else + printf("%s v%s\n", + xt_params->program_name, + xt_params->program_version); + exit(0); + + case 'w': + if (p->restore) { + xtables_error(PARAMETER_PROBLEM, + "You cannot use `-w' from " + "iptables-restore"); + } + + args->wait = parse_wait_time(argc, argv); + break; + + case 'W': + if (p->restore) { + xtables_error(PARAMETER_PROBLEM, + "You cannot use `-W' from " + "iptables-restore"); + } + + parse_wait_interval(argc, argv); + wait_interval_set = true; + break; + + case '0': + case 18 : /* ebtables --Ln */ + set_option(p->ops, &cs->options, OPT_LINENUMBERS, + &args->invflags, invert); + break; + + case 'M': + xtables_modprobe_program = optarg; + break; + + case 'c': + set_option(p->ops, &cs->options, OPT_COUNTERS, + &args->invflags, invert); + args->pcnt = optarg; + args->bcnt = strchr(args->pcnt, ','); + if (args->bcnt) + args->bcnt++; + if (!args->bcnt && xs_has_arg(argc, argv)) + args->bcnt = argv[optind++]; + if (!args->bcnt) + xtables_error(PARAMETER_PROBLEM, + "%s requires packet and byte counter", + p->ops->option_name(OPT_COUNTERS)); + + if (sscanf(args->pcnt, "%llu", &args->pcnt_cnt) != 1) + xtables_error(PARAMETER_PROBLEM, + "%s packet counter not numeric", + p->ops->option_name(OPT_COUNTERS)); + + if (sscanf(args->bcnt, "%llu", &args->bcnt_cnt) != 1) + xtables_error(PARAMETER_PROBLEM, + "%s byte counter not numeric", + p->ops->option_name(OPT_COUNTERS)); + break; + + case '4': + if (args->family == AF_INET) + break; + + if (p->restore && args->family == AF_INET6) + return; + + exit_tryhelp(2, p->line); + + case '6': + if (args->family == AF_INET6) + break; + + if (p->restore && args->family == AF_INET) + return; + + exit_tryhelp(2, p->line); + + case 1: /* non option */ + if (optarg[0] == '!' && optarg[1] == '\0') { + if (invert) + xtables_error(PARAMETER_PROBLEM, + "multiple consecutive ! not" + " allowed"); + invert = true; + optarg[0] = '\0'; + continue; + } + fprintf(stderr, "Bad argument `%s'\n", optarg); + exit_tryhelp(2, p->line); + + default: + check_inverse(args, optarg, &invert, argc, argv); + if (p->ops->command_default(cs, xt_params, invert)) + /* cf. ip6tables.c */ + continue; + break; + } + invert = false; + } + + if (!family_is_bridge && + strcmp(p->table, "nat") == 0 && + ((p->policy != NULL && strcmp(p->policy, "DROP") == 0) || + (cs->jumpto != NULL && strcmp(cs->jumpto, "DROP") == 0))) + xtables_error(PARAMETER_PROBLEM, + "\nThe \"nat\" table is not intended for filtering, " + "the use of DROP is therefore inhibited.\n\n"); + + if (!args->wait && wait_interval_set) + xtables_error(PARAMETER_PROBLEM, + "--wait-interval only makes sense with --wait\n"); + + for (matchp = cs->matches; matchp; matchp = matchp->next) + xtables_option_mfcall(matchp->match); + if (cs->target != NULL) + xtables_option_tfcall(cs->target); + + /* Fix me: must put inverse options checking here --MN */ + + if (optind < argc) + xtables_error(PARAMETER_PROBLEM, + "unknown arguments found on commandline"); + if (!p->command) + xtables_error(PARAMETER_PROBLEM, "no command specified"); + if (invert) + xtables_error(PARAMETER_PROBLEM, + "nothing appropriate following !"); + + if (p->ops->post_parse) + p->ops->post_parse(p->command, cs, args); + + generic_opt_check(p->ops, p->command, cs->options); + + if (p->chain != NULL && strlen(p->chain) >= XT_EXTENSION_MAXNAMELEN) + xtables_error(PARAMETER_PROBLEM, + "chain name `%s' too long (must be under %u chars)", + p->chain, XT_EXTENSION_MAXNAMELEN); + + if (p->command == CMD_APPEND || + p->command == CMD_DELETE || + p->command == CMD_CHECK || + p->command == CMD_INSERT || + p->command == CMD_REPLACE || + p->command == CMD_CHANGE_COUNTERS) { + if (strcmp(p->chain, "PREROUTING") == 0 + || strcmp(p->chain, "INPUT") == 0) { + /* -o not valid with incoming packets. */ + option_test_and_reject(p, cs, OPT_VIANAMEOUT); + /* same with --logical-out */ + option_test_and_reject(p, cs, OPT_LOGICALOUT); + } + + if (strcmp(p->chain, "POSTROUTING") == 0 + || strcmp(p->chain, "OUTPUT") == 0) { + /* -i not valid with outgoing packets */ + option_test_and_reject(p, cs, OPT_VIANAMEIN); + /* same with --logical-in */ + option_test_and_reject(p, cs, OPT_LOGICALIN); + } + } +} + +void ipv4_proto_parse(struct iptables_command_state *cs, + struct xtables_args *args) +{ + cs->fw.ip.proto = xtables_parse_protocol(cs->protocol); + + if (cs->fw.ip.proto == 0 && + (args->invflags & XT_INV_PROTO)) + xtables_error(PARAMETER_PROBLEM, + "rule would never match protocol"); + + cs->fw.ip.invflags = args->invflags; +} + +/* These are invalid numbers as upper layer protocol */ +static int is_exthdr(uint16_t proto) +{ + return (proto == IPPROTO_ROUTING || + proto == IPPROTO_FRAGMENT || + proto == IPPROTO_AH || + proto == IPPROTO_DSTOPTS); +} + +void ipv6_proto_parse(struct iptables_command_state *cs, + struct xtables_args *args) +{ + cs->fw6.ipv6.proto = xtables_parse_protocol(cs->protocol); + + if (cs->fw6.ipv6.proto == 0 && + (args->invflags & XT_INV_PROTO)) + xtables_error(PARAMETER_PROBLEM, + "rule would never match protocol"); + + cs->fw6.ipv6.invflags = args->invflags; + + /* this is needed for ip6tables-legacy only */ + args->flags |= IP6T_F_PROTO; + cs->fw6.ipv6.flags |= IP6T_F_PROTO; + + if (is_exthdr(cs->fw6.ipv6.proto) + && (cs->fw6.ipv6.invflags & XT_INV_PROTO) == 0) + fprintf(stderr, + "Warning: never matched protocol: %s. " + "use extension match instead.\n", + cs->protocol); +} + +void ipv4_post_parse(int command, struct iptables_command_state *cs, + struct xtables_args *args) +{ + cs->fw.ip.flags = args->flags; + /* We already set invflags in proto_parse, but we need to refresh it + * to include new parsed options. + */ + cs->fw.ip.invflags = args->invflags; + + memcpy(cs->fw.ip.iniface, args->iniface, IFNAMSIZ); + memcpy(cs->fw.ip.outiface, args->outiface, IFNAMSIZ); + + if (args->goto_set) + cs->fw.ip.flags |= IPT_F_GOTO; + + /* nft-variants use cs->counters, legacy uses cs->fw.counters */ + cs->counters.pcnt = args->pcnt_cnt; + cs->counters.bcnt = args->bcnt_cnt; + cs->fw.counters.pcnt = args->pcnt_cnt; + cs->fw.counters.bcnt = args->bcnt_cnt; + + if (command & (CMD_REPLACE | CMD_INSERT | + CMD_DELETE | CMD_APPEND | CMD_CHECK)) { + if (!(cs->options & OPT_DESTINATION)) + args->dhostnetworkmask = "0.0.0.0/0"; + if (!(cs->options & OPT_SOURCE)) + args->shostnetworkmask = "0.0.0.0/0"; + } + + if (args->shostnetworkmask) + xtables_ipparse_multiple(args->shostnetworkmask, + &args->s.addr.v4, &args->s.mask.v4, + &args->s.naddrs); + if (args->dhostnetworkmask) + xtables_ipparse_multiple(args->dhostnetworkmask, + &args->d.addr.v4, &args->d.mask.v4, + &args->d.naddrs); + + if ((args->s.naddrs > 1 || args->d.naddrs > 1) && + (cs->fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) + xtables_error(PARAMETER_PROBLEM, + "! not allowed with multiple" + " source or destination IP addresses"); +} + +void ipv6_post_parse(int command, struct iptables_command_state *cs, + struct xtables_args *args) +{ + cs->fw6.ipv6.flags = args->flags; + /* We already set invflags in proto_parse, but we need to refresh it + * to include new parsed options. + */ + cs->fw6.ipv6.invflags = args->invflags; + + memcpy(cs->fw6.ipv6.iniface, args->iniface, IFNAMSIZ); + memcpy(cs->fw6.ipv6.iniface_mask, + args->iniface_mask, IFNAMSIZ*sizeof(unsigned char)); + + memcpy(cs->fw6.ipv6.outiface, args->outiface, IFNAMSIZ); + memcpy(cs->fw6.ipv6.outiface_mask, + args->outiface_mask, IFNAMSIZ*sizeof(unsigned char)); + + if (args->goto_set) + cs->fw6.ipv6.flags |= IP6T_F_GOTO; + + /* nft-variants use cs->counters, legacy uses cs->fw6.counters */ + cs->counters.pcnt = args->pcnt_cnt; + cs->counters.bcnt = args->bcnt_cnt; + cs->fw6.counters.pcnt = args->pcnt_cnt; + cs->fw6.counters.bcnt = args->bcnt_cnt; + + if (command & (CMD_REPLACE | CMD_INSERT | + CMD_DELETE | CMD_APPEND | CMD_CHECK)) { + if (!(cs->options & OPT_DESTINATION)) + args->dhostnetworkmask = "::0/0"; + if (!(cs->options & OPT_SOURCE)) + args->shostnetworkmask = "::0/0"; + } + + if (args->shostnetworkmask) + xtables_ip6parse_multiple(args->shostnetworkmask, + &args->s.addr.v6, + &args->s.mask.v6, + &args->s.naddrs); + if (args->dhostnetworkmask) + xtables_ip6parse_multiple(args->dhostnetworkmask, + &args->d.addr.v6, + &args->d.mask.v6, + &args->d.naddrs); + + if ((args->s.naddrs > 1 || args->d.naddrs > 1) && + (cs->fw6.ipv6.invflags & (IP6T_INV_SRCIP | IP6T_INV_DSTIP))) + xtables_error(PARAMETER_PROBLEM, + "! not allowed with multiple" + " source or destination IP addresses"); +} + +unsigned char * +make_delete_mask(const struct xtables_rule_match *matches, + const struct xtables_target *target, + size_t entry_size) +{ + /* Establish mask for comparison */ + unsigned int size = entry_size; + const struct xtables_rule_match *matchp; + unsigned char *mask, *mptr; + + for (matchp = matches; matchp; matchp = matchp->next) + size += XT_ALIGN(sizeof(struct xt_entry_match)) + matchp->match->size; + + mask = xtables_calloc(1, size + + XT_ALIGN(sizeof(struct xt_entry_target)) + + target->size); + + memset(mask, 0xFF, entry_size); + mptr = mask + entry_size; + + for (matchp = matches; matchp; matchp = matchp->next) { + memset(mptr, 0xFF, + XT_ALIGN(sizeof(struct xt_entry_match)) + + matchp->match->userspacesize); + mptr += XT_ALIGN(sizeof(struct xt_entry_match)) + matchp->match->size; + } + + memset(mptr, 0xFF, + XT_ALIGN(sizeof(struct xt_entry_target)) + + target->userspacesize); + + return mask; +} + +void xtables_clear_args(struct xtables_args *args) +{ + free(args->s.addr.ptr); + free(args->s.mask.ptr); + free(args->d.addr.ptr); + free(args->d.mask.ptr); +} |