summaryrefslogtreecommitdiffstats
path: root/iptables/xshared.c
diff options
context:
space:
mode:
Diffstat (limited to 'iptables/xshared.c')
-rw-r--r--iptables/xshared.c1596
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(&param, 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);
+}