#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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. */ 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) { printf("\n"); if (t->help == NULL) printf("%s does not take any options\n", t->name); else t->help(); } } for (; m != NULL; m = m->next) { printf("\n"); if (m->match->help == NULL) printf("%s does not take any options\n", m->match->name); else m->match->help(); } } static const char * 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; } return NULL; } static struct xtables_match * find_proto(const char *pname, enum xtables_tryload tryload, int nolookup, struct xtables_rule_match **matches) { unsigned int proto; if (xtables_strtoui(pname, NULL, &proto, 0, UINT8_MAX)) { const char *protoname = proto_to_name(proto, nolookup); if (protoname) return xtables_find_match(protoname, tryload, matches); } else return xtables_find_match(pname, tryload, matches); return NULL; } /* * Some explanations (after four different bugs in 3 different releases): If * we encounter a parameter, that has not been parsed yet, it's not an option * of an explicitly loaded match or a target. However, we support implicit * loading of the protocol match extension. '-p tcp' means 'l4 proto 6' and at * the same time 'load tcp protocol match on demand if we specify --dport'. * * To make this work, we need to make sure: * - the parameter has not been parsed by a match (m above) * - a protocol has been specified * - the protocol extension has not been loaded yet, or is loaded and unused * [think of ip6tables-restore!] * - the protocol extension can be successively loaded */ static struct xtables_match *load_proto(struct iptables_command_state *cs) { if (cs->protocol == NULL) 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); } static int command_default(struct iptables_command_state *cs, struct xtables_globals *gl, bool invert) { struct xtables_rule_match *matchp; struct xtables_match *m; if (cs->target != NULL && (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, invert, cs->target, &cs->fw); return 0; } for (matchp = cs->matches; matchp; matchp = matchp->next) { m = matchp->match; if (matchp->completed || (m->x6_parse == NULL && m->parse == NULL)) continue; 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, invert, m, &cs->fw); return 0; } m = load_proto(cs); if (m != NULL) { size_t size; size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size; m->m = xtables_calloc(1, size); m->m->u.match_size = size; strcpy(m->m->u.user.name, m->name); m->m->u.user.revision = m->revision; xs_init_match(m); if (m->x6_options != NULL) gl->opts = xtables_options_xfrm(gl->orig_opts, gl->opts, m->x6_options, &m->option_offset); else gl->opts = xtables_merge_options(gl->orig_opts, gl->opts, m->extra_opts, &m->option_offset); if (gl->opts == NULL) xtables_error(OTHER_PROBLEM, "can't alloc memory!"); optind--; /* Indicate to rerun getopt *immediately* */ return 1; } if (cs->c == ':') xtables_error(PARAMETER_PROBLEM, "option \"%s\" " "requires an argument", 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); } static mainfunc_t subcmd_get(const char *cmd, const struct subcommand *cb) { for (; cb->name != NULL; ++cb) if (strcmp(cb->name, cmd) == 0) return cb->main; return NULL; } int subcmd_main(int argc, char **argv, const struct subcommand *cb) { const char *cmd = basename(*argv); mainfunc_t f = subcmd_get(cmd, cb); if (f == NULL && argc > 1) { /* * Unable to find a main method for our command name? * Let's try again with the first argument! */ ++argv; --argc; f = subcmd_get(*argv, cb); } /* now we should have a valid function pointer */ if (f != NULL) return f(argc, argv); fprintf(stderr, "ERROR: No valid subcommand given.\nValid subcommands:\n"); for (; cb->name != NULL; ++cb) fprintf(stderr, " * %s\n", cb->name); exit(EXIT_FAILURE); } void xs_init_target(struct xtables_target *target) { if (target->udata_size != 0) { free(target->udata); target->udata = xtables_calloc(1, target->udata_size); } if (target->init != NULL) target->init(target->t); } void xs_init_match(struct xtables_match *match) { if (match->udata_size != 0) { /* * As soon as a subsequent instance of the same match * is used, e.g. "-m time -m time", the first instance * is no longer reachable anyway, so we can free udata. * Same goes for target. */ free(match->udata); match->udata = xtables_calloc(1, match->udata_size); } if (match->init != NULL) match->init(match->m); } static void alarm_ignore(int i) { } static int xtables_lock(int wait) { struct sigaction sigact_alarm; const char *lock_file; int fd; lock_file = getenv("XTABLES_LOCKFILE"); if (lock_file == NULL || lock_file[0] == '\0') lock_file = XT_LOCK_NAME; fd = open(lock_file, O_CREAT, 0600); if (fd < 0) { fprintf(stderr, "Fatal: can't open lock file %s: %s\n", lock_file, strerror(errno)); return XT_LOCK_FAILED; } if (wait != -1) { sigact_alarm.sa_handler = alarm_ignore; sigact_alarm.sa_flags = SA_RESETHAND; sigemptyset(&sigact_alarm.sa_mask); sigaction(SIGALRM, &sigact_alarm, NULL); alarm(wait); } if (flock(fd, LOCK_EX) == 0) return fd; 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) { if (lock >= 0) close(lock); } int xtables_lock_or_exit(int wait) { int lock = xtables_lock(wait); if (lock == XT_LOCK_FAILED) { xtables_free_opts(1); exit(RESOURCE_PROBLEM); } if (lock == XT_LOCK_BUSY) { fprintf(stderr, "Another app is currently holding the xtables lock. "); if (wait == 0) fprintf(stderr, "Perhaps you want to use the -w option?\n"); else fprintf(stderr, "Stopped waiting after %ds.\n", wait); xtables_free_opts(1); exit(RESOURCE_PROBLEM); } return lock; } int parse_wait_time(int argc, char *argv[]) { int wait = -1; if (optarg) { if (sscanf(optarg, "%i", &wait) != 1) xtables_error(PARAMETER_PROBLEM, "wait seconds not numeric"); } else if (xs_has_arg(argc, argv)) if (sscanf(argv[optind++], "%i", &wait) != 1) xtables_error(PARAMETER_PROBLEM, "wait seconds not numeric"); return wait; } void parse_wait_interval(int argc, char *argv[]) { const char *arg; unsigned int usec; int ret; if (optarg) arg = optarg; else if (xs_has_arg(argc, argv)) arg = argv[optind++]; else xtables_error(PARAMETER_PROBLEM, "wait interval value required"); ret = sscanf(arg, "%u", &usec); if (ret == 1) { if (usec > 999999) xtables_error(PARAMETER_PROBLEM, "too long usec wait %u > 999999 usec", usec); fprintf(stderr, "Ignoring deprecated --wait-interval option.\n"); return; } xtables_error(PARAMETER_PROBLEM, "wait interval not numeric"); } int parse_counters(const char *string, struct xt_counters *ctr) { int ret; if (!string) return 0; ret = sscanf(string, "[%llu:%llu]", (unsigned long long *)&ctr->pcnt, (unsigned long long *)&ctr->bcnt); return ret == 2; } /* Tokenize counters argument of typical iptables-restore format rule. * * If *bufferp contains counters, update *pcntp and *bcntp to point at them, * change bytes after counters in *bufferp to nul-bytes, update *bufferp to * point to after the counters and return true. * If *bufferp does not contain counters, return false. * If syntax is wrong in *bufferp, call xtables_error() and hence exit(). * */ bool tokenize_rule_counters(char **bufferp, char **pcntp, char **bcntp, int line) { char *ptr, *buffer = *bufferp, *pcnt, *bcnt; if (buffer[0] != '[') return false; /* we have counters in our input */ ptr = strchr(buffer, ']'); if (!ptr) xtables_error(PARAMETER_PROBLEM, "Bad line %u: need ]", line); pcnt = strtok(buffer+1, ":"); if (!pcnt) xtables_error(PARAMETER_PROBLEM, "Bad line %u: need :", line); bcnt = strtok(NULL, "]"); if (!bcnt) xtables_error(PARAMETER_PROBLEM, "Bad line %u: need ]", line); *pcntp = pcnt; *bcntp = bcnt; /* start command parsing after counter */ *bufferp = ptr + 1; return true; } inline bool xs_has_arg(int argc, char *argv[]) { return optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!'; } /* function adding one argument to store, updating argc * returns if argument added, does not return otherwise */ void add_argv(struct argv_store *store, const char *what, int quoted) { DEBUGP("add_argv: %s\n", what); if (store->argc + 1 >= MAX_ARGC) xtables_error(PARAMETER_PROBLEM, "Parser cannot handle more arguments"); if (!what) xtables_error(PARAMETER_PROBLEM, "Trying to store NULL argument"); store->argv[store->argc] = xtables_strdup(what); store->argvattr[store->argc] = quoted; store->argv[++store->argc] = NULL; } void free_argv(struct argv_store *store) { while (store->argc) { store->argc--; free(store->argv[store->argc]); store->argvattr[store->argc] = 0; } } /* Save parsed rule for comparison with next rule to perform action aggregation * on duplicate conditions. */ void save_argv(struct argv_store *dst, struct argv_store *src) { int i; free_argv(dst); for (i = 0; i < src->argc; i++) { dst->argvattr[i] = src->argvattr[i]; dst->argv[i] = src->argv[i]; src->argv[i] = NULL; } dst->argc = src->argc; src->argc = 0; } struct xt_param_buf { char buffer[1024]; int len; }; static void add_param(struct xt_param_buf *param, const char *curchar) { param->buffer[param->len++] = *curchar; if (param->len >= sizeof(param->buffer)) xtables_error(PARAMETER_PROBLEM, "Parameter too long!"); } void add_param_to_argv(struct argv_store *store, char *parsestart, int line) { int quote_open = 0, escaped = 0, quoted = 0; struct xt_param_buf param = {}; char *curchar; /* After fighting with strtok enough, here's now * a 'real' parser. According to Rusty I'm now no * longer a real hacker, but I can live with that */ for (curchar = parsestart; *curchar; curchar++) { if (quote_open) { if (escaped) { add_param(¶m, curchar); escaped = 0; continue; } else if (*curchar == '\\') { escaped = 1; continue; } else if (*curchar == '"') { quote_open = 0; } else { add_param(¶m, curchar); continue; } } else { if (*curchar == '"') { quote_open = 1; quoted = 1; continue; } } switch (*curchar) { case '"': break; case ' ': case '\t': case '\n': if (!param.len) { /* two spaces? */ continue; } break; default: /* regular character, copy to buffer */ add_param(¶m, curchar); continue; } param.buffer[param.len] = '\0'; add_argv(store, param.buffer, quoted); param.len = 0; quoted = 0; } if (param.len) { param.buffer[param.len] = '\0'; add_argv(store, param.buffer, 0); } } #ifdef DEBUG void debug_print_argv(struct argv_store *store) { int i; for (i = 0; i < store->argc; i++) fprintf(stderr, "argv[%d]: %s\n", i, store->argv[i]); } #endif 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]; if (!mask->s_addr && !(format & FMT_NUMERIC)) return "anywhere"; if (format & FMT_NUMERIC) strncpy(buf, xtables_ipaddr_to_numeric(addr), BUFSIZ - 1); else strncpy(buf, xtables_ipaddr_to_anyname(addr), BUFSIZ - 1); buf[BUFSIZ - 1] = '\0'; strncat(buf, xtables_ipmask_to_numeric(mask), BUFSIZ - strlen(buf) - 1); return buf; } void print_ipv4_addresses(const struct ipt_entry *fw, unsigned int format) { fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); printf(FMT("%-19s ", "%s "), ipv4_addr_to_string(&fw->ip.src, &fw->ip.smsk, format)); fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); printf(FMT("%-19s ", "-> %s"), 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) { static char buf[BUFSIZ]; if (IN6_IS_ADDR_UNSPECIFIED(addr) && !(format & FMT_NUMERIC)) return "anywhere"; if (format & FMT_NUMERIC) strncpy(buf, xtables_ip6addr_to_numeric(addr), BUFSIZ - 1); else strncpy(buf, xtables_ip6addr_to_anyname(addr), BUFSIZ - 1); buf[BUFSIZ - 1] = '\0'; strncat(buf, xtables_ip6mask_to_numeric(mask), BUFSIZ - strlen(buf) - 1); return buf; } void print_ipv6_addresses(const struct ip6t_entry *fw6, unsigned int format) { fputc(fw6->ipv6.invflags & IP6T_INV_SRCIP ? '!' : ' ', stdout); printf(FMT("%-19s ", "%s "), ipv6_addr_to_string(&fw6->ipv6.src, &fw6->ipv6.smsk, format)); fputc(fw6->ipv6.invflags & IP6T_INV_DSTIP ? '!' : ' ', stdout); printf(FMT("%-19s ", "-> %s"), ipv6_addr_to_string(&fw6->ipv6.dst, &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 */ void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags, unsigned int format) { const char *anyname = format & FMT_NUMERIC ? "*" : "any"; char iface[IFNAMSIZ + 2]; if (!(format & FMT_VIA)) return; snprintf(iface, IFNAMSIZ + 2, "%s%s", invflags & IPT_INV_VIA_IN ? "!" : "", iniface[0] != '\0' ? iniface : anyname); printf(FMT(" %-6s ", "in %s "), iface); snprintf(iface, IFNAMSIZ + 2, "%s%s", invflags & IPT_INV_VIA_OUT ? "!" : "", outiface[0] != '\0' ? outiface : anyname); printf(FMT("%-6s ", "out %s "), iface); } /* This assumes that mask is contiguous, and byte-bounded. */ void save_iface(char letter, const char *iface, const unsigned char *mask, int invert) { 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; } } } 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 (invert) xtables_error(PARAMETER_PROBLEM, "unexpected ! flag before --match"); m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, &cs->matches); size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size; m->m = xtables_calloc(1, size); m->m->u.match_size = size; if (m->real_name == NULL) { strcpy(m->m->u.user.name, m->name); } else { strcpy(m->m->u.user.name, m->real_name); if (!(m->ext_flags & XTABLES_EXT_ALIAS)) fprintf(stderr, "Notice: the %s match is converted into %s match " "in rule listing and saving.\n", m->name, m->real_name); } m->m->u.user.revision = m->revision; xs_init_match(m); if (m == m->next) return; /* Merge options for non-cloned matches */ if (m->x6_options != NULL) opts = xtables_options_xfrm(xt_params->orig_opts, opts, m->x6_options, &m->option_offset); else if (m->extra_opts != NULL) opts = xtables_merge_options(xt_params->orig_opts, opts, m->extra_opts, &m->option_offset); if (opts == NULL) xtables_error(OTHER_PROBLEM, "can't alloc memory!"); xt_params->opts = opts; } static const char *xt_parse_target(const char *targetname) { const char *ptr; if (strlen(targetname) < 1) xtables_error(PARAMETER_PROBLEM, "Invalid target name (too short)"); if (strlen(targetname) >= XT_EXTENSION_MAXNAMELEN) xtables_error(PARAMETER_PROBLEM, "Invalid target name `%s' (%u chars max)", targetname, XT_EXTENSION_MAXNAMELEN - 1); for (ptr = targetname; *ptr; ptr++) if (isspace(*ptr)) xtables_error(PARAMETER_PROBLEM, "Invalid target name `%s'", targetname); return targetname; } void command_jump(struct iptables_command_state *cs, const char *jumpto) { struct option *opts = xt_params->opts; size_t size; cs->jumpto = xt_parse_target(jumpto); /* TRY_LOAD (may be chain name) */ cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD); if (cs->target == NULL) return; size = XT_ALIGN(sizeof(struct xt_entry_target)) + cs->target->size; cs->target->t = xtables_calloc(1, size); cs->target->t->u.target_size = size; if (cs->target->real_name == NULL) { strcpy(cs->target->t->u.user.name, cs->jumpto); } else { /* Alias support for userspace side */ strcpy(cs->target->t->u.user.name, cs->target->real_name); if (!(cs->target->ext_flags & XTABLES_EXT_ALIAS)) fprintf(stderr, "Notice: The %s target is converted into %s target " "in rule listing and saving.\n", cs->jumpto, cs->target->real_name); } cs->target->t->u.user.revision = cs->target->revision; xs_init_target(cs->target); if (cs->target->x6_options != NULL) opts = xtables_options_xfrm(xt_params->orig_opts, opts, cs->target->x6_options, &cs->target->option_offset); else opts = xtables_merge_options(xt_params->orig_opts, opts, cs->target->extra_opts, &cs->target->option_offset); if (opts == NULL) xtables_error(OTHER_PROBLEM, "can't alloc memory!"); xt_params->opts = opts; } 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', 'N', 'X', 'P', 'E', 'S', 'Z', 'C' }; int i; for (i = 0; option > 1; option >>= 1, i++) ; if (i >= ARRAY_SIZE(cmdflags)) xtables_error(OTHER_PROBLEM, "cmd2char(): Invalid command number %u.", 1 << i); return cmdflags[i]; } 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", cmd2char(newcmd), cmd2char(*cmd & (~othercmds))); *cmd |= newcmd; } /* Can't be zero. */ static int parse_rulenumber(const char *rule) { unsigned int rulenum; if (!xtables_strtoui(rule, NULL, &rulenum, 1, INT_MAX)) xtables_error(PARAMETER_PROBLEM, "Invalid rule number `%s'", rule); return rulenum; } #define NUMBER_OF_OPT ARRAY_SIZE(optflags) static const char optflags[] = { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '0', 'c', 'f', 2, 3, 'l', 4, 5, 6 }; /* Table of legal combinations of commands and options. If any of the * given commands make an option legal, that option is legal (applies to * CMD_LIST and CMD_ZERO only). * Key: * + compulsory * x illegal * optional */ static const 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 --line -c -f 2 3 l 4 5 6 */ /*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' ',' ',' ',' ',' ',' ',' '}, /*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' ',' ',' ',' ',' ',' ',' '}, /*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'}, /*REPLACE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' ',' ',' ',' ',' ',' ',' '}, /*APPEND*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' ',' ',' ',' ',' ',' ',' '}, /*LIST*/ {' ','x','x','x','x',' ',' ','x','x',' ','x','x','x','x','x','x','x','x'}, /*FLUSH*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'}, /*ZERO*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'}, /*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'}, /*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'}, /*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x',' ','x','x','x','x','x','x','x'}, /*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'}, /*LIST_RULES*/{'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'}, /*ZERO_NUM*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'}, /*CHECK*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' ',' ',' ',' ',' ',' ',' '}, }; static void generic_opt_check(int command, int options) { int i, j, legal = 0; /* 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; i < NUMBER_OF_OPT; i++) { legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */ for (j = 0; j < NUMBER_OF_CMD; j++) { if (!(command & (1< 1; option >>= 1, ptr++) ; return *ptr; } static const int inverse_for_options[NUMBER_OF_OPT] = { /* -n */ 0, /* -s */ IPT_INV_SRCIP, /* -d */ IPT_INV_DSTIP, /* -p */ XT_INV_PROTO, /* -j */ 0, /* -v */ 0, /* -x */ 0, /* -i */ IPT_INV_VIA_IN, /* -o */ IPT_INV_VIA_OUT, /*--line*/ 0, /* -c */ 0, /* -f */ IPT_INV_FRAG, /* 2 */ IPT_INV_SRCDEVADDR, /* 3 */ IPT_INV_TGTDEVADDR, /* -l */ IPT_INV_ARPHLN, /* 4 */ IPT_INV_ARPOP, /* 5 */ IPT_INV_ARPHRD, /* 6 */ IPT_INV_PROTO, }; static void set_option(unsigned int *options, unsigned int option, uint16_t *invflg, bool invert) { if (*options & option) xtables_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed", opt2char(option)); *options |= option; if (invert) { unsigned int i; for (i = 0; 1 << i != option; i++); if (!inverse_for_options[i]) xtables_error(PARAMETER_PROBLEM, "cannot have ! before -%c", opt2char(option)); *invflg |= inverse_for_options[i]; } } 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 (((format & (FMT_NUMERIC | FMT_NOTABLE)) == FMT_NUMERIC) || !pname) printf(FMT("%-4hu ", "%hu "), proto); else printf(FMT("%-4s ", "%s "), pname); } void save_rule_details(const char *iniface, unsigned const char *iniface_mask, const char *outiface, unsigned const char *outiface_mask, uint16_t proto, int frag, uint8_t invflags) { if (iniface != NULL) { save_iface('i', iniface, iniface_mask, invflags & IPT_INV_VIA_IN); } if (outiface != NULL) { save_iface('o', outiface, outiface_mask, invflags & IPT_INV_VIA_OUT); } if (proto > 0) { const char *pname = proto_to_name(proto, 0); 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; } static void xtables_printhelp(const struct xtables_rule_match *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= 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: 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 do_parse(int argc, char *argv[], struct xt_cmd_parse *p, struct iptables_command_state *cs, struct xtables_args *args) { 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; xt_params->opts = xt_params->orig_opts; while ((cs->c = getopt_long(argc, argv, optstring_lookup(afinfo->family), xt_params->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': 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)) { p->rulenum = parse_rulenumber(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, CMD_NONE, invert); p->chain = optarg; if (xs_has_arg(argc, argv)) p->policy = argv[optind++]; else xtables_error(PARAMETER_PROBLEM, "-%c requires a chain and a policy", cmd2char(CMD_SET_POLICY)); break; case 'h': if (!optarg) optarg = argv[optind]; /* iptables -p icmp -h */ if (!cs->matches && cs->protocol) xtables_find_match(cs->protocol, XTF_TRY_LOAD, &cs->matches); xtables_printhelp(cs->matches); 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(&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; args->proto = xtables_parse_protocol(cs->protocol); if (args->proto == 0 && (args->invflags & XT_INV_PROTO)) xtables_error(PARAMETER_PROBLEM, "rule would never match protocol"); /* 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(&cs->options, OPT_SOURCE, &args->invflags, invert); args->shostnetworkmask = optarg; break; case 'd': check_inverse(args, optarg, &invert, argc, argv); set_option(&cs->options, OPT_DESTINATION, &args->invflags, invert); args->dhostnetworkmask = optarg; break; #ifdef IPT_F_GOTO case 'g': set_option(&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(&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(&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(&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(&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(&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(&cs->options, OPT_P_TYPE, &args->invflags, invert); args->arp_ptype = optarg; break; case 'j': set_option(&cs->options, OPT_JUMP, &args->invflags, invert); command_jump(cs, optarg); break; case 'i': check_empty_interface(args, optarg); check_inverse(args, optarg, &invert, argc, argv); set_option(&cs->options, OPT_VIANAMEIN, &args->invflags, invert); xtables_parse_interface(optarg, args->iniface, args->iniface_mask); break; case 'o': check_empty_interface(args, optarg); check_inverse(args, optarg, &invert, argc, argv); set_option(&cs->options, OPT_VIANAMEOUT, &args->invflags, invert); xtables_parse_interface(optarg, args->outiface, args->outiface_mask); break; case 'f': if (args->family == AF_INET6) { xtables_error(PARAMETER_PROBLEM, "`-f' is not supported in IPv6, " "use -m frag instead"); } set_option(&cs->options, OPT_FRAGMENT, &args->invflags, invert); args->flags |= IPT_F_FRAG; break; case 'v': if (!p->verbose) set_option(&cs->options, OPT_VERBOSE, &args->invflags, invert); p->verbose++; break; case 'm': command_match(cs, invert); break; case 'n': set_option(&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(&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': set_option(&cs->options, OPT_LINENUMBERS, &args->invflags, invert); break; case 'M': xtables_modprobe_program = optarg; break; case 'c': set_option(&cs->options, OPT_COUNTERS, &args->invflags, invert); args->pcnt = optarg; args->bcnt = strchr(args->pcnt + 1, ','); if (args->bcnt) args->bcnt++; if (!args->bcnt && xs_has_arg(argc, argv)) args->bcnt = argv[optind++]; if (!args->bcnt) xtables_error(PARAMETER_PROBLEM, "-%c requires packet and byte counter", opt2char(OPT_COUNTERS)); if (sscanf(args->pcnt, "%llu", &args->pcnt_cnt) != 1) xtables_error(PARAMETER_PROBLEM, "-%c packet counter not numeric", opt2char(OPT_COUNTERS)); if (sscanf(args->bcnt, "%llu", &args->bcnt_cnt) != 1) xtables_error(PARAMETER_PROBLEM, "-%c byte counter not numeric", opt2char(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: if (command_default(cs, xt_params, invert)) /* cf. ip6tables.c */ continue; break; } invert = false; } if (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); if (p->command == CMD_REPLACE && (args->s.naddrs != 1 || args->d.naddrs != 1)) xtables_error(PARAMETER_PROBLEM, "Replacement rule does not " "specify a unique address"); generic_opt_check(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_DELETE_NUM || p->command == CMD_CHECK || p->command == CMD_INSERT || p->command == CMD_REPLACE) { if (strcmp(p->chain, "PREROUTING") == 0 || strcmp(p->chain, "INPUT") == 0) { /* -o not valid with incoming packets. */ if (cs->options & OPT_VIANAMEOUT) xtables_error(PARAMETER_PROBLEM, "Can't use -%c with %s\n", opt2char(OPT_VIANAMEOUT), p->chain); } if (strcmp(p->chain, "POSTROUTING") == 0 || strcmp(p->chain, "OUTPUT") == 0) { /* -i not valid with outgoing packets */ if (cs->options & OPT_VIANAMEIN) xtables_error(PARAMETER_PROBLEM, "Can't use -%c with %s\n", opt2char(OPT_VIANAMEIN), p->chain); } } } void ipv4_proto_parse(struct iptables_command_state *cs, struct xtables_args *args) { cs->fw.ip.proto = args->proto; 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 = args->proto; 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.iniface_mask, args->iniface_mask, IFNAMSIZ*sizeof(unsigned char)); memcpy(cs->fw.ip.outiface, args->outiface, IFNAMSIZ); memcpy(cs->fw.ip.outiface_mask, args->outiface_mask, IFNAMSIZ*sizeof(unsigned char)); 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; }