summaryrefslogtreecommitdiffstats
path: root/iptables
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2012-09-27 19:12:53 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2013-12-30 23:50:09 +0100
commit384958620abab397062b67fb2763e813b63f74f0 (patch)
treeec01cb88585150a37f122bfbf39ea33218bafdb6 /iptables
parent99b85b7837707bd6c6d578c9328e1321fceb8082 (diff)
use nf_tables and nf_tables compatibility interface
This patch adds the following utilities: * xtables * xtables-restore * xtables-save * xtables-config They all use Patrick's nf_tables infrastructure plus my compatibility layer. xtables, xtables-restore and xtables-save are syntax compatible with ip[6]tables, ip[6]tables-restore and ip[6]tables-save. Semantics aims to be similar, still the main exception is that there is no commit operation. Thus, we incrementally add/delete rules without entire table locking. The following options are also not yet implemented: -Z (this requires adding expr->ops->reset(...) so nft_counters can reset internal state of expressions while dumping it) -R and -E (this requires adding this feature to nf_tables) -f (can be implemented with expressions: payload 6 (2-bytes) + bitwise a&b^!b + cmp neq 0) -IPv6 support. But those are a matter of time to get them done. A new utility, xtables-config, is available to register tables and chains. By default there is a configuration file that adds backward compatible tables and chains under iptables/etc/xtables.conf. You have to call this utility first to register tables and chains. However, it would be possible to automagically register tables and chains while using xtables and xtables-restore to get similar operation than with iptables. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'iptables')
-rw-r--r--iptables/Makefile.am25
-rw-r--r--iptables/ip6tables.c1
-rw-r--r--iptables/iptables.c1
-rw-r--r--iptables/nft.c2764
-rw-r--r--iptables/nft.h62
-rw-r--r--iptables/xshared.h1
-rw-r--r--iptables/xtables-config-parser.y213
-rw-r--r--iptables/xtables-config-syntax.l53
-rw-r--r--iptables/xtables-config.c107
-rw-r--r--iptables/xtables-multi.c10
-rw-r--r--iptables/xtables-multi.h4
-rw-r--r--iptables/xtables-restore.c417
-rw-r--r--iptables/xtables-save.c122
-rw-r--r--iptables/xtables-standalone.c80
-rw-r--r--iptables/xtables.c1251
15 files changed, 5109 insertions, 2 deletions
diff --git a/iptables/Makefile.am b/iptables/Makefile.am
index a4246eb3..2b1f3fa4 100644
--- a/iptables/Makefile.am
+++ b/iptables/Makefile.am
@@ -1,7 +1,8 @@
# -*- Makefile -*-
AM_CFLAGS = ${regular_CFLAGS}
-AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include ${kinclude_CPPFLAGS}
+AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include ${kinclude_CPPFLAGS} ${libmnl_CPPFLAGS} ${libnftables_CPPFLAGS}
+AM_YFLAGS = -d
xtables_multi_SOURCES = xtables-multi.c iptables-xml.c
xtables_multi_CFLAGS = ${AM_CFLAGS}
@@ -24,11 +25,27 @@ endif
xtables_multi_SOURCES += xshared.c
xtables_multi_LDADD += ../libxtables/libxtables.la -lm
+if ENABLE_NFTABLES
+if HAVE_LIBMNL
+if HAVE_LIBNFTABLES
+xtables_multi_SOURCES += xtables-save.c xtables-restore.c \
+ xtables-standalone.c xtables.c nft.c \
+ xtables-config-parser.y xtables-config-syntax.l \
+ xtables-config.c
+xtables_multi_LDADD += -lmnl -lnftables
+xtables_multi_CFLAGS += -DENABLE_NFTABLES
+# yacc and lex generate dirty code
+xtables_multi-xtables-config-parser.o xtables_multi-xtables-config-syntax.o: AM_CFLAGS += -Wno-missing-prototypes -Wno-missing-declarations -Wno-implicit-function-declaration -Wno-nested-externs -Wno-undef -Wno-redundant-decls
+endif
+endif
+endif
+
sbin_PROGRAMS = xtables-multi
man_MANS = iptables.8 iptables-restore.8 iptables-save.8 \
iptables-xml.1 ip6tables.8 ip6tables-restore.8 \
ip6tables-save.8 iptables-extensions.8
-CLEANFILES = iptables.8
+CLEANFILES = iptables.8 \
+ xtables-config-parser.c xtables-config-syntax.c
vx_bin_links = iptables-xml
if ENABLE_IPV4
@@ -37,6 +54,9 @@ endif
if ENABLE_IPV6
v6_sbin_links = ip6tables ip6tables-restore ip6tables-save
endif
+if ENABLE_NFTABLES
+x_sbin_links = xtables xtables-restore xtables-save xtables-config
+endif
iptables-extensions.8: iptables-extensions.8.tmpl ../extensions/matches.man ../extensions/targets.man
${AM_VERBOSE_GEN} sed \
@@ -52,3 +72,4 @@ install-exec-hook:
for i in ${vx_bin_links}; do ${LN_S} -f "${sbindir}/xtables-multi" "${DESTDIR}${bindir}/$$i"; done;
for i in ${v4_sbin_links}; do ${LN_S} -f xtables-multi "${DESTDIR}${sbindir}/$$i"; done;
for i in ${v6_sbin_links}; do ${LN_S} -f xtables-multi "${DESTDIR}${sbindir}/$$i"; done;
+ for i in ${x_sbin_links}; do ${LN_S} -f xtables-multi "${DESTDIR}${sbindir}/$$i"; done;
diff --git a/iptables/ip6tables.c b/iptables/ip6tables.c
index a5199d5e..2ebfd6c0 100644
--- a/iptables/ip6tables.c
+++ b/iptables/ip6tables.c
@@ -121,6 +121,7 @@ struct xtables_globals ip6tables_globals = {
.program_version = IPTABLES_VERSION,
.orig_opts = original_opts,
.exit_err = ip6tables_exit_error,
+ .compat_rev = xtables_compatible_revision,
};
/* Table of legal combinations of commands and options. If any of the
diff --git a/iptables/iptables.c b/iptables/iptables.c
index 5cd2596e..471bff06 100644
--- a/iptables/iptables.c
+++ b/iptables/iptables.c
@@ -120,6 +120,7 @@ struct xtables_globals iptables_globals = {
.program_version = IPTABLES_VERSION,
.orig_opts = original_opts,
.exit_err = iptables_exit_error,
+ .compat_rev = xtables_compatible_revision,
};
/* Table of legal combinations of commands and options. If any of the
diff --git a/iptables/nft.c b/iptables/nft.c
new file mode 100644
index 00000000..91383bfb
--- /dev/null
+++ b/iptables/nft.c
@@ -0,0 +1,2764 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#if 0
+#define DEBUGP(x, args...) fprintf(stdout, x, ## args)
+#define NLDEBUG
+#define DEBUG_DEL
+#else
+#define DEBUGP(x, args...)
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <netdb.h> /* getprotobynumber */
+#include <time.h>
+
+#include <xtables.h>
+#include <libiptc/libxtc.h>
+#include <libiptc/xtcshared.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter_ipv4/ip_tables.h> /* FIXME: only IPV4 by now */
+
+#include <linux/netlink.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_tables_compat.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftables/table.h>
+#include <libnftables/chain.h>
+#include <libnftables/rule.h>
+#include <libnftables/expr.h>
+
+#include <netinet/in.h> /* inet_ntoa */
+#include <arpa/inet.h>
+
+#include "nft.h"
+#include "xshared.h" /* proto_to_name */
+
+static void *nft_fn;
+
+static int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
+ int (*cb)(const struct nlmsghdr *nlh, void *data),
+ void *data)
+{
+ int ret;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+
+ if (mnl_socket_sendto(h->nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_send");
+ return -1;
+ }
+
+ ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, h->seq, h->portid, cb, data);
+ if (ret <= 0)
+ break;
+
+ ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static void nft_table_init_one(struct nft_handle *h, const char *name, int portid)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nft_table *t;
+
+ t = nft_table_alloc();
+ if (t == NULL)
+ return;
+
+ nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)name);
+
+ nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET,
+ NLM_F_ACK|NLM_F_EXCL, h->seq);
+ nft_table_nlmsg_build_payload(nlh, t);
+ nft_table_free(t);
+
+ if (mnl_talk(h, nlh, NULL, NULL) < 0) {
+ if (errno != EEXIST)
+ perror("mnl-talk:nft_table_init_one");
+ }
+}
+
+#define FILTER 0
+#define MANGLE 1
+#define RAW 2
+#define SECURITY 3
+#define TABLES_MAX 4
+
+static struct default_table {
+ const char *name;
+ uint32_t prio;
+} tables[TABLES_MAX] = {
+ [RAW] = {
+ .name = "raw",
+ .prio = -300, /* NF_IP_PRI_RAW */
+ },
+ [MANGLE] = {
+ .name = "mangle",
+ .prio = -150, /* NF_IP_PRI_MANGLE */
+ },
+ [FILTER] = {
+ .name = "filter",
+ .prio = 0, /* NF_IP_PRI_FILTER */
+ },
+ [SECURITY] = {
+ .name = "security",
+ .prio = 150, /* NF_IP_PRI_SECURITY */
+ },
+ /* nat already registered by nf_tables */
+};
+
+static void nft_table_init(struct nft_handle *h)
+{
+ int i;
+
+ for (i=0; i<TABLES_MAX; i++)
+ nft_table_init_one(h, tables[i].name, h->portid);
+}
+
+static struct default_chain {
+ const char *name;
+ uint32_t hook;
+} chains[TABLES_MAX][NF_IP_NUMHOOKS] = {
+ [FILTER] = {
+ {
+ .name = "INPUT",
+ .hook = NF_INET_LOCAL_IN,
+ },
+ {
+ .name = "FORWARD",
+ .hook = NF_INET_FORWARD,
+ },
+ {
+ .name = "OUTPUT",
+ .hook = NF_INET_LOCAL_OUT,
+ },
+ },
+ [MANGLE] = {
+ {
+ .name = "PREROUTING",
+ .hook = NF_INET_PRE_ROUTING,
+ },
+ {
+ .name = "INPUT",
+ .hook = NF_INET_LOCAL_IN,
+ },
+ {
+ .name = "FORWARD",
+ .hook = NF_INET_FORWARD,
+ },
+ {
+ .name = "OUTPUT",
+ .hook = NF_INET_LOCAL_OUT,
+ },
+ {
+ .name = "POSTROUTING",
+ .hook = NF_INET_POST_ROUTING,
+ },
+ },
+ [RAW] = {
+ {
+ .name = "PREROUTING",
+ .hook = NF_INET_PRE_ROUTING,
+ },
+ {
+ .name = "OUTPUT",
+ .hook = NF_INET_LOCAL_OUT,
+ },
+ },
+ [SECURITY] = {
+ {
+ .name = "INPUT",
+ .hook = NF_INET_LOCAL_IN,
+ },
+ {
+ .name = "FORWARD",
+ .hook = NF_INET_FORWARD,
+ },
+ {
+ .name = "OUTPUT",
+ .hook = NF_INET_LOCAL_OUT,
+ },
+ },
+};
+
+static void
+nft_chain_default_add(struct nft_handle *h, struct default_table *table,
+ struct default_chain *chain, int policy)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nft_chain *c;
+
+ c = nft_chain_alloc();
+ if (c == NULL)
+ return;
+
+ nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table->name);
+ nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain->name);
+ nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_HOOKNUM, chain->hook);
+ nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_PRIO, table->prio);
+ nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_POLICY, policy);
+
+ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET,
+ NLM_F_ACK|NLM_F_EXCL, h->seq);
+ nft_chain_nlmsg_build_payload(nlh, c);
+ nft_chain_free(c);
+
+ if (mnl_talk(h, nlh, NULL, NULL) < 0) {
+ if (errno != EEXIST)
+ perror("mnl_talk:nft_chain_default_add");
+ }
+}
+
+static void nft_chain_init(struct nft_handle *h)
+{
+ int i, j;
+
+ for (i=0; i<TABLES_MAX; i++) {
+ for (j=0; j<NF_IP_NUMHOOKS; j++) {
+ if (chains[i][j].name == NULL)
+ break;
+
+ nft_chain_default_add(h, &tables[i], &chains[i][j],
+ NF_ACCEPT);
+ }
+ }
+}
+
+int nft_init(struct nft_handle *h)
+{
+ h->nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (h->nl == NULL) {
+ perror("mnl_socket_open");
+ return -1;
+ }
+
+ if (mnl_socket_bind(h->nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ return -1;
+ }
+ h->portid = mnl_socket_get_portid(h->nl);
+
+/*
+ * In case we want to inconditionally register all tables / chain.
+ * This is not flexible and performance consuming.
+ *
+ * nft_table_init(h);
+ * nft_chain_init(h);
+ */
+
+ return 0;
+}
+
+void nft_fini(struct nft_handle *h)
+{
+ mnl_socket_close(h->nl);
+}
+
+int nft_table_add(struct nft_handle *h, const struct nft_table *t)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET,
+ NLM_F_ACK|NLM_F_EXCL, h->seq);
+ nft_table_nlmsg_build_payload(nlh, t);
+
+ return mnl_talk(h, nlh, NULL, NULL);
+}
+
+int nft_chain_add(struct nft_handle *h, const struct nft_chain *c)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET,
+ NLM_F_ACK|NLM_F_EXCL, h->seq);
+ nft_chain_nlmsg_build_payload(nlh, c);
+
+ return mnl_talk(h, nlh, NULL, NULL);
+}
+
+static void nft_chain_print_debug(struct nft_chain *c, struct nlmsghdr *nlh)
+{
+#ifdef NLDEBUG
+ char tmp[1024];
+
+ nft_chain_snprintf(tmp, sizeof(tmp), c, 0, 0);
+ printf("DEBUG: chain: %s", tmp);
+ mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
+#endif
+}
+
+static int
+__nft_chain_set(struct nft_handle *h, const char *table,
+ const char *chain, int policy,
+ const struct xt_counters *counters)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nft_chain *c;
+ int ret;
+
+ c = nft_chain_alloc();
+ if (c == NULL)
+ return -1;
+
+ nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table);
+ nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain);
+ nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_POLICY, policy);
+ if (counters) {
+ nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_BYTES,
+ counters->bcnt);
+ nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_PACKETS,
+ counters->pcnt);
+ }
+
+ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET,
+ NLM_F_ACK, h->seq);
+ nft_chain_nlmsg_build_payload(nlh, c);
+
+ nft_chain_print_debug(c, nlh);
+
+ nft_chain_free(c);
+
+ ret = mnl_talk(h, nlh, NULL, NULL);
+ if (ret < 0)
+ perror("mnl_talk:__nft_chain_policy");
+
+ return ret;
+}
+
+int nft_chain_set(struct nft_handle *h, const char *table,
+ const char *chain, const char *policy,
+ const struct xt_counters *counters)
+{
+ int ret = -1;
+
+ nft_fn = nft_chain_set;
+
+ if (strcmp(policy, "DROP") == 0)
+ ret = __nft_chain_set(h, table, chain, NF_DROP, counters);
+ else if (strcmp(policy, "ACCEPT") == 0)
+ ret = __nft_chain_set(h, table, chain, NF_ACCEPT, counters);
+
+ /* the core expects 1 for success and 0 for error */
+ return ret == 0 ? 1 : 0;
+}
+
+static void __add_match(struct nft_rule_expr *e, struct xt_entry_match *m)
+{
+ void *info;
+
+ nft_rule_expr_set(e, NFT_EXPR_MT_NAME, m->u.user.name, strlen(m->u.user.name));
+ nft_rule_expr_set_u32(e, NFT_EXPR_MT_REV, m->u.user.revision);
+
+ info = calloc(1, m->u.match_size);
+ if (info == NULL)
+ return;
+
+ memcpy(info, m->data, m->u.match_size);
+ nft_rule_expr_set(e, NFT_EXPR_MT_INFO, info, m->u.match_size - sizeof(*m));
+}
+
+static void add_match(struct nft_rule *r, struct xt_entry_match *m)
+{
+ struct nft_rule_expr *expr;
+
+ expr = nft_rule_expr_alloc("match");
+ if (expr == NULL)
+ return;
+
+ __add_match(expr, m);
+ nft_rule_add_expr(r, expr);
+}
+
+static void __add_target(struct nft_rule_expr *e, struct xt_entry_target *t)
+{
+ void *info = NULL;
+
+ nft_rule_expr_set(e, NFT_EXPR_TG_NAME, t->u.user.name,
+ strlen(t->u.user.name));
+ nft_rule_expr_set_u32(e, NFT_EXPR_TG_REV, t->u.user.revision);
+
+ if (info == NULL) {
+ info = calloc(1, t->u.target_size);
+ if (info == NULL)
+ return;
+
+ memcpy(info, t->data, t->u.target_size);
+ }
+
+ nft_rule_expr_set(e, NFT_EXPR_TG_INFO, info, t->u.target_size - sizeof(*t));
+}
+
+static void add_target(struct nft_rule *r, struct xt_entry_target *t)
+{
+ struct nft_rule_expr *expr;
+
+ expr = nft_rule_expr_alloc("target");
+ if (expr == NULL)
+ return;
+
+ __add_target(expr, t);
+ nft_rule_add_expr(r, expr);
+}
+
+static void add_jumpto(struct nft_rule *r, const char *name, int verdict)
+{
+ struct nft_rule_expr *expr;
+
+ expr = nft_rule_expr_alloc("immediate");
+ if (expr == NULL)
+ return;
+
+ nft_rule_expr_set_u32(expr, NFT_EXPR_IMM_DREG, NFT_REG_VERDICT);
+ nft_rule_expr_set_u32(expr, NFT_EXPR_IMM_VERDICT, verdict);
+ nft_rule_expr_set_str(expr, NFT_EXPR_IMM_CHAIN, (char *)name);
+ nft_rule_add_expr(r, expr);
+}
+
+static void add_verdict(struct nft_rule *r, int verdict)
+{
+ struct nft_rule_expr *expr;
+
+ expr = nft_rule_expr_alloc("immediate");
+ if (expr == NULL)
+ return;
+
+ nft_rule_expr_set_u32(expr, NFT_EXPR_IMM_DREG, NFT_REG_VERDICT);
+ nft_rule_expr_set_u32(expr, NFT_EXPR_IMM_VERDICT, verdict);
+ nft_rule_add_expr(r, expr);
+}
+
+static void nft_rule_print_debug(struct nft_rule *r, struct nlmsghdr *nlh)
+{
+#ifdef NLDEBUG
+ char tmp[1024];
+
+ nft_rule_snprintf(tmp, sizeof(tmp), r, 0, 0);
+ printf("DEBUG: rule: %s", tmp);
+ mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
+#endif
+}
+
+static void add_meta(struct nft_rule *r, uint32_t key)
+{
+ struct nft_rule_expr *expr;
+
+ expr = nft_rule_expr_alloc("meta");
+ if (expr == NULL)
+ return;
+
+ nft_rule_expr_set_u32(expr, NFT_EXPR_META_KEY, key);
+ nft_rule_expr_set_u32(expr, NFT_EXPR_META_DREG, NFT_REG_1);
+
+ nft_rule_add_expr(r, expr);
+}
+
+static void add_payload(struct nft_rule *r, int offset, int len)
+{
+ struct nft_rule_expr *expr;
+
+ expr = nft_rule_expr_alloc("payload");
+ if (expr == NULL)
+ return;
+
+ nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_BASE,
+ NFT_PAYLOAD_NETWORK_HEADER);
+ nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_DREG, NFT_REG_1);
+ nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_OFFSET, offset);
+ nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_LEN, len);
+
+ nft_rule_add_expr(r, expr);
+}
+
+static void add_cmp_ptr(struct nft_rule *r, uint32_t op, void *data, size_t len)
+{
+ struct nft_rule_expr *expr;
+
+ expr = nft_rule_expr_alloc("cmp");
+ if (expr == NULL)
+ return;
+
+ nft_rule_expr_set_u8(expr, NFT_EXPR_CMP_SREG, NFT_REG_1);
+ nft_rule_expr_set_u8(expr, NFT_EXPR_CMP_OP, op);
+ nft_rule_expr_set(expr, NFT_EXPR_CMP_DATA, data, len);
+
+ nft_rule_add_expr(r, expr);
+}
+
+static void add_cmp_u32(struct nft_rule *r, uint32_t val, uint32_t op)
+{
+ add_cmp_ptr(r, op, &val, sizeof(val));
+}
+
+static void add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes)
+{
+ struct nft_rule_expr *expr;
+
+ expr = nft_rule_expr_alloc("counter");
+ if (expr == NULL)
+ return;
+
+ nft_rule_expr_set_u64(expr, NFT_EXPR_CTR_BYTES, packets);
+ nft_rule_expr_set_u64(expr, NFT_EXPR_CTR_PACKETS, bytes);
+
+ nft_rule_add_expr(r, expr);
+}
+
+int
+nft_rule_add(struct nft_handle *h, const char *chain, const char *table,
+ struct iptables_command_state *cs, bool append, bool verbose)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct xtables_rule_match *matchp;
+ struct nft_rule *r;
+ int ret = 1;
+ uint32_t op;
+ int flags = append ? NLM_F_APPEND : 0;
+
+ nft_fn = nft_rule_add;
+
+ r = nft_rule_alloc();
+ if (r == NULL) {
+ ret = 0;
+ goto err;
+ }
+
+ nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table);
+ nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain);
+
+ if (cs->fw.ip.iniface[0] != '\0') {
+ int iface_len = strlen(cs->fw.ip.iniface);
+
+ if (cs->fw.ip.invflags & IPT_INV_VIA_IN)
+ op = NFT_CMP_NEQ;
+ else
+ op = NFT_CMP_EQ;
+
+ if (cs->fw.ip.iniface[iface_len - 1] == '+') {
+ add_meta(r, NFT_META_IIFNAME);
+ add_cmp_ptr(r, op, cs->fw.ip.iniface, iface_len - 1);
+ } else {
+ add_meta(r, NFT_META_IIF);
+ add_cmp_u32(r, if_nametoindex(cs->fw.ip.iniface), op);
+ }
+ }
+ if (cs->fw.ip.outiface[0] != '\0') {
+ int iface_len = strlen(cs->fw.ip.outiface);
+
+ if (cs->fw.ip.invflags & IPT_INV_VIA_OUT)
+ op = NFT_CMP_NEQ;
+ else
+ op = NFT_CMP_EQ;
+
+ if (cs->fw.ip.outiface[iface_len - 1] == '+') {
+ add_meta(r, NFT_META_OIFNAME);
+ add_cmp_ptr(r, op, cs->fw.ip.outiface, iface_len - 1);
+ } else {
+ add_meta(r, NFT_META_OIF);
+ add_cmp_u32(r, if_nametoindex(cs->fw.ip.outiface), op);
+ }
+ }
+ if (cs->fw.ip.src.s_addr != 0) {
+ add_payload(r, offsetof(struct iphdr, saddr), 4);
+ if (cs->fw.ip.invflags & IPT_INV_SRCIP)
+ op = NFT_CMP_NEQ;
+ else
+ op = NFT_CMP_EQ;
+
+ add_cmp_u32(r, cs->fw.ip.src.s_addr, op);
+ }
+ if (cs->fw.ip.dst.s_addr != 0) {
+ add_payload(r, offsetof(struct iphdr, daddr), 4);
+ if (cs->fw.ip.invflags & IPT_INV_DSTIP)
+ op = NFT_CMP_NEQ;
+ else
+ op = NFT_CMP_EQ;
+
+ add_cmp_u32(r, cs->fw.ip.dst.s_addr, op);
+ }
+ if (cs->fw.ip.proto != 0) {
+ add_payload(r, offsetof(struct iphdr, protocol), 1);
+ if (cs->fw.ip.invflags & XT_INV_PROTO)
+ op = NFT_CMP_NEQ;
+ else
+ op = NFT_CMP_EQ;
+
+ add_cmp_u32(r, cs->fw.ip.proto, op);
+ }
+
+ for (matchp = cs->matches; matchp; matchp = matchp->next)
+ add_match(r, matchp->match->m);
+
+ /* Counters need to me added before the target, otherwise they are
+ * increased for each rule because of the way nf_tables works.
+ */
+ add_counters(r, cs->counters.pcnt, cs->counters.bcnt);
+
+ /* If no target at all, add nothing (default to continue) */
+ if (cs->target != NULL) {
+ /* Standard target? */
+ if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
+ add_verdict(r, NF_ACCEPT);
+ else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
+ add_verdict(r, NF_DROP);
+ else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
+ add_verdict(r, NFT_RETURN);
+ else
+ add_target(r, cs->target->t);
+ } else if (strlen(cs->jumpto) > 0) {
+ /* Not standard, then it's a go / jump to chain */
+ if (cs->fw.ip.flags & IPT_F_GOTO)
+ add_jumpto(r, cs->jumpto, NFT_GOTO);
+ else
+ add_jumpto(r, cs->jumpto, NFT_JUMP);
+ }
+
+ /* NLM_F_CREATE autoloads the built-in table if it does not exists */
+ nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, AF_INET,
+ NLM_F_ACK|NLM_F_CREATE|flags, h->seq);
+ nft_rule_nlmsg_build_payload(nlh, r);
+
+ nft_rule_print_debug(r, nlh);
+
+ nft_rule_free(r);
+
+ ret = mnl_talk(h, nlh, NULL, NULL);
+ if (ret < 0)
+ perror("mnl_talk:nft_rule_add");
+
+err:
+ /* the core expects 1 for success and 0 for error */
+ return ret == 0 ? 1 : 0;
+}
+
+static void nft_match_save(struct nft_rule_expr *expr)
+{
+ const char *name;
+ const struct xtables_match *match;
+ struct xt_entry_match *emu;
+ const void *mtinfo;
+ size_t len;
+
+ name = nft_rule_expr_get_str(expr, NFT_EXPR_MT_NAME);
+
+ match = xtables_find_match(name, XTF_TRY_LOAD, NULL);
+ if (match == NULL)
+ return;
+
+ mtinfo = nft_rule_expr_get(expr, NFT_EXPR_MT_INFO, &len);
+ if (mtinfo == NULL)
+ return;
+
+ emu = calloc(1, sizeof(struct xt_entry_match) + len);
+ if (emu == NULL)
+ return;
+
+ memcpy(&emu->data, mtinfo, len);
+
+ if (match->alias)
+ printf("-m %s", match->alias(emu));
+ else
+ printf("-m %s", match->name);
+
+ /* FIXME missing parameter */
+ match->save(NULL, emu);
+
+ printf(" ");
+
+ free(emu);
+}
+
+static void nft_target_save(struct nft_rule_expr *expr)
+{
+ const char *name;
+ const struct xtables_target *target;
+ struct xt_entry_target *emu;
+ const void *tginfo;
+ size_t len;
+
+ name = nft_rule_expr_get_str(expr, NFT_EXPR_TG_NAME);
+
+ /* Standard target not supported, we use native immediate expression */
+ if (strcmp(name, "") == 0) {
+ printf("ERROR: standard target seen, should not happen\n");
+ return;
+ }
+
+ target = xtables_find_target(name, XTF_TRY_LOAD);
+ if (target == NULL)
+ return;
+
+ tginfo = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, &len);
+ if (tginfo == NULL)
+ return;
+
+ emu = calloc(1, sizeof(struct xt_entry_match) + len);
+ if (emu == NULL)
+ return;
+
+ memcpy(emu->data, tginfo, len);
+
+ if (target->alias)
+ printf("-j %s", target->alias(emu));
+ else
+ printf("-j %s", target->name);
+
+ /* FIXME missing parameter */
+ target->save(NULL, emu);
+
+ free(emu);
+}
+
+static void nft_immediate_save(struct nft_rule_expr *expr)
+{
+ uint32_t verdict;
+
+ verdict = nft_rule_expr_get_u32(expr, NFT_EXPR_IMM_VERDICT);
+
+ switch(verdict) {
+ case NF_ACCEPT:
+ printf("-j ACCEPT");
+ break;
+ case NF_DROP:
+ printf("-j DROP");
+ break;
+ case NFT_RETURN:
+ printf("-j RETURN");
+ break;
+ case NFT_GOTO:
+ printf("-g %s",
+ nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN));
+ break;
+ case NFT_JUMP:
+ printf("-j %s",
+ nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN));
+ break;
+ }
+}
+
+static void
+nft_print_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter)
+{
+ uint8_t key = nft_rule_expr_get_u8(e, NFT_EXPR_META_KEY);
+ uint32_t value;
+ const char *name;
+ char ifname[IFNAMSIZ];
+ const char *ifname_ptr;
+ size_t len;
+
+ e = nft_rule_expr_iter_next(iter);
+ if (e == NULL)
+ return;
+
+ name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME);
+ /* meta should be followed by cmp */
+ if (strcmp(name, "cmp") != 0) {
+ DEBUGP("skipping no cmp after meta\n");
+ return;
+ }
+
+ switch(key) {
+ case NFT_META_IIF:
+ value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA);
+ if_indextoname(value, ifname);
+
+ switch(nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP)) {
+ case NFT_CMP_EQ:
+ printf("-i %s ", ifname);
+ break;
+ case NFT_CMP_NEQ:
+ printf("! -i %s ", ifname);
+ break;
+ }
+ break;
+ case NFT_META_OIF:
+ value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA);
+ if_indextoname(value, ifname);
+
+ switch(nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP)) {
+ case NFT_CMP_EQ:
+ printf("-o %s ", ifname);
+ break;
+ case NFT_CMP_NEQ:
+ printf("! -o %s ", ifname);
+ break;
+ }
+ break;
+ case NFT_META_IIFNAME:
+ ifname_ptr = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len);
+ memcpy(ifname, ifname_ptr, len);
+ ifname[len] = '\0';
+
+ /* if this is zero, then assume this is a interface mask */
+ if (if_nametoindex(ifname) == 0) {
+ ifname[len] = '+';
+ ifname[len+1] = '\0';
+ }
+
+ switch(nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP)) {
+ case NFT_CMP_EQ:
+ printf("-i %s ", ifname);
+ break;
+ case NFT_CMP_NEQ:
+ printf("! -i %s ", ifname);
+ break;
+ }
+ break;
+ case NFT_META_OIFNAME:
+ ifname_ptr = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len);
+ memcpy(ifname, ifname_ptr, len);
+ ifname[len] = '\0';
+
+ /* if this is zero, then assume this is a interface mask */
+ if (if_nametoindex(ifname) == 0) {
+ ifname[len] = '+';
+ ifname[len+1] = '\0';
+ }
+
+ switch(nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP)) {
+ case NFT_CMP_EQ:
+ printf("-o %s ", ifname);
+ break;
+ case NFT_CMP_NEQ:
+ printf("! -o %s ", ifname);
+ break;
+ }
+ break;
+ default:
+ DEBUGP("unknown meta key %d\n", key);
+ break;
+ }
+}
+
+static void
+get_cmp_data(struct nft_rule_expr_iter *iter, void *data, size_t dlen, bool *inv)
+{
+ struct nft_rule_expr *e;
+ const char *name;
+ size_t len;
+ uint8_t op;
+
+ e = nft_rule_expr_iter_next(iter);
+ if (e == NULL)
+ return;
+
+ name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME);
+ if (strcmp(name, "cmp") != 0) {
+ DEBUGP("skipping no cmp after meta\n");
+ return;
+ }
+
+ memcpy(data, nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len), dlen);
+ op = nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP);
+ if (op == NFT_CMP_NEQ)
+ *inv = true;
+ else
+ *inv = false;
+}
+
+static void print_proto(uint16_t proto, int invert)
+{
+ const struct protoent *pent = getprotobynumber(proto);
+
+ if (invert)
+ printf("! ");
+
+ if (pent) {
+ printf("-p %s ", pent->p_name);
+ return;
+ }
+
+ printf("-p %u ", proto);
+}
+
+static const char *mask_to_str(uint32_t mask)
+{
+ static char mask_str[sizeof("255.255.255.255")];
+ uint32_t bits, hmask = ntohl(mask);
+ struct in_addr mask_addr = {
+ .s_addr = mask,
+ };
+ int i;
+
+ if (mask == 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
+ sprintf(mask_str, "%s", inet_ntoa(mask_addr));
+
+ return mask_str;
+}
+
+static void
+nft_print_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter)
+{
+ uint32_t offset;
+ bool inv;
+
+ offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET);
+
+ switch(offset) {
+ struct in_addr addr;
+ uint8_t proto;
+
+ case offsetof(struct iphdr, saddr):
+ get_cmp_data(iter, &addr, sizeof(addr), &inv);
+ if (inv)
+ printf("! -s %s/%s ", inet_ntoa(addr),
+ mask_to_str(0xffffffff));
+ else
+ printf("-s %s/%s ", inet_ntoa(addr),
+ mask_to_str(0xffffffff));
+ break;
+ case offsetof(struct iphdr, daddr):
+ get_cmp_data(iter, &addr, sizeof(addr), &inv);
+ if (inv)
+ printf("! -d %s/%s ", inet_ntoa(addr),
+ mask_to_str(0xffffffff));
+ else
+ printf("-d %s/%s ", inet_ntoa(addr),
+ mask_to_str(0xffffffff));
+ break;
+ case offsetof(struct iphdr, protocol):
+ get_cmp_data(iter, &proto, sizeof(proto), &inv);
+ print_proto(proto, inv);
+ break;
+ default:
+ DEBUGP("unknown payload offset %d\n", offset);
+ break;
+ }
+}
+
+static void
+nft_print_counters(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
+ bool counters)
+{
+ if (counters) {
+ printf("-c %lu %lu ",
+ nft_rule_expr_get_u64(e, NFT_EXPR_CTR_PACKETS),
+ nft_rule_expr_get_u64(e, NFT_EXPR_CTR_BYTES));
+ }
+}
+
+static void nft_rule_print_save(struct nft_rule *r, bool counters)
+{
+ struct nft_rule_expr_iter *iter;
+ struct nft_rule_expr *expr;
+
+ /* print chain name */
+ printf("-A %s ", nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN));
+
+ iter = nft_rule_expr_iter_create(r);
+ if (iter == NULL)
+ return;
+
+ expr = nft_rule_expr_iter_next(iter);
+ while (expr != NULL) {
+ const char *name =
+ nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME);
+
+ if (strcmp(name, "counter") == 0) {
+ nft_print_counters(expr, iter, counters);
+ } else if (strcmp(name, "payload") == 0) {
+ nft_print_payload(expr, iter);
+ } else if (strcmp(name, "meta") == 0) {
+ nft_print_meta(expr, iter);
+ } else if (strcmp(name, "match") == 0) {
+ nft_match_save(expr);
+ } else if (strcmp(name, "target") == 0) {
+ nft_target_save(expr);
+ } else if (strcmp(name, "immediate") == 0) {
+ nft_immediate_save(expr);
+ }
+
+ expr = nft_rule_expr_iter_next(iter);
+ }
+
+ printf("\n");
+}
+
+static int nft_chain_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nft_chain *c;
+ struct nft_chain_list *list = data;
+
+ c = nft_chain_alloc();
+ if (c == NULL) {
+ perror("OOM");
+ goto err;
+ }
+
+ if (nft_chain_nlmsg_parse(nlh, c) < 0) {
+ perror("nft_rule_nlmsg_parse");
+ goto out;
+ }
+
+ nft_chain_list_add(c, list);
+
+ return MNL_CB_OK;
+out:
+ nft_chain_free(c);
+err:
+ return MNL_CB_OK;
+}
+
+static struct nft_chain_list *nft_chain_list_get(struct nft_handle *h)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ int ret;
+ struct nft_chain_list *list;
+
+ list = nft_chain_list_alloc();
+ if (list == NULL) {
+ DEBUGP("cannot allocate rule list\n");
+ return 0;
+ }
+
+ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, AF_INET,
+ NLM_F_DUMP, h->seq);
+
+ ret = mnl_talk(h, nlh, nft_chain_list_cb, list);
+ if (ret < 0)
+ perror("mnl_talk:nft_chain_list_get");
+
+ return list;
+}
+
+struct nft_chain_list *nft_chain_dump(struct nft_handle *h)
+{
+ return nft_chain_list_get(h);
+}
+
+static const char *policy_name[NF_ACCEPT+1] = {
+ [NF_DROP] = "DROP",
+ [NF_ACCEPT] = "ACCEPT",
+};
+
+static void nft_chain_print_save(struct nft_chain *c, bool basechain)
+{
+ const char *chain = nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME);
+ uint64_t pkts = nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_PACKETS);
+ uint64_t bytes = nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_BYTES);
+
+ /* print chain name */
+ if (basechain) {
+ uint32_t pol = NF_ACCEPT;
+
+ /* no default chain policy? don't crash, display accept */
+ if (nft_chain_attr_get(c, NFT_CHAIN_ATTR_POLICY))
+ pol = nft_chain_attr_get_u32(c, NFT_CHAIN_ATTR_POLICY);
+
+ printf(":%s %s [%lu:%lu]\n", chain, policy_name[pol],
+ pkts, bytes);
+ } else
+ printf(":%s - [%lu:%lu]\n", chain, pkts, bytes);
+}
+
+int nft_chain_save(struct nft_handle *h, struct nft_chain_list *list,
+ const char *table)
+{
+ struct nft_chain_list_iter *iter;
+ struct nft_chain *c;
+
+ iter = nft_chain_list_iter_create(list);
+ if (iter == NULL) {
+ DEBUGP("cannot allocate rule list iterator\n");
+ return 0;
+ }
+
+ c = nft_chain_list_iter_next(iter);
+ while (c != NULL) {
+ const char *chain_table =
+ nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE);
+ bool basechain = false;
+
+ if (strcmp(table, chain_table) != 0)
+ goto next;
+
+ if (nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM))
+ basechain = true;
+
+ nft_chain_print_save(c, basechain);
+next:
+ c = nft_chain_list_iter_next(iter);
+ }
+
+ nft_chain_list_free(list);
+
+ return 1;
+}
+
+static int nft_rule_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nft_rule *r;
+ struct nft_rule_list *list = data;
+
+ r = nft_rule_alloc();
+ if (r == NULL) {
+ perror("OOM");
+ goto err;
+ }
+
+ if (nft_rule_nlmsg_parse(nlh, r) < 0) {
+ perror("nft_rule_nlmsg_parse");
+ goto out;
+ }
+
+ nft_rule_list_add(r, list);
+
+ return MNL_CB_OK;
+out:
+ nft_rule_free(r);
+err:
+ return MNL_CB_OK;
+}
+
+static struct nft_rule_list *nft_rule_list_get(struct nft_handle *h)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nft_rule_list *list;
+ int ret;
+
+ list = nft_rule_list_alloc();
+ if (list == NULL) {
+ DEBUGP("cannot allocate rule list\n");
+ return 0;
+ }
+
+ nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, AF_INET,
+ NLM_F_DUMP, h->seq);
+
+ ret = mnl_talk(h, nlh, nft_rule_list_cb, list);
+ if (ret < 0) {
+ perror("mnl_talk:nft_rule_save");
+ nft_rule_list_free(list);
+ return NULL;
+ }
+
+ return list;
+}
+
+int nft_rule_save(struct nft_handle *h, const char *table, bool counters)
+{
+ struct nft_rule_list *list;
+ struct nft_rule_list_iter *iter;
+ struct nft_rule *r;
+
+ list = nft_rule_list_get(h);
+ if (list == NULL) {
+ DEBUGP("cannot retrieve rule list from kernel\n");
+ return 0;
+ }
+
+ iter = nft_rule_list_iter_create(list);
+ if (iter == NULL) {
+ DEBUGP("cannot allocate rule list iterator\n");
+ return 0;
+ }
+
+ r = nft_rule_list_iter_next(iter);
+ while (r != NULL) {
+ const char *rule_table =
+ nft_rule_attr_get_str(r, NFT_RULE_ATTR_TABLE);
+
+ if (strcmp(table, rule_table) != 0)
+ goto next;
+
+ nft_rule_print_save(r, counters);
+
+next:
+ r = nft_rule_list_iter_next(iter);
+ }
+
+ nft_rule_list_free(list);
+
+ /* the core expects 1 for success and 0 for error */
+ return 1;
+}
+
+static void
+__nft_rule_flush(struct nft_handle *h, const char *table, const char *chain)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nft_rule *r;
+
+ r = nft_rule_alloc();
+ if (r == NULL)
+ return;
+
+ nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table);
+ nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain);
+
+ /* Delete all rules in this table + chain */
+ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, AF_INET,
+ NLM_F_ACK, h->seq);
+ nft_rule_nlmsg_build_payload(nlh, r);
+ nft_rule_free(r);
+
+ if (mnl_talk(h, nlh, NULL, NULL) < 0) {
+ if (errno != EEXIST)
+ perror("mnl_talk:__nft_rule_flush");
+ }
+}
+
+int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table)
+{
+ int ret;
+ struct nft_chain_list *list;
+ struct nft_chain_list_iter *iter;
+ struct nft_chain *c;
+
+ nft_fn = nft_rule_flush;
+
+ list = nft_chain_list_get(h);
+ if (list == NULL) {
+ ret = 0;
+ goto err;
+ }
+
+ iter = nft_chain_list_iter_create(list);
+ if (iter == NULL) {
+ DEBUGP("cannot allocate rule list iterator\n");
+ return 0;
+ }
+
+ c = nft_chain_list_iter_next(iter);
+ while (c != NULL) {
+ const char *table_name =
+ nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE);
+ const char *chain_name =
+ nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME);
+
+ if (strcmp(table, table_name) != 0)
+ goto next;
+
+ if (chain != NULL && strcmp(chain, chain_name) != 0)
+ goto next;
+
+ __nft_rule_flush(h, table_name, chain_name);
+
+next:
+ c = nft_chain_list_iter_next(iter);
+ }
+
+err:
+ nft_chain_list_free(list);
+
+ /* the core expects 1 for success and 0 for error */
+ return ret == 0 ? 1 : 0;
+}
+
+int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nft_chain *c;
+ int ret;
+
+ c = nft_chain_alloc();
+ if (c == NULL) {
+ DEBUGP("cannot allocate chain\n");
+ return -1;
+ }
+
+ nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table);
+ nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain);
+
+ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET,
+ NLM_F_ACK|NLM_F_EXCL, h->seq);
+ nft_chain_nlmsg_build_payload(nlh, c);
+ nft_chain_free(c);
+
+ ret = mnl_talk(h, nlh, NULL, NULL);
+ if (ret < 0) {
+ if (errno != EEXIST)
+ perror("mnl_talk:nft_chain_add");
+ }
+
+ /* the core expects 1 for success and 0 for error */
+ return ret == 0 ? 1 : 0;
+}
+
+static int __nft_chain_del(struct nft_handle *h, struct nft_chain *c)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ int ret;
+
+ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELCHAIN, AF_INET,
+ NLM_F_ACK, h->seq);
+ nft_chain_nlmsg_build_payload(nlh, c);
+
+ ret = mnl_talk(h, nlh, NULL, NULL);
+ if (ret < 0) {
+ if (errno != EEXIST && errno != ENOENT)
+ perror("mnl_talk:__nft_chain_del");
+ }
+
+ return ret;
+}
+
+static bool nft_chain_builtin(struct nft_chain *c)
+{
+ /* Check if this chain has hook number, in that case is built-in.
+ * Should we better export the flags to user-space via nf_tables?
+ */
+ return nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM) != NULL;
+}
+
+int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table)
+{
+ struct nft_chain_list *list;
+ struct nft_chain_list_iter *iter;
+ struct nft_chain *c;
+ int ret = 0;
+ int deleted_ctr = 0;
+
+ list = nft_chain_list_get(h);
+ if (list == NULL)
+ goto err;
+
+ iter = nft_chain_list_iter_create(list);
+ if (iter == NULL) {
+ DEBUGP("cannot allocate rule list iterator\n");
+ return 0;
+ }
+
+ c = nft_chain_list_iter_next(iter);
+ while (c != NULL) {
+ const char *table_name =
+ nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE);
+ const char *chain_name =
+ nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME);
+
+ /* don't delete built-in chain */
+ if (nft_chain_builtin(c))
+ goto next;
+
+ if (strcmp(table, table_name) != 0)
+ goto next;
+
+ if (chain != NULL && strcmp(chain, chain_name) != 0)
+ goto next;
+
+ ret = __nft_chain_del(h, c);
+ if (ret < 0)
+ break;
+
+ deleted_ctr++;
+next:
+ c = nft_chain_list_iter_next(iter);
+ }
+
+err:
+ nft_chain_list_free(list);
+
+ /* chain not found */
+ if (ret < 0 && deleted_ctr == 0)
+ errno = ENOENT;
+
+ /* the core expects 1 for success and 0 for error */
+ return ret == 0 ? 1 : 0;
+}
+
+int nft_chain_user_rename(struct nft_handle *h,const char *chain,
+ const char *table, const char *newname)
+{
+ int ret;
+
+ /* XXX need new operation in nf_tables to support this */
+ ret = nft_chain_user_del(h, chain, table);
+ if (ret < 0)
+ return ret;
+
+ return nft_chain_user_add(h, newname, table);
+}
+
+static int nft_table_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nft_table *t;
+ struct nft_table_list *list = data;
+
+ t = nft_table_alloc();
+ if (t == NULL) {
+ perror("OOM");
+ goto err;
+ }
+
+ if (nft_table_nlmsg_parse(nlh, t) < 0) {
+ perror("nft_rule_nlmsg_parse");
+ goto out;
+ }
+
+ nft_table_list_add(t, list);
+
+ return MNL_CB_OK;
+out:
+ nft_table_free(t);
+err:
+ return MNL_CB_OK;
+}
+
+static struct nft_table_list *nft_table_list_get(struct nft_handle *h)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ int ret;
+ struct nft_table_list *list;
+
+ list = nft_table_list_alloc();
+ if (list == NULL) {
+ DEBUGP("cannot allocate table list\n");
+ return 0;
+ }
+
+ nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, AF_INET,
+ NLM_F_DUMP, h->seq);
+
+ ret = mnl_talk(h, nlh, nft_table_list_cb, list);
+ if (ret < 0)
+ perror("mnl_talk:nft_table_list_get");
+
+ return list;
+}
+
+bool nft_table_find(struct nft_handle *h, const char *tablename)
+{
+ struct nft_table_list *list;
+ struct nft_table_list_iter *iter;
+ struct nft_table *t;
+ bool ret = false;
+
+ list = nft_table_list_get(h);
+ if (list == NULL)
+ goto err;
+
+ iter = nft_table_list_iter_create(list);
+ if (iter == NULL) {
+ DEBUGP("cannot allocate rule list iterator\n");
+ goto err;
+ }
+
+ t = nft_table_list_iter_next(iter);
+ while (t != NULL) {
+ const char *this_tablename =
+ nft_table_attr_get(t, NFT_TABLE_ATTR_NAME);
+
+ if (strcmp(tablename, this_tablename) == 0)
+ return true;
+
+ t = nft_table_list_iter_next(iter);
+ }
+
+ nft_table_list_free(list);
+
+err:
+ return ret;
+}
+
+int nft_for_each_table(struct nft_handle *h,
+ int (*func)(struct nft_handle *h, const char *tablename, bool counters),
+ bool counters)
+{
+ int ret = 1;
+ struct nft_table_list *list;
+ struct nft_table_list_iter *iter;
+ struct nft_table *t;
+
+ list = nft_table_list_get(h);
+ if (list == NULL) {
+ ret = 0;
+ goto err;
+ }
+
+ iter = nft_table_list_iter_create(list);
+ if (iter == NULL) {
+ DEBUGP("cannot allocate rule list iterator\n");
+ return 0;
+ }
+
+ t = nft_table_list_iter_next(iter);
+ while (t != NULL) {
+ const char *tablename =
+ nft_table_attr_get(t, NFT_TABLE_ATTR_NAME);
+
+ func(h, tablename, counters);
+
+ t = nft_table_list_iter_next(iter);
+ }
+
+ nft_table_list_free(list);
+
+err:
+ /* the core expects 1 for success and 0 for error */
+ return ret == 0 ? 1 : 0;
+}
+
+static inline int
+match_different(const struct xt_entry_match *a,
+ const unsigned char *a_elems,
+ const unsigned char *b_elems,
+ unsigned char **maskptr)
+{
+ const struct xt_entry_match *b;
+ unsigned int i;
+
+ /* Offset of b is the same as a. */
+ b = (void *)b_elems + ((unsigned char *)a - a_elems);
+
+ if (a->u.match_size != b->u.match_size)
+ return 1;
+
+ if (strcmp(a->u.user.name, b->u.user.name) != 0)
+ return 1;
+
+ *maskptr += XT_ALIGN(sizeof(*a));
+
+ for (i = 0; i < a->u.match_size - XT_ALIGN(sizeof(*a)); i++)
+ if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
+ return 1;
+ *maskptr += i;
+ return 0;
+}
+
+static bool
+is_same(const struct iptables_command_state *a, const struct iptables_command_state *b)
+{
+ unsigned int i;
+
+ /* Always compare head structures: ignore mask here. */
+ if (a->fw.ip.src.s_addr != b->fw.ip.src.s_addr
+ || a->fw.ip.dst.s_addr != b->fw.ip.dst.s_addr
+ || a->fw.ip.smsk.s_addr != b->fw.ip.smsk.s_addr
+ || a->fw.ip.dmsk.s_addr != b->fw.ip.dmsk.s_addr
+ || a->fw.ip.proto != b->fw.ip.proto
+ || a->fw.ip.flags != b->fw.ip.flags
+ || a->fw.ip.invflags != b->fw.ip.invflags) {
+ DEBUGP("different src/dst/proto/flags/invflags\n");
+ return false;
+ }
+
+ for (i = 0; i < IFNAMSIZ; i++) {
+ if (a->fw.ip.iniface_mask[i] != b->fw.ip.iniface_mask[i]) {
+ DEBUGP("different iniface mask %x, %x (%d)\n",
+ a->fw.ip.iniface_mask[i] & 0xff, b->fw.ip.iniface_mask[i] & 0xff, i);
+ return false;
+ }
+ if ((a->fw.ip.iniface[i] & a->fw.ip.iniface_mask[i])
+ != (b->fw.ip.iniface[i] & b->fw.ip.iniface_mask[i])) {
+ DEBUGP("different iniface\n");
+ return false;
+ }
+ if (a->fw.ip.outiface_mask[i] != b->fw.ip.outiface_mask[i]) {
+ DEBUGP("different outiface mask\n");
+ return false;
+ }
+ if ((a->fw.ip.outiface[i] & a->fw.ip.outiface_mask[i])
+ != (b->fw.ip.outiface[i] & b->fw.ip.outiface_mask[i])) {
+ DEBUGP("different outiface\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void
+nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
+ struct iptables_command_state *cs)
+{
+ uint8_t key = nft_rule_expr_get_u8(e, NFT_EXPR_META_KEY);
+ uint32_t value;
+ const char *name;
+ const void *ifname;
+ size_t len;
+
+ e = nft_rule_expr_iter_next(iter);
+ if (e == NULL)
+ return;
+
+ name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME);
+ if (strcmp(name, "cmp") != 0) {
+ DEBUGP("skipping no cmp after meta\n");
+ return;
+ }
+
+ switch(key) {
+ case NFT_META_IIF:
+ value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA);
+ if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ cs->fw.ip.invflags |= IPT_INV_VIA_IN;
+
+ if_indextoname(value, cs->fw.ip.iniface);
+
+ memset(cs->fw.ip.iniface_mask, 0xff,
+ strlen(cs->fw.ip.iniface)+1);
+ break;
+ case NFT_META_OIF:
+ value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA);
+ if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ cs->fw.ip.invflags |= IPT_INV_VIA_OUT;
+
+ if_indextoname(value, cs->fw.ip.outiface);
+
+ memset(cs->fw.ip.outiface_mask, 0xff,
+ strlen(cs->fw.ip.outiface)+1);
+ break;
+ case NFT_META_IIFNAME:
+ ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len);
+ if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ cs->fw.ip.invflags |= IPT_INV_VIA_IN;
+
+ memcpy(cs->fw.ip.iniface, ifname, len);
+ cs->fw.ip.iniface[len] = '\0';
+
+ /* If zero, then this is an interface mask */
+ if (if_nametoindex(cs->fw.ip.iniface) == 0) {
+ cs->fw.ip.iniface[len] = '+';
+ cs->fw.ip.iniface[len+1] = '\0';
+ }
+
+ memset(cs->fw.ip.iniface_mask, 0xff, len);
+ break;
+ case NFT_META_OIFNAME:
+ ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len);
+ if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ cs->fw.ip.invflags |= IPT_INV_VIA_OUT;
+
+ memcpy(cs->fw.ip.outiface, ifname, len);
+ cs->fw.ip.outiface[len] = '\0';
+
+ /* If zero, then this is an interface mask */
+ if (if_nametoindex(cs->fw.ip.outiface) == 0) {
+ cs->fw.ip.outiface[len] = '+';
+ cs->fw.ip.outiface[len+1] = '\0';
+ }
+
+ memset(cs->fw.ip.outiface_mask, 0xff, len);
+ break;
+ default:
+ DEBUGP("unknown meta key %d\n", key);
+ break;
+ }
+}
+
+static void
+nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
+ struct iptables_command_state *cs)
+{
+ uint32_t offset;
+ bool inv;
+
+ offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET);
+
+ switch(offset) {
+ struct in_addr addr;
+ uint8_t proto;
+
+ case offsetof(struct iphdr, saddr):
+ get_cmp_data(iter, &addr, sizeof(addr), &inv);
+ cs->fw.ip.src.s_addr = addr.s_addr;
+ cs->fw.ip.smsk.s_addr = 0xffffffff;
+ if (inv)
+ cs->fw.ip.invflags |= IPT_INV_SRCIP;
+ break;
+ case offsetof(struct iphdr, daddr):
+ get_cmp_data(iter, &addr, sizeof(addr), &inv);
+ cs->fw.ip.dst.s_addr = addr.s_addr;
+ cs->fw.ip.dmsk.s_addr = 0xffffffff;
+ if (inv)
+ cs->fw.ip.invflags |= IPT_INV_DSTIP;
+ break;
+ case offsetof(struct iphdr, protocol):
+ get_cmp_data(iter, &proto, sizeof(proto), &inv);
+ cs->fw.ip.proto = proto;
+ if (inv)
+ cs->fw.ip.invflags |= IPT_INV_PROTO;
+ break;
+ default:
+ DEBUGP("unknown payload offset %d\n", offset);
+ break;
+ }
+}
+
+static void
+nft_parse_counter(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
+ struct xt_counters *counters)
+{
+ counters->pcnt = nft_rule_expr_get_u64(e, NFT_EXPR_CTR_PACKETS);
+ counters->bcnt = nft_rule_expr_get_u64(e, NFT_EXPR_CTR_BYTES);
+}
+
+static void
+nft_parse_immediate(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
+ struct iptables_command_state *cs)
+{
+ int verdict = nft_rule_expr_get_u32(e, NFT_EXPR_IMM_VERDICT);
+ const char *chain = nft_rule_expr_get_str(e, NFT_EXPR_IMM_CHAIN);
+
+ /* Standard target? */
+ switch(verdict) {
+ case NF_ACCEPT:
+ cs->jumpto = "ACCEPT";
+ return;
+ case NF_DROP:
+ cs->jumpto = "DROP";
+ return;
+ case NFT_RETURN:
+ cs->jumpto = "RETURN";
+ return;
+ case NFT_GOTO:
+ cs->fw.ip.flags |= IPT_F_GOTO;
+ case NFT_JUMP:
+ cs->jumpto = chain;
+ return;
+ }
+}
+
+static void
+nft_rule_to_iptables_command_state(struct nft_rule *r,
+ struct iptables_command_state *cs)
+{
+ struct nft_rule_expr_iter *iter;
+ struct nft_rule_expr *expr;
+
+ iter = nft_rule_expr_iter_create(r);
+ if (iter == NULL)
+ return;
+
+ expr = nft_rule_expr_iter_next(iter);
+ while (expr != NULL) {
+ const char *name =
+ nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME);
+
+ if (strcmp(name, "counter") == 0) {
+ nft_parse_counter(expr, iter, &cs->counters);
+ } else if (strcmp(name, "payload") == 0) {
+ nft_parse_payload(expr, iter, cs);
+ } else if (strcmp(name, "meta") == 0) {
+ nft_parse_meta(expr, iter, cs);
+ } else if (strcmp(name, "immediate") == 0) {
+ nft_parse_immediate(expr, iter, cs);
+ }
+
+ expr = nft_rule_expr_iter_next(iter);
+ }
+
+ nft_rule_expr_iter_destroy(iter);
+}
+
+static int matches_howmany(struct xtables_rule_match *matches)
+{
+ struct xtables_rule_match *matchp;
+ int matches_ctr = 0;
+
+ for (matchp = matches; matchp; matchp = matchp->next)
+ matches_ctr++;
+
+ return matches_ctr;
+}
+
+static bool
+__find_match(struct nft_rule_expr *expr, struct xtables_rule_match *matches)
+{
+ const char *matchname = nft_rule_expr_get_str(expr, NFT_EXPR_MT_NAME);
+ /* Netlink aligns this match info, don't trust this length variable */
+ const char *data = nft_rule_expr_get_str(expr, NFT_EXPR_MT_INFO);
+ struct xtables_rule_match *matchp;
+ bool found = false;
+
+ for (matchp = matches; matchp; matchp = matchp->next) {
+ struct xt_entry_match *m = matchp->match->m;
+
+ if (strcmp(m->u.user.name, matchname) != 0) {
+ DEBUGP("mismatching match name\n");
+ continue;
+ }
+
+ if (memcmp(data, m->data, m->u.user.match_size - sizeof(*m)) != 0) {
+ DEBUGP("mismatch match data\n");
+ continue;
+ }
+ found = true;
+ break;
+ }
+
+ return found;
+}
+
+static bool find_matches(struct xtables_rule_match *matches, struct nft_rule *r)
+{
+ struct nft_rule_expr_iter *iter;
+ struct nft_rule_expr *expr;
+ int kernel_matches = 0;
+
+ iter = nft_rule_expr_iter_create(r);
+ if (iter == NULL) {
+ DEBUGP("cannot allocate rule list iterator\n");
+ return false;
+ }
+
+ expr = nft_rule_expr_iter_next(iter);
+ while (expr != NULL) {
+ const char *name =
+ nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME);
+
+ if (strcmp(name, "match") == 0) {
+ if (!__find_match(expr, matches))
+ return false;
+
+ kernel_matches++;
+ }
+ expr = nft_rule_expr_iter_next(iter);
+ }
+ nft_rule_expr_iter_destroy(iter);
+
+ /* same number of matches? */
+ if (matches_howmany(matches) != kernel_matches)
+ return false;
+
+ return true;
+}
+
+static bool __find_target(struct nft_rule_expr *expr, struct xt_entry_target *t)
+{
+ size_t len;
+ const char *tgname = nft_rule_expr_get_str(expr, NFT_EXPR_TG_NAME);
+ /* Netlink aligns this target info, don't trust this length variable */
+ const char *data = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, &len);
+
+ if (strcmp(t->u.user.name, tgname) != 0) {
+ DEBUGP("mismatching target name\n");
+ return false;
+ }
+
+ if (memcmp(data, t->data, t->u.user.target_size - sizeof(*t)) != 0)
+ return false;
+
+ return true;
+}
+
+static int targets_howmany(struct xtables_target *target)
+{
+ return target != NULL ? 1 : 0;
+}
+
+static bool
+find_target(struct xtables_target *target, struct nft_rule *r)
+{
+ struct nft_rule_expr_iter *iter;
+ struct nft_rule_expr *expr;
+ int kernel_targets = 0;
+
+ /* Special case: we use native immediate expressions to emulated
+ * standard targets. Also, we don't want to crash with no targets.
+ */
+ if (target == NULL || strcmp(target->name, "standard") == 0)
+ return true;
+
+ iter = nft_rule_expr_iter_create(r);
+ if (iter == NULL) {
+ DEBUGP("cannot allocate rule list iterator\n");
+ return false;
+ }
+
+ expr = nft_rule_expr_iter_next(iter);
+ while (expr != NULL) {
+ const char *name =
+ nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME);
+
+ if (strcmp(name, "target") == 0) {
+ /* we may support several targets in the future */
+ if (!__find_target(expr, target->t))
+ return false;
+
+ kernel_targets++;
+ }
+ expr = nft_rule_expr_iter_next(iter);
+ }
+ nft_rule_expr_iter_destroy(iter);
+
+ /* same number of targets? */
+ if (targets_howmany(target) != kernel_targets) {
+ DEBUGP("kernel targets is %d but we passed %d\n",
+ kernel_targets, targets_howmany(target));
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+find_immediate(struct nft_rule *r, const char *jumpto)
+{
+ struct nft_rule_expr_iter *iter;
+ struct nft_rule_expr *expr;
+
+ iter = nft_rule_expr_iter_create(r);
+ if (iter == NULL) {
+ DEBUGP("cannot allocate rule list iterator\n");
+ return false;
+ }
+
+ expr = nft_rule_expr_iter_next(iter);
+ while (expr != NULL) {
+ const char *name =
+ nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME);
+
+ if (strcmp(name, "immediate") == 0) {
+ int verdict = nft_rule_expr_get_u32(expr, NFT_EXPR_IMM_VERDICT);
+ const char *verdict_name = NULL;
+
+ /* No target specified but immediate shows up, this
+ * is not the rule we are looking for.
+ */
+ if (strlen(jumpto) == 0)
+ return false;
+
+ switch(verdict) {
+ case NF_ACCEPT:
+ verdict_name = "ACCEPT";
+ break;
+ case NF_DROP:
+ verdict_name = "DROP";
+ break;
+ case NFT_RETURN:
+ verdict_name = "RETURN";
+ break;
+ }
+
+ /* Standard target? */
+ if (verdict_name && strcmp(jumpto, verdict_name) != 0)
+ return false;
+ }
+ expr = nft_rule_expr_iter_next(iter);
+ }
+ nft_rule_expr_iter_destroy(iter);
+
+ return true;
+}
+
+static void
+__nft_rule_del(struct nft_handle *h, struct nft_rule *r)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ int ret;
+
+ nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, AF_INET,
+ NLM_F_ACK, h->seq);
+ nft_rule_nlmsg_build_payload(nlh, r);
+
+ nft_rule_print_debug(r, nlh);
+
+ ret = mnl_talk(h, nlh, NULL, NULL);
+ if (ret < 0)
+ perror("mnl_talk:nft_rule_del");
+}
+
+static int
+__nft_rule_check(struct nft_handle *h, const char *chain, const char *table,
+ struct iptables_command_state *cs,
+ bool delete, int rulenum, bool verbose)
+{
+ struct nft_rule_list *list;
+ struct nft_rule_list_iter *iter;
+ struct nft_rule *r;
+ int ret = 0;
+ int rule_ctr = 0;
+ bool found = false;
+
+ list = nft_rule_list_get(h);
+ if (list == NULL) {
+ DEBUGP("cannot retrieve rule list from kernel\n");
+ return 0;
+ }
+
+ iter = nft_rule_list_iter_create(list);
+ if (iter == NULL) {
+ DEBUGP("cannot allocate rule list iterator\n");
+ return 0;
+ }
+
+ r = nft_rule_list_iter_next(iter);
+ while (r != NULL) {
+ const char *rule_table =
+ nft_rule_attr_get_str(r, NFT_RULE_ATTR_TABLE);
+ const char *rule_chain =
+ nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN);
+ struct iptables_command_state this = {};
+
+ if (strcmp(table, rule_table) != 0 ||
+ strcmp(chain, rule_chain) != 0) {
+ DEBUGP("different chain / table\n");
+ goto next;
+ }
+
+ if (rulenum >= 0) {
+ /* Delete by rule number case */
+ if (rule_ctr != rulenum) {
+ rule_ctr++;
+ goto next;
+ }
+ } else {
+ /* Delete by matching rule case */
+ DEBUGP("comparing with... ");
+#ifdef DEBUG_DEL
+ nft_rule_print_save(r, 0);
+#endif
+
+ nft_rule_to_iptables_command_state(r, &this);
+
+ if (!is_same(cs, &this))
+ goto next;
+
+ if (!find_matches(cs->matches, r)) {
+ DEBUGP("matches not found\n");
+ goto next;
+ }
+
+ if (!find_target(cs->target, r)) {
+ DEBUGP("target not found\n");
+ goto next;
+ }
+
+ if (!find_immediate(r, cs->jumpto)) {
+ DEBUGP("immediate not found\n");
+ goto next;
+ }
+
+ found = true;
+ break;
+ }
+next:
+ r = nft_rule_list_iter_next(iter);
+ }
+
+ if (found) {
+ ret = 1;
+
+ if (delete) {
+ DEBUGP("deleting rule\n");
+ __nft_rule_del(h, r);
+ }
+ }
+
+ nft_rule_list_iter_destroy(iter);
+ nft_rule_list_free(list);
+
+ if (ret == 0)
+ errno = ENOENT;
+
+ return ret;
+}
+
+int nft_rule_check(struct nft_handle *h, const char *chain,
+ const char *table, struct iptables_command_state *e,
+ bool verbose)
+{
+ nft_fn = nft_rule_check;
+
+ return __nft_rule_check(h, chain, table, e, false, -1, verbose);
+}
+
+int nft_rule_delete(struct nft_handle *h, const char *chain,
+ const char *table, struct iptables_command_state *e,
+ bool verbose)
+{
+ nft_fn = nft_rule_delete;
+
+ return __nft_rule_check(h, chain, table, e, true, -1, verbose);
+}
+
+int nft_rule_delete_num(struct nft_handle *h, const char *chain,
+ const char *table, int rulenum,
+ bool verbose)
+{
+ nft_fn = nft_rule_delete_num;
+
+ return __nft_rule_check(h, chain, table, NULL, true, rulenum, verbose);
+}
+
+int nft_rule_replace(struct nft_handle *h, const char *chain,
+ const char *table, struct iptables_command_state *cs,
+ int rulenum, bool verbose)
+{
+ int ret;
+
+ nft_fn = nft_rule_replace;
+
+ ret = __nft_rule_check(h, chain, table, NULL, true, rulenum, verbose);
+ if (ret < 0)
+ return ret;
+
+ /* XXX needs to be inserted in position, this is appending */
+ return nft_rule_add(h, chain, table, cs, true, verbose);
+}
+
+/*
+ * iptables print output emulation
+ */
+
+#define FMT_NUMERIC 0x0001
+#define FMT_NOCOUNTS 0x0002
+#define FMT_KILOMEGAGIGA 0x0004
+#define FMT_OPTIONS 0x0008
+#define FMT_NOTABLE 0x0010
+#define FMT_NOTARGET 0x0020
+#define FMT_VIA 0x0040
+#define FMT_NONEWLINE 0x0080
+#define FMT_LINENUMBERS 0x0100
+
+#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
+ | FMT_NUMERIC | FMT_NOTABLE)
+#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
+
+static void
+print_num(uint64_t number, unsigned int format)
+{
+ if (format & FMT_KILOMEGAGIGA) {
+ if (number > 99999) {
+ number = (number + 500) / 1000;
+ if (number > 9999) {
+ number = (number + 500) / 1000;
+ if (number > 9999) {
+ number = (number + 500) / 1000;
+ if (number > 9999) {
+ number = (number + 500) / 1000;
+ printf(FMT("%4lluT ","%lluT "), (unsigned long long)number);
+ }
+ else printf(FMT("%4lluG ","%lluG "), (unsigned long long)number);
+ }
+ else printf(FMT("%4lluM ","%lluM "), (unsigned long long)number);
+ } else
+ printf(FMT("%4lluK ","%lluK "), (unsigned long long)number);
+ } else
+ printf(FMT("%5llu ","%llu "), (unsigned long long)number);
+ } else
+ printf(FMT("%8llu ","%llu "), (unsigned long long)number);
+}
+
+static void
+print_header(unsigned int format, const char *chain, const char *pol,
+ const struct xt_counters *counters, bool basechain, uint32_t refs)
+{
+ printf("Chain %s", chain);
+ if (basechain) {
+ printf(" (policy %s", pol);
+ if (!(format & FMT_NOCOUNTS)) {
+ fputc(' ', stdout);
+ print_num(counters->pcnt, (format|FMT_NOTABLE));
+ fputs("packets, ", stdout);
+ print_num(counters->bcnt, (format|FMT_NOTABLE));
+ fputs("bytes", stdout);
+ }
+ printf(")\n");
+ } else {
+ printf(" (%u 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");
+}
+
+static void
+print_match(struct nft_rule_expr *expr, int numeric)
+{
+ size_t len;
+ const char *match_name = nft_rule_expr_get_str(expr, NFT_EXPR_MT_NAME);
+ const void *match_info = nft_rule_expr_get(expr, NFT_EXPR_MT_INFO, &len);
+ const struct xtables_match *match =
+ xtables_find_match(match_name, XTF_TRY_LOAD, NULL);
+ struct xt_entry_match *m =
+ calloc(1, sizeof(struct xt_entry_match) + len);
+
+ /* emulate struct xt_entry_match since ->print needs it */
+ memcpy((void *)&m->data, match_info, len);
+
+ if (match) {
+ if (match->print)
+ /* FIXME missing first parameter */
+ match->print(NULL, m, numeric);
+ else
+ printf("%s ", match_name);
+ } else {
+ if (match_name[0])
+ printf("UNKNOWN match `%s' ", match_name);
+ }
+
+ free(m);
+}
+
+static void
+print_firewall(const struct iptables_command_state *cs, struct nft_rule *r,
+ unsigned int num, unsigned int format)
+{
+ const struct xtables_target *target = NULL;
+ const char *targname = NULL;
+ const void *targinfo = NULL;
+ uint8_t flags;
+ char buf[BUFSIZ];
+ struct nft_rule_expr_iter *iter;
+ struct nft_rule_expr *expr;
+ struct xt_entry_target *t;
+ size_t target_len = 0;
+
+ iter = nft_rule_expr_iter_create(r);
+ if (iter == NULL) {
+ DEBUGP("cannot allocate rule list iterator\n");
+ return;
+ }
+
+ expr = nft_rule_expr_iter_next(iter);
+ while (expr != NULL) {
+ const char *name =
+ nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME);
+
+ if (strcmp(name, "target") == 0) {
+ targname = nft_rule_expr_get_str(expr, NFT_EXPR_TG_NAME);
+ targinfo = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, &target_len);
+ break;
+ } else if (strcmp(name, "immediate") == 0) {
+ uint32_t verdict =
+ nft_rule_expr_get_u32(expr, NFT_EXPR_IMM_VERDICT);
+
+ switch(verdict) {
+ case NF_ACCEPT:
+ targname = "ACCEPT";
+ break;
+ case NF_DROP:
+ targname = "DROP";
+ break;
+ case NFT_RETURN:
+ targname = "RETURN";
+ break;
+ case NFT_GOTO:
+ targname = nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN);
+ break;
+ case NFT_JUMP:
+ targname = nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN);
+ break;
+ }
+ }
+ expr = nft_rule_expr_iter_next(iter);
+ }
+ nft_rule_expr_iter_destroy(iter);
+
+ flags = cs->fw.ip.flags;
+
+ if (format & FMT_LINENUMBERS)
+ printf(FMT("%-4u ", "%u "), num);
+
+ if (!(format & FMT_NOCOUNTS)) {
+ print_num(cs->counters.pcnt, format);
+ print_num(cs->counters.bcnt, format);
+ }
+
+ if (!(format & FMT_NOTARGET))
+ printf(FMT("%-9s ", "%s "), targname ? targname : "");
+
+ fputc(cs->fw.ip.invflags & XT_INV_PROTO ? '!' : ' ', stdout);
+ {
+ const char *pname =
+ proto_to_name(cs->fw.ip.proto, format&FMT_NUMERIC);
+ if (pname)
+ printf(FMT("%-5s", "%s "), pname);
+ else
+ printf(FMT("%-5hu", "%hu "), cs->fw.ip.proto);
+ }
+
+ if (format & FMT_OPTIONS) {
+ if (format & FMT_NOTABLE)
+ fputs("opt ", stdout);
+ fputc(cs->fw.ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout);
+ fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout);
+ fputc(' ', stdout);
+ }
+
+ if (format & FMT_VIA) {
+ char iface[IFNAMSIZ+2];
+ if (cs->fw.ip.invflags & IPT_INV_VIA_IN) {
+ iface[0] = '!';
+ iface[1] = '\0';
+ }
+ else iface[0] = '\0';
+
+ if (cs->fw.ip.iniface[0] != '\0') {
+ strcat(iface, cs->fw.ip.iniface);
+ }
+ else if (format & FMT_NUMERIC) strcat(iface, "*");
+ else strcat(iface, "any");
+ printf(FMT(" %-6s ","in %s "), iface);
+
+ if (cs->fw.ip.invflags & IPT_INV_VIA_OUT) {
+ iface[0] = '!';
+ iface[1] = '\0';
+ }
+ else iface[0] = '\0';
+
+ if (cs->fw.ip.outiface[0] != '\0') {
+ strcat(iface, cs->fw.ip.outiface);
+ }
+ else if (format & FMT_NUMERIC) strcat(iface, "*");
+ else strcat(iface, "any");
+ printf(FMT("%-6s ","out %s "), iface);
+ }
+
+ fputc(cs->fw.ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
+ if (cs->fw.ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC))
+ printf(FMT("%-19s ","%s "), "anywhere");
+ else {
+ if (format & FMT_NUMERIC)
+ strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.src));
+ else
+ strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.src));
+ strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.smsk));
+ printf(FMT("%-19s ","%s "), buf);
+ }
+
+ fputc(cs->fw.ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
+ if (cs->fw.ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC))
+ printf(FMT("%-19s ","-> %s"), "anywhere");
+ else {
+ if (format & FMT_NUMERIC)
+ strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.dst));
+ else
+ strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.dst));
+ strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.dmsk));
+ printf(FMT("%-19s ","-> %s"), buf);
+ }
+
+ if (format & FMT_NOTABLE)
+ fputs(" ", stdout);
+
+#ifdef IPT_F_GOTO
+ if(cs->fw.ip.flags & IPT_F_GOTO)
+ printf("[goto] ");
+#endif
+
+ iter = nft_rule_expr_iter_create(r);
+ if (iter == NULL) {
+ DEBUGP("cannot allocate rule list iterator\n");
+ return;
+ }
+
+ expr = nft_rule_expr_iter_next(iter);
+ while (expr != NULL) {
+ const char *name =
+ nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME);
+
+ if (strcmp(name, "match") == 0)
+ print_match(expr, format & FMT_NUMERIC);
+
+ expr = nft_rule_expr_iter_next(iter);
+ }
+ nft_rule_expr_iter_destroy(iter);
+
+ t = calloc(1, sizeof(struct xt_entry_target) + target_len);
+ if (t == NULL)
+ return;
+
+ /* emulate struct xt_entry_match since ->print needs it */
+ memcpy((void *)&t->data, targinfo, target_len);
+
+ if (targname) {
+ target = xtables_find_target(targname, XTF_TRY_LOAD);
+ if (target) {
+ if (target->print)
+ /* FIXME missing first parameter */
+ target->print(NULL, t, format & FMT_NUMERIC);
+ } else
+ printf("[%ld bytes of unknown target data] ",
+ target_len);
+ }
+ free(t);
+
+ if (!(format & FMT_NONEWLINE))
+ fputc('\n', stdout);
+}
+
+static int
+__nft_rule_list(struct nft_handle *h, struct nft_chain *c, const char *table,
+ int rulenum, unsigned int format,
+ void (*cb)(const struct iptables_command_state *cs,
+ struct nft_rule *r, unsigned int num,
+ unsigned int format))
+{
+ struct nft_rule_list *list;
+ struct nft_rule_list_iter *iter;
+ struct nft_rule *r;
+ int rule_ctr = 0, ret = 0;
+ const char *chain = nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME);
+
+ list = nft_rule_list_get(h);
+ if (list == NULL) {
+ DEBUGP("cannot retrieve rule list from kernel\n");
+ return 0;
+ }
+
+ iter = nft_rule_list_iter_create(list);
+ if (iter == NULL) {
+ DEBUGP("cannot allocate rule list iterator\n");
+ return 0;
+ }
+
+ r = nft_rule_list_iter_next(iter);
+ while (r != NULL) {
+ const char *rule_table =
+ nft_rule_attr_get_str(r, NFT_RULE_ATTR_TABLE);
+ const char *rule_chain =
+ nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN);
+
+ rule_ctr++;
+
+ if (strcmp(table, rule_table) != 0 ||
+ strcmp(chain, rule_chain) != 0)
+ goto next;
+
+ if (rulenum > 0) {
+ /* List by rule number case */
+ if (rule_ctr != rulenum) {
+ rule_ctr++;
+ goto next;
+ }
+ } else {
+ struct iptables_command_state cs = {};
+ /* Show all rules case */
+ nft_rule_to_iptables_command_state(r, &cs);
+
+ cb(&cs, r, rule_ctr, format);
+ }
+next:
+ r = nft_rule_list_iter_next(iter);
+ }
+
+ nft_rule_list_iter_destroy(iter);
+ nft_rule_list_free(list);
+
+ if (ret == 0)
+ errno = ENOENT;
+
+ return ret;
+}
+
+int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
+ int rulenum, unsigned int format)
+{
+ struct nft_chain_list *list;
+ struct nft_chain_list_iter *iter;
+ struct nft_chain *c;
+
+ list = nft_chain_dump(h);
+
+ iter = nft_chain_list_iter_create(list);
+ if (iter == NULL) {
+ DEBUGP("cannot allocate rule list iterator\n");
+ return 0;
+ }
+
+ c = nft_chain_list_iter_next(iter);
+ while (c != NULL) {
+ const char *chain_table =
+ nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE);
+ const char *chain_name =
+ nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME);
+ uint32_t policy =
+ nft_chain_attr_get_u32(c, NFT_CHAIN_ATTR_POLICY);
+ uint32_t refs =
+ nft_chain_attr_get_u32(c, NFT_CHAIN_ATTR_USE);
+ struct xt_counters ctrs = {
+ .pcnt = nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_PACKETS),
+ .bcnt = nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_BYTES),
+ };
+ bool basechain = false;
+
+ if (nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM))
+ basechain = true;
+
+ if (strcmp(table, chain_table) != 0)
+ goto next;
+ if (chain && strcmp(chain, chain_name) != 0)
+ goto next;
+
+ print_header(format, chain_name, policy_name[policy], &ctrs,
+ basechain, refs);
+
+ /* this is a base chain */
+ if (nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM)) {
+ __nft_rule_list(h, c, table, rulenum, format,
+ print_firewall);
+ }
+next:
+ c = nft_chain_list_iter_next(iter);
+ }
+
+ nft_chain_list_free(list);
+
+ return 1;
+}
+
+static void
+list_save(const struct iptables_command_state *cs, struct nft_rule *r,
+ unsigned int num, unsigned int format)
+{
+ nft_rule_print_save(r, !(format & FMT_NOCOUNTS));
+}
+
+static int
+nft_rule_list_chain_save(struct nft_handle *h, const char *table,
+ struct nft_chain_list *list, int counters)
+{
+ struct nft_chain_list_iter *iter;
+ struct nft_chain *c;
+
+ iter = nft_chain_list_iter_create(list);
+ if (iter == NULL) {
+ DEBUGP("cannot allocate rule list iterator\n");
+ return 0;
+ }
+
+ c = nft_chain_list_iter_next(iter);
+ while (c != NULL) {
+ const char *chain_table =
+ nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE);
+ const char *chain_name =
+ nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME);
+ uint32_t policy =
+ nft_chain_attr_get_u32(c, NFT_CHAIN_ATTR_POLICY);
+
+ if (strcmp(table, chain_table) != 0)
+ goto next;
+
+ /* this is a base chain */
+ if (nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM)) {
+ printf("-P %s %s", chain_name, policy_name[policy]);
+
+ if (counters) {
+ printf(" -c %lu %lu\n",
+ nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_PACKETS),
+ nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_BYTES));
+ } else
+ printf("\n");
+ } else {
+ printf("-N %s\n", chain_name);
+ }
+next:
+ c = nft_chain_list_iter_next(iter);
+ }
+
+ return 1;
+}
+
+int nft_rule_list_save(struct nft_handle *h, const char *chain,
+ const char *table, int rulenum, int counters)
+{
+ struct nft_chain_list *list;
+ struct nft_chain_list_iter *iter;
+ struct nft_chain *c;
+
+ list = nft_chain_dump(h);
+
+ /* Dump policies and custom chains first */
+ nft_rule_list_chain_save(h, table, list, counters);
+
+ /* Now dump out rules in this table */
+ iter = nft_chain_list_iter_create(list);
+ if (iter == NULL) {
+ DEBUGP("cannot allocate rule list iterator\n");
+ return 0;
+ }
+
+ c = nft_chain_list_iter_next(iter);
+ while (c != NULL) {
+ const char *chain_table =
+ nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE);
+ const char *chain_name =
+ nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME);
+
+ if (strcmp(table, chain_table) != 0)
+ goto next;
+ if (chain && strcmp(chain, chain_name) != 0)
+ goto next;
+
+ __nft_rule_list(h, c, table, rulenum,
+ counters ? 0 : FMT_NOCOUNTS, list_save);
+next:
+ c = nft_chain_list_iter_next(iter);
+ }
+
+ nft_chain_list_free(list);
+
+ return 1;
+}
+
+int nft_compatible_revision(const char *name, uint8_t rev, int opt)
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ uint32_t portid, seq, type;
+ int ret = 0;
+
+ if (opt == IPT_SO_GET_REVISION_MATCH)
+ type = 0;
+ else
+ type = 1;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (NFNL_SUBSYS_NFT_COMPAT << 8) | NFNL_MSG_COMPAT_GET;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_seq = seq = time(NULL);
+
+ struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+ nfg->nfgen_family = AF_INET;
+ nfg->version = NFNETLINK_V0;
+ nfg->res_id = 0;
+
+ mnl_attr_put_strz(nlh, NFTA_COMPAT_NAME, name);
+ mnl_attr_put_u32(nlh, NFTA_COMPAT_REV, htonl(rev));
+ mnl_attr_put_u32(nlh, NFTA_COMPAT_TYPE, htonl(type));
+
+ DEBUGP("requesting `%s' rev=%d type=%d via nft_compat\n",
+ name, rev, type);
+
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ return 0;
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ goto err;
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_send");
+ goto err;
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret == -1) {
+ perror("mnl_socket_recvfrom");
+ goto err;
+ }
+
+ ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
+ if (ret == -1) {
+ perror("mnl_cb_run");
+ goto err;
+ }
+
+err:
+ mnl_socket_close(nl);
+
+ return ret < 0 ? 0 : 1;
+}
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *nft_strerror(int err)
+{
+ unsigned int i;
+ static struct table_struct {
+ void *fn;
+ int err;
+ const char *message;
+ } table[] =
+ {
+ { nft_chain_user_del, ENOTEMPTY, "Chain is not empty" },
+ { nft_chain_user_del, EINVAL, "Can't delete built-in chain" },
+ { nft_chain_user_del, EMLINK,
+ "Can't delete chain with references left" },
+ { nft_chain_user_add, EEXIST, "Chain already exists" },
+ { nft_rule_add, E2BIG, "Index of insertion too big" },
+ { nft_rule_replace, E2BIG, "Index of replacement too big" },
+ { nft_rule_delete_num, E2BIG, "Index of deletion too big" },
+/* { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
+ { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" }, */
+ { nft_rule_add, ELOOP, "Loop found in table" },
+ { nft_rule_add, EINVAL, "Target problem" },
+ /* ENOENT for DELETE probably means no matching rule */
+ { nft_rule_delete, ENOENT,
+ "Bad rule (does a matching rule exist in that chain?)" },
+ { nft_chain_set, ENOENT, "Bad built-in chain name" },
+ { nft_chain_set, EINVAL, "Bad policy name" },
+ { NULL, EPERM, "Permission denied (you must be root)" },
+ { NULL, 0, "Incompatible with this kernel" },
+ { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
+ { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
+ { NULL, ENOMEM, "Memory allocation problem" },
+ { NULL, ENOENT, "No chain/target/match by that name" },
+ };
+
+ for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+ if ((!table[i].fn || table[i].fn == nft_fn)
+ && table[i].err == err)
+ return table[i].message;
+ }
+
+ return strerror(err);
+}
diff --git a/iptables/nft.h b/iptables/nft.h
new file mode 100644
index 00000000..f5a9efb8
--- /dev/null
+++ b/iptables/nft.h
@@ -0,0 +1,62 @@
+#ifndef _NFT_H_
+#define _NFT_H_
+
+#include "xshared.h"
+
+struct nft_handle {
+ struct mnl_socket *nl;
+ uint32_t portid;
+ uint32_t seq;
+};
+
+int nft_init(struct nft_handle *h);
+void nft_fini(struct nft_handle *h);
+
+/*
+ * Operations with tables.
+ */
+struct nft_table;
+
+int nft_table_add(struct nft_handle *h, const struct nft_table *t);
+int nft_for_each_table(struct nft_handle *h, int (*func)(struct nft_handle *h, const char *tablename, bool counters), bool counters);
+bool nft_table_find(struct nft_handle *h, const char *tablename);
+
+/*
+ * Operations with chains.
+ */
+struct nft_chain;
+
+int nft_chain_add(struct nft_handle *h, const struct nft_chain *c);
+int nft_chain_set(struct nft_handle *h, const char *table, const char *chain, const char *policy, const struct xt_counters *counters);
+struct nft_chain_list *nft_chain_dump(struct nft_handle *h);
+int nft_chain_save(struct nft_handle *h, struct nft_chain_list *list, const char *table);
+int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table);
+int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table);
+int nft_chain_user_rename(struct nft_handle *h, const char *chain, const char *table, const char *newname);
+
+/*
+ * Operations with rule-set.
+ */
+struct nft_rule;
+
+int nft_rule_add(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool append, bool verbose);
+int nft_rule_check(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool verbose);
+int nft_rule_delete(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, bool verbose);
+int nft_rule_delete_num(struct nft_handle *h, const char *chain, const char *table, int rulenum, bool verbose);
+int nft_rule_replace(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cmd, int rulenum, bool verbose);
+int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, int rulenum, unsigned int format);
+int nft_rule_list_save(struct nft_handle *h, const char *chain, const char *table, int rulenum, int counters);
+int nft_rule_save(struct nft_handle *h, const char *table, bool counters);
+int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table);
+
+/*
+ * revision compatibility.
+ */
+int nft_compatible_revision(const char *name, uint8_t rev, int opt);
+
+/*
+ * Error reporting.
+ */
+const char *nft_strerror(int err);
+
+#endif
diff --git a/iptables/xshared.h b/iptables/xshared.h
index 1e2b9b8e..27c5b786 100644
--- a/iptables/xshared.h
+++ b/iptables/xshared.h
@@ -58,6 +58,7 @@ struct iptables_command_state {
unsigned int options;
struct xtables_rule_match *matches;
struct xtables_target *target;
+ struct xt_counters counters;
char *protocol;
int proto_used;
const char *jumpto;
diff --git a/iptables/xtables-config-parser.y b/iptables/xtables-config-parser.y
new file mode 100644
index 00000000..fe5bcbf1
--- /dev/null
+++ b/iptables/xtables-config-parser.y
@@ -0,0 +1,213 @@
+%{
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <libiptc/linux_list.h>
+#include <libnftables/table.h>
+#include <libnftables/chain.h>
+
+#include <netinet/in.h>
+#include <linux/netfilter.h>
+
+extern char *yytext;
+extern int yylineno;
+
+static LIST_HEAD(xtables_stack);
+
+struct stack_elem {
+ struct list_head head;
+ int token;
+ size_t size;
+ char data[];
+};
+
+static void *stack_push(int token, size_t size)
+{
+ struct stack_elem *e;
+
+ e = calloc(1, sizeof(struct stack_elem) + size);
+
+ e->token = token;
+ e->size = size;
+
+ list_add(&e->head, &xtables_stack);
+
+ return e->data;
+}
+
+static struct stack_elem *stack_pop(void)
+{
+ struct stack_elem *e;
+
+ e = list_entry(xtables_stack.next, struct stack_elem, head);
+
+ if (&e->head == &xtables_stack)
+ return NULL;
+
+ list_del(&e->head);
+ return e;
+}
+
+static inline void stack_put_i32(void *data, int value)
+{
+ memcpy(data, &value, sizeof(int));
+}
+
+static inline void stack_put_str(void *data, const char *str)
+{
+ memcpy(data, str, strlen(str)+1);
+}
+
+static void stack_free(struct stack_elem *e)
+{
+ free(e);
+}
+
+%}
+
+%union {
+ int val;
+ char *string;
+}
+
+%token T_TABLE
+%token T_CHAIN
+%token T_HOOK
+%token T_PRIO
+
+%token <string> T_STRING
+%token <val> T_INTEGER
+
+%%
+
+configfile :
+ | lines
+ ;
+
+lines : line
+ | lines line
+ ;
+
+line : table
+ ;
+
+table : T_TABLE T_STRING T_PRIO T_INTEGER '{' chains '}'
+ {
+ /* added in reverse order to pop it in order */
+ void *data = stack_push(T_PRIO, sizeof(int32_t));
+ stack_put_i32(data, $4);
+ data = stack_push(T_TABLE, strlen($2));
+ stack_put_str(data, $2);
+ }
+ ;
+
+chains : chain
+ | chains chain
+ ;
+
+chain : T_CHAIN T_STRING T_HOOK T_STRING
+ {
+ /* added in reverse order to pop it in order */
+ void *data = stack_push(T_HOOK, strlen($4));
+ stack_put_str(data, $4);
+ data = stack_push(T_CHAIN, strlen($2));
+ stack_put_str(data, $2);
+ }
+ ;
+
+%%
+
+int __attribute__((noreturn))
+yyerror(char *msg)
+{
+ fprintf(stderr, "parsing config file in line (%d), symbol '%s': %s\n",
+ yylineno, yytext, msg);
+ exit(EXIT_FAILURE);
+}
+
+static int hooknametonum(const char *hookname)
+{
+ if (strcmp(hookname, "NF_INET_LOCAL_IN") == 0)
+ return NF_INET_LOCAL_IN;
+ else if (strcmp(hookname, "NF_INET_FORWARD") == 0)
+ return NF_INET_FORWARD;
+ else if (strcmp(hookname, "NF_INET_LOCAL_OUT") == 0)
+ return NF_INET_LOCAL_OUT;
+ else if (strcmp(hookname, "NF_INET_PRE_ROUTING") == 0)
+ return NF_INET_PRE_ROUTING;
+ else if (strcmp(hookname, "NF_INET_POST_ROUTING") == 0)
+ return NF_INET_POST_ROUTING;
+
+ return -1;
+}
+
+int xtables_config_parse(char *filename, struct nft_table_list *table_list,
+ struct nft_chain_list *chain_list)
+{
+ FILE *fp;
+ struct stack_elem *e;
+ struct nft_table *table = NULL;
+ struct nft_chain *chain = NULL;
+ int prio = 0;
+
+ fp = fopen(filename, "r");
+ if (!fp)
+ return -1;
+
+ yyrestart(fp);
+ yyparse();
+ fclose(fp);
+
+ for (e = stack_pop(); e != NULL; e = stack_pop()) {
+ switch(e->token) {
+ case T_TABLE:
+ table = nft_table_alloc();
+ if (table == NULL) {
+ perror("nft_table_alloc");
+ return -1;
+ }
+ nft_table_attr_set(table, NFT_TABLE_ATTR_NAME, e->data);
+ nft_table_list_add(table, table_list);
+ break;
+ case T_PRIO:
+ prio = *((int32_t *)e->data);
+ break;
+ case T_CHAIN:
+ chain = nft_chain_alloc();
+ if (chain == NULL) {
+ perror("nft_chain_alloc");
+ return -1;
+ }
+ nft_chain_attr_set(chain, NFT_CHAIN_ATTR_TABLE,
+ (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME));
+ nft_chain_attr_set(chain, NFT_CHAIN_ATTR_NAME, e->data);
+ nft_chain_list_add(chain, chain_list);
+ break;
+ case T_HOOK:
+ nft_chain_attr_set_u32(chain, NFT_CHAIN_ATTR_HOOKNUM,
+ hooknametonum(e->data));
+ nft_chain_attr_set_s32(chain, NFT_CHAIN_ATTR_PRIO, prio);
+ break;
+ default:
+ printf("unknown token type %d\n", e->token);
+ break;
+ }
+ stack_free(e);
+ }
+
+ return 0;
+}
diff --git a/iptables/xtables-config-syntax.l b/iptables/xtables-config-syntax.l
new file mode 100644
index 00000000..7a66ef39
--- /dev/null
+++ b/iptables/xtables-config-syntax.l
@@ -0,0 +1,53 @@
+%{
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <string.h>
+#include "xtables-config-parser.h"
+%}
+
+%option yylineno
+%option noinput
+%option nounput
+
+ws [ \t]+
+comment #.*$
+nl [\n\r]
+
+is_on [o|O][n|N]
+is_off [o|O][f|F][f|F]
+integer [\-\+]?[0-9]+
+string [a-zA-Z][a-zA-Z0-9\.\-\_]*
+
+%%
+"table" { return T_TABLE; }
+"chain" { return T_CHAIN; }
+"hook" { return T_HOOK; }
+"prio" { return T_PRIO; }
+
+{integer} { yylval.val = atoi(yytext); return T_INTEGER; }
+{string} { yylval.string = strdup(yytext); return T_STRING; }
+
+{comment} ;
+{ws} ;
+{nl} ;
+
+<<EOF>> { yyterminate(); }
+
+. { return yytext[0]; }
+
+%%
+
+int
+yywrap()
+{
+ return 1;
+}
diff --git a/iptables/xtables-config.c b/iptables/xtables-config.c
new file mode 100644
index 00000000..16918bf6
--- /dev/null
+++ b/iptables/xtables-config.c
@@ -0,0 +1,107 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+
+#include <libnftables/table.h>
+#include <libnftables/chain.h>
+
+#include "xtables-multi.h"
+#include "xtables-config-parser.h"
+
+#include "nft.h"
+
+extern int xtables_config_parse(const char *filename,
+ struct nft_table_list *table_list,
+ struct nft_chain_list *chain_list);
+
+#define XTABLES_CONFIG_DEFAULT "/etc/xtables.conf"
+
+int xtables_config_main(int argc, char *argv[])
+{
+ struct nft_table_list *table_list = nft_table_list_alloc();
+ struct nft_chain_list *chain_list = nft_chain_list_alloc();
+ struct nft_table_list_iter *titer;
+ struct nft_chain_list_iter *citer;
+ struct nft_table *table;
+ struct nft_chain *chain;
+ const char *filename = NULL;
+ struct nft_handle h;
+
+ if (argc > 2) {
+ fprintf(stderr, "Usage: %s [<config_file>]\n", argv[0]);
+ return EXIT_SUCCESS;
+ }
+ if (argc == 1)
+ filename = XTABLES_CONFIG_DEFAULT;
+ else
+ filename = argv[1];
+
+ if (xtables_config_parse(filename, table_list, chain_list) < 0) {
+ if (errno == ENOENT) {
+ fprintf(stderr, "configuration file `%s' does not "
+ "exists\n", filename);
+ } else {
+ fprintf(stderr, "Fatal error: %s\n", strerror(errno));
+ }
+ return EXIT_FAILURE;
+ }
+
+ nft_init(&h);
+
+ /* Stage 1) create tables */
+ titer = nft_table_list_iter_create(table_list);
+ while ((table = nft_table_list_iter_next(titer)) != NULL) {
+ if (nft_table_add(&h, table) < 0) {
+ if (errno == EEXIST) {
+ printf("table `%s' already exists, skipping\n",
+ (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME));
+ } else {
+ printf("table `%s' cannot be create, reason `%s'. Exitting\n",
+ (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME),
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+ continue;
+ }
+ printf("table `%s' has been created\n",
+ (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME));
+ }
+
+ /* Stage 2) create chains */
+ citer = nft_chain_list_iter_create(chain_list);
+ while ((chain = nft_chain_list_iter_next(citer)) != NULL) {
+ if (nft_chain_add(&h, chain) < 0) {
+ if (errno == EEXIST) {
+ printf("chain `%s' already exists in table `%s', skipping\n",
+ (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME),
+ (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_TABLE));
+ } else {
+ printf("chain `%s' cannot be create, reason `%s'. Exitting\n",
+ (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME),
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+ continue;
+ }
+
+ printf("chain `%s' in table `%s' has been created\n",
+ (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME),
+ (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_TABLE));
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/iptables/xtables-multi.c b/iptables/xtables-multi.c
index 8014d5fb..c1746434 100644
--- a/iptables/xtables-multi.c
+++ b/iptables/xtables-multi.c
@@ -13,6 +13,10 @@
#include "ip6tables-multi.h"
#endif
+#ifdef ENABLE_NFTABLES
+#include "xtables-multi.h"
+#endif
+
static const struct subcommand multi_subcommands[] = {
#ifdef ENABLE_IPV4
{"iptables", iptables_main},
@@ -32,6 +36,12 @@ static const struct subcommand multi_subcommands[] = {
{"ip6tables-restore", ip6tables_restore_main},
{"restore6", ip6tables_restore_main},
#endif
+#ifdef ENABLE_NFTABLES
+ {"xtables", xtables_main},
+ {"xtables-save", xtables_save_main},
+ {"xtables-restore", xtables_restore_main},
+ {"xtables-config", xtables_config_main},
+#endif
{NULL},
};
diff --git a/iptables/xtables-multi.h b/iptables/xtables-multi.h
index 615724b1..be2b3ad8 100644
--- a/iptables/xtables-multi.h
+++ b/iptables/xtables-multi.h
@@ -2,5 +2,9 @@
#define _XTABLES_MULTI_H 1
extern int iptables_xml_main(int, char **);
+extern int xtables_main(int, char **);
+extern int xtables_save_main(int, char **);
+extern int xtables_restore_main(int, char **);
+extern int xtables_config_main(int, char **);
#endif /* _XTABLES_MULTI_H */
diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
new file mode 100644
index 00000000..09922a0c
--- /dev/null
+++ b/iptables/xtables-restore.c
@@ -0,0 +1,417 @@
+/* Code to restore the iptables state, from file by iptables-save.
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ * based on previous code from Rusty Russell <rusty@linuxcare.com.au>
+ *
+ * This code is distributed under the terms of GNU GPL v2
+ */
+
+#include <getopt.h>
+#include <sys/errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "iptables.h"
+#include "xtables.h"
+#include "libiptc/libiptc.h"
+#include "xtables-multi.h"
+#include "nft.h"
+
+#ifdef DEBUG
+#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...)
+#endif
+
+static int binary = 0, counters = 0, verbose = 0, noflush = 0;
+
+/* Keeping track of external matches and targets. */
+static const struct option options[] = {
+ {.name = "binary", .has_arg = false, .val = 'b'},
+ {.name = "counters", .has_arg = false, .val = 'c'},
+ {.name = "verbose", .has_arg = false, .val = 'v'},
+ {.name = "test", .has_arg = false, .val = 't'},
+ {.name = "help", .has_arg = false, .val = 'h'},
+ {.name = "noflush", .has_arg = false, .val = 'n'},
+ {.name = "modprobe", .has_arg = true, .val = 'M'},
+ {.name = "table", .has_arg = true, .val = 'T'},
+ {NULL},
+};
+
+static void print_usage(const char *name, const char *version) __attribute__((noreturn));
+
+#define prog_name xtables_globals.program_name
+
+static void print_usage(const char *name, const char *version)
+{
+ fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n"
+ " [ --binary ]\n"
+ " [ --counters ]\n"
+ " [ --verbose ]\n"
+ " [ --test ]\n"
+ " [ --help ]\n"
+ " [ --noflush ]\n"
+ " [ --table=<TABLE> ]\n"
+ " [ --modprobe=<command>]\n", name);
+
+ exit(1);
+}
+
+static int parse_counters(char *string, struct xt_counters *ctr)
+{
+ unsigned long long pcnt, bcnt;
+ int ret;
+
+ ret = sscanf(string, "[%llu:%llu]", &pcnt, &bcnt);
+ ctr->pcnt = pcnt;
+ ctr->bcnt = bcnt;
+ return ret == 2;
+}
+
+/* global new argv and argc */
+static char *newargv[255];
+static int newargc;
+
+/* function adding one argument to newargv, updating newargc
+ * returns true if argument added, false otherwise */
+static int add_argv(char *what) {
+ DEBUGP("add_argv: %s\n", what);
+ if (what && newargc + 1 < ARRAY_SIZE(newargv)) {
+ newargv[newargc] = strdup(what);
+ newargv[++newargc] = NULL;
+ return 1;
+ } else {
+ xtables_error(PARAMETER_PROBLEM,
+ "Parser cannot handle more arguments\n");
+ return 0;
+ }
+}
+
+static void free_argv(void) {
+ int i;
+
+ for (i = 0; i < newargc; i++)
+ free(newargv[i]);
+}
+
+static void add_param_to_argv(char *parsestart)
+{
+ int quote_open = 0, escaped = 0, param_len = 0;
+ char param_buffer[1024], *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) {
+ param_buffer[param_len++] = *curchar;
+ escaped = 0;
+ continue;
+ } else if (*curchar == '\\') {
+ escaped = 1;
+ continue;
+ } else if (*curchar == '"') {
+ quote_open = 0;
+ *curchar = ' ';
+ } else {
+ param_buffer[param_len++] = *curchar;
+ continue;
+ }
+ } else {
+ if (*curchar == '"') {
+ quote_open = 1;
+ continue;
+ }
+ }
+
+ if (*curchar == ' '
+ || *curchar == '\t'
+ || * curchar == '\n') {
+ if (!param_len) {
+ /* two spaces? */
+ continue;
+ }
+
+ param_buffer[param_len] = '\0';
+
+ /* check if table name specified */
+ if (!strncmp(param_buffer, "-t", 2)
+ || !strncmp(param_buffer, "--table", 8)) {
+ xtables_error(PARAMETER_PROBLEM,
+ "The -t option (seen in line %u) cannot be "
+ "used in xtables-restore.\n", line);
+ exit(1);
+ }
+
+ add_argv(param_buffer);
+ param_len = 0;
+ } else {
+ /* regular character, copy to buffer */
+ param_buffer[param_len++] = *curchar;
+
+ if (param_len >= sizeof(param_buffer))
+ xtables_error(PARAMETER_PROBLEM,
+ "Parameter too long!");
+ }
+ }
+}
+
+int
+xtables_restore_main(int argc, char *argv[])
+{
+ struct nft_handle h;
+ char buffer[10240];
+ int c;
+ char curtable[XT_TABLE_MAXNAMELEN + 1];
+ FILE *in;
+ int in_table = 0, testing = 0;
+ const char *tablename = NULL;
+ const struct xtc_ops *ops = &iptc_ops;
+
+ line = 0;
+
+ xtables_globals.program_name = "xtables-restore";
+ c = xtables_init_all(&xtables_globals, NFPROTO_IPV4);
+ if (c < 0) {
+ fprintf(stderr, "%s/%s Failed to initialize xtables\n",
+ xtables_globals.program_name,
+ xtables_globals.program_version);
+ exit(1);
+ }
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+ init_extensions();
+ init_extensions4();
+#endif
+
+ nft_init(&h);
+
+ while ((c = getopt_long(argc, argv, "bcvthnM:T:", options, NULL)) != -1) {
+ switch (c) {
+ case 'b':
+ binary = 1;
+ break;
+ case 'c':
+ counters = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 't':
+ testing = 1;
+ break;
+ case 'h':
+ print_usage("xtables-restore",
+ IPTABLES_VERSION);
+ break;
+ case 'n':
+ noflush = 1;
+ break;
+ case 'M':
+ xtables_modprobe_program = optarg;
+ break;
+ case 'T':
+ tablename = optarg;
+ break;
+ }
+ }
+
+ if (optind == argc - 1) {
+ in = fopen(argv[optind], "re");
+ if (!in) {
+ fprintf(stderr, "Can't open %s: %s\n", argv[optind],
+ strerror(errno));
+ exit(1);
+ }
+ }
+ else if (optind < argc) {
+ fprintf(stderr, "Unknown arguments found on commandline\n");
+ exit(1);
+ }
+ else in = stdin;
+
+ /* Grab standard input. */
+ while (fgets(buffer, sizeof(buffer), in)) {
+ int ret = 0;
+
+ line++;
+ if (buffer[0] == '\n')
+ continue;
+ else if (buffer[0] == '#') {
+ if (verbose)
+ fputs(buffer, stdout);
+ continue;
+ } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
+ /* FIXME commit/testing operation not supported */
+ if (!testing) {
+ DEBUGP("Calling commit\n");
+ ret = 1;
+ } else {
+ DEBUGP("Not calling commit, testing\n");
+ ret = 1;
+ }
+ in_table = 0;
+ } else if ((buffer[0] == '*') && (!in_table)) {
+ /* New table */
+ char *table;
+
+ table = strtok(buffer+1, " \t\n");
+ DEBUGP("line %u, table '%s'\n", line, table);
+ if (!table) {
+ xtables_error(PARAMETER_PROBLEM,
+ "%s: line %u table name invalid\n",
+ xt_params->program_name, line);
+ exit(1);
+ }
+ strncpy(curtable, table, XT_TABLE_MAXNAMELEN);
+ curtable[XT_TABLE_MAXNAMELEN] = '\0';
+
+ if (tablename && (strcmp(tablename, table) != 0))
+ continue;
+
+ if (noflush == 0) {
+ DEBUGP("Cleaning all chains of table '%s'\n",
+ table);
+ nft_rule_flush(&h, NULL, table);
+
+ DEBUGP("Deleting all user-defined chains "
+ "of table '%s'\n", table);
+ nft_chain_user_del(&h, NULL, table);
+ }
+
+ ret = 1;
+ in_table = 1;
+
+ } else if ((buffer[0] == ':') && (in_table)) {
+ /* New chain. */
+ char *policy, *chain = NULL;
+ struct xt_counters count = {};
+
+ chain = strtok(buffer+1, " \t\n");
+ DEBUGP("line %u, chain '%s'\n", line, chain);
+ if (!chain) {
+ xtables_error(PARAMETER_PROBLEM,
+ "%s: line %u chain name invalid\n",
+ xt_params->program_name, line);
+ exit(1);
+ }
+
+ if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
+ xtables_error(PARAMETER_PROBLEM,
+ "Invalid chain name `%s' "
+ "(%u chars max)",
+ chain, XT_EXTENSION_MAXNAMELEN - 1);
+
+ policy = strtok(NULL, " \t\n");
+ DEBUGP("line %u, policy '%s'\n", line, policy);
+ if (!policy) {
+ xtables_error(PARAMETER_PROBLEM,
+ "%s: line %u policy invalid\n",
+ xt_params->program_name, line);
+ exit(1);
+ }
+
+ if (strcmp(policy, "-") != 0) {
+ if (counters) {
+ char *ctrs;
+ ctrs = strtok(NULL, " \t\n");
+
+ if (!ctrs || !parse_counters(ctrs, &count))
+ xtables_error(PARAMETER_PROBLEM,
+ "invalid policy counters "
+ "for chain '%s'\n", chain);
+
+ }
+
+ DEBUGP("Setting policy of chain %s to %s\n",
+ chain, policy);
+ }
+
+ if (nft_chain_set(&h, curtable, chain, policy, &count) < 0) {
+ xtables_error(OTHER_PROBLEM,
+ "Can't set policy `%s'"
+ " on `%s' line %u: %s\n",
+ policy, chain, line,
+ ops->strerror(errno));
+ }
+
+ ret = 1;
+
+ } else if (in_table) {
+ int a;
+ char *ptr = buffer;
+ char *pcnt = NULL;
+ char *bcnt = NULL;
+ char *parsestart;
+
+ /* reset the newargv */
+ newargc = 0;
+
+ if (buffer[0] == '[') {
+ /* we have counters in our input */
+ ptr = strchr(buffer, ']');
+ if (!ptr)
+ xtables_error(PARAMETER_PROBLEM,
+ "Bad line %u: need ]\n",
+ line);
+
+ pcnt = strtok(buffer+1, ":");
+ if (!pcnt)
+ xtables_error(PARAMETER_PROBLEM,
+ "Bad line %u: need :\n",
+ line);
+
+ bcnt = strtok(NULL, "]");
+ if (!bcnt)
+ xtables_error(PARAMETER_PROBLEM,
+ "Bad line %u: need ]\n",
+ line);
+
+ /* start command parsing after counter */
+ parsestart = ptr + 1;
+ } else {
+ /* start command parsing at start of line */
+ parsestart = buffer;
+ }
+
+ add_argv(argv[0]);
+ add_argv("-t");
+ add_argv(curtable);
+
+ if (counters && pcnt && bcnt) {
+ add_argv("--set-counters");
+ add_argv((char *) pcnt);
+ add_argv((char *) bcnt);
+ }
+
+ add_param_to_argv(parsestart);
+
+ DEBUGP("calling do_command4(%u, argv, &%s, handle):\n",
+ newargc, curtable);
+
+ for (a = 0; a < newargc; a++)
+ DEBUGP("argv[%u]: %s\n", a, newargv[a]);
+
+ ret = do_commandx(&h, newargc, newargv, &newargv[2]);
+
+ free_argv();
+ fflush(stdout);
+ }
+ if (tablename && (strcmp(tablename, curtable) != 0))
+ continue;
+ if (!ret) {
+ fprintf(stderr, "%s: line %u failed\n",
+ xt_params->program_name, line);
+ exit(1);
+ }
+ }
+ if (in_table) {
+ fprintf(stderr, "%s: COMMIT expected at line %u\n",
+ xt_params->program_name, line + 1);
+ exit(1);
+ }
+
+ fclose(in);
+ return 0;
+}
diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c
new file mode 100644
index 00000000..046c948d
--- /dev/null
+++ b/iptables/xtables-save.c
@@ -0,0 +1,122 @@
+/* Code to save the xtables state, in human readable-form. */
+/* (C) 1999 by Paul 'Rusty' Russell <rusty@rustcorp.com.au> and
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This code is distributed under the terms of GNU GPL v2
+ *
+ */
+#include <getopt.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <netdb.h>
+#include "libiptc/libiptc.h"
+#include "iptables.h"
+#include "xtables-multi.h"
+#include "nft.h"
+
+#include <libnftables/chain.h>
+
+#ifndef NO_SHARED_LIBS
+#include <dlfcn.h>
+#endif
+
+static bool show_counters = false;
+
+static const struct option options[] = {
+ {.name = "counters", .has_arg = false, .val = 'c'},
+ {.name = "dump", .has_arg = false, .val = 'd'},
+ {.name = "table", .has_arg = true, .val = 't'},
+ {.name = "modprobe", .has_arg = true, .val = 'M'},
+ {NULL},
+};
+
+static int
+do_output(struct nft_handle *h, const char *tablename, bool counters)
+{
+ struct nft_chain_list *chain_list;
+
+ if (!tablename)
+ return nft_for_each_table(h, do_output, counters);
+
+ if (!nft_table_find(h, tablename)) {
+ printf("Table `%s' does not exist\n", tablename);
+ return 0;
+ }
+
+ chain_list = nft_chain_dump(h);
+
+ time_t now = time(NULL);
+
+ printf("# Generated by xtables-save v%s on %s",
+ IPTABLES_VERSION, ctime(&now));
+ printf("*%s\n", tablename);
+
+ /* Dump out chain names first,
+ * thereby preventing dependency conflicts */
+ nft_chain_save(h, chain_list, tablename);
+ nft_rule_save(h, tablename, counters);
+
+ now = time(NULL);
+ printf("COMMIT\n");
+ printf("# Completed on %s", ctime(&now));
+
+ return 1;
+}
+
+/* Format:
+ * :Chain name POLICY packets bytes
+ * rule
+ */
+int
+xtables_save_main(int argc, char *argv[])
+{
+ const char *tablename = NULL;
+ struct nft_handle h;
+ int c;
+
+ xtables_globals.program_name = "xtables-save";
+ /* XXX xtables_init_all does several things we don't want */
+ c = xtables_init_all(&xtables_globals, NFPROTO_IPV4);
+ if (c < 0) {
+ fprintf(stderr, "%s/%s Failed to initialize xtables\n",
+ xtables_globals.program_name,
+ xtables_globals.program_version);
+ exit(1);
+ }
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+ init_extensions();
+ init_extensions4();
+#endif
+ nft_init(&h);
+
+ while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
+ switch (c) {
+ case 'c':
+ show_counters = true;
+ break;
+
+ case 't':
+ /* Select specific table. */
+ tablename = optarg;
+ break;
+ case 'M':
+ xtables_modprobe_program = optarg;
+ break;
+ case 'd':
+ do_output(&h, tablename, show_counters);
+ exit(0);
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr, "Unknown arguments found on commandline\n");
+ exit(1);
+ }
+
+ return !do_output(&h, tablename, show_counters);
+}
diff --git a/iptables/xtables-standalone.c b/iptables/xtables-standalone.c
new file mode 100644
index 00000000..f746c902
--- /dev/null
+++ b/iptables/xtables-standalone.c
@@ -0,0 +1,80 @@
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * Based on the ipchains code by Paul Russell and Michael Neuling
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ * Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ * Marc Boucher <marc+nf@mbsi.ca>
+ * James Morris <jmorris@intercode.com.au>
+ * Harald Welte <laforge@gnumonks.org>
+ * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * iptables -- IP firewall administration for kernels with
+ * firewall table (aimed for the 2.3 kernels)
+ *
+ * See the accompanying manual page iptables(8) for information
+ * about proper usage of this program.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <iptables.h>
+#include "xtables-multi.h"
+#include "nft.h"
+
+int
+xtables_main(int argc, char *argv[])
+{
+ int ret;
+ char *table = "filter";
+ struct nft_handle h;
+
+ iptables_globals.program_name = "xtables";
+ ret = xtables_init_all(&xtables_globals, NFPROTO_IPV4);
+ if (ret < 0) {
+ fprintf(stderr, "%s/%s Failed to initialize xtables\n",
+ iptables_globals.program_name,
+ iptables_globals.program_version);
+ exit(1);
+ }
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+ init_extensions();
+ init_extensions4();
+#endif
+
+ nft_init(&h);
+
+ ret = do_commandx(&h, argc, argv, &table);
+ if (!ret) {
+ if (errno == EINVAL) {
+ fprintf(stderr, "iptables: %s. "
+ "Run `dmesg' for more information.\n",
+ nft_strerror(errno));
+ } else {
+ fprintf(stderr, "iptables: %s.\n",
+ nft_strerror(errno));
+ }
+ if (errno == EAGAIN) {
+ exit(RESOURCE_PROBLEM);
+ }
+ }
+
+ exit(!ret);
+}
diff --git a/iptables/xtables.c b/iptables/xtables.c
new file mode 100644
index 00000000..a6875757
--- /dev/null
+++ b/iptables/xtables.c
@@ -0,0 +1,1251 @@
+/* Code to take an iptables-style command line and do it. */
+
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ * Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ * Marc Boucher <marc+nf@mbsi.ca>
+ * James Morris <jmorris@intercode.com.au>
+ * Harald Welte <laforge@gnumonks.org>
+ * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <unistd.h>
+#include <iptables.h>
+#include <xtables.h>
+#include <fcntl.h>
+#include "xshared.h"
+#include "nft.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define FMT_NUMERIC 0x0001
+#define FMT_NOCOUNTS 0x0002
+#define FMT_KILOMEGAGIGA 0x0004
+#define FMT_OPTIONS 0x0008
+#define FMT_NOTABLE 0x0010
+#define FMT_NOTARGET 0x0020
+#define FMT_VIA 0x0040
+#define FMT_NONEWLINE 0x0080
+#define FMT_LINENUMBERS 0x0100
+
+#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
+ | FMT_NUMERIC | FMT_NOTABLE)
+#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
+
+
+#define CMD_NONE 0x0000U
+#define CMD_INSERT 0x0001U
+#define CMD_DELETE 0x0002U
+#define CMD_DELETE_NUM 0x0004U
+#define CMD_REPLACE 0x0008U
+#define CMD_APPEND 0x0010U
+#define CMD_LIST 0x0020U
+#define CMD_FLUSH 0x0040U
+#define CMD_ZERO 0x0080U
+#define CMD_NEW_CHAIN 0x0100U
+#define CMD_DELETE_CHAIN 0x0200U
+#define CMD_SET_POLICY 0x0400U
+#define CMD_RENAME_CHAIN 0x0800U
+#define CMD_LIST_RULES 0x1000U
+#define CMD_ZERO_NUM 0x2000U
+#define CMD_CHECK 0x4000U
+#define NUMBER_OF_CMD 16
+static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
+ 'N', 'X', 'P', 'E', 'S', 'Z', 'C' };
+
+#define OPT_FRAGMENT 0x00800U
+#define NUMBER_OF_OPT ARRAY_SIZE(optflags)
+static const char optflags[]
+= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '0', 'c', 'f'};
+
+static struct option original_opts[] = {
+ {.name = "append", .has_arg = 1, .val = 'A'},
+ {.name = "delete", .has_arg = 1, .val = 'D'},
+ {.name = "check", .has_arg = 1, .val = 'C'},
+ {.name = "insert", .has_arg = 1, .val = 'I'},
+ {.name = "replace", .has_arg = 1, .val = 'R'},
+ {.name = "list", .has_arg = 2, .val = 'L'},
+ {.name = "list-rules", .has_arg = 2, .val = 'S'},
+ {.name = "flush", .has_arg = 2, .val = 'F'},
+ {.name = "zero", .has_arg = 2, .val = 'Z'},
+ {.name = "new-chain", .has_arg = 1, .val = 'N'},
+ {.name = "delete-chain", .has_arg = 2, .val = 'X'},
+ {.name = "rename-chain", .has_arg = 1, .val = 'E'},
+ {.name = "policy", .has_arg = 1, .val = 'P'},
+ {.name = "source", .has_arg = 1, .val = 's'},
+ {.name = "destination", .has_arg = 1, .val = 'd'},
+ {.name = "src", .has_arg = 1, .val = 's'}, /* synonym */
+ {.name = "dst", .has_arg = 1, .val = 'd'}, /* synonym */
+ {.name = "protocol", .has_arg = 1, .val = 'p'},
+ {.name = "in-interface", .has_arg = 1, .val = 'i'},
+ {.name = "jump", .has_arg = 1, .val = 'j'},
+ {.name = "table", .has_arg = 1, .val = 't'},
+ {.name = "match", .has_arg = 1, .val = 'm'},
+ {.name = "numeric", .has_arg = 0, .val = 'n'},
+ {.name = "out-interface", .has_arg = 1, .val = 'o'},
+ {.name = "verbose", .has_arg = 0, .val = 'v'},
+ {.name = "exact", .has_arg = 0, .val = 'x'},
+ {.name = "fragments", .has_arg = 0, .val = 'f'},
+ {.name = "version", .has_arg = 0, .val = 'V'},
+ {.name = "help", .has_arg = 2, .val = 'h'},
+ {.name = "line-numbers", .has_arg = 0, .val = '0'},
+ {.name = "modprobe", .has_arg = 1, .val = 'M'},
+ {.name = "set-counters", .has_arg = 1, .val = 'c'},
+ {.name = "goto", .has_arg = 1, .val = 'g'},
+ {.name = "ipv4", .has_arg = 0, .val = '4'},
+ {.name = "ipv6", .has_arg = 0, .val = '6'},
+ {NULL},
+};
+
+void iptables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
+
+struct xtables_globals xtables_globals = {
+ .option_offset = 0,
+ .program_version = IPTABLES_VERSION,
+ .orig_opts = original_opts,
+ .exit_err = iptables_exit_error,
+ .compat_rev = nft_compatible_revision,
+};
+
+/* 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 */
+/*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' '},
+/*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' '},
+/*DELETE_NUM*/{'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'},
+/*FLUSH*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*ZERO*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*ZERO_NUM*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x',' ','x'},
+/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*LIST_RULES*/{'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*CHECK*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' '},
+};
+
+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,
+};
+
+#define opts xtables_globals.opts
+#define prog_name xtables_globals.program_name
+#define prog_vers xtables_globals.program_version
+
+/* Primitive headers... */
+/* defined in netinet/in.h */
+#if 0
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH 51
+#endif
+#endif
+
+enum {
+ IPT_DOTTED_ADDR = 0,
+ IPT_DOTTED_MASK
+};
+
+static void __attribute__((noreturn))
+exit_tryhelp(int status)
+{
+ if (line != -1)
+ fprintf(stderr, "Error occurred at line: %d\n", line);
+ fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+ prog_name, prog_name);
+ xtables_free_opts(1);
+ exit(status);
+}
+
+static void
+exit_printhelp(const struct xtables_rule_match *matches)
+{
+ 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"
+
+"Options:\n"
+" --ipv4 -4 Nothing (line is ignored by ip6tables-restore)\n"
+" --ipv6 -6 Error (line is ignored by iptables-restore)\n"
+"[!] --proto -p proto protocol: by number or name, eg. `tcp'\n"
+"[!] --source -s address[/mask][...]\n"
+" source specification\n"
+"[!] --destination -d address[/mask][...]\n"
+" destination specification\n"
+"[!] --in-interface -i input name[+]\n"
+" network interface name ([+] for wildcard)\n"
+" --jump -j target\n"
+" target for rule (may load target extension)\n"
+#ifdef IPT_F_GOTO
+" --goto -g chain\n"
+" jump to chain with no return\n"
+#endif
+" --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"
+" --line-numbers print line numbers when listing\n"
+" --exact -x expand numbers (display exact values)\n"
+"[!] --fragment -f match second or further fragments only\n"
+" --modprobe=<command> try to insert modules using this command\n"
+" --set-counters PKTS BYTES set the counter during insert/append\n"
+"[!] --version -V print package version.\n");
+
+ print_extension_helps(xtables_targets, matches);
+ exit(0);
+}
+
+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<<j)))
+ continue;
+
+ if (!(options & (1<<i))) {
+ if (commands_v_options[j][i] == '+')
+ xtables_error(PARAMETER_PROBLEM,
+ "You need to supply the `-%c' "
+ "option for this command\n",
+ optflags[i]);
+ } else {
+ if (commands_v_options[j][i] != 'x')
+ legal = 1;
+ else if (legal == 0)
+ legal = -1;
+ }
+ }
+ if (legal == -1)
+ xtables_error(PARAMETER_PROBLEM,
+ "Illegal option `-%c' with this command\n",
+ optflags[i]);
+ }
+}
+
+static char
+opt2char(int option)
+{
+ const char *ptr;
+ for (ptr = optflags; option > 1; option >>= 1, ptr++);
+
+ return *ptr;
+}
+
+static char
+cmd2char(int option)
+{
+ const char *ptr;
+ for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
+
+ return *ptr;
+}
+
+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)));
+ *cmd |= newcmd;
+}
+
+/*
+ * All functions starting with "parse" should succeed, otherwise
+ * the program fails.
+ * Most routines return pointers to static data that may change
+ * between calls to the same or other routines with a few exceptions:
+ * "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
+ * return global static data.
+*/
+
+/* Christophe Burki wants `-p 6' to imply `-m tcp'. */
+/* 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;
+}
+
+static const char *
+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;
+}
+
+static void
+set_option(unsigned int *options, unsigned int option, uint8_t *invflg,
+ int 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];
+ }
+}
+
+static int
+add_entry(const char *chain,
+ const char *table,
+ struct iptables_command_state *cs,
+ unsigned int nsaddrs,
+ const struct in_addr saddrs[],
+ const struct in_addr smasks[],
+ unsigned int ndaddrs,
+ const struct in_addr daddrs[],
+ const struct in_addr dmasks[],
+ bool verbose, struct nft_handle *h, bool append)
+{
+ unsigned int i, j;
+ int ret = 1;
+
+ for (i = 0; i < nsaddrs; i++) {
+ cs->fw.ip.src.s_addr = saddrs[i].s_addr;
+ cs->fw.ip.smsk.s_addr = smasks[i].s_addr;
+ for (j = 0; j < ndaddrs; j++) {
+ cs->fw.ip.dst.s_addr = daddrs[j].s_addr;
+ cs->fw.ip.dmsk.s_addr = dmasks[j].s_addr;
+
+ ret = nft_rule_add(h, chain, table, cs, append, verbose);
+ }
+ }
+
+ return ret;
+}
+
+static int
+replace_entry(const char *chain, const char *table,
+ struct iptables_command_state *cs,
+ unsigned int rulenum,
+ const struct in_addr *saddr, const struct in_addr *smask,
+ const struct in_addr *daddr, const struct in_addr *dmask,
+ bool verbose, struct nft_handle *h)
+{
+ cs->fw.ip.src.s_addr = saddr->s_addr;
+ cs->fw.ip.dst.s_addr = daddr->s_addr;
+ cs->fw.ip.smsk.s_addr = smask->s_addr;
+ cs->fw.ip.dmsk.s_addr = dmask->s_addr;
+
+ return nft_rule_replace(h, chain, table, cs, rulenum, verbose);
+}
+
+static int
+delete_entry(const char *chain, const char *table,
+ struct iptables_command_state *cs,
+ unsigned int nsaddrs,
+ const struct in_addr saddrs[],
+ const struct in_addr smasks[],
+ unsigned int ndaddrs,
+ const struct in_addr daddrs[],
+ const struct in_addr dmasks[],
+ bool verbose,
+ struct nft_handle *h)
+{
+ unsigned int i, j;
+ int ret = 1;
+
+ for (i = 0; i < nsaddrs; i++) {
+ cs->fw.ip.src.s_addr = saddrs[i].s_addr;
+ cs->fw.ip.smsk.s_addr = smasks[i].s_addr;
+ for (j = 0; j < ndaddrs; j++) {
+ cs->fw.ip.dst.s_addr = daddrs[j].s_addr;
+ cs->fw.ip.dmsk.s_addr = dmasks[j].s_addr;
+ ret = nft_rule_delete(h, chain, table, cs, verbose);
+ }
+ }
+
+ return ret;
+}
+
+static int
+check_entry(const char *chain, const char *table,
+ struct iptables_command_state *cs,
+ unsigned int nsaddrs, const struct in_addr *saddrs,
+ const struct in_addr *smasks, unsigned int ndaddrs,
+ const struct in_addr *daddrs, const struct in_addr *dmasks,
+ bool verbose, struct nft_handle *h)
+{
+ unsigned int i, j;
+ int ret = 1;
+
+ for (i = 0; i < nsaddrs; i++) {
+ cs->fw.ip.src.s_addr = saddrs[i].s_addr;
+ cs->fw.ip.smsk.s_addr = smasks[i].s_addr;
+ for (j = 0; j < ndaddrs; j++) {
+ cs->fw.ip.dst.s_addr = daddrs[j].s_addr;
+ cs->fw.ip.dmsk.s_addr = dmasks[j].s_addr;
+ ret = nft_rule_check(h, chain, table, cs, verbose);
+ }
+ }
+
+ return ret;
+}
+
+static int
+zero_entries(const xt_chainlabel chain, int verbose,
+ struct xtc_handle *handle)
+{
+ /* XXX iterate over chains and reset counters */
+ return 1;
+}
+
+static int
+list_entries(struct nft_handle *h, const char *chain, const char *table,
+ int rulenum, int verbose, int numeric, int expanded,
+ int linenumbers)
+{
+ unsigned int format;
+
+ format = FMT_OPTIONS;
+ if (!verbose)
+ format |= FMT_NOCOUNTS;
+ else
+ format |= FMT_VIA;
+
+ if (numeric)
+ format |= FMT_NUMERIC;
+
+ if (!expanded)
+ format |= FMT_KILOMEGAGIGA;
+
+ if (linenumbers)
+ format |= FMT_LINENUMBERS;
+
+ /* FIXME should return found or not, and errno = ENOENT in such case */
+ return nft_rule_list(h, chain, table, rulenum, format);
+}
+
+static int
+list_rules(struct nft_handle *h, const char *chain, const char *table,
+ int rulenum, int counters)
+{
+ if (counters)
+ counters = -1; /* iptables -c format */
+
+ nft_rule_list_save(h, chain, table, rulenum, counters);
+
+ /* FIXME found */
+ return 1;
+}
+
+static void clear_rule_matches(struct xtables_rule_match **matches)
+{
+ struct xtables_rule_match *matchp, *tmp;
+
+ for (matchp = *matches; matchp;) {
+ tmp = matchp->next;
+ if (matchp->match->m) {
+ free(matchp->match->m);
+ matchp->match->m = NULL;
+ }
+ if (matchp->match == matchp->match->next) {
+ free(matchp->match);
+ matchp->match = NULL;
+ }
+ free(matchp);
+ matchp = tmp;
+ }
+
+ *matches = NULL;
+}
+
+static void command_jump(struct iptables_command_state *cs)
+{
+ size_t size;
+
+ set_option(&cs->options, OPT_JUMP, &cs->fw.ip.invflags, cs->invert);
+ cs->jumpto = parse_target(optarg);
+ /* 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(iptables_globals.orig_opts, opts,
+ cs->target->x6_options,
+ &cs->target->option_offset);
+ else
+ opts = xtables_merge_options(iptables_globals.orig_opts, opts,
+ cs->target->extra_opts,
+ &cs->target->option_offset);
+ if (opts == NULL)
+ xtables_error(OTHER_PROBLEM, "can't alloc memory!");
+}
+
+static void command_match(struct iptables_command_state *cs)
+{
+ struct xtables_match *m;
+ size_t size;
+
+ if (cs->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(iptables_globals.orig_opts, opts,
+ m->x6_options, &m->option_offset);
+ else if (m->extra_opts != NULL)
+ opts = xtables_merge_options(iptables_globals.orig_opts, opts,
+ m->extra_opts, &m->option_offset);
+ if (opts == NULL)
+ xtables_error(OTHER_PROBLEM, "can't alloc memory!");
+}
+
+int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table)
+{
+ struct iptables_command_state cs;
+ unsigned int nsaddrs = 0, ndaddrs = 0;
+ struct in_addr *saddrs = NULL, *smasks = NULL;
+ struct in_addr *daddrs = NULL, *dmasks = NULL;
+
+ int verbose = 0;
+ const char *chain = NULL;
+ const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
+ const char *policy = NULL, *newname = NULL;
+ unsigned int rulenum = 0, command = 0;
+ const char *pcnt = NULL, *bcnt = NULL;
+ int ret = 1;
+ struct xtables_match *m;
+ struct xtables_rule_match *matchp;
+ struct xtables_target *t;
+ unsigned long long cnt;
+
+ memset(&cs, 0, sizeof(cs));
+ cs.jumpto = "";
+ cs.argv = argv;
+
+ /* 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;
+
+ opts = xt_params->orig_opts;
+ while ((cs.c = getopt_long(argc, argv,
+ "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:46",
+ opts, NULL)) != -1) {
+ switch (cs.c) {
+ /*
+ * Command selection
+ */
+ case 'A':
+ add_command(&command, CMD_APPEND, CMD_NONE,
+ cs.invert);
+ chain = optarg;
+ break;
+
+ case 'C':
+ add_command(&command, CMD_CHECK, CMD_NONE,
+ cs.invert);
+ chain = optarg;
+ break;
+
+ case 'D':
+ add_command(&command, CMD_DELETE, CMD_NONE,
+ cs.invert);
+ chain = optarg;
+ if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!') {
+ rulenum = parse_rulenumber(argv[optind++]);
+ command = CMD_DELETE_NUM;
+ }
+ break;
+
+ case 'R':
+ add_command(&command, CMD_REPLACE, CMD_NONE,
+ cs.invert);
+ chain = optarg;
+ if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ rulenum = parse_rulenumber(argv[optind++]);
+ else
+ xtables_error(PARAMETER_PROBLEM,
+ "-%c requires a rule number",
+ cmd2char(CMD_REPLACE));
+ break;
+
+ case 'I':
+ add_command(&command, CMD_INSERT, CMD_NONE,
+ cs.invert);
+ chain = optarg;
+ if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ rulenum = parse_rulenumber(argv[optind++]);
+ else rulenum = 1;
+ break;
+
+ case 'L':
+ add_command(&command, CMD_LIST,
+ CMD_ZERO | CMD_ZERO_NUM, cs.invert);
+ if (optarg) chain = optarg;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ chain = argv[optind++];
+ if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ rulenum = parse_rulenumber(argv[optind++]);
+ break;
+
+ case 'S':
+ add_command(&command, CMD_LIST_RULES,
+ CMD_ZERO|CMD_ZERO_NUM, cs.invert);
+ if (optarg) chain = optarg;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ chain = argv[optind++];
+ if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ rulenum = parse_rulenumber(argv[optind++]);
+ break;
+
+ case 'F':
+ add_command(&command, CMD_FLUSH, CMD_NONE,
+ cs.invert);
+ if (optarg) chain = optarg;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ chain = argv[optind++];
+ break;
+
+ case 'Z':
+ add_command(&command, CMD_ZERO, CMD_LIST|CMD_LIST_RULES,
+ cs.invert);
+ if (optarg) chain = optarg;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ chain = argv[optind++];
+ if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!') {
+ rulenum = parse_rulenumber(argv[optind++]);
+ command = CMD_ZERO_NUM;
+ }
+ break;
+
+ case 'N':
+ if (optarg && (*optarg == '-' || *optarg == '!'))
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name not allowed to start "
+ "with `%c'\n", *optarg);
+ if (xtables_find_target(optarg, XTF_TRY_LOAD))
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name may not clash "
+ "with target name\n");
+ add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
+ cs.invert);
+ chain = optarg;
+ break;
+
+ case 'X':
+ add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
+ cs.invert);
+ if (optarg) chain = optarg;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ chain = argv[optind++];
+ break;
+
+ case 'E':
+ add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
+ cs.invert);
+ chain = optarg;
+ if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ newname = argv[optind++];
+ else
+ xtables_error(PARAMETER_PROBLEM,
+ "-%c requires old-chain-name and "
+ "new-chain-name",
+ cmd2char(CMD_RENAME_CHAIN));
+ break;
+
+ case 'P':
+ add_command(&command, CMD_SET_POLICY, CMD_NONE,
+ cs.invert);
+ chain = optarg;
+ if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ 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);
+
+ exit_printhelp(cs.matches);
+
+ /*
+ * Option selection
+ */
+ case 'p':
+ set_option(&cs.options, OPT_PROTOCOL, &cs.fw.ip.invflags,
+ cs.invert);
+
+ /* Canonicalize into lower case */
+ for (cs.protocol = optarg; *cs.protocol; cs.protocol++)
+ *cs.protocol = tolower(*cs.protocol);
+
+ cs.protocol = optarg;
+ cs.fw.ip.proto = xtables_parse_protocol(cs.protocol);
+
+ if (cs.fw.ip.proto == 0
+ && (cs.fw.ip.invflags & XT_INV_PROTO))
+ xtables_error(PARAMETER_PROBLEM,
+ "rule would never match protocol");
+ break;
+
+ case 's':
+ set_option(&cs.options, OPT_SOURCE, &cs.fw.ip.invflags,
+ cs.invert);
+ shostnetworkmask = optarg;
+ break;
+
+ case 'd':
+ set_option(&cs.options, OPT_DESTINATION, &cs.fw.ip.invflags,
+ cs.invert);
+ dhostnetworkmask = optarg;
+ break;
+
+#ifdef IPT_F_GOTO
+ case 'g':
+ set_option(&cs.options, OPT_JUMP, &cs.fw.ip.invflags,
+ cs.invert);
+ cs.fw.ip.flags |= IPT_F_GOTO;
+ cs.jumpto = parse_target(optarg);
+ break;
+#endif
+
+ case 'j':
+ command_jump(&cs);
+ break;
+
+
+ case 'i':
+ if (*optarg == '\0')
+ xtables_error(PARAMETER_PROBLEM,
+ "Empty interface is likely to be "
+ "undesired");
+ set_option(&cs.options, OPT_VIANAMEIN, &cs.fw.ip.invflags,
+ cs.invert);
+ xtables_parse_interface(optarg,
+ cs.fw.ip.iniface,
+ cs.fw.ip.iniface_mask);
+ break;
+
+ case 'o':
+ if (*optarg == '\0')
+ xtables_error(PARAMETER_PROBLEM,
+ "Empty interface is likely to be "
+ "undesired");
+ set_option(&cs.options, OPT_VIANAMEOUT, &cs.fw.ip.invflags,
+ cs.invert);
+ xtables_parse_interface(optarg,
+ cs.fw.ip.outiface,
+ cs.fw.ip.outiface_mask);
+ break;
+
+ case 'f':
+ set_option(&cs.options, OPT_FRAGMENT, &cs.fw.ip.invflags,
+ cs.invert);
+ cs.fw.ip.flags |= IPT_F_FRAG;
+ break;
+
+ case 'v':
+ if (!verbose)
+ set_option(&cs.options, OPT_VERBOSE,
+ &cs.fw.ip.invflags, cs.invert);
+ verbose++;
+ break;
+
+ case 'm':
+ command_match(&cs);
+ break;
+
+ case 'n':
+ set_option(&cs.options, OPT_NUMERIC, &cs.fw.ip.invflags,
+ cs.invert);
+ break;
+
+ case 't':
+ if (cs.invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "unexpected ! flag before --table");
+ *table = optarg;
+ break;
+
+ case 'x':
+ set_option(&cs.options, OPT_EXPANDED, &cs.fw.ip.invflags,
+ cs.invert);
+ break;
+
+ case 'V':
+ if (cs.invert)
+ printf("Not %s ;-)\n", prog_vers);
+ else
+ printf("%s v%s\n",
+ prog_name, prog_vers);
+ exit(0);
+
+ case '0':
+ set_option(&cs.options, OPT_LINENUMBERS, &cs.fw.ip.invflags,
+ cs.invert);
+ break;
+
+ case 'M':
+ xtables_modprobe_program = optarg;
+ break;
+
+ case 'c':
+
+ set_option(&cs.options, OPT_COUNTERS, &cs.fw.ip.invflags,
+ cs.invert);
+ pcnt = optarg;
+ bcnt = strchr(pcnt + 1, ',');
+ if (bcnt)
+ bcnt++;
+ if (!bcnt && optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ bcnt = argv[optind++];
+ if (!bcnt)
+ xtables_error(PARAMETER_PROBLEM,
+ "-%c requires packet and byte counter",
+ opt2char(OPT_COUNTERS));
+
+ if (sscanf(pcnt, "%llu", &cnt) != 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "-%c packet counter not numeric",
+ opt2char(OPT_COUNTERS));
+ cs.counters.pcnt = cnt;
+
+ if (sscanf(bcnt, "%llu", &cnt) != 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "-%c byte counter not numeric",
+ opt2char(OPT_COUNTERS));
+ cs.counters.bcnt = cnt;
+ break;
+
+ case '4':
+ /* This is indeed the IPv4 iptables */
+ break;
+
+ case '6':
+ /* This is not the IPv6 ip6tables */
+ if (line != -1)
+ return 1; /* success: line ignored */
+ fprintf(stderr, "This is the IPv4 version of iptables.\n");
+ exit_tryhelp(2);
+
+ case 1: /* non option */
+ if (optarg[0] == '!' && optarg[1] == '\0') {
+ if (cs.invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "multiple consecutive ! not"
+ " allowed");
+ cs.invert = TRUE;
+ optarg[0] = '\0';
+ continue;
+ }
+ fprintf(stderr, "Bad argument `%s'\n", optarg);
+ exit_tryhelp(2);
+
+ default:
+ if (command_default(&cs, &iptables_globals) == 1)
+ /* cf. ip6tables.c */
+ continue;
+ break;
+ }
+ cs.invert = FALSE;
+ }
+
+ if (strcmp(*table, "nat") == 0 &&
+ ((policy != NULL && strcmp(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");
+
+ 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 (!command)
+ xtables_error(PARAMETER_PROBLEM, "no command specified");
+ if (cs.invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "nothing appropriate following !");
+
+ if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND | CMD_CHECK)) {
+ if (!(cs.options & OPT_DESTINATION))
+ dhostnetworkmask = "0.0.0.0/0";
+ if (!(cs.options & OPT_SOURCE))
+ shostnetworkmask = "0.0.0.0/0";
+ }
+
+ if (shostnetworkmask)
+ xtables_ipparse_multiple(shostnetworkmask, &saddrs,
+ &smasks, &nsaddrs);
+
+ if (dhostnetworkmask)
+ xtables_ipparse_multiple(dhostnetworkmask, &daddrs,
+ &dmasks, &ndaddrs);
+
+ if ((nsaddrs > 1 || ndaddrs > 1) &&
+ (cs.fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))
+ xtables_error(PARAMETER_PROBLEM, "! not allowed with multiple"
+ " source or destination IP addresses");
+
+ if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
+ xtables_error(PARAMETER_PROBLEM, "Replacement rule does not "
+ "specify a unique address");
+
+ generic_opt_check(command, cs.options);
+
+ if (chain != NULL && strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name `%s' too long (must be under %u chars)",
+ chain, XT_EXTENSION_MAXNAMELEN);
+
+ if (command == CMD_APPEND
+ || command == CMD_DELETE
+ || command == CMD_CHECK
+ || command == CMD_INSERT
+ || command == CMD_REPLACE) {
+ if (strcmp(chain, "PREROUTING") == 0
+ || strcmp(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),
+ chain);
+ }
+
+ if (strcmp(chain, "POSTROUTING") == 0
+ || strcmp(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),
+ chain);
+ }
+
+ /*
+ * Contrary to what iptables does, we assume that any jumpto
+ * is a custom chain jumps (if no target is found). Later on,
+ * nf_table will spot the error if the chain does not exists.
+ */
+ }
+
+ switch (command) {
+ case CMD_APPEND:
+ ret = add_entry(chain, *table, &cs,
+ nsaddrs, saddrs, smasks,
+ ndaddrs, daddrs, dmasks,
+ cs.options&OPT_VERBOSE,
+ h, true);
+ break;
+ case CMD_DELETE:
+ ret = delete_entry(chain, *table, &cs,
+ nsaddrs, saddrs, smasks,
+ ndaddrs, daddrs, dmasks,
+ cs.options&OPT_VERBOSE, h);
+ break;
+ case CMD_DELETE_NUM:
+ ret = nft_rule_delete_num(h, chain, *table, rulenum - 1, verbose);
+ break;
+ case CMD_CHECK:
+ ret = check_entry(chain, *table, &cs,
+ nsaddrs, saddrs, smasks,
+ ndaddrs, daddrs, dmasks,
+ cs.options&OPT_VERBOSE, h);
+ break;
+ case CMD_REPLACE:
+ /* FIXME replace at rulenum */
+ ret = replace_entry(chain, *table, &cs, rulenum - 1,
+ saddrs, smasks, daddrs, dmasks,
+ cs.options&OPT_VERBOSE, h);
+ break;
+ case CMD_INSERT:
+ /* FIXME insert at rulenum */
+ ret = add_entry(chain, *table, &cs,
+ nsaddrs, saddrs, smasks,
+ ndaddrs, daddrs, dmasks,
+ cs.options&OPT_VERBOSE, h, false);
+ break;
+ case CMD_FLUSH:
+ ret = nft_rule_flush(h, chain, *table);
+ break;
+ case CMD_ZERO:
+ /* FIXME */
+// ret = zero_entries(chain, cs.options&OPT_VERBOSE, *handle);
+ break;
+ case CMD_ZERO_NUM:
+ /* FIXME */
+// ret = iptc_zero_counter(chain, rulenum, *handle);
+ break;
+ case CMD_LIST:
+ case CMD_LIST|CMD_ZERO:
+ case CMD_LIST|CMD_ZERO_NUM:
+ /* FIXME */
+ ret = list_entries(h, chain, *table,
+ rulenum,
+ cs.options&OPT_VERBOSE,
+ cs.options&OPT_NUMERIC,
+ cs.options&OPT_EXPANDED,
+ cs.options&OPT_LINENUMBERS);
+/* if (ret && (command & CMD_ZERO))
+ ret = zero_entries(chain,
+ cs.options&OPT_VERBOSE, *handle);
+ if (ret && (command & CMD_ZERO_NUM))
+ ret = iptc_zero_counter(chain, rulenum, *handle); */
+ break;
+ case CMD_LIST_RULES:
+ case CMD_LIST_RULES|CMD_ZERO:
+ case CMD_LIST_RULES|CMD_ZERO_NUM:
+ /* FIXME */
+ ret = list_rules(h, chain, *table, rulenum, cs.options&OPT_VERBOSE);
+/* if (ret && (command & CMD_ZERO))
+ ret = zero_entries(chain,
+ cs.options&OPT_VERBOSE, *handle);
+ if (ret && (command & CMD_ZERO_NUM))
+ ret = iptc_zero_counter(chain, rulenum, *handle); */
+ break;
+ case CMD_NEW_CHAIN:
+ ret = nft_chain_user_add(h, chain, *table);
+ break;
+ case CMD_DELETE_CHAIN:
+ ret = nft_chain_user_del(h, chain, *table);
+ break;
+ case CMD_RENAME_CHAIN:
+ /* XXX iptables allows renaming an used chain, we don't */
+ ret = nft_chain_user_rename(h, chain, *table, newname);
+ break;
+ case CMD_SET_POLICY:
+ ret = nft_chain_set(h, *table, chain, policy, NULL);
+ if (ret < 0)
+ xtables_error(PARAMETER_PROBLEM, "Wrong policy `%s'\n",
+ policy);
+ break;
+ default:
+ /* We should never reach this... */
+ exit_tryhelp(2);
+ }
+
+/* if (verbose > 1)
+ dump_entries(*handle); */
+
+ clear_rule_matches(&cs.matches);
+
+ free(saddrs);
+ free(smasks);
+ free(daddrs);
+ free(dmasks);
+ xtables_free_opts(1);
+
+ return ret;
+}