summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac45
-rw-r--r--etc/xtables.conf75
-rw-r--r--extensions/libxt_mangle.c388
-rw-r--r--include/iptables.h2
-rw-r--r--include/linux/netfilter/nf_tables.h718
-rw-r--r--include/linux/netfilter/nf_tables_compat.h20
-rw-r--r--include/linux/netfilter/nfnetlink.h64
-rw-r--r--include/linux/netfilter_arp.h19
-rw-r--r--include/linux/netfilter_arp/arp_tables.h204
-rw-r--r--include/linux/netfilter_arp/arpt_mangle.h26
-rw-r--r--include/xtables.h3
-rw-r--r--iptables/.gitignore3
-rw-r--r--iptables/Makefile.am37
-rw-r--r--iptables/ip6tables.c1
-rw-r--r--iptables/iptables.c1
-rw-r--r--iptables/nft-arp.c649
-rw-r--r--iptables/nft-ipv4.c424
-rw-r--r--iptables/nft-ipv6.c345
-rw-r--r--iptables/nft-shared.c773
-rw-r--r--iptables/nft-shared.h195
-rw-r--r--iptables/nft.c2542
-rw-r--r--iptables/nft.h175
-rw-r--r--iptables/xshared.h1
-rw-r--r--iptables/xtables-arp-standalone.c88
-rw-r--r--iptables/xtables-arp.c1502
-rw-r--r--iptables/xtables-compat-multi.c39
-rw-r--r--iptables/xtables-config-parser.y250
-rw-r--r--iptables/xtables-config-syntax.l54
-rw-r--r--iptables/xtables-config.c46
-rw-r--r--iptables/xtables-events.c217
-rw-r--r--iptables/xtables-multi.c12
-rw-r--r--iptables/xtables-multi.h11
-rw-r--r--iptables/xtables-restore.c488
-rw-r--r--iptables/xtables-save.c154
-rw-r--r--iptables/xtables-standalone.c104
-rw-r--r--iptables/xtables.c1261
-rw-r--r--libxtables/xtables.c6
37 files changed, 10937 insertions, 5 deletions
diff --git a/configure.ac b/configure.ac
index e83304c5..2521ccc8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -60,6 +60,9 @@ AC_ARG_ENABLE([nfsynproxy],
AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH],
[Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]),
[pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig'])
+AC_ARG_ENABLE([nftables],
+ AS_HELP_STRING([--disable-nftables], [Do not build nftables compat]),
+ [enable_nftables="$enableval"], [enable_nftables="yes"])
libiptc_LDFLAGS2="";
AX_CHECK_LINKER_FLAGS([-Wl,--no-as-needed],
@@ -106,6 +109,7 @@ AM_CONDITIONAL([ENABLE_DEVEL], [test "$enable_devel" = "yes"])
AM_CONDITIONAL([ENABLE_LIBIPQ], [test "$enable_libipq" = "yes"])
AM_CONDITIONAL([ENABLE_BPFC], [test "$enable_bpfc" = "yes"])
AM_CONDITIONAL([ENABLE_SYNCONF], [test "$enable_nfsynproxy" = "yes"])
+AM_CONDITIONAL([ENABLE_NFTABLES], [test "$enable_nftables" = "yes"])
if test "x$enable_bpfc" = "xyes" || test "x$enable_nfsynproxy" = "xyes"; then
AC_CHECK_LIB(pcap, pcap_compile,, AC_MSG_ERROR(missing libpcap library required by bpf compiler or nfsynproxy tool))
@@ -115,6 +119,46 @@ PKG_CHECK_MODULES([libnfnetlink], [libnfnetlink >= 1.0],
[nfnetlink=1], [nfnetlink=0])
AM_CONDITIONAL([HAVE_LIBNFNETLINK], [test "$nfnetlink" = 1])
+if test "x$enable_nftables" = "xyes"; then
+ PKG_CHECK_MODULES([libmnl], [libmnl >= 1.0], [mnl=1], [mnl=0])
+
+ PKG_CHECK_MODULES([libnftnl], [libnftnl >= 1.0], [nftables=1], [nftables=0])
+
+ AM_PROG_LEX
+ AC_PROG_YACC
+
+ if test -z "$ac_cv_prog_YACC"
+ then
+ echo "*** Error: No suitable bison/yacc found. ***"
+ echo " Please install the 'bison' package."
+ exit 1
+ fi
+ if test -z "$ac_cv_prog_LEX"
+ then
+ echo "*** Error: No suitable flex/lex found. ***"
+ echo " Please install the 'flex' package."
+ exit 1
+ fi
+
+ AC_MSG_CHECKING(flex version)
+ flex_version=`$ac_cv_prog_LEX --version | sed 's/version//g' | awk '/flex/ {print $2}'`
+ flex_major=`echo $flex_version| cut -d . -f 1`
+ flex_minor=`echo $flex_version| cut -d . -f 2`
+ flex_rev=`echo $flex_version| cut -d . -f 3`
+
+ if test "$flex_major" -eq "2" && test "$flex_minor" -eq "5" && test "$flex_rev" -ge "33"; then
+ AC_MSG_RESULT([$flex_version. OK])
+ else
+ AC_MSG_WARN([flex version $flex_version found.
+ Version 2.5.33 or greater is required. You may experience problems
+ while compilating the nftables compatibility layer for iptables.
+ Please, consider to upgrade flex.])
+ fi
+fi
+
+AM_CONDITIONAL([HAVE_LIBMNL], [test "$mnl" = 1])
+AM_CONDITIONAL([HAVE_LIBNFTNL], [test "$nftables" = 1])
+
regular_CFLAGS="-Wall -Waggregate-return -Wmissing-declarations \
-Wmissing-prototypes -Wredundant-decls -Wshadow -Wstrict-prototypes \
-Winline -pipe";
@@ -182,6 +226,7 @@ Iptables Configuration:
Large file support: ${enable_largefile}
BPF utils support: ${enable_bpfc}
nfsynproxy util support: ${enable_nfsynproxy}
+ nftables support: ${enable_nftables}
Build parameters:
Put plugins into executable (static): ${enable_static}
diff --git a/etc/xtables.conf b/etc/xtables.conf
new file mode 100644
index 00000000..d37b0d7c
--- /dev/null
+++ b/etc/xtables.conf
@@ -0,0 +1,75 @@
+family ipv4 {
+ table raw {
+ chain PREROUTING hook NF_INET_PRE_ROUTING prio -300
+ chain OUTPUT hook NF_INET_LOCAL_OUT prio -300
+ }
+
+ table mangle {
+ chain PREROUTING hook NF_INET_PRE_ROUTING prio -150
+ chain INPUT hook NF_INET_LOCAL_IN prio -150
+ chain FORWARD hook NF_INET_FORWARD prio -150
+ chain OUTPUT hook NF_INET_LOCAL_OUT prio -150
+ chain POSTROUTING hook NF_INET_POST_ROUTING prio -150
+ }
+
+ table filter {
+ chain INPUT hook NF_INET_LOCAL_IN prio 0
+ chain FORWARD hook NF_INET_FORWARD prio 0
+ chain OUTPUT hook NF_INET_LOCAL_OUT prio 0
+ }
+
+ table nat {
+ chain PREROUTING hook NF_INET_PRE_ROUTING prio -100
+ chain INPUT hook NF_INET_LOCAL_IN prio -100
+ chain OUTPUT hook NF_INET_LOCAL_OUT prio 100
+ chain POSTROUTING hook NF_INET_POST_ROUTING prio 100
+ }
+
+ table security {
+ chain INPUT hook NF_INET_LOCAL_IN prio 50
+ chain FORWARD hook NF_INET_FORWARD prio 50
+ chain OUTPUT hook NF_INET_LOCAL_OUT prio 50
+ }
+}
+
+family ipv6 {
+ table raw {
+ chain PREROUTING hook NF_INET_PRE_ROUTING prio -300
+ chain OUTPUT hook NF_INET_LOCAL_OUT prio -300
+ }
+
+ table mangle {
+ chain PREROUTING hook NF_INET_PRE_ROUTING prio -150
+ chain INPUT hook NF_INET_LOCAL_IN prio -150
+ chain FORWARD hook NF_INET_FORWARD prio -150
+ chain OUTPUT hook NF_INET_LOCAL_OUT prio -150
+ chain POSTROUTING hook NF_INET_POST_ROUTING prio -150
+ }
+
+ table filter {
+ chain INPUT hook NF_INET_LOCAL_IN prio 0
+ chain FORWARD hook NF_INET_FORWARD prio 0
+ chain OUTPUT hook NF_INET_LOCAL_OUT prio 0
+ }
+
+ table nat {
+ chain PREROUTING hook NF_INET_PRE_ROUTING prio -100
+ chain INPUT hook NF_INET_LOCAL_IN prio -100
+ chain OUTPUT hook NF_INET_LOCAL_OUT prio 100
+ chain POSTROUTING hook NF_INET_POST_ROUTING prio 100
+ }
+
+ table security {
+ chain INPUT hook NF_INET_LOCAL_IN prio 50
+ chain FORWARD hook NF_INET_FORWARD prio 50
+ chain OUTPUT hook NF_INET_LOCAL_OUT prio 50
+ }
+}
+
+family arp {
+ table filter {
+ chain INPUT hook NF_ARP_IN prio 0
+ chain FORWARD hook NF_ARP_FORWARD prio 0
+ chain OUTPUT hook NF_ARP_OUT prio 0
+ }
+} \ No newline at end of file
diff --git a/extensions/libxt_mangle.c b/extensions/libxt_mangle.c
new file mode 100644
index 00000000..4b20feb3
--- /dev/null
+++ b/extensions/libxt_mangle.c
@@ -0,0 +1,388 @@
+/*
+ * 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.
+ *
+ * Authors:
+ * Libarptc code from: Bart De Schuymer <bdschuym@pandora.be>
+ * Port to libxtables: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <getopt.h>
+#include <errno.h>
+#include <netinet/ether.h>
+
+#include <xtables.h>
+#include <linux/netfilter_arp/arpt_mangle.h>
+
+static void mangle_help(void)
+{
+ printf(
+"mangle target options:\n"
+"--mangle-ip-s IP address\n"
+"--mangle-ip-d IP address\n"
+"--mangle-mac-s MAC address\n"
+"--mangle-mac-d MAC address\n"
+"--mangle-target target (DROP, CONTINUE or ACCEPT -- default is ACCEPT)\n"
+ );
+}
+
+enum {
+ MANGLE_IPS = 0,
+ MANGLE_IPT = 1,
+ MANGLE_DEVS = 2,
+ MANGLE_DEVT = 3,
+ MANGLE_TARGET = 4,
+};
+
+static const struct xt_option_entry mangle_opts[] = {
+ { .name = "mangle-ip-s", .id = MANGLE_IPS, .type = XTTYPE_STRING },
+ { .name = "mangle-ip-d", .id = MANGLE_IPT, .type = XTTYPE_STRING },
+ { .name = "mangle-mac-s", .id = MANGLE_DEVS, .type = XTTYPE_STRING },
+ { .name = "mangle-mac-d", .id = MANGLE_DEVT, .type = XTTYPE_STRING },
+ { .name = "mangle-target", .id = MANGLE_TARGET,
+ .type = XTTYPE_STRING },
+ XTOPT_TABLEEND,
+};
+
+
+static struct in_addr *network_to_addr(const char *name)
+{
+ struct netent *net;
+ static struct in_addr addr;
+
+ if ((net = getnetbyname(name)) != NULL) {
+ if (net->n_addrtype != AF_INET)
+ return (struct in_addr *) NULL;
+ addr.s_addr = htonl((unsigned long) net->n_net);
+ return &addr;
+ }
+
+ return (struct in_addr *) NULL;
+}
+
+static void inaddrcpy(struct in_addr *dst, struct in_addr *src)
+{
+ dst->s_addr = src->s_addr;
+}
+
+static struct in_addr *host_to_addr(const char *name, unsigned int *naddr)
+{
+ struct hostent *host;
+ struct in_addr *addr;
+ unsigned int i;
+
+ *naddr = 0;
+ if ((host = gethostbyname(name)) != NULL) {
+ if (host->h_addrtype != AF_INET ||
+ host->h_length != sizeof(struct in_addr))
+ return (struct in_addr *) NULL;
+
+ while (host->h_addr_list[*naddr] != (char *) NULL)
+ (*naddr)++;
+ addr = xtables_calloc(*naddr, sizeof(struct in_addr));
+ for (i = 0; i < *naddr; i++)
+ inaddrcpy(&(addr[i]),
+ (struct in_addr *) host->h_addr_list[i]);
+ return addr;
+ }
+
+ return (struct in_addr *) NULL;
+}
+
+static int string_to_number(const char *s, unsigned int min,
+ unsigned int max, unsigned int *ret)
+{
+ long number;
+ char *end;
+
+ /* Handle hex, octal, etc. */
+ errno = 0;
+ number = strtol(s, &end, 0);
+ if (*end == '\0' && end != s) {
+ /* we parsed a number, let's see if we want this */
+ if (errno != ERANGE && min <= number && number <= max) {
+ *ret = number;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static struct in_addr *dotted_to_addr(const char *dotted)
+{
+ static struct in_addr addr;
+ unsigned char *addrp;
+ char *p, *q;
+ unsigned int onebyte;
+ int i;
+ char buf[20];
+
+ /* copy dotted string, because we need to modify it */
+ strncpy(buf, dotted, sizeof(buf) - 1);
+ addrp = (unsigned char *) &(addr.s_addr);
+
+ p = buf;
+ for (i = 0; i < 3; i++) {
+ if ((q = strchr(p, '.')) == NULL)
+ return (struct in_addr *) NULL;
+
+ *q = '\0';
+ if (string_to_number(p, 0, 255, &onebyte) == -1)
+ return (struct in_addr *) NULL;
+
+ addrp[i] = (unsigned char) onebyte;
+ p = q + 1;
+ }
+
+ /* we've checked 3 bytes, now we check the last one */
+ if (string_to_number(p, 0, 255, &onebyte) == -1)
+ return (struct in_addr *) NULL;
+
+ addrp[3] = (unsigned char) onebyte;
+
+ return &addr;
+}
+
+static struct in_addr *parse_hostnetwork(const char *name,
+ unsigned int *naddrs)
+{
+ struct in_addr *addrp, *addrptmp;
+
+ if ((addrptmp = dotted_to_addr(name)) != NULL ||
+ (addrptmp = network_to_addr(name)) != NULL) {
+ addrp = xtables_malloc(sizeof(struct in_addr));
+ inaddrcpy(addrp, addrptmp);
+ *naddrs = 1;
+ return addrp;
+ }
+ if ((addrp = host_to_addr(name, naddrs)) != NULL)
+ return addrp;
+
+ xtables_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
+}
+
+static void mangle_parse(struct xt_option_call *cb)
+{
+ const struct arpt_entry *e = cb->xt_entry;
+ struct arpt_mangle *mangle = cb->data;
+ struct in_addr *ipaddr;
+ struct ether_addr *macaddr;
+
+ /* mangle target is by default "ACCEPT". Setting it here,
+ * since original arpt_mangle.c init() no longer exists*/
+ mangle->target = NF_ACCEPT;
+
+ xtables_option_parse(cb);
+ switch (cb->entry->id) {
+ case MANGLE_IPS:
+/*
+ if (e->arp.arpln_mask == 0)
+ xtables_error(PARAMETER_PROBLEM, "no pln defined");
+
+ if (e->arp.invflags & ARPT_INV_ARPPLN)
+ xtables_error(PARAMETER_PROBLEM,
+ "! pln not allowed for --mangle-ip-s");
+*/
+/*
+ if (e->arp.arpln != 4)
+ xtables_error(PARAMETER_PROBLEM, "only pln=4 supported");
+*/
+ {
+ unsigned int nr;
+ ipaddr = parse_hostnetwork(cb->arg, &nr);
+ }
+ mangle->u_s.src_ip.s_addr = ipaddr->s_addr;
+ free(ipaddr);
+ mangle->flags |= ARPT_MANGLE_SIP;
+ break;
+ case MANGLE_IPT:
+/*
+ if (e->arp.arpln_mask == 0)
+ xtables_error(PARAMETER_PROBLEM, "no pln defined");
+
+ if (e->arp.invflags & ARPT_INV_ARPPLN)
+ xtables_error(PARAMETER_PROBLEM,
+ "! pln not allowed for --mangle-ip-d");
+*/
+/*
+ if (e->arp.arpln != 4)
+ xtables_error(PARAMETER_PROBLEM, "only pln=4 supported");
+*/
+ {
+ unsigned int nr;
+ ipaddr = parse_hostnetwork(cb->arg, &nr);
+ }
+ mangle->u_t.tgt_ip.s_addr = ipaddr->s_addr;
+ free(ipaddr);
+ mangle->flags |= ARPT_MANGLE_TIP;
+ break;
+ case MANGLE_DEVS:
+ if (e->arp.arhln_mask == 0)
+ xtables_error(PARAMETER_PROBLEM,
+ "no --h-length defined");
+ if (e->arp.invflags & ARPT_INV_ARPHLN)
+ xtables_error(PARAMETER_PROBLEM,
+ "! --h-length not allowed for "
+ "--mangle-mac-s");
+ if (e->arp.arhln != 6)
+ xtables_error(PARAMETER_PROBLEM,
+ "only --h-length 6 supported");
+ macaddr = ether_aton(cb->arg);
+ if (macaddr == NULL)
+ xtables_error(PARAMETER_PROBLEM, "invalid source MAC");
+ memcpy(mangle->src_devaddr, macaddr, e->arp.arhln);
+ mangle->flags |= ARPT_MANGLE_SDEV;
+ break;
+ case MANGLE_DEVT:
+ if (e->arp.arhln_mask == 0)
+ xtables_error(PARAMETER_PROBLEM,
+ "no --h-length defined");
+ if (e->arp.invflags & ARPT_INV_ARPHLN)
+ xtables_error(PARAMETER_PROBLEM,
+ "! hln not allowed for --mangle-mac-d");
+ if (e->arp.arhln != 6)
+ xtables_error(PARAMETER_PROBLEM,
+ "only --h-length 6 supported");
+ macaddr = ether_aton(cb->arg);
+ if (macaddr == NULL)
+ xtables_error(PARAMETER_PROBLEM, "invalid target MAC");
+ memcpy(mangle->tgt_devaddr, macaddr, e->arp.arhln);
+ mangle->flags |= ARPT_MANGLE_TDEV;
+ break;
+ case MANGLE_TARGET:
+ if (!strcmp(cb->arg, "DROP"))
+ mangle->target = NF_DROP;
+ else if (!strcmp(cb->arg, "ACCEPT"))
+ mangle->target = NF_ACCEPT;
+ else if (!strcmp(cb->arg, "CONTINUE"))
+ mangle->target = ARPT_CONTINUE;
+ else
+ xtables_error(PARAMETER_PROBLEM,
+ "bad target for --mangle-target");
+ break;
+ }
+}
+
+static void mangle_fcheck(struct xt_fcheck_call *cb)
+{
+}
+
+static char *addr_to_dotted(const struct in_addr *addrp)
+{
+ static char buf[20];
+ const unsigned char *bytep;
+
+ bytep = (const unsigned char *) &(addrp->s_addr);
+ sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
+ return buf;
+}
+
+static char *addr_to_host(const struct in_addr *addr)
+{
+ struct hostent *host;
+
+ if ((host = gethostbyaddr((char *) addr,
+ sizeof(struct in_addr), AF_INET)) != NULL)
+ return (char *) host->h_name;
+
+ return (char *) NULL;
+}
+
+static char *addr_to_network(const struct in_addr *addr)
+{
+ struct netent *net;
+
+ if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL)
+ return (char *) net->n_name;
+
+ return (char *) NULL;
+}
+
+static char *addr_to_anyname(const struct in_addr *addr)
+{
+ char *name;
+
+ if ((name = addr_to_host(addr)) != NULL ||
+ (name = addr_to_network(addr)) != NULL)
+ return name;
+
+ return addr_to_dotted(addr);
+}
+
+static void print_mac(const unsigned char *mac, int l)
+{
+ int j;
+
+ for (j = 0; j < l; j++)
+ printf("%02x%s", mac[j],
+ (j==l-1) ? "" : ":");
+}
+
+static void mangle_print(const void *ip, const struct xt_entry_target *target,
+ int numeric)
+{
+ const struct arpt_mangle *m = (const void *)target;
+ char buf[100];
+
+ if (m->flags & ARPT_MANGLE_SIP) {
+ if (numeric)
+ sprintf(buf, "%s", addr_to_dotted(&(m->u_s.src_ip)));
+ else
+ sprintf(buf, "%s", addr_to_anyname(&(m->u_s.src_ip)));
+ printf("--mangle-ip-s %s ", buf);
+ }
+ if (m->flags & ARPT_MANGLE_SDEV) {
+ printf("--mangle-mac-s ");
+ print_mac((unsigned char *)m->src_devaddr, 6);
+ printf(" ");
+ }
+ if (m->flags & ARPT_MANGLE_TIP) {
+ if (numeric)
+ sprintf(buf, "%s", addr_to_dotted(&(m->u_t.tgt_ip)));
+ else
+ sprintf(buf, "%s", addr_to_anyname(&(m->u_t.tgt_ip)));
+ printf("--mangle-ip-d %s ", buf);
+ }
+ if (m->flags & ARPT_MANGLE_TDEV) {
+ printf("--mangle-mac-d ");
+ print_mac((unsigned char *)m->tgt_devaddr, 6);
+ printf(" ");
+ }
+ if (m->target != NF_ACCEPT) {
+ printf("--mangle-target ");
+ if (m->target == NF_DROP)
+ printf("DROP ");
+ else
+ printf("CONTINUE ");
+ }
+}
+
+static void mangle_save(const void *ip, const struct xt_entry_target *target)
+{
+}
+
+static struct xtables_target mangle_tg_reg = {
+ .family = NFPROTO_ARP,
+ .name = "mangle",
+ .version = XTABLES_VERSION,
+ .size = XT_ALIGN(sizeof(struct arpt_mangle)),
+ .userspacesize = XT_ALIGN(sizeof(struct arpt_mangle)),
+ .help = mangle_help,
+ .x6_parse = mangle_parse,
+ .x6_fcheck = mangle_fcheck,
+ .print = mangle_print,
+ .save = mangle_save,
+ .x6_options = mangle_opts,
+};
+
+void _init(void)
+{
+ xtables_register_target(&mangle_tg_reg);
+}
diff --git a/include/iptables.h b/include/iptables.h
index ac9dc0e5..78c10abd 100644
--- a/include/iptables.h
+++ b/include/iptables.h
@@ -20,4 +20,6 @@ extern void print_rule4(const struct ipt_entry *e,
extern struct xtables_globals iptables_globals;
+extern struct xtables_globals xtables_globals;
+
#endif /*_IPTABLES_USER_H*/
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
new file mode 100644
index 00000000..fbfd229a
--- /dev/null
+++ b/include/linux/netfilter/nf_tables.h
@@ -0,0 +1,718 @@
+#ifndef _LINUX_NF_TABLES_H
+#define _LINUX_NF_TABLES_H
+
+#define NFT_CHAIN_MAXNAMELEN 32
+
+enum nft_registers {
+ NFT_REG_VERDICT,
+ NFT_REG_1,
+ NFT_REG_2,
+ NFT_REG_3,
+ NFT_REG_4,
+ __NFT_REG_MAX
+};
+#define NFT_REG_MAX (__NFT_REG_MAX - 1)
+
+/**
+ * enum nft_verdicts - nf_tables internal verdicts
+ *
+ * @NFT_CONTINUE: continue evaluation of the current rule
+ * @NFT_BREAK: terminate evaluation of the current rule
+ * @NFT_JUMP: push the current chain on the jump stack and jump to a chain
+ * @NFT_GOTO: jump to a chain without pushing the current chain on the jump stack
+ * @NFT_RETURN: return to the topmost chain on the jump stack
+ *
+ * The nf_tables verdicts share their numeric space with the netfilter verdicts.
+ */
+enum nft_verdicts {
+ NFT_CONTINUE = -1,
+ NFT_BREAK = -2,
+ NFT_JUMP = -3,
+ NFT_GOTO = -4,
+ NFT_RETURN = -5,
+};
+
+/**
+ * enum nf_tables_msg_types - nf_tables netlink message types
+ *
+ * @NFT_MSG_NEWTABLE: create a new table (enum nft_table_attributes)
+ * @NFT_MSG_GETTABLE: get a table (enum nft_table_attributes)
+ * @NFT_MSG_DELTABLE: delete a table (enum nft_table_attributes)
+ * @NFT_MSG_NEWCHAIN: create a new chain (enum nft_chain_attributes)
+ * @NFT_MSG_GETCHAIN: get a chain (enum nft_chain_attributes)
+ * @NFT_MSG_DELCHAIN: delete a chain (enum nft_chain_attributes)
+ * @NFT_MSG_NEWRULE: create a new rule (enum nft_rule_attributes)
+ * @NFT_MSG_GETRULE: get a rule (enum nft_rule_attributes)
+ * @NFT_MSG_DELRULE: delete a rule (enum nft_rule_attributes)
+ * @NFT_MSG_NEWSET: create a new set (enum nft_set_attributes)
+ * @NFT_MSG_GETSET: get a set (enum nft_set_attributes)
+ * @NFT_MSG_DELSET: delete a set (enum nft_set_attributes)
+ * @NFT_MSG_NEWSETELEM: create a new set element (enum nft_set_elem_attributes)
+ * @NFT_MSG_GETSETELEM: get a set element (enum nft_set_elem_attributes)
+ * @NFT_MSG_DELSETELEM: delete a set element (enum nft_set_elem_attributes)
+ */
+enum nf_tables_msg_types {
+ NFT_MSG_NEWTABLE,
+ NFT_MSG_GETTABLE,
+ NFT_MSG_DELTABLE,
+ NFT_MSG_NEWCHAIN,
+ NFT_MSG_GETCHAIN,
+ NFT_MSG_DELCHAIN,
+ NFT_MSG_NEWRULE,
+ NFT_MSG_GETRULE,
+ NFT_MSG_DELRULE,
+ NFT_MSG_NEWSET,
+ NFT_MSG_GETSET,
+ NFT_MSG_DELSET,
+ NFT_MSG_NEWSETELEM,
+ NFT_MSG_GETSETELEM,
+ NFT_MSG_DELSETELEM,
+ NFT_MSG_MAX,
+};
+
+/**
+ * enum nft_list_attributes - nf_tables generic list netlink attributes
+ *
+ * @NFTA_LIST_ELEM: list element (NLA_NESTED)
+ */
+enum nft_list_attributes {
+ NFTA_LIST_UNPEC,
+ NFTA_LIST_ELEM,
+ __NFTA_LIST_MAX
+};
+#define NFTA_LIST_MAX (__NFTA_LIST_MAX - 1)
+
+/**
+ * enum nft_hook_attributes - nf_tables netfilter hook netlink attributes
+ *
+ * @NFTA_HOOK_HOOKNUM: netfilter hook number (NLA_U32)
+ * @NFTA_HOOK_PRIORITY: netfilter hook priority (NLA_U32)
+ */
+enum nft_hook_attributes {
+ NFTA_HOOK_UNSPEC,
+ NFTA_HOOK_HOOKNUM,
+ NFTA_HOOK_PRIORITY,
+ __NFTA_HOOK_MAX
+};
+#define NFTA_HOOK_MAX (__NFTA_HOOK_MAX - 1)
+
+/**
+ * enum nft_table_flags - nf_tables table flags
+ *
+ * @NFT_TABLE_F_DORMANT: this table is not active
+ */
+enum nft_table_flags {
+ NFT_TABLE_F_DORMANT = 0x1,
+};
+
+/**
+ * enum nft_table_attributes - nf_tables table netlink attributes
+ *
+ * @NFTA_TABLE_NAME: name of the table (NLA_STRING)
+ * @NFTA_TABLE_FLAGS: bitmask of enum nft_table_flags (NLA_U32)
+ */
+enum nft_table_attributes {
+ NFTA_TABLE_UNSPEC,
+ NFTA_TABLE_NAME,
+ NFTA_TABLE_FLAGS,
+ __NFTA_TABLE_MAX
+};
+#define NFTA_TABLE_MAX (__NFTA_TABLE_MAX - 1)
+
+/**
+ * enum nft_chain_attributes - nf_tables chain netlink attributes
+ *
+ * @NFTA_CHAIN_TABLE: name of the table containing the chain (NLA_STRING)
+ * @NFTA_CHAIN_HANDLE: numeric handle of the chain (NLA_U64)
+ * @NFTA_CHAIN_NAME: name of the chain (NLA_STRING)
+ * @NFTA_CHAIN_HOOK: hook specification for basechains (NLA_NESTED: nft_hook_attributes)
+ * @NFTA_CHAIN_POLICY: numeric policy of the chain (NLA_U32)
+ * @NFTA_CHAIN_USE: number of references to this chain (NLA_U32)
+ * @NFTA_CHAIN_TYPE: type name of the string (NLA_NUL_STRING)
+ * @NFTA_CHAIN_COUNTERS: counter specification of the chain (NLA_NESTED: nft_counter_attributes)
+ */
+enum nft_chain_attributes {
+ NFTA_CHAIN_UNSPEC,
+ NFTA_CHAIN_TABLE,
+ NFTA_CHAIN_HANDLE,
+ NFTA_CHAIN_NAME,
+ NFTA_CHAIN_HOOK,
+ NFTA_CHAIN_POLICY,
+ NFTA_CHAIN_USE,
+ NFTA_CHAIN_TYPE,
+ NFTA_CHAIN_COUNTERS,
+ __NFTA_CHAIN_MAX
+};
+#define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1)
+
+/**
+ * enum nft_rule_attributes - nf_tables rule netlink attributes
+ *
+ * @NFTA_RULE_TABLE: name of the table containing the rule (NLA_STRING)
+ * @NFTA_RULE_CHAIN: name of the chain containing the rule (NLA_STRING)
+ * @NFTA_RULE_HANDLE: numeric handle of the rule (NLA_U64)
+ * @NFTA_RULE_EXPRESSIONS: list of expressions (NLA_NESTED: nft_expr_attributes)
+ * @NFTA_RULE_COMPAT: compatibility specifications of the rule (NLA_NESTED: nft_rule_compat_attributes)
+ * @NFTA_RULE_POSITION: numeric handle of the previous rule (NLA_U64)
+ */
+enum nft_rule_attributes {
+ NFTA_RULE_UNSPEC,
+ NFTA_RULE_TABLE,
+ NFTA_RULE_CHAIN,
+ NFTA_RULE_HANDLE,
+ NFTA_RULE_EXPRESSIONS,
+ NFTA_RULE_COMPAT,
+ NFTA_RULE_POSITION,
+ __NFTA_RULE_MAX
+};
+#define NFTA_RULE_MAX (__NFTA_RULE_MAX - 1)
+
+/**
+ * enum nft_rule_compat_flags - nf_tables rule compat flags
+ *
+ * @NFT_RULE_COMPAT_F_INV: invert the check result
+ */
+enum nft_rule_compat_flags {
+ NFT_RULE_COMPAT_F_INV = (1 << 1),
+ NFT_RULE_COMPAT_F_MASK = NFT_RULE_COMPAT_F_INV,
+};
+
+/**
+ * enum nft_rule_compat_attributes - nf_tables rule compat attributes
+ *
+ * @NFTA_RULE_COMPAT_PROTO: numerice value of handled protocol (NLA_U32)
+ * @NFTA_RULE_COMPAT_FLAGS: bitmask of enum nft_rule_compat_flags (NLA_U32)
+ */
+enum nft_rule_compat_attributes {
+ NFTA_RULE_COMPAT_UNSPEC,
+ NFTA_RULE_COMPAT_PROTO,
+ NFTA_RULE_COMPAT_FLAGS,
+ __NFTA_RULE_COMPAT_MAX
+};
+#define NFTA_RULE_COMPAT_MAX (__NFTA_RULE_COMPAT_MAX - 1)
+
+/**
+ * enum nft_set_flags - nf_tables set flags
+ *
+ * @NFT_SET_ANONYMOUS: name allocation, automatic cleanup on unlink
+ * @NFT_SET_CONSTANT: set contents may not change while bound
+ * @NFT_SET_INTERVAL: set contains intervals
+ * @NFT_SET_MAP: set is used as a dictionary
+ */
+enum nft_set_flags {
+ NFT_SET_ANONYMOUS = 0x1,
+ NFT_SET_CONSTANT = 0x2,
+ NFT_SET_INTERVAL = 0x4,
+ NFT_SET_MAP = 0x8,
+};
+
+/**
+ * enum nft_set_attributes - nf_tables set netlink attributes
+ *
+ * @NFTA_SET_TABLE: table name (NLA_STRING)
+ * @NFTA_SET_NAME: set name (NLA_STRING)
+ * @NFTA_SET_FLAGS: bitmask of enum nft_set_flags (NLA_U32)
+ * @NFTA_SET_KEY_TYPE: key data type, informational purpose only (NLA_U32)
+ * @NFTA_SET_KEY_LEN: key data length (NLA_U32)
+ * @NFTA_SET_DATA_TYPE: mapping data type (NLA_U32)
+ * @NFTA_SET_DATA_LEN: mapping data length (NLA_U32)
+ */
+enum nft_set_attributes {
+ NFTA_SET_UNSPEC,
+ NFTA_SET_TABLE,
+ NFTA_SET_NAME,
+ NFTA_SET_FLAGS,
+ NFTA_SET_KEY_TYPE,
+ NFTA_SET_KEY_LEN,
+ NFTA_SET_DATA_TYPE,
+ NFTA_SET_DATA_LEN,
+ __NFTA_SET_MAX
+};
+#define NFTA_SET_MAX (__NFTA_SET_MAX - 1)
+
+/**
+ * enum nft_set_elem_flags - nf_tables set element flags
+ *
+ * @NFT_SET_ELEM_INTERVAL_END: element ends the previous interval
+ */
+enum nft_set_elem_flags {
+ NFT_SET_ELEM_INTERVAL_END = 0x1,
+};
+
+/**
+ * enum nft_set_elem_attributes - nf_tables set element netlink attributes
+ *
+ * @NFTA_SET_ELEM_KEY: key value (NLA_NESTED: nft_data)
+ * @NFTA_SET_ELEM_DATA: data value of mapping (NLA_NESTED: nft_data_attributes)
+ * @NFTA_SET_ELEM_FLAGS: bitmask of nft_set_elem_flags (NLA_U32)
+ */
+enum nft_set_elem_attributes {
+ NFTA_SET_ELEM_UNSPEC,
+ NFTA_SET_ELEM_KEY,
+ NFTA_SET_ELEM_DATA,
+ NFTA_SET_ELEM_FLAGS,
+ __NFTA_SET_ELEM_MAX
+};
+#define NFTA_SET_ELEM_MAX (__NFTA_SET_ELEM_MAX - 1)
+
+/**
+ * enum nft_set_elem_list_attributes - nf_tables set element list netlink attributes
+ *
+ * @NFTA_SET_ELEM_LIST_TABLE: table of the set to be changed (NLA_STRING)
+ * @NFTA_SET_ELEM_LIST_SET: name of the set to be changed (NLA_STRING)
+ * @NFTA_SET_ELEM_LIST_ELEMENTS: list of set elements (NLA_NESTED: nft_set_elem_attributes)
+ */
+enum nft_set_elem_list_attributes {
+ NFTA_SET_ELEM_LIST_UNSPEC,
+ NFTA_SET_ELEM_LIST_TABLE,
+ NFTA_SET_ELEM_LIST_SET,
+ NFTA_SET_ELEM_LIST_ELEMENTS,
+ __NFTA_SET_ELEM_LIST_MAX
+};
+#define NFTA_SET_ELEM_LIST_MAX (__NFTA_SET_ELEM_LIST_MAX - 1)
+
+/**
+ * enum nft_data_types - nf_tables data types
+ *
+ * @NFT_DATA_VALUE: generic data
+ * @NFT_DATA_VERDICT: netfilter verdict
+ *
+ * The type of data is usually determined by the kernel directly and is not
+ * explicitly specified by userspace. The only difference are sets, where
+ * userspace specifies the key and mapping data types.
+ *
+ * The values 0xffffff00-0xffffffff are reserved for internally used types.
+ * The remaining range can be freely used by userspace to encode types, all
+ * values are equivalent to NFT_DATA_VALUE.
+ */
+enum nft_data_types {
+ NFT_DATA_VALUE,
+ NFT_DATA_VERDICT = 0xffffff00U,
+};
+
+#define NFT_DATA_RESERVED_MASK 0xffffff00U
+
+/**
+ * enum nft_data_attributes - nf_tables data netlink attributes
+ *
+ * @NFTA_DATA_VALUE: generic data (NLA_BINARY)
+ * @NFTA_DATA_VERDICT: nf_tables verdict (NLA_NESTED: nft_verdict_attributes)
+ */
+enum nft_data_attributes {
+ NFTA_DATA_UNSPEC,
+ NFTA_DATA_VALUE,
+ NFTA_DATA_VERDICT,
+ __NFTA_DATA_MAX
+};
+#define NFTA_DATA_MAX (__NFTA_DATA_MAX - 1)
+
+/**
+ * enum nft_verdict_attributes - nf_tables verdict netlink attributes
+ *
+ * @NFTA_VERDICT_CODE: nf_tables verdict (NLA_U32: enum nft_verdicts)
+ * @NFTA_VERDICT_CHAIN: jump target chain name (NLA_STRING)
+ */
+enum nft_verdict_attributes {
+ NFTA_VERDICT_UNSPEC,
+ NFTA_VERDICT_CODE,
+ NFTA_VERDICT_CHAIN,
+ __NFTA_VERDICT_MAX
+};
+#define NFTA_VERDICT_MAX (__NFTA_VERDICT_MAX - 1)
+
+/**
+ * enum nft_expr_attributes - nf_tables expression netlink attributes
+ *
+ * @NFTA_EXPR_NAME: name of the expression type (NLA_STRING)
+ * @NFTA_EXPR_DATA: type specific data (NLA_NESTED)
+ */
+enum nft_expr_attributes {
+ NFTA_EXPR_UNSPEC,
+ NFTA_EXPR_NAME,
+ NFTA_EXPR_DATA,
+ __NFTA_EXPR_MAX
+};
+#define NFTA_EXPR_MAX (__NFTA_EXPR_MAX - 1)
+
+/**
+ * enum nft_immediate_attributes - nf_tables immediate expression netlink attributes
+ *
+ * @NFTA_IMMEDIATE_DREG: destination register to load data into (NLA_U32)
+ * @NFTA_IMMEDIATE_DATA: data to load (NLA_NESTED: nft_data_attributes)
+ */
+enum nft_immediate_attributes {
+ NFTA_IMMEDIATE_UNSPEC,
+ NFTA_IMMEDIATE_DREG,
+ NFTA_IMMEDIATE_DATA,
+ __NFTA_IMMEDIATE_MAX
+};
+#define NFTA_IMMEDIATE_MAX (__NFTA_IMMEDIATE_MAX - 1)
+
+/**
+ * enum nft_bitwise_attributes - nf_tables bitwise expression netlink attributes
+ *
+ * @NFTA_BITWISE_SREG: source register (NLA_U32: nft_registers)
+ * @NFTA_BITWISE_DREG: destination register (NLA_U32: nft_registers)
+ * @NFTA_BITWISE_LEN: length of operands (NLA_U32)
+ * @NFTA_BITWISE_MASK: mask value (NLA_NESTED: nft_data_attributes)
+ * @NFTA_BITWISE_XOR: xor value (NLA_NESTED: nft_data_attributes)
+ *
+ * The bitwise expression performs the following operation:
+ *
+ * dreg = (sreg & mask) ^ xor
+ *
+ * which allow to express all bitwise operations:
+ *
+ * mask xor
+ * NOT: 1 1
+ * OR: 0 x
+ * XOR: 1 x
+ * AND: x 0
+ */
+enum nft_bitwise_attributes {
+ NFTA_BITWISE_UNSPEC,
+ NFTA_BITWISE_SREG,
+ NFTA_BITWISE_DREG,
+ NFTA_BITWISE_LEN,
+ NFTA_BITWISE_MASK,
+ NFTA_BITWISE_XOR,
+ __NFTA_BITWISE_MAX
+};
+#define NFTA_BITWISE_MAX (__NFTA_BITWISE_MAX - 1)
+
+/**
+ * enum nft_byteorder_ops - nf_tables byteorder operators
+ *
+ * @NFT_BYTEORDER_NTOH: network to host operator
+ * @NFT_BYTEORDER_HTON: host to network opertaor
+ */
+enum nft_byteorder_ops {
+ NFT_BYTEORDER_NTOH,
+ NFT_BYTEORDER_HTON,
+};
+
+/**
+ * enum nft_byteorder_attributes - nf_tables byteorder expression netlink attributes
+ *
+ * @NFTA_BYTEORDER_SREG: source register (NLA_U32: nft_registers)
+ * @NFTA_BYTEORDER_DREG: destination register (NLA_U32: nft_registers)
+ * @NFTA_BYTEORDER_OP: operator (NLA_U32: enum nft_byteorder_ops)
+ * @NFTA_BYTEORDER_LEN: length of the data (NLA_U32)
+ * @NFTA_BYTEORDER_SIZE: data size in bytes (NLA_U32: 2 or 4)
+ */
+enum nft_byteorder_attributes {
+ NFTA_BYTEORDER_UNSPEC,
+ NFTA_BYTEORDER_SREG,
+ NFTA_BYTEORDER_DREG,
+ NFTA_BYTEORDER_OP,
+ NFTA_BYTEORDER_LEN,
+ NFTA_BYTEORDER_SIZE,
+ __NFTA_BYTEORDER_MAX
+};
+#define NFTA_BYTEORDER_MAX (__NFTA_BYTEORDER_MAX - 1)
+
+/**
+ * enum nft_cmp_ops - nf_tables relational operator
+ *
+ * @NFT_CMP_EQ: equal
+ * @NFT_CMP_NEQ: not equal
+ * @NFT_CMP_LT: less than
+ * @NFT_CMP_LTE: less than or equal to
+ * @NFT_CMP_GT: greater than
+ * @NFT_CMP_GTE: greater than or equal to
+ */
+enum nft_cmp_ops {
+ NFT_CMP_EQ,
+ NFT_CMP_NEQ,
+ NFT_CMP_LT,
+ NFT_CMP_LTE,
+ NFT_CMP_GT,
+ NFT_CMP_GTE,
+};
+
+/**
+ * enum nft_cmp_attributes - nf_tables cmp expression netlink attributes
+ *
+ * @NFTA_CMP_SREG: source register of data to compare (NLA_U32: nft_registers)
+ * @NFTA_CMP_OP: cmp operation (NLA_U32: nft_cmp_ops)
+ * @NFTA_CMP_DATA: data to compare against (NLA_NESTED: nft_data_attributes)
+ */
+enum nft_cmp_attributes {
+ NFTA_CMP_UNSPEC,
+ NFTA_CMP_SREG,
+ NFTA_CMP_OP,
+ NFTA_CMP_DATA,
+ __NFTA_CMP_MAX
+};
+#define NFTA_CMP_MAX (__NFTA_CMP_MAX - 1)
+
+/**
+ * enum nft_lookup_attributes - nf_tables set lookup expression netlink attributes
+ *
+ * @NFTA_LOOKUP_SET: name of the set where to look for (NLA_STRING)
+ * @NFTA_LOOKUP_SREG: source register of the data to look for (NLA_U32: nft_registers)
+ * @NFTA_LOOKUP_DREG: destination register (NLA_U32: nft_registers)
+ */
+enum nft_lookup_attributes {
+ NFTA_LOOKUP_UNSPEC,
+ NFTA_LOOKUP_SET,
+ NFTA_LOOKUP_SREG,
+ NFTA_LOOKUP_DREG,
+ __NFTA_LOOKUP_MAX
+};
+#define NFTA_LOOKUP_MAX (__NFTA_LOOKUP_MAX - 1)
+
+/**
+ * enum nft_payload_bases - nf_tables payload expression offset bases
+ *
+ * @NFT_PAYLOAD_LL_HEADER: link layer header
+ * @NFT_PAYLOAD_NETWORK_HEADER: network header
+ * @NFT_PAYLOAD_TRANSPORT_HEADER: transport header
+ */
+enum nft_payload_bases {
+ NFT_PAYLOAD_LL_HEADER,
+ NFT_PAYLOAD_NETWORK_HEADER,
+ NFT_PAYLOAD_TRANSPORT_HEADER,
+};
+
+/**
+ * enum nft_payload_attributes - nf_tables payload expression netlink attributes
+ *
+ * @NFTA_PAYLOAD_DREG: destination register to load data into (NLA_U32: nft_registers)
+ * @NFTA_PAYLOAD_BASE: payload base (NLA_U32: nft_payload_bases)
+ * @NFTA_PAYLOAD_OFFSET: payload offset relative to base (NLA_U32)
+ * @NFTA_PAYLOAD_LEN: payload length (NLA_U32)
+ */
+enum nft_payload_attributes {
+ NFTA_PAYLOAD_UNSPEC,
+ NFTA_PAYLOAD_DREG,
+ NFTA_PAYLOAD_BASE,
+ NFTA_PAYLOAD_OFFSET,
+ NFTA_PAYLOAD_LEN,
+ __NFTA_PAYLOAD_MAX
+};
+#define NFTA_PAYLOAD_MAX (__NFTA_PAYLOAD_MAX - 1)
+
+/**
+ * enum nft_exthdr_attributes - nf_tables IPv6 extension header expression netlink attributes
+ *
+ * @NFTA_EXTHDR_DREG: destination register (NLA_U32: nft_registers)
+ * @NFTA_EXTHDR_TYPE: extension header type (NLA_U8)
+ * @NFTA_EXTHDR_OFFSET: extension header offset (NLA_U32)
+ * @NFTA_EXTHDR_LEN: extension header length (NLA_U32)
+ */
+enum nft_exthdr_attributes {
+ NFTA_EXTHDR_UNSPEC,
+ NFTA_EXTHDR_DREG,
+ NFTA_EXTHDR_TYPE,
+ NFTA_EXTHDR_OFFSET,
+ NFTA_EXTHDR_LEN,
+ __NFTA_EXTHDR_MAX
+};
+#define NFTA_EXTHDR_MAX (__NFTA_EXTHDR_MAX - 1)
+
+/**
+ * enum nft_meta_keys - nf_tables meta expression keys
+ *
+ * @NFT_META_LEN: packet length (skb->len)
+ * @NFT_META_PROTOCOL: packet ethertype protocol (skb->protocol), invalid in OUTPUT
+ * @NFT_META_PRIORITY: packet priority (skb->priority)
+ * @NFT_META_MARK: packet mark (skb->mark)
+ * @NFT_META_IIF: packet input interface index (dev->ifindex)
+ * @NFT_META_OIF: packet output interface index (dev->ifindex)
+ * @NFT_META_IIFNAME: packet input interface name (dev->name)
+ * @NFT_META_OIFNAME: packet output interface name (dev->name)
+ * @NFT_META_IIFTYPE: packet input interface type (dev->type)
+ * @NFT_META_OIFTYPE: packet output interface type (dev->type)
+ * @NFT_META_SKUID: originating socket UID (fsuid)
+ * @NFT_META_SKGID: originating socket GID (fsgid)
+ * @NFT_META_NFTRACE: packet nftrace bit
+ * @NFT_META_RTCLASSID: realm value of packet's route (skb->dst->tclassid)
+ * @NFT_META_SECMARK: packet secmark (skb->secmark)
+ */
+enum nft_meta_keys {
+ NFT_META_LEN,
+ NFT_META_PROTOCOL,
+ NFT_META_PRIORITY,
+ NFT_META_MARK,
+ NFT_META_IIF,
+ NFT_META_OIF,
+ NFT_META_IIFNAME,
+ NFT_META_OIFNAME,
+ NFT_META_IIFTYPE,
+ NFT_META_OIFTYPE,
+ NFT_META_SKUID,
+ NFT_META_SKGID,
+ NFT_META_NFTRACE,
+ NFT_META_RTCLASSID,
+ NFT_META_SECMARK,
+};
+
+/**
+ * enum nft_meta_attributes - nf_tables meta expression netlink attributes
+ *
+ * @NFTA_META_DREG: destination register (NLA_U32)
+ * @NFTA_META_KEY: meta data item to load (NLA_U32: nft_meta_keys)
+ */
+enum nft_meta_attributes {
+ NFTA_META_UNSPEC,
+ NFTA_META_DREG,
+ NFTA_META_KEY,
+ __NFTA_META_MAX
+};
+#define NFTA_META_MAX (__NFTA_META_MAX - 1)
+
+/**
+ * enum nft_ct_keys - nf_tables ct expression keys
+ *
+ * @NFT_CT_STATE: conntrack state (bitmask of enum ip_conntrack_info)
+ * @NFT_CT_DIRECTION: conntrack direction (enum ip_conntrack_dir)
+ * @NFT_CT_STATUS: conntrack status (bitmask of enum ip_conntrack_status)
+ * @NFT_CT_MARK: conntrack mark value
+ * @NFT_CT_SECMARK: conntrack secmark value
+ * @NFT_CT_EXPIRATION: relative conntrack expiration time in ms
+ * @NFT_CT_HELPER: connection tracking helper assigned to conntrack
+ * @NFT_CT_L3PROTOCOL: conntrack layer 3 protocol
+ * @NFT_CT_SRC: conntrack layer 3 protocol source (IPv4/IPv6 address)
+ * @NFT_CT_DST: conntrack layer 3 protocol destination (IPv4/IPv6 address)
+ * @NFT_CT_PROTOCOL: conntrack layer 4 protocol
+ * @NFT_CT_PROTO_SRC: conntrack layer 4 protocol source
+ * @NFT_CT_PROTO_DST: conntrack layer 4 protocol destination
+ */
+enum nft_ct_keys {
+ NFT_CT_STATE,
+ NFT_CT_DIRECTION,
+ NFT_CT_STATUS,
+ NFT_CT_MARK,
+ NFT_CT_SECMARK,
+ NFT_CT_EXPIRATION,
+ NFT_CT_HELPER,
+ NFT_CT_L3PROTOCOL,
+ NFT_CT_SRC,
+ NFT_CT_DST,
+ NFT_CT_PROTOCOL,
+ NFT_CT_PROTO_SRC,
+ NFT_CT_PROTO_DST,
+};
+
+/**
+ * enum nft_ct_attributes - nf_tables ct expression netlink attributes
+ *
+ * @NFTA_CT_DREG: destination register (NLA_U32)
+ * @NFTA_CT_KEY: conntrack data item to load (NLA_U32: nft_ct_keys)
+ * @NFTA_CT_DIRECTION: direction in case of directional keys (NLA_U8)
+ */
+enum nft_ct_attributes {
+ NFTA_CT_UNSPEC,
+ NFTA_CT_DREG,
+ NFTA_CT_KEY,
+ NFTA_CT_DIRECTION,
+ __NFTA_CT_MAX
+};
+#define NFTA_CT_MAX (__NFTA_CT_MAX - 1)
+
+/**
+ * enum nft_limit_attributes - nf_tables limit expression netlink attributes
+ *
+ * @NFTA_LIMIT_RATE: refill rate (NLA_U64)
+ * @NFTA_LIMIT_UNIT: refill unit (NLA_U64)
+ */
+enum nft_limit_attributes {
+ NFTA_LIMIT_UNSPEC,
+ NFTA_LIMIT_RATE,
+ NFTA_LIMIT_UNIT,
+ __NFTA_LIMIT_MAX
+};
+#define NFTA_LIMIT_MAX (__NFTA_LIMIT_MAX - 1)
+
+/**
+ * enum nft_counter_attributes - nf_tables counter expression netlink attributes
+ *
+ * @NFTA_COUNTER_BYTES: number of bytes (NLA_U64)
+ * @NFTA_COUNTER_PACKETS: number of packets (NLA_U64)
+ */
+enum nft_counter_attributes {
+ NFTA_COUNTER_UNSPEC,
+ NFTA_COUNTER_BYTES,
+ NFTA_COUNTER_PACKETS,
+ __NFTA_COUNTER_MAX
+};
+#define NFTA_COUNTER_MAX (__NFTA_COUNTER_MAX - 1)
+
+/**
+ * enum nft_log_attributes - nf_tables log expression netlink attributes
+ *
+ * @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U32)
+ * @NFTA_LOG_PREFIX: prefix to prepend to log messages (NLA_STRING)
+ * @NFTA_LOG_SNAPLEN: length of payload to include in netlink message (NLA_U32)
+ * @NFTA_LOG_QTHRESHOLD: queue threshold (NLA_U32)
+ */
+enum nft_log_attributes {
+ NFTA_LOG_UNSPEC,
+ NFTA_LOG_GROUP,
+ NFTA_LOG_PREFIX,
+ NFTA_LOG_SNAPLEN,
+ NFTA_LOG_QTHRESHOLD,
+ __NFTA_LOG_MAX
+};
+#define NFTA_LOG_MAX (__NFTA_LOG_MAX - 1)
+
+/**
+ * enum nft_reject_types - nf_tables reject expression reject types
+ *
+ * @NFT_REJECT_ICMP_UNREACH: reject using ICMP unreachable
+ * @NFT_REJECT_TCP_RST: reject using TCP RST
+ */
+enum nft_reject_types {
+ NFT_REJECT_ICMP_UNREACH,
+ NFT_REJECT_TCP_RST,
+};
+
+/**
+ * enum nft_reject_attributes - nf_tables reject expression netlink attributes
+ *
+ * @NFTA_REJECT_TYPE: packet type to use (NLA_U32: nft_reject_types)
+ * @NFTA_REJECT_ICMP_CODE: ICMP code to use (NLA_U8)
+ */
+enum nft_reject_attributes {
+ NFTA_REJECT_UNSPEC,
+ NFTA_REJECT_TYPE,
+ NFTA_REJECT_ICMP_CODE,
+ __NFTA_REJECT_MAX
+};
+#define NFTA_REJECT_MAX (__NFTA_REJECT_MAX - 1)
+
+/**
+ * enum nft_nat_types - nf_tables nat expression NAT types
+ *
+ * @NFT_NAT_SNAT: source NAT
+ * @NFT_NAT_DNAT: destination NAT
+ */
+enum nft_nat_types {
+ NFT_NAT_SNAT,
+ NFT_NAT_DNAT,
+};
+
+/**
+ * enum nft_nat_attributes - nf_tables nat expression netlink attributes
+ *
+ * @NFTA_NAT_TYPE: NAT type (NLA_U32: nft_nat_types)
+ * @NFTA_NAT_FAMILY: NAT family (NLA_U32)
+ * @NFTA_NAT_REG_ADDR_MIN: source register of address range start (NLA_U32: nft_registers)
+ * @NFTA_NAT_REG_ADDR_MAX: source register of address range end (NLA_U32: nft_registers)
+ * @NFTA_NAT_REG_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers)
+ * @NFTA_NAT_REG_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers)
+ */
+enum nft_nat_attributes {
+ NFTA_NAT_UNSPEC,
+ NFTA_NAT_TYPE,
+ NFTA_NAT_FAMILY,
+ NFTA_NAT_REG_ADDR_MIN,
+ NFTA_NAT_REG_ADDR_MAX,
+ NFTA_NAT_REG_PROTO_MIN,
+ NFTA_NAT_REG_PROTO_MAX,
+ __NFTA_NAT_MAX
+};
+#define NFTA_NAT_MAX (__NFTA_NAT_MAX - 1)
+
+#endif /* _LINUX_NF_TABLES_H */
diff --git a/include/linux/netfilter/nf_tables_compat.h b/include/linux/netfilter/nf_tables_compat.h
new file mode 100644
index 00000000..36fb81d8
--- /dev/null
+++ b/include/linux/netfilter/nf_tables_compat.h
@@ -0,0 +1,20 @@
+#ifndef _NFT_COMPAT_NFNETLINK_H_
+#define _NFT_COMPAT_NFNETLINK_H_
+
+#define NFT_COMPAT_NAME_MAX 32
+
+enum {
+ NFNL_MSG_COMPAT_GET,
+ NFNL_MSG_COMPAT_MAX
+};
+
+enum {
+ NFTA_COMPAT_UNSPEC = 0,
+ NFTA_COMPAT_NAME,
+ NFTA_COMPAT_REV,
+ NFTA_COMPAT_TYPE,
+ __NFTA_COMPAT_MAX,
+};
+#define NFTA_COMPAT_MAX (__NFTA_COMPAT_MAX - 1)
+
+#endif
diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
new file mode 100644
index 00000000..06eea26b
--- /dev/null
+++ b/include/linux/netfilter/nfnetlink.h
@@ -0,0 +1,64 @@
+#ifndef _NFNETLINK_H
+#define _NFNETLINK_H
+#include <linux/types.h>
+#include <linux/netfilter/nfnetlink_compat.h>
+
+enum nfnetlink_groups {
+ NFNLGRP_NONE,
+#define NFNLGRP_NONE NFNLGRP_NONE
+ NFNLGRP_CONNTRACK_NEW,
+#define NFNLGRP_CONNTRACK_NEW NFNLGRP_CONNTRACK_NEW
+ NFNLGRP_CONNTRACK_UPDATE,
+#define NFNLGRP_CONNTRACK_UPDATE NFNLGRP_CONNTRACK_UPDATE
+ NFNLGRP_CONNTRACK_DESTROY,
+#define NFNLGRP_CONNTRACK_DESTROY NFNLGRP_CONNTRACK_DESTROY
+ NFNLGRP_CONNTRACK_EXP_NEW,
+#define NFNLGRP_CONNTRACK_EXP_NEW NFNLGRP_CONNTRACK_EXP_NEW
+ NFNLGRP_CONNTRACK_EXP_UPDATE,
+#define NFNLGRP_CONNTRACK_EXP_UPDATE NFNLGRP_CONNTRACK_EXP_UPDATE
+ NFNLGRP_CONNTRACK_EXP_DESTROY,
+#define NFNLGRP_CONNTRACK_EXP_DESTROY NFNLGRP_CONNTRACK_EXP_DESTROY
+ NFNLGRP_NFTABLES,
+#define NFNLGRP_NFTABLES NFNLGRP_NFTABLES
+ __NFNLGRP_MAX,
+};
+#define NFNLGRP_MAX (__NFNLGRP_MAX - 1)
+
+/* General form of address family dependent message.
+ */
+struct nfgenmsg {
+ __u8 nfgen_family; /* AF_xxx */
+ __u8 version; /* nfnetlink version */
+ __be16 res_id; /* resource id */
+};
+
+#define NFNETLINK_V0 0
+
+/* netfilter netlink message types are split in two pieces:
+ * 8 bit subsystem, 8bit operation.
+ */
+
+#define NFNL_SUBSYS_ID(x) ((x & 0xff00) >> 8)
+#define NFNL_MSG_TYPE(x) (x & 0x00ff)
+
+/* No enum here, otherwise __stringify() trick of MODULE_ALIAS_NFNL_SUBSYS()
+ * won't work anymore */
+#define NFNL_SUBSYS_NONE 0
+#define NFNL_SUBSYS_CTNETLINK 1
+#define NFNL_SUBSYS_CTNETLINK_EXP 2
+#define NFNL_SUBSYS_QUEUE 3
+#define NFNL_SUBSYS_ULOG 4
+#define NFNL_SUBSYS_OSF 5
+#define NFNL_SUBSYS_IPSET 6
+#define NFNL_SUBSYS_ACCT 7
+#define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8
+#define NFNL_SUBSYS_CTHELPER 9
+#define NFNL_SUBSYS_NFTABLES 10
+#define NFNL_SUBSYS_NFT_COMPAT 11
+#define NFNL_SUBSYS_COUNT 12
+
+/* Reserved control nfnetlink messages */
+#define NFNL_MSG_BATCH_BEGIN NLMSG_MIN_TYPE
+#define NFNL_MSG_BATCH_END NLMSG_MIN_TYPE+1
+
+#endif /* _NFNETLINK_H */
diff --git a/include/linux/netfilter_arp.h b/include/linux/netfilter_arp.h
new file mode 100644
index 00000000..92bc6ddc
--- /dev/null
+++ b/include/linux/netfilter_arp.h
@@ -0,0 +1,19 @@
+#ifndef __LINUX_ARP_NETFILTER_H
+#define __LINUX_ARP_NETFILTER_H
+
+/* ARP-specific defines for netfilter.
+ * (C)2002 Rusty Russell IBM -- This code is GPL.
+ */
+
+#include <linux/netfilter.h>
+
+/* There is no PF_ARP. */
+#define NF_ARP 0
+
+/* ARP Hooks */
+#define NF_ARP_IN 0
+#define NF_ARP_OUT 1
+#define NF_ARP_FORWARD 2
+#define NF_ARP_NUMHOOKS 3
+
+#endif /* __LINUX_ARP_NETFILTER_H */
diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h
new file mode 100644
index 00000000..bb1ec648
--- /dev/null
+++ b/include/linux/netfilter_arp/arp_tables.h
@@ -0,0 +1,204 @@
+/*
+ * Format of an ARP firewall descriptor
+ *
+ * src, tgt, src_mask, tgt_mask, arpop, arpop_mask are always stored in
+ * network byte order.
+ * flags are stored in host byte order (of course).
+ */
+
+#ifndef _ARPTABLES_H
+#define _ARPTABLES_H
+
+#include <linux/types.h>
+
+#include <linux/netfilter_arp.h>
+
+#include <linux/netfilter/x_tables.h>
+
+#define ARPT_FUNCTION_MAXNAMELEN XT_FUNCTION_MAXNAMELEN
+#define ARPT_TABLE_MAXNAMELEN XT_TABLE_MAXNAMELEN
+#define arpt_entry_target xt_entry_target
+#define arpt_standard_target xt_standard_target
+#define arpt_error_target xt_error_target
+#define ARPT_CONTINUE XT_CONTINUE
+#define ARPT_RETURN XT_RETURN
+#define arpt_counters_info xt_counters_info
+#define arpt_counters xt_counters
+#define ARPT_STANDARD_TARGET XT_STANDARD_TARGET
+#define ARPT_ERROR_TARGET XT_ERROR_TARGET
+#define ARPT_ENTRY_ITERATE(entries, size, fn, args...) \
+ XT_ENTRY_ITERATE(struct arpt_entry, entries, size, fn, ## args)
+
+#define ARPT_DEV_ADDR_LEN_MAX 16
+
+struct arpt_devaddr_info {
+ char addr[ARPT_DEV_ADDR_LEN_MAX];
+ char mask[ARPT_DEV_ADDR_LEN_MAX];
+};
+
+/* Yes, Virginia, you have to zero the padding. */
+struct arpt_arp {
+ /* Source and target IP addr */
+ struct in_addr src, tgt;
+ /* Mask for src and target IP addr */
+ struct in_addr smsk, tmsk;
+
+ /* Device hw address length, src+target device addresses */
+ __u8 arhln, arhln_mask;
+ struct arpt_devaddr_info src_devaddr;
+ struct arpt_devaddr_info tgt_devaddr;
+
+ /* ARP operation code. */
+ __be16 arpop, arpop_mask;
+
+ /* ARP hardware address and protocol address format. */
+ __be16 arhrd, arhrd_mask;
+ __be16 arpro, arpro_mask;
+
+ /* The protocol address length is only accepted if it is 4
+ * so there is no use in offering a way to do filtering on it.
+ */
+
+ char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
+ unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
+
+ /* Flags word */
+ __u8 flags;
+ /* Inverse flags */
+ __u16 invflags;
+};
+
+/* Values for "flag" field in struct arpt_ip (general arp structure).
+ * No flags defined yet.
+ */
+#define ARPT_F_MASK 0x00 /* All possible flag bits mask. */
+
+/* Values for "inv" field in struct arpt_arp. */
+#define ARPT_INV_VIA_IN 0x0001 /* Invert the sense of IN IFACE. */
+#define ARPT_INV_VIA_OUT 0x0002 /* Invert the sense of OUT IFACE */
+#define ARPT_INV_SRCIP 0x0004 /* Invert the sense of SRC IP. */
+#define ARPT_INV_TGTIP 0x0008 /* Invert the sense of TGT IP. */
+#define ARPT_INV_SRCDEVADDR 0x0010 /* Invert the sense of SRC DEV ADDR. */
+#define ARPT_INV_TGTDEVADDR 0x0020 /* Invert the sense of TGT DEV ADDR. */
+#define ARPT_INV_ARPOP 0x0040 /* Invert the sense of ARP OP. */
+#define ARPT_INV_ARPHRD 0x0080 /* Invert the sense of ARP HRD. */
+#define ARPT_INV_ARPPRO 0x0100 /* Invert the sense of ARP PRO. */
+#define ARPT_INV_ARPHLN 0x0200 /* Invert the sense of ARP HLN. */
+#define ARPT_INV_MASK 0x03FF /* All possible flag bits mask. */
+
+/* This structure defines each of the firewall rules. Consists of 3
+ parts which are 1) general ARP header stuff 2) match specific
+ stuff 3) the target to perform if the rule matches */
+struct arpt_entry
+{
+ struct arpt_arp arp;
+
+ /* Size of arpt_entry + matches */
+ __u16 target_offset;
+ /* Size of arpt_entry + matches + target */
+ __u16 next_offset;
+
+ /* Back pointer */
+ unsigned int comefrom;
+
+ /* Packet and byte counters. */
+ struct xt_counters counters;
+
+ /* The matches (if any), then the target. */
+ unsigned char elems[0];
+};
+
+/*
+ * New IP firewall options for [gs]etsockopt at the RAW IP level.
+ * Unlike BSD Linux inherits IP options so you don't have to use a raw
+ * socket for this. Instead we check rights in the calls.
+ *
+ * ATTENTION: check linux/in.h before adding new number here.
+ */
+#define ARPT_BASE_CTL 96
+
+#define ARPT_SO_SET_REPLACE (ARPT_BASE_CTL)
+#define ARPT_SO_SET_ADD_COUNTERS (ARPT_BASE_CTL + 1)
+#define ARPT_SO_SET_MAX ARPT_SO_SET_ADD_COUNTERS
+
+#define ARPT_SO_GET_INFO (ARPT_BASE_CTL)
+#define ARPT_SO_GET_ENTRIES (ARPT_BASE_CTL + 1)
+/* #define ARPT_SO_GET_REVISION_MATCH (APRT_BASE_CTL + 2) */
+#define ARPT_SO_GET_REVISION_TARGET (ARPT_BASE_CTL + 3)
+#define ARPT_SO_GET_MAX (ARPT_SO_GET_REVISION_TARGET)
+
+/* The argument to ARPT_SO_GET_INFO */
+struct arpt_getinfo {
+ /* Which table: caller fills this in. */
+ char name[XT_TABLE_MAXNAMELEN];
+
+ /* Kernel fills these in. */
+ /* Which hook entry points are valid: bitmask */
+ unsigned int valid_hooks;
+
+ /* Hook entry points: one per netfilter hook. */
+ unsigned int hook_entry[NF_ARP_NUMHOOKS];
+
+ /* Underflow points. */
+ unsigned int underflow[NF_ARP_NUMHOOKS];
+
+ /* Number of entries */
+ unsigned int num_entries;
+
+ /* Size of entries. */
+ unsigned int size;
+};
+
+/* The argument to ARPT_SO_SET_REPLACE. */
+struct arpt_replace {
+ /* Which table. */
+ char name[XT_TABLE_MAXNAMELEN];
+
+ /* Which hook entry points are valid: bitmask. You can't
+ change this. */
+ unsigned int valid_hooks;
+
+ /* Number of entries */
+ unsigned int num_entries;
+
+ /* Total size of new entries */
+ unsigned int size;
+
+ /* Hook entry points. */
+ unsigned int hook_entry[NF_ARP_NUMHOOKS];
+
+ /* Underflow points. */
+ unsigned int underflow[NF_ARP_NUMHOOKS];
+
+ /* Information about old entries: */
+ /* Number of counters (must be equal to current number of entries). */
+ unsigned int num_counters;
+ /* The old entries' counters. */
+ struct xt_counters *counters;
+
+ /* The entries (hang off end: not really an array). */
+ struct arpt_entry entries[0];
+};
+
+/* The argument to ARPT_SO_GET_ENTRIES. */
+struct arpt_get_entries {
+ /* Which table: user fills this in. */
+ char name[XT_TABLE_MAXNAMELEN];
+
+ /* User fills this in: total entry size. */
+ unsigned int size;
+
+ /* The entries. */
+ struct arpt_entry entrytable[0];
+};
+
+/* Helper functions */
+static __inline__ struct xt_entry_target *arpt_get_target(struct arpt_entry *e)
+{
+ return (void *)e + e->target_offset;
+}
+
+/*
+ * Main firewall chains definitions and global var's definitions.
+ */
+#endif /* _ARPTABLES_H */
diff --git a/include/linux/netfilter_arp/arpt_mangle.h b/include/linux/netfilter_arp/arpt_mangle.h
new file mode 100644
index 00000000..250f5029
--- /dev/null
+++ b/include/linux/netfilter_arp/arpt_mangle.h
@@ -0,0 +1,26 @@
+#ifndef _ARPT_MANGLE_H
+#define _ARPT_MANGLE_H
+#include <linux/netfilter_arp/arp_tables.h>
+
+#define ARPT_MANGLE_ADDR_LEN_MAX sizeof(struct in_addr)
+struct arpt_mangle
+{
+ char src_devaddr[ARPT_DEV_ADDR_LEN_MAX];
+ char tgt_devaddr[ARPT_DEV_ADDR_LEN_MAX];
+ union {
+ struct in_addr src_ip;
+ } u_s;
+ union {
+ struct in_addr tgt_ip;
+ } u_t;
+ u_int8_t flags;
+ int target;
+};
+
+#define ARPT_MANGLE_SDEV 0x01
+#define ARPT_MANGLE_TDEV 0x02
+#define ARPT_MANGLE_SIP 0x04
+#define ARPT_MANGLE_TIP 0x08
+#define ARPT_MANGLE_MASK 0x0f
+
+#endif /* _ARPT_MANGLE_H */
diff --git a/include/xtables.h b/include/xtables.h
index 02172670..bad11a89 100644
--- a/include/xtables.h
+++ b/include/xtables.h
@@ -401,6 +401,7 @@ struct xtables_globals
struct option *orig_opts;
struct option *opts;
void (*exit_err)(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
+ int (*compat_rev)(const char *name, uint8_t rev, int opt);
};
#define XT_GETOPT_TABLEEND {.name = NULL, .has_arg = false}
@@ -432,6 +433,8 @@ extern struct xtables_match *xtables_find_match(const char *name,
enum xtables_tryload, struct xtables_rule_match **match);
extern struct xtables_target *xtables_find_target(const char *name,
enum xtables_tryload);
+extern int xtables_compatible_revision(const char *name, uint8_t revision,
+ int opt);
extern void xtables_rule_matches_free(struct xtables_rule_match **matches);
diff --git a/iptables/.gitignore b/iptables/.gitignore
index 31baf7d7..6c0ade1a 100644
--- a/iptables/.gitignore
+++ b/iptables/.gitignore
@@ -11,5 +11,8 @@
/iptables-static
/iptables-xml
/xtables-multi
+/xtables-config-parser.c
+/xtables-config-parser.h
+/xtables-config-syntax.c
/xtables.pc
diff --git a/iptables/Makefile.am b/iptables/Makefile.am
index a4246eb3..41bca7c7 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_CFLAGS} ${libnftnl_CFLAGS}
+AM_YFLAGS = -d
xtables_multi_SOURCES = xtables-multi.c iptables-xml.c
xtables_multi_CFLAGS = ${AM_CFLAGS}
@@ -24,11 +25,37 @@ endif
xtables_multi_SOURCES += xshared.c
xtables_multi_LDADD += ../libxtables/libxtables.la -lm
+# nftables compatibility layer
+if ENABLE_NFTABLES
+xtables_compat_multi_SOURCES = xtables-compat-multi.c iptables-xml.c
+xtables_compat_multi_CFLAGS = ${AM_CFLAGS}
+xtables_compat_multi_LDADD = ../extensions/libext.a
+if ENABLE_STATIC
+xtables_compat_multi_CFLAGS += -DALL_INCLUSIVE
+endif
+xtables_compat_multi_CFLAGS += -DENABLE_NFTABLES -DENABLE_IPV4 -DENABLE_IPV6
+xtables_compat_multi_SOURCES += xtables-config-parser.y xtables-config-syntax.l
+xtables_compat_multi_SOURCES += xtables-save.c xtables-restore.c \
+ xtables-standalone.c xtables.c nft.c \
+ nft-shared.c nft-ipv4.c nft-ipv6.c nft-arp.c \
+ xtables-config.c xtables-events.c \
+ xtables-arp-standalone.c xtables-arp.c
+xtables_compat_multi_LDADD += ${libmnl_LIBS} ${libnftnl_LIBS}
+# yacc and lex generate dirty code
+xtables_compat_multi-xtables-config-parser.o xtables_compat_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
+xtables_compat_multi_SOURCES += xshared.c
+xtables_compat_multi_LDADD += ../libxtables/libxtables.la -lm
+endif
+
sbin_PROGRAMS = xtables-multi
+if ENABLE_NFTABLES
+sbin_PROGRAMS += xtables-compat-multi
+endif
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 +64,11 @@ endif
if ENABLE_IPV6
v6_sbin_links = ip6tables ip6tables-restore ip6tables-save
endif
+if ENABLE_NFTABLES
+x_sbin_links = iptables-compat iptables-compat-restore iptables-compat-save \
+ ip6tables-compat ip6tables-compat-restore ip6tables-compat-save \
+ arptables-compat xtables-config xtables-events
+endif
iptables-extensions.8: iptables-extensions.8.tmpl ../extensions/matches.man ../extensions/targets.man
${AM_VERBOSE_GEN} sed \
@@ -52,3 +84,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-compat-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-arp.c b/iptables/nft-arp.c
new file mode 100644
index 00000000..17101369
--- /dev/null
+++ b/iptables/nft-arp.c
@@ -0,0 +1,649 @@
+/*
+ * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2013 by Giuseppe Longo <giuseppelng@gmail.com>
+ *
+ * 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 <string.h>
+#include <netdb.h>
+#include <net/if_arp.h>
+
+#include <xtables.h>
+#include <libiptc/libxtc.h>
+#include <net/if_arp.h>
+#include <netinet/if_ether.h>
+
+#include <linux/netfilter_arp/arp_tables.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "nft-shared.h"
+#include "nft.h"
+
+/* a few names */
+char *opcodes[] =
+{
+ "Request",
+ "Reply",
+ "Request_Reverse",
+ "Reply_Reverse",
+ "DRARP_Request",
+ "DRARP_Reply",
+ "DRARP_Error",
+ "InARP_Request",
+ "ARP_NAK",
+};
+
+static char *
+addr_to_dotted(const struct in_addr *addrp)
+{
+ static char buf[20];
+ const unsigned char *bytep;
+
+ bytep = (const unsigned char *) &(addrp->s_addr);
+ sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
+ return buf;
+}
+
+static char *
+addr_to_host(const struct in_addr *addr)
+{
+ struct hostent *host;
+
+ if ((host = gethostbyaddr((char *) addr,
+ sizeof(struct in_addr), AF_INET)) != NULL)
+ return (char *) host->h_name;
+
+ return (char *) NULL;
+}
+
+static char *
+addr_to_network(const struct in_addr *addr)
+{
+ struct netent *net;
+
+ if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL)
+ return (char *) net->n_name;
+
+ return (char *) NULL;
+}
+
+static char *
+addr_to_anyname(const struct in_addr *addr)
+{
+ char *name;
+
+ if ((name = addr_to_host(addr)) != NULL ||
+ (name = addr_to_network(addr)) != NULL)
+ return name;
+
+ return addr_to_dotted(addr);
+}
+
+static char *
+mask_to_dotted(const struct in_addr *mask)
+{
+ int i;
+ static char buf[20];
+ u_int32_t maskaddr, bits;
+
+ maskaddr = ntohl(mask->s_addr);
+
+ if (maskaddr == 0xFFFFFFFFL)
+ /* we don't want to see "/32" */
+ return "";
+
+ i = 32;
+ bits = 0xFFFFFFFEL;
+ while (--i >= 0 && maskaddr != bits)
+ bits <<= 1;
+ if (i >= 0)
+ sprintf(buf, "/%d", i);
+ else
+ /* mask was not a decent combination of 1's and 0's */
+ sprintf(buf, "/%s", addr_to_dotted(mask));
+
+ return buf;
+}
+
+static void print_mac(const unsigned char *mac, int l)
+{
+ int j;
+
+ for (j = 0; j < l; j++)
+ printf("%02x%s", mac[j],
+ (j==l-1) ? "" : ":");
+}
+
+static void print_mac_and_mask(const unsigned char *mac, const unsigned char *mask, int l)
+{
+ int i;
+
+ print_mac(mac, l);
+ for (i = 0; i < l ; i++)
+ if (mask[i] != 255)
+ break;
+ if (i == l)
+ return;
+ printf("/");
+ print_mac(mask, l);
+}
+
+static uint8_t arpt_to_ipt_flags(uint16_t invflags)
+{
+ uint8_t result = 0;
+
+ if (invflags & ARPT_INV_VIA_IN)
+ result |= IPT_INV_VIA_IN;
+
+ if (invflags & ARPT_INV_VIA_OUT)
+ result |= IPT_INV_VIA_OUT;
+
+ if (invflags & ARPT_INV_SRCIP)
+ result |= IPT_INV_SRCIP;
+
+ if (invflags & ARPT_INV_TGTIP)
+ result |= IPT_INV_DSTIP;
+
+ if (invflags & ARPT_INV_ARPPRO)
+ result |= IPT_INV_PROTO;
+
+ if (invflags & ARPT_INV_MASK)
+ result |= IPT_INV_MASK;
+
+ return result;
+}
+
+static int nft_arp_add(struct nft_rule *r, void *data)
+{
+ struct arpt_entry *fw = data;
+ uint8_t flags = arpt_to_ipt_flags(fw->arp.invflags);
+ struct xt_entry_target *t;
+ char *targname;
+ int ret;
+
+ if (fw->arp.iniface[0] != '\0')
+ add_iniface(r, fw->arp.iniface, flags);
+
+ if (fw->arp.outiface[0] != '\0')
+ add_outiface(r, fw->arp.outiface, flags);
+
+ if (fw->arp.arhrd != 0) {
+ add_payload(r, offsetof(struct arphdr, ar_hrd), 2);
+ add_cmp_u16(r, fw->arp.arhrd, NFT_CMP_EQ);
+ }
+
+ if (fw->arp.arpro != 0) {
+ add_payload(r, offsetof(struct arphdr, ar_pro), 2);
+ add_cmp_u16(r, fw->arp.arpro, NFT_CMP_EQ);
+ }
+
+ if (fw->arp.arhln != 0)
+ add_proto(r, offsetof(struct arphdr, ar_hln), 1,
+ fw->arp.arhln, flags);
+
+ add_proto(r, offsetof(struct arphdr, ar_pln), 1, 4, 0);
+
+ if (fw->arp.arpop != 0) {
+ add_payload(r, offsetof(struct arphdr, ar_op), 2);
+ add_cmp_u16(r, fw->arp.arpop, NFT_CMP_EQ);
+ }
+
+ if (fw->arp.src_devaddr.addr[0] != '\0') {
+ add_payload(r, sizeof(struct arphdr), fw->arp.arhln);
+ add_cmp_ptr(r, NFT_CMP_EQ, fw->arp.src_devaddr.addr, fw->arp.arhln);
+ }
+
+ if (fw->arp.src.s_addr != 0)
+ add_addr(r, sizeof(struct arphdr) + fw->arp.arhln,
+ &fw->arp.src.s_addr, 4, flags);
+
+ if (fw->arp.tgt_devaddr.addr[0] != '\0') {
+ add_payload(r, sizeof(struct arphdr) + fw->arp.arhln + 4, fw->arp.arhln);
+ add_cmp_ptr(r, NFT_CMP_EQ, fw->arp.tgt_devaddr.addr, fw->arp.arhln);
+ }
+
+ if (fw->arp.tgt.s_addr != 0)
+ add_addr(r, sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr),
+ &fw->arp.tgt.s_addr, 4, flags);
+
+ /* Counters need to me added before the target, otherwise they are
+ * increased for each rule because of the way nf_tables works.
+ */
+ if (add_counters(r, fw->counters.pcnt, fw->counters.bcnt) < 0)
+ return -1;
+
+ t = nft_arp_get_target(fw);
+ targname = t->u.user.name;
+
+ /* Standard target? */
+ if (strcmp(targname, XTC_LABEL_ACCEPT) == 0)
+ ret = add_verdict(r, NF_ACCEPT);
+ else if (strcmp(targname, XTC_LABEL_DROP) == 0)
+ ret = add_verdict(r, NF_DROP);
+ else if (strcmp(targname, XTC_LABEL_RETURN) == 0)
+ ret = add_verdict(r, NFT_RETURN);
+ else if (xtables_find_target(targname, XTF_TRY_LOAD) != NULL)
+ ret = add_target(r, t);
+ else
+ ret = add_jumpto(r, targname, NFT_JUMP);
+
+ return ret;
+}
+
+static uint16_t ipt_to_arpt_flags(uint8_t invflags)
+{
+ uint16_t result = 0;
+
+ if (invflags & IPT_INV_VIA_IN)
+ result |= ARPT_INV_VIA_IN;
+
+ if (invflags & IPT_INV_VIA_OUT)
+ result |= ARPT_INV_VIA_OUT;
+
+ if (invflags & IPT_INV_SRCIP)
+ result |= ARPT_INV_SRCIP;
+
+ if (invflags & IPT_INV_DSTIP)
+ result |= ARPT_INV_TGTIP;
+
+ if (invflags & IPT_INV_PROTO)
+ result |= ARPT_INV_ARPPRO;
+
+ if (invflags & IPT_INV_MASK)
+ result |= ARPT_INV_MASK;
+
+ return result;
+}
+
+static void nft_arp_parse_meta(struct nft_rule_expr *e, uint8_t key,
+ void *data)
+{
+ struct arpt_entry *fw = data;
+ uint8_t flags = 0;
+
+ parse_meta(e, key, fw->arp.iniface, fw->arp.iniface_mask,
+ fw->arp.outiface, fw->arp.outiface_mask,
+ &flags);
+
+ fw->arp.invflags |= ipt_to_arpt_flags(flags);
+}
+
+static void nft_arp_parse_target(struct xtables_target *target, void *data)
+{
+ struct arpt_entry *fw = data;
+ struct xt_entry_target **t;
+
+ fw->target_offset = offsetof(struct arpt_entry, elems);
+ fw->next_offset = fw->target_offset + target->t->u.target_size;
+
+ t = (void *) &fw->elems;
+ *t = target->t;
+}
+
+static void nft_arp_parse_immediate(const char *jumpto, bool nft_goto,
+ void *data)
+{
+ struct xtables_target *target;
+ size_t size;
+
+ target = xtables_find_target(XT_STANDARD_TARGET,
+ XTF_LOAD_MUST_SUCCEED);
+
+ size = XT_ALIGN(sizeof(struct xt_entry_target)) + target->size;
+
+ target->t = xtables_calloc(1, size);
+ target->t->u.target_size = size;
+ strcpy(target->t->u.user.name, jumpto);
+ target->t->u.user.revision = target->revision;
+
+ nft_arp_parse_target(target, data);
+}
+
+static void nft_arp_parse_payload(struct nft_rule_expr_iter *iter,
+ uint32_t offset, void *data)
+{
+ struct arpt_entry *fw = data;
+ struct in_addr addr;
+ unsigned short int ar_hrd, ar_pro, ar_op, ar_hln;
+ bool inv;
+
+ switch (offset) {
+ case offsetof(struct arphdr, ar_hrd):
+ get_cmp_data(iter, &ar_hrd, sizeof(ar_hrd), &inv);
+ fw->arp.arhrd = ar_hrd;
+ fw->arp.arhrd_mask = 0xffff;
+ if (inv)
+ fw->arp.invflags |= ARPT_INV_ARPHRD;
+ break;
+ case offsetof(struct arphdr, ar_pro):
+ get_cmp_data(iter, &ar_pro, sizeof(ar_pro), &inv);
+ fw->arp.arpro = ar_pro;
+ fw->arp.arpro_mask = 0xffff;
+ if (inv)
+ fw->arp.invflags |= ARPT_INV_ARPPRO;
+ break;
+ case offsetof(struct arphdr, ar_op):
+ get_cmp_data(iter, &ar_op, sizeof(ar_op), &inv);
+ fw->arp.arpop = ar_op;
+ fw->arp.arpop_mask = 0xffff;
+ if (inv)
+ fw->arp.invflags |= ARPT_INV_ARPOP;
+ break;
+ case offsetof(struct arphdr, ar_hln):
+ get_cmp_data(iter, &ar_hln, sizeof(ar_op), &inv);
+ fw->arp.arhln = ar_hln;
+ fw->arp.arhln_mask = 0xff;
+ if (inv)
+ fw->arp.invflags |= ARPT_INV_ARPOP;
+ break;
+ default:
+ if (!fw->arp.arhln)
+ break;
+
+ if (offset == sizeof(struct arphdr) + fw->arp.arhln) {
+ get_cmp_data(iter, &addr, sizeof(addr), &inv);
+ fw->arp.src.s_addr = addr.s_addr;
+ fw->arp.smsk.s_addr = 0xffffffff;
+ if (inv)
+ fw->arp.invflags |= ARPT_INV_SRCIP;
+ } else if (offset == sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr)) {
+ get_cmp_data(iter, &addr, sizeof(addr), &inv);
+ fw->arp.tgt.s_addr = addr.s_addr;
+ fw->arp.tmsk.s_addr = 0xffffffff;
+ if (inv)
+ fw->arp.invflags |= ARPT_INV_TGTIP;
+ }
+ break;
+ }
+}
+
+void nft_rule_to_arpt_entry(struct nft_rule *r, struct arpt_entry *fw)
+{
+ struct nft_rule_expr_iter *iter;
+ struct nft_rule_expr *expr;
+ int family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY);
+
+ 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, &fw->counters);
+ else if (strcmp(name, "payload") == 0)
+ nft_parse_payload(expr, iter, family, fw);
+ else if (strcmp(name, "meta") == 0)
+ nft_parse_meta(expr, iter, family, fw);
+ else if (strcmp(name, "immediate") == 0)
+ nft_parse_immediate(expr, iter, family, fw);
+ else if (strcmp(name, "target") == 0)
+ nft_parse_target(expr, iter, family, fw);
+
+ expr = nft_rule_expr_iter_next(iter);
+ }
+
+ nft_rule_expr_iter_destroy(iter);
+}
+
+static void
+nft_arp_print_firewall(struct nft_rule *r, unsigned int num,
+ unsigned int format)
+{
+ struct arpt_entry fw = {};
+ const char *targname;
+ struct xtables_target *target = NULL;
+ const struct xt_entry_target *t;
+ char buf[BUFSIZ];
+ int i;
+ char iface[IFNAMSIZ+2];
+ int print_iface = 0;
+
+ nft_rule_to_arpt_entry(r, &fw);
+
+ if (format & FMT_LINENUMBERS)
+ printf("%u ", num);
+
+ if (fw.target_offset) {
+ t = nft_arp_get_target(&fw);
+ targname = t->u.user.name;
+ target = xtables_find_target(targname, XTF_TRY_LOAD);
+ if (!(format & FMT_NOTARGET))
+ printf("-j %s ", targname);
+ }
+
+ iface[0] = '\0';
+
+ if (fw.arp.iniface[0] != '\0') {
+ strcat(iface, fw.arp.iniface);
+ print_iface = 1;
+ }
+ else if (format & FMT_VIA) {
+ print_iface = 1;
+ if (format & FMT_NUMERIC) strcat(iface, "*");
+ else strcat(iface, "any");
+ }
+ if (print_iface)
+ printf("%s-i %s ", fw.arp.invflags & ARPT_INV_VIA_IN ? "! ": "", iface);
+
+ print_iface = 0;
+ iface[0] = '\0';
+
+ if (fw.arp.outiface[0] != '\0') {
+ strcat(iface, fw.arp.outiface);
+ print_iface = 1;
+ }
+ else if (format & FMT_VIA) {
+ print_iface = 1;
+ if (format & FMT_NUMERIC) strcat(iface, "*");
+ else strcat(iface, "any");
+ }
+ if (print_iface)
+ printf("%s-o %s ", fw.arp.invflags & ARPT_INV_VIA_OUT ? "! " : "", iface);
+
+ if (fw.arp.smsk.s_addr != 0L) {
+ printf("%s", fw.arp.invflags & ARPT_INV_SRCIP
+ ? "! " : "");
+ if (format & FMT_NUMERIC)
+ sprintf(buf, "%s", addr_to_dotted(&(fw.arp.src)));
+ else
+ sprintf(buf, "%s", addr_to_anyname(&(fw.arp.src)));
+ strncat(buf, mask_to_dotted(&(fw.arp.smsk)),
+ sizeof(buf) - strlen(buf) - 1);
+ printf("-s %s ", buf);
+ }
+
+ for (i = 0; i < ARPT_DEV_ADDR_LEN_MAX; i++)
+ if (fw.arp.src_devaddr.mask[i] != 0)
+ break;
+ if (i == ARPT_DEV_ADDR_LEN_MAX)
+ goto after_devsrc;
+ printf("%s", fw.arp.invflags & ARPT_INV_SRCDEVADDR
+ ? "! " : "");
+ printf("--src-mac ");
+ print_mac_and_mask((unsigned char *)fw.arp.src_devaddr.addr,
+ (unsigned char *)fw.arp.src_devaddr.mask, ETH_ALEN);
+ printf(" ");
+after_devsrc:
+
+ if (fw.arp.tmsk.s_addr != 0L) {
+ printf("%s",fw.arp.invflags & ARPT_INV_TGTIP
+ ? "! " : "");
+ if (format & FMT_NUMERIC)
+ sprintf(buf, "%s", addr_to_dotted(&(fw.arp.tgt)));
+ else
+ sprintf(buf, "%s", addr_to_anyname(&(fw.arp.tgt)));
+ strncat(buf, mask_to_dotted(&(fw.arp.tmsk)),
+ sizeof(buf) - strlen(buf) - 1);
+ printf("-d %s ", buf);
+ }
+
+ for (i = 0; i <ARPT_DEV_ADDR_LEN_MAX; i++)
+ if (fw.arp.tgt_devaddr.mask[i] != 0)
+ break;
+ if (i == ARPT_DEV_ADDR_LEN_MAX)
+ goto after_devdst;
+ printf("%s",fw.arp.invflags & ARPT_INV_TGTDEVADDR
+ ? "! " : "");
+ printf("--dst-mac ");
+ print_mac_and_mask((unsigned char *)fw.arp.tgt_devaddr.addr,
+ (unsigned char *)fw.arp.tgt_devaddr.mask, ETH_ALEN);
+ printf(" ");
+after_devdst:
+
+ if (fw.arp.arhln_mask != 0) {
+ printf("%s",fw.arp.invflags & ARPT_INV_ARPHLN
+ ? "! " : "");
+ printf("--h-length %d", fw.arp.arhln);
+ if (fw.arp.arhln_mask != 255)
+ printf("/%d", fw.arp.arhln_mask);
+ printf(" ");
+ }
+
+ if (fw.arp.arpop_mask != 0) {
+ int tmp = ntohs(fw.arp.arpop);
+
+ printf("%s",fw.arp.invflags & ARPT_INV_ARPOP
+ ? "! " : "");
+ if (tmp <= NUMOPCODES && !(format & FMT_NUMERIC))
+ printf("--opcode %s", opcodes[tmp-1]);
+ else
+ printf("--opcode %d", tmp);
+ if (fw.arp.arpop_mask != 65535)
+ printf("/%d", ntohs(fw.arp.arpop_mask));
+ printf(" ");
+ }
+
+ if (fw.arp.arhrd_mask != 0) {
+ uint16_t tmp = ntohs(fw.arp.arhrd);
+
+ printf("%s", fw.arp.invflags & ARPT_INV_ARPHRD
+ ? "! " : "");
+ if (tmp == 1 && !(format & FMT_NUMERIC))
+ printf("--h-type %s", "Ethernet");
+ else
+ printf("--h-type %u", tmp);
+ if (fw.arp.arhrd_mask != 65535)
+ printf("/%d", ntohs(fw.arp.arhrd_mask));
+ printf(" ");
+ }
+
+ if (fw.arp.arpro_mask != 0) {
+ int tmp = ntohs(fw.arp.arpro);
+
+ printf("%s", fw.arp.invflags & ARPT_INV_ARPPRO
+ ? "! " : "");
+ if (tmp == 0x0800 && !(format & FMT_NUMERIC))
+ printf("--proto-type %s", "IPv4");
+ else
+ printf("--proto-type 0x%x", tmp);
+ if (fw.arp.arpro_mask != 65535)
+ printf("/%x", ntohs(fw.arp.arpro_mask));
+ printf(" ");
+ }
+
+ if (target) {
+ if (target->print)
+ /* Print the target information. */
+ target->print(&fw.arp, t, format & FMT_NUMERIC);
+ }
+
+ if (!(format & FMT_NOCOUNTS)) {
+ printf(", pcnt=");
+ xtables_print_num(fw.counters.pcnt, format);
+ printf("-- bcnt=");
+ xtables_print_num(fw.counters.bcnt, format);
+ }
+
+ if (!(format & FMT_NONEWLINE))
+ fputc('\n', stdout);
+}
+
+static bool nft_arp_is_same(const void *data_a,
+ const void *data_b)
+{
+ const struct arpt_entry *a = data_a;
+ const struct arpt_entry *b = data_b;
+
+ if (a->arp.src.s_addr != b->arp.src.s_addr
+ || a->arp.tgt.s_addr != b->arp.tgt.s_addr
+ || a->arp.smsk.s_addr != b->arp.tmsk.s_addr
+ || a->arp.arpro != b->arp.arpro
+ || a->arp.flags != b->arp.flags
+ || a->arp.invflags != b->arp.invflags) {
+ DEBUGP("different src/dst/proto/flags/invflags\n");
+ return false;
+ }
+
+ return is_same_interfaces(a->arp.src_devaddr.addr,
+ a->arp.tgt_devaddr.addr,
+ (unsigned char*)a->arp.src_devaddr.mask,
+ (unsigned char*)a->arp.tgt_devaddr.mask,
+ b->arp.src_devaddr.addr,
+ a->arp.tgt_devaddr.addr,
+ (unsigned char*)b->arp.src_devaddr.mask,
+ (unsigned char*)b->arp.tgt_devaddr.mask);
+}
+
+static bool nft_arp_rule_find(struct nft_family_ops *ops, struct nft_rule *r,
+ void *data)
+{
+ struct arpt_entry *fw = data;
+ struct xt_entry_target *t_fw, *t_this;
+ char *targname_fw, *targname_this;
+ struct arpt_entry this = {};
+
+ /* Delete by matching rule case */
+ nft_rule_to_arpt_entry(r, &this);
+
+ if (!ops->is_same(fw, &this))
+ return false;
+
+ t_fw = nft_arp_get_target(fw);
+ t_this = nft_arp_get_target(&this);
+
+ targname_fw = t_fw->u.user.name;
+ targname_this = t_this->u.user.name;
+
+ if (!strcmp(targname_fw, targname_this) &&
+ (!strcmp(targname_fw, "mangle") ||
+ !strcmp(targname_fw, "CLASSIFY"))) {
+ if (memcmp(t_fw->data, t_this->data,
+ t_fw->u.user.target_size - sizeof(*t_fw)) != 0) {
+ DEBUGP("Different target\n");
+ return false;
+ }
+ return true;
+ }
+
+ if (strcmp(targname_fw, targname_this) != 0) {
+ DEBUGP("Different verdict\n");
+ return false;
+ }
+
+ return true;
+}
+
+struct nft_family_ops nft_family_ops_arp = {
+ .add = nft_arp_add,
+ .is_same = nft_arp_is_same,
+ .print_payload = NULL,
+ .parse_meta = nft_arp_parse_meta,
+ .parse_payload = nft_arp_parse_payload,
+ .parse_immediate = nft_arp_parse_immediate,
+ .print_firewall = nft_arp_print_firewall,
+ .post_parse = NULL,
+ .rule_find = nft_arp_rule_find,
+ .parse_target = nft_arp_parse_target,
+};
diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c
new file mode 100644
index 00000000..d05e80eb
--- /dev/null
+++ b/iptables/nft-ipv4.c
@@ -0,0 +1,424 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+ *
+ * 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 <string.h>
+#include <stdio.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+
+#include <xtables.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "nft.h"
+#include "nft-shared.h"
+
+static int nft_ipv4_add(struct nft_rule *r, void *data)
+{
+ struct iptables_command_state *cs = data;
+ struct xtables_rule_match *matchp;
+ uint32_t op;
+
+ if (cs->fw.ip.iniface[0] != '\0')
+ add_iniface(r, cs->fw.ip.iniface, cs->fw.ip.invflags);
+
+ if (cs->fw.ip.outiface[0] != '\0')
+ add_outiface(r, cs->fw.ip.outiface, cs->fw.ip.invflags);
+
+ if (cs->fw.ip.src.s_addr != 0)
+ add_addr(r, offsetof(struct iphdr, saddr),
+ &cs->fw.ip.src.s_addr, 4, cs->fw.ip.invflags);
+
+ if (cs->fw.ip.dst.s_addr != 0)
+ add_addr(r, offsetof(struct iphdr, daddr),
+ &cs->fw.ip.dst.s_addr, 4, cs->fw.ip.invflags);
+
+ if (cs->fw.ip.proto != 0)
+ add_proto(r, offsetof(struct iphdr, protocol), 1,
+ cs->fw.ip.proto, cs->fw.ip.invflags);
+
+ if (cs->fw.ip.flags & IPT_F_FRAG) {
+ add_payload(r, offsetof(struct iphdr, frag_off), 2);
+ /* get the 13 bits that contain the fragment offset */
+ add_bitwise_u16(r, 0x1fff, !0x1fff);
+
+ /* if offset is non-zero, this is a fragment */
+ if (cs->fw.ip.invflags & IPT_INV_FRAG)
+ op = NFT_CMP_EQ;
+ else
+ op = NFT_CMP_NEQ;
+
+ add_cmp_u16(r, 0, op);
+ }
+
+ add_compat(r, cs->fw.ip.proto, cs->fw.ip.invflags);
+
+ for (matchp = cs->matches; matchp; matchp = matchp->next) {
+ if (add_match(r, matchp->match->m) < 0)
+ break;
+ }
+
+ /* Counters need to me added before the target, otherwise they are
+ * increased for each rule because of the way nf_tables works.
+ */
+ if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0)
+ return -1;
+
+ return add_action(r, cs, !!(cs->fw.ip.flags & IPT_F_GOTO));
+}
+
+static bool nft_ipv4_is_same(const void *data_a,
+ const void *data_b)
+{
+ const struct iptables_command_state *a = data_a;
+ const struct iptables_command_state *b = data_b;
+
+ 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;
+ }
+
+ return is_same_interfaces(a->fw.ip.iniface, a->fw.ip.outiface,
+ a->fw.ip.iniface_mask, a->fw.ip.outiface_mask,
+ b->fw.ip.iniface, b->fw.ip.outiface,
+ b->fw.ip.iniface_mask, b->fw.ip.outiface_mask);
+}
+
+static void get_frag(struct nft_rule_expr_iter *iter, bool *inv)
+{
+ struct nft_rule_expr *e;
+ const char *name;
+ uint8_t op;
+
+ e = nft_rule_expr_iter_next(iter);
+ if (e == NULL)
+ return;
+
+ /* we assume correct mask and xor */
+ name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME);
+ if (strcmp(name, "bitwise") != 0) {
+ DEBUGP("skipping no bitwise after payload\n");
+ return;
+ }
+
+ /* Now check for cmp */
+ e = nft_rule_expr_iter_next(iter);
+ if (e == NULL)
+ return;
+
+ /* we assume correct data */
+ name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME);
+ if (strcmp(name, "cmp") != 0) {
+ DEBUGP("skipping no cmp after payload\n");
+ return;
+ }
+
+ op = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_OP);
+ if (op == NFT_CMP_EQ)
+ *inv = true;
+ else
+ *inv = false;
+}
+
+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_ipv4_parse_meta(struct nft_rule_expr *e, uint8_t key,
+ void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ parse_meta(e, key, cs->fw.ip.iniface, cs->fw.ip.iniface_mask,
+ cs->fw.ip.outiface, cs->fw.ip.outiface_mask,
+ &cs->fw.ip.invflags);
+}
+
+static void nft_ipv4_parse_payload(struct nft_rule_expr_iter *iter,
+ uint32_t offset, void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ switch(offset) {
+ struct in_addr addr;
+ uint8_t proto;
+ bool inv;
+
+ 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;
+ case offsetof(struct iphdr, frag_off):
+ cs->fw.ip.flags |= IPT_F_FRAG;
+ get_frag(iter, &inv);
+ if (inv)
+ cs->fw.ip.invflags |= IPT_INV_FRAG;
+ break;
+ default:
+ DEBUGP("unknown payload offset %d\n", offset);
+ break;
+ }
+}
+
+static void nft_ipv4_parse_immediate(const char *jumpto, bool nft_goto,
+ void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ cs->jumpto = jumpto;
+
+ if (nft_goto)
+ cs->fw.ip.flags |= IPT_F_GOTO;
+}
+
+static void print_ipv4_addr(const struct iptables_command_state *cs,
+ unsigned int format)
+{
+ char buf[BUFSIZ];
+
+ 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);
+ }
+}
+
+static void print_fragment(unsigned int flags, unsigned int invflags,
+ unsigned int format)
+{
+ if (!(format & FMT_OPTIONS))
+ return;
+
+ if (format & FMT_NOTABLE)
+ fputs("opt ", stdout);
+ fputc(invflags & IPT_INV_FRAG ? '!' : '-', stdout);
+ fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout);
+ fputc(' ', stdout);
+}
+
+static void nft_ipv4_print_firewall(struct nft_rule *r, unsigned int num,
+ unsigned int format)
+{
+ struct iptables_command_state cs = {};
+
+ nft_rule_to_iptables_command_state(r, &cs);
+
+ print_firewall_details(&cs, cs.jumpto, cs.fw.ip.flags,
+ cs.fw.ip.invflags, cs.fw.ip.proto,
+ num, format);
+ print_fragment(cs.fw.ip.flags, cs.fw.ip.invflags, format);
+ print_ifaces(cs.fw.ip.iniface, cs.fw.ip.outiface, cs.fw.ip.invflags,
+ format);
+ print_ipv4_addr(&cs, format);
+
+ if (format & FMT_NOTABLE)
+ fputs(" ", stdout);
+
+#ifdef IPT_F_GOTO
+ if (cs.fw.ip.flags & IPT_F_GOTO)
+ printf("[goto] ");
+#endif
+
+ print_matches_and_target(&cs, format);
+
+ if (!(format & FMT_NONEWLINE))
+ fputc('\n', stdout);
+}
+
+static void save_ipv4_addr(char letter, const struct in_addr *addr,
+ uint32_t mask, int invert)
+{
+ if (!mask && !invert && !addr->s_addr)
+ return;
+
+ printf("%s-%c %s/%s ", invert ? "! " : "", letter, inet_ntoa(*addr),
+ mask_to_str(mask));
+}
+
+static void nft_ipv4_save_firewall(const void *data, unsigned int format)
+{
+ const struct iptables_command_state *cs = data;
+
+ save_firewall_details(cs, cs->fw.ip.invflags, cs->fw.ip.proto,
+ cs->fw.ip.iniface, cs->fw.ip.iniface_mask,
+ cs->fw.ip.outiface, cs->fw.ip.outiface_mask,
+ format);
+
+ if (cs->fw.ip.flags & IPT_F_FRAG) {
+ if (cs->fw.ip.invflags & IPT_INV_FRAG)
+ printf("! ");
+ printf("-f ");
+ }
+
+ save_ipv4_addr('s', &cs->fw.ip.src, cs->fw.ip.smsk.s_addr,
+ cs->fw.ip.invflags & IPT_INV_SRCIP);
+ save_ipv4_addr('d', &cs->fw.ip.dst, cs->fw.ip.dmsk.s_addr,
+ cs->fw.ip.invflags & IPT_INV_DSTIP);
+
+ save_matches_and_target(cs->matches, cs->target,
+ cs->jumpto, cs->fw.ip.flags, &cs->fw);
+
+ if (cs->target == NULL && strlen(cs->jumpto) > 0) {
+ printf("-%c %s", cs->fw.ip.flags & IPT_F_GOTO ? 'g' : 'j',
+ cs->jumpto);
+ }
+ printf("\n");
+}
+
+static void nft_ipv4_proto_parse(struct iptables_command_state *cs,
+ struct xtables_args *args)
+{
+ cs->fw.ip.proto = args->proto;
+ cs->fw.ip.invflags = args->invflags;
+}
+
+static void nft_ipv4_post_parse(int command,
+ struct iptables_command_state *cs,
+ struct xtables_args *args)
+{
+ cs->fw.ip.flags = args->flags;
+ /* We already set invflags in proto_parse, but we need to refresh it
+ * to include new parsed options.
+ */
+ cs->fw.ip.invflags = args->invflags;
+
+ strncpy(cs->fw.ip.iniface, args->iniface, IFNAMSIZ);
+ memcpy(cs->fw.ip.iniface_mask,
+ args->iniface_mask, IFNAMSIZ*sizeof(unsigned char));
+
+ strncpy(cs->fw.ip.outiface, args->outiface, IFNAMSIZ);
+ memcpy(cs->fw.ip.outiface_mask,
+ args->outiface_mask, IFNAMSIZ*sizeof(unsigned char));
+
+ if (args->goto_set)
+ cs->fw.ip.flags |= IPT_F_GOTO;
+
+ cs->counters.pcnt = args->pcnt_cnt;
+ cs->counters.bcnt = args->bcnt_cnt;
+
+ if (command & (CMD_REPLACE | CMD_INSERT |
+ CMD_DELETE | CMD_APPEND | CMD_CHECK)) {
+ if (!(cs->options & OPT_DESTINATION))
+ args->dhostnetworkmask = "0.0.0.0/0";
+ if (!(cs->options & OPT_SOURCE))
+ args->shostnetworkmask = "0.0.0.0/0";
+ }
+
+ if (args->shostnetworkmask)
+ xtables_ipparse_multiple(args->shostnetworkmask,
+ &args->s.addr.v4, &args->s.mask.v4,
+ &args->s.naddrs);
+ if (args->dhostnetworkmask)
+ xtables_ipparse_multiple(args->dhostnetworkmask,
+ &args->d.addr.v4, &args->d.mask.v4,
+ &args->d.naddrs);
+
+ if ((args->s.naddrs > 1 || args->d.naddrs > 1) &&
+ (cs->fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))
+ xtables_error(PARAMETER_PROBLEM,
+ "! not allowed with multiple"
+ " source or destination IP addresses");
+}
+
+static void nft_ipv4_parse_target(struct xtables_target *t, void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ cs->target = t;
+}
+
+static bool nft_ipv4_rule_find(struct nft_family_ops *ops,
+ struct nft_rule *r, void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ return nft_ipv46_rule_find(ops, r, cs);
+}
+
+struct nft_family_ops nft_family_ops_ipv4 = {
+ .add = nft_ipv4_add,
+ .is_same = nft_ipv4_is_same,
+ .parse_meta = nft_ipv4_parse_meta,
+ .parse_payload = nft_ipv4_parse_payload,
+ .parse_immediate = nft_ipv4_parse_immediate,
+ .print_firewall = nft_ipv4_print_firewall,
+ .save_firewall = nft_ipv4_save_firewall,
+ .proto_parse = nft_ipv4_proto_parse,
+ .post_parse = nft_ipv4_post_parse,
+ .parse_target = nft_ipv4_parse_target,
+ .rule_find = nft_ipv4_rule_find,
+};
diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c
new file mode 100644
index 00000000..f08598ae
--- /dev/null
+++ b/iptables/nft-ipv6.c
@@ -0,0 +1,345 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+ *
+ * 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 <string.h>
+#include <stdio.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/ip6.h>
+
+#include <xtables.h>
+
+#include "nft.h"
+#include "nft-shared.h"
+
+static int nft_ipv6_add(struct nft_rule *r, void *data)
+{
+ struct iptables_command_state *cs = data;
+ struct xtables_rule_match *matchp;
+
+ if (cs->fw6.ipv6.iniface[0] != '\0')
+ add_iniface(r, cs->fw6.ipv6.iniface, cs->fw6.ipv6.invflags);
+
+ if (cs->fw6.ipv6.outiface[0] != '\0')
+ add_outiface(r, cs->fw6.ipv6.outiface, cs->fw6.ipv6.invflags);
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src))
+ add_addr(r, offsetof(struct ip6_hdr, ip6_src),
+ &cs->fw6.ipv6.src, 16, cs->fw6.ipv6.invflags);
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dst))
+ add_addr(r, offsetof(struct ip6_hdr, ip6_dst),
+ &cs->fw6.ipv6.dst, 16, cs->fw6.ipv6.invflags);
+
+ if (cs->fw6.ipv6.proto != 0)
+ add_proto(r, offsetof(struct ip6_hdr, ip6_nxt), 1,
+ cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags);
+
+ add_compat(r, cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags);
+
+ for (matchp = cs->matches; matchp; matchp = matchp->next) {
+ if (add_match(r, matchp->match->m) < 0)
+ break;
+ }
+
+ /* Counters need to me added before the target, otherwise they are
+ * increased for each rule because of the way nf_tables works.
+ */
+ if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0)
+ return -1;
+
+ return add_action(r, cs, !!(cs->fw6.ipv6.flags & IP6T_F_GOTO));
+}
+
+static bool nft_ipv6_is_same(const void *data_a,
+ const void *data_b)
+{
+ const struct iptables_command_state *a = data_a;
+ const struct iptables_command_state *b = data_b;
+
+ if (memcmp(a->fw6.ipv6.src.s6_addr, b->fw6.ipv6.src.s6_addr,
+ sizeof(struct in6_addr)) != 0
+ || memcmp(a->fw6.ipv6.dst.s6_addr, b->fw6.ipv6.dst.s6_addr,
+ sizeof(struct in6_addr)) != 0
+ || a->fw6.ipv6.proto != b->fw6.ipv6.proto
+ || a->fw6.ipv6.flags != b->fw6.ipv6.flags
+ || a->fw6.ipv6.invflags != b->fw6.ipv6.invflags) {
+ DEBUGP("different src/dst/proto/flags/invflags\n");
+ return false;
+ }
+
+ return is_same_interfaces(a->fw6.ipv6.iniface, a->fw6.ipv6.outiface,
+ a->fw6.ipv6.iniface_mask,
+ a->fw6.ipv6.outiface_mask,
+ b->fw6.ipv6.iniface, b->fw6.ipv6.outiface,
+ b->fw6.ipv6.iniface_mask,
+ b->fw6.ipv6.outiface_mask);
+}
+
+static void nft_ipv6_parse_meta(struct nft_rule_expr *e, uint8_t key,
+ void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ parse_meta(e, key, cs->fw6.ipv6.iniface,
+ cs->fw6.ipv6.iniface_mask, cs->fw6.ipv6.outiface,
+ cs->fw6.ipv6.outiface_mask, &cs->fw6.ipv6.invflags);
+}
+
+static void nft_ipv6_parse_payload(struct nft_rule_expr_iter *iter,
+ uint32_t offset, void *data)
+{
+ struct iptables_command_state *cs = data;
+ switch (offset) {
+ struct in6_addr addr;
+ uint8_t proto;
+ bool inv;
+
+ case offsetof(struct ip6_hdr, ip6_src):
+ get_cmp_data(iter, &addr, sizeof(addr), &inv);
+ memcpy(cs->fw6.ipv6.src.s6_addr, &addr, sizeof(addr));
+ if (inv)
+ cs->fw6.ipv6.invflags |= IPT_INV_SRCIP;
+ break;
+ case offsetof(struct ip6_hdr, ip6_dst):
+ get_cmp_data(iter, &addr, sizeof(addr), &inv);
+ memcpy(cs->fw6.ipv6.dst.s6_addr, &addr, sizeof(addr));
+ if (inv)
+ cs->fw6.ipv6.invflags |= IPT_INV_DSTIP;
+ break;
+ case offsetof(struct ip6_hdr, ip6_nxt):
+ get_cmp_data(iter, &proto, sizeof(proto), &inv);
+ cs->fw6.ipv6.flags |= IP6T_F_PROTO;
+ cs->fw6.ipv6.proto = proto;
+ if (inv)
+ cs->fw6.ipv6.invflags |= IPT_INV_PROTO;
+ default:
+ DEBUGP("unknown payload offset %d\n", offset);
+ break;
+ }
+}
+
+static void nft_ipv6_parse_immediate(const char *jumpto, bool nft_goto,
+ void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ cs->jumpto = jumpto;
+
+ if (nft_goto)
+ cs->fw6.ipv6.flags |= IP6T_F_GOTO;
+}
+
+static void print_ipv6_addr(const struct iptables_command_state *cs,
+ unsigned int format)
+{
+ char buf[BUFSIZ];
+
+ fputc(cs->fw6.ipv6.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
+ if (IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src)
+ && !(format & FMT_NUMERIC))
+ printf(FMT("%-19s ","%s "), "anywhere");
+ else {
+ if (format & FMT_NUMERIC)
+ strcpy(buf,
+ xtables_ip6addr_to_numeric(&cs->fw6.ipv6.src));
+ else
+ strcpy(buf,
+ xtables_ip6addr_to_anyname(&cs->fw6.ipv6.src));
+ strcat(buf, xtables_ip6mask_to_numeric(&cs->fw6.ipv6.smsk));
+ printf(FMT("%-19s ","%s "), buf);
+ }
+
+
+ fputc(cs->fw6.ipv6.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
+ if (IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dst)
+ && !(format & FMT_NUMERIC))
+ printf(FMT("%-19s ","-> %s"), "anywhere");
+ else {
+ if (format & FMT_NUMERIC)
+ strcpy(buf,
+ xtables_ip6addr_to_numeric(&cs->fw6.ipv6.dst));
+ else
+ strcpy(buf,
+ xtables_ip6addr_to_anyname(&cs->fw6.ipv6.dst));
+ strcat(buf, xtables_ip6mask_to_numeric(&cs->fw6.ipv6.dmsk));
+ printf(FMT("%-19s ","-> %s"), buf);
+ }
+}
+
+static void nft_ipv6_print_firewall(struct nft_rule *r, unsigned int num,
+ unsigned int format)
+{
+ struct iptables_command_state cs = {};
+
+ nft_rule_to_iptables_command_state(r, &cs);
+
+ print_firewall_details(&cs, cs.jumpto, cs.fw6.ipv6.flags,
+ cs.fw6.ipv6.invflags, cs.fw6.ipv6.proto,
+ num, format);
+ print_ifaces(cs.fw6.ipv6.iniface, cs.fw6.ipv6.outiface,
+ cs.fw6.ipv6.invflags, format);
+ print_ipv6_addr(&cs, format);
+
+ if (format & FMT_NOTABLE)
+ fputs(" ", stdout);
+
+ if (cs.fw6.ipv6.flags & IP6T_F_GOTO)
+ printf("[goto] ");
+
+ print_matches_and_target(&cs, format);
+
+ if (!(format & FMT_NONEWLINE))
+ fputc('\n', stdout);
+}
+
+static void save_ipv6_addr(char letter, const struct in6_addr *addr,
+ int invert)
+{
+ char addr_str[INET6_ADDRSTRLEN];
+
+ if (!invert && IN6_IS_ADDR_UNSPECIFIED(addr))
+ return;
+
+ inet_ntop(AF_INET6, addr, addr_str, INET6_ADDRSTRLEN);
+ printf("%s-%c %s ", invert ? "! " : "", letter, addr_str);
+}
+
+static void nft_ipv6_save_firewall(const void *data, unsigned int format)
+{
+ const struct iptables_command_state *cs = data;
+
+ save_firewall_details(cs, cs->fw6.ipv6.invflags, cs->fw6.ipv6.proto,
+ cs->fw6.ipv6.iniface, cs->fw6.ipv6.iniface_mask,
+ cs->fw6.ipv6.outiface, cs->fw6.ipv6.outiface_mask,
+ format);
+
+ save_ipv6_addr('s', &cs->fw6.ipv6.src,
+ cs->fw6.ipv6.invflags & IPT_INV_SRCIP);
+ save_ipv6_addr('d', &cs->fw6.ipv6.dst,
+ cs->fw6.ipv6.invflags & IPT_INV_DSTIP);
+
+ save_matches_and_target(cs->matches, cs->target,
+ cs->jumpto, cs->fw6.ipv6.flags, &cs->fw6);
+
+ if (cs->target == NULL && strlen(cs->jumpto) > 0) {
+ printf("-%c %s", cs->fw6.ipv6.flags & IP6T_F_GOTO ? 'g' : 'j',
+ cs->jumpto);
+ }
+ printf("\n");
+}
+
+/* These are invalid numbers as upper layer protocol */
+static int is_exthdr(uint16_t proto)
+{
+ return (proto == IPPROTO_ROUTING ||
+ proto == IPPROTO_FRAGMENT ||
+ proto == IPPROTO_AH ||
+ proto == IPPROTO_DSTOPTS);
+}
+
+static void nft_ipv6_proto_parse(struct iptables_command_state *cs,
+ struct xtables_args *args)
+{
+ cs->fw6.ipv6.proto = args->proto;
+ cs->fw6.ipv6.invflags = args->invflags;
+
+ if (is_exthdr(cs->fw6.ipv6.proto)
+ && (cs->fw6.ipv6.invflags & XT_INV_PROTO) == 0)
+ fprintf(stderr,
+ "Warning: never matched protocol: %s. "
+ "use extension match instead.\n",
+ cs->protocol);
+}
+
+static void nft_ipv6_post_parse(int command, struct iptables_command_state *cs,
+ struct xtables_args *args)
+{
+ if (args->proto != 0)
+ args->flags |= IP6T_F_PROTO;
+
+ cs->fw6.ipv6.flags = args->flags;
+ /* We already set invflags in proto_parse, but we need to refresh it
+ * to include new parsed options.
+ */
+ cs->fw6.ipv6.invflags = args->invflags;
+
+ strncpy(cs->fw6.ipv6.iniface, args->iniface, IFNAMSIZ);
+ memcpy(cs->fw6.ipv6.iniface_mask,
+ args->iniface_mask, IFNAMSIZ*sizeof(unsigned char));
+
+ strncpy(cs->fw6.ipv6.outiface, args->outiface, IFNAMSIZ);
+ memcpy(cs->fw6.ipv6.outiface_mask,
+ args->outiface_mask, IFNAMSIZ*sizeof(unsigned char));
+
+ if (args->goto_set)
+ cs->fw6.ipv6.flags |= IP6T_F_GOTO;
+
+ cs->fw6.counters.pcnt = args->pcnt_cnt;
+ cs->fw6.counters.bcnt = args->bcnt_cnt;
+
+ if (command & (CMD_REPLACE | CMD_INSERT |
+ CMD_DELETE | CMD_APPEND | CMD_CHECK)) {
+ if (!(cs->options & OPT_DESTINATION))
+ args->dhostnetworkmask = "::0/0";
+ if (!(cs->options & OPT_SOURCE))
+ args->shostnetworkmask = "::0/0";
+ }
+
+ if (args->shostnetworkmask)
+ xtables_ip6parse_multiple(args->shostnetworkmask,
+ &args->s.addr.v6,
+ &args->s.mask.v6,
+ &args->s.naddrs);
+ if (args->dhostnetworkmask)
+ xtables_ip6parse_multiple(args->dhostnetworkmask,
+ &args->d.addr.v6,
+ &args->d.mask.v6,
+ &args->d.naddrs);
+
+ if ((args->s.naddrs > 1 || args->d.naddrs > 1) &&
+ (cs->fw6.ipv6.invflags & (IP6T_INV_SRCIP | IP6T_INV_DSTIP)))
+ xtables_error(PARAMETER_PROBLEM,
+ "! not allowed with multiple"
+ " source or destination IP addresses");
+}
+
+static void nft_ipv6_parse_target(struct xtables_target *t, void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ cs->target = t;
+}
+
+static bool nft_ipv6_rule_find(struct nft_family_ops *ops,
+ struct nft_rule *r, void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ return nft_ipv46_rule_find(ops, r, cs);
+}
+
+struct nft_family_ops nft_family_ops_ipv6 = {
+ .add = nft_ipv6_add,
+ .is_same = nft_ipv6_is_same,
+ .parse_meta = nft_ipv6_parse_meta,
+ .parse_payload = nft_ipv6_parse_payload,
+ .parse_immediate = nft_ipv6_parse_immediate,
+ .print_firewall = nft_ipv6_print_firewall,
+ .save_firewall = nft_ipv6_save_firewall,
+ .proto_parse = nft_ipv6_proto_parse,
+ .post_parse = nft_ipv6_post_parse,
+ .parse_target = nft_ipv6_parse_target,
+ .rule_find = nft_ipv6_rule_find,
+};
diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
new file mode 100644
index 00000000..ada71e6b
--- /dev/null
+++ b/iptables/nft-shared.c
@@ -0,0 +1,773 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+ *
+ * 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 <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <netdb.h>
+#include <errno.h>
+
+#include <xtables.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+#include "nft-shared.h"
+#include "xshared.h"
+#include "nft.h"
+
+extern struct nft_family_ops nft_family_ops_ipv4;
+extern struct nft_family_ops nft_family_ops_ipv6;
+extern struct nft_family_ops nft_family_ops_arp;
+
+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);
+}
+
+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);
+}
+
+/* bitwise operation is = sreg & mask ^ xor */
+void add_bitwise_u16(struct nft_rule *r, int mask, int xor)
+{
+ struct nft_rule_expr *expr;
+
+ expr = nft_rule_expr_alloc("bitwise");
+ if (expr == NULL)
+ return;
+
+ nft_rule_expr_set_u32(expr, NFT_EXPR_BITWISE_SREG, NFT_REG_1);
+ nft_rule_expr_set_u32(expr, NFT_EXPR_BITWISE_DREG, NFT_REG_1);
+ nft_rule_expr_set_u32(expr, NFT_EXPR_BITWISE_LEN, sizeof(uint16_t));
+ nft_rule_expr_set(expr, NFT_EXPR_BITWISE_MASK, &mask, sizeof(uint16_t));
+ nft_rule_expr_set(expr, NFT_EXPR_BITWISE_XOR, &xor, sizeof(uint16_t));
+
+ nft_rule_add_expr(r, expr);
+}
+
+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_u32(expr, NFT_EXPR_CMP_SREG, NFT_REG_1);
+ nft_rule_expr_set_u32(expr, NFT_EXPR_CMP_OP, op);
+ nft_rule_expr_set(expr, NFT_EXPR_CMP_DATA, data, len);
+
+ nft_rule_add_expr(r, expr);
+}
+
+void add_cmp_u8(struct nft_rule *r, uint8_t val, uint32_t op)
+{
+ add_cmp_ptr(r, op, &val, sizeof(val));
+}
+
+void add_cmp_u16(struct nft_rule *r, uint16_t val, uint32_t op)
+{
+ add_cmp_ptr(r, op, &val, sizeof(val));
+}
+
+void add_cmp_u32(struct nft_rule *r, uint32_t val, uint32_t op)
+{
+ add_cmp_ptr(r, op, &val, sizeof(val));
+}
+
+void add_iniface(struct nft_rule *r, char *iface, int invflags)
+{
+ int iface_len;
+ uint32_t op;
+
+ iface_len = strlen(iface);
+
+ if (invflags & IPT_INV_VIA_IN)
+ op = NFT_CMP_NEQ;
+ else
+ op = NFT_CMP_EQ;
+
+ add_meta(r, NFT_META_IIFNAME);
+ if (iface[iface_len - 1] == '+')
+ add_cmp_ptr(r, op, iface, iface_len - 1);
+ else
+ add_cmp_ptr(r, op, iface, iface_len + 1);
+}
+
+void add_outiface(struct nft_rule *r, char *iface, int invflags)
+{
+ int iface_len;
+ uint32_t op;
+
+ iface_len = strlen(iface);
+
+ if (invflags & IPT_INV_VIA_OUT)
+ op = NFT_CMP_NEQ;
+ else
+ op = NFT_CMP_EQ;
+
+ add_meta(r, NFT_META_OIFNAME);
+ if (iface[iface_len - 1] == '+')
+ add_cmp_ptr(r, op, iface, iface_len - 1);
+ else
+ add_cmp_ptr(r, op, iface, iface_len + 1);
+}
+
+void add_addr(struct nft_rule *r, int offset,
+ void *data, size_t len, int invflags)
+{
+ uint32_t op;
+
+ add_payload(r, offset, len);
+
+ if (invflags & IPT_INV_SRCIP || invflags & IPT_INV_DSTIP)
+ op = NFT_CMP_NEQ;
+ else
+ op = NFT_CMP_EQ;
+
+ add_cmp_ptr(r, op, data, len);
+}
+
+void add_proto(struct nft_rule *r, int offset, size_t len,
+ uint8_t proto, int invflags)
+{
+ uint32_t op;
+
+ add_payload(r, offset, len);
+
+ if (invflags & XT_INV_PROTO)
+ op = NFT_CMP_NEQ;
+ else
+ op = NFT_CMP_EQ;
+
+ add_cmp_u8(r, proto, op);
+}
+
+bool is_same_interfaces(const char *a_iniface, const char *a_outiface,
+ unsigned const char *a_iniface_mask,
+ unsigned const char *a_outiface_mask,
+ const char *b_iniface, const char *b_outiface,
+ unsigned const char *b_iniface_mask,
+ unsigned const char *b_outiface_mask)
+{
+ int i;
+
+ for (i = 0; i < IFNAMSIZ; i++) {
+ if (a_iniface_mask[i] != b_iniface_mask[i]) {
+ DEBUGP("different iniface mask %x, %x (%d)\n",
+ a_iniface_mask[i] & 0xff, b_iniface_mask[i] & 0xff, i);
+ return false;
+ }
+ if ((a_iniface[i] & a_iniface_mask[i])
+ != (b_iniface[i] & b_iniface_mask[i])) {
+ DEBUGP("different iniface\n");
+ return false;
+ }
+ if (a_outiface_mask[i] != b_outiface_mask[i]) {
+ DEBUGP("different outiface mask\n");
+ return false;
+ }
+ if ((a_outiface[i] & a_outiface_mask[i])
+ != (b_outiface[i] & b_outiface_mask[i])) {
+ DEBUGP("different outiface\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface,
+ unsigned char *iniface_mask, char *outiface,
+ unsigned char *outiface_mask, uint8_t *invflags)
+{
+ uint32_t value;
+ const void *ifname;
+ uint32_t len;
+
+ switch(key) {
+ case NFT_META_IIF:
+ value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA);
+ if (nft_rule_expr_get_u32(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ *invflags |= IPT_INV_VIA_IN;
+
+ if_indextoname(value, iniface);
+
+ memset(iniface_mask, 0xff, strlen(iniface)+1);
+ break;
+ case NFT_META_OIF:
+ value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA);
+ if (nft_rule_expr_get_u32(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ *invflags |= IPT_INV_VIA_OUT;
+
+ if_indextoname(value, outiface);
+
+ memset(outiface_mask, 0xff, strlen(outiface)+1);
+ break;
+ case NFT_META_IIFNAME:
+ ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len);
+ if (nft_rule_expr_get_u32(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ *invflags |= IPT_INV_VIA_IN;
+
+ memcpy(iniface, ifname, len);
+
+ if (iniface[len] == '\0')
+ memset(iniface_mask, 0xff, len);
+ else {
+ iniface[len] = '+';
+ iniface[len+1] = '\0';
+ memset(iniface_mask, 0xff, len + 1);
+ }
+ break;
+ case NFT_META_OIFNAME:
+ ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len);
+ if (nft_rule_expr_get_u32(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ *invflags |= IPT_INV_VIA_OUT;
+
+ memcpy(outiface, ifname, len);
+
+ if (outiface[len] == '\0')
+ memset(outiface_mask, 0xff, len);
+ else {
+ outiface[len] = '+';
+ outiface[len+1] = '\0';
+ memset(outiface_mask, 0xff, len + 1);
+ }
+ break;
+ default:
+ DEBUGP("unknown meta key %d\n", key);
+ break;
+ }
+}
+
+void nft_parse_target(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
+ int family, void *data)
+{
+ uint32_t tg_len;
+ const char *targname = nft_rule_expr_get_str(e, NFT_EXPR_TG_NAME);
+ const void *targinfo = nft_rule_expr_get(e, NFT_EXPR_TG_INFO, &tg_len);
+ struct xtables_target *target;
+ struct xt_entry_target *t;
+ struct nft_family_ops *ops;
+ size_t size;
+
+ target = xtables_find_target(targname, XTF_TRY_LOAD);
+ if (target == NULL)
+ return;
+
+ size = XT_ALIGN(sizeof(struct xt_entry_target)) + tg_len;
+
+ t = calloc(1, size);
+ if (t == NULL) {
+ fprintf(stderr, "OOM");
+ exit(EXIT_FAILURE);
+ }
+ memcpy(&t->data, targinfo, tg_len);
+ t->u.target_size = size;
+ t->u.user.revision = nft_rule_expr_get_u32(e, NFT_EXPR_TG_REV);
+ strcpy(t->u.user.name, target->name);
+
+ target->t = t;
+
+ ops = nft_family_ops_lookup(family);
+ ops->parse_target(target, data);
+}
+
+static void
+nft_parse_match(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
+ struct iptables_command_state *cs)
+{
+ uint32_t mt_len;
+ const char *mt_name = nft_rule_expr_get_str(e, NFT_EXPR_MT_NAME);
+ const void *mt_info = nft_rule_expr_get(e, NFT_EXPR_MT_INFO, &mt_len);
+ struct xtables_match *match;
+ struct xt_entry_match *m;
+
+ match = xtables_find_match(mt_name, XTF_TRY_LOAD, &cs->matches);
+ if (match == NULL)
+ return;
+
+ m = calloc(1, sizeof(struct xt_entry_match) + mt_len);
+ if (m == NULL) {
+ fprintf(stderr, "OOM");
+ exit(EXIT_FAILURE);
+ }
+
+ memcpy(&m->data, mt_info, mt_len);
+ m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match));
+ m->u.user.revision = nft_rule_expr_get_u32(e, NFT_EXPR_TG_REV);
+ strcpy(m->u.user.name, match->name);
+
+ match->m = m;
+}
+
+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);
+}
+
+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;
+ uint32_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_u32(e, NFT_EXPR_CMP_OP);
+ if (op == NFT_CMP_NEQ)
+ *inv = true;
+ else
+ *inv = false;
+}
+
+void
+nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
+ int family, void *data)
+{
+ uint8_t key = nft_rule_expr_get_u32(e, NFT_EXPR_META_KEY);
+ struct nft_family_ops *ops = nft_family_ops_lookup(family);
+ const char *name;
+
+ 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;
+ }
+
+ ops->parse_meta(e, key, data);
+}
+
+void
+nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
+ int family, void *data)
+{
+ struct nft_family_ops *ops = nft_family_ops_lookup(family);
+ uint32_t offset;
+
+ offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET);
+
+ ops->parse_payload(iter, offset, data);
+}
+
+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);
+}
+
+void
+nft_parse_immediate(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
+ int family, void *data)
+{
+ 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);
+ struct nft_family_ops *ops;
+ const char *jumpto = NULL;
+ bool nft_goto = false;
+
+ /* Standard target? */
+ switch(verdict) {
+ case NF_ACCEPT:
+ jumpto = "ACCEPT";
+ break;
+ case NF_DROP:
+ jumpto = "DROP";
+ break;
+ case NFT_RETURN:
+ jumpto = "RETURN";
+ break;;
+ case NFT_GOTO:
+ nft_goto = true;
+ case NFT_JUMP:
+ jumpto = chain;
+ break;
+ }
+
+ ops = nft_family_ops_lookup(family);
+ ops->parse_immediate(jumpto, nft_goto, data);
+}
+
+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;
+ int family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY);
+
+ 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, family, cs);
+ else if (strcmp(name, "meta") == 0)
+ nft_parse_meta(expr, iter, family, cs);
+ else if (strcmp(name, "immediate") == 0)
+ nft_parse_immediate(expr, iter, family, cs);
+ else if (strcmp(name, "match") == 0)
+ nft_parse_match(expr, iter, cs);
+ else if (strcmp(name, "target") == 0)
+ nft_parse_target(expr, iter, family, cs);
+
+ expr = nft_rule_expr_iter_next(iter);
+ }
+
+ nft_rule_expr_iter_destroy(iter);
+
+ if (cs->target != NULL)
+ cs->jumpto = cs->target->name;
+ else if (cs->jumpto != NULL)
+ cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD);
+ else
+ cs->jumpto = "";
+}
+
+void print_firewall_details(const struct iptables_command_state *cs,
+ const char *targname, uint8_t flags,
+ uint8_t invflags, uint8_t proto,
+ unsigned int num, unsigned int format)
+{
+ if (format & FMT_LINENUMBERS)
+ printf(FMT("%-4u ", "%u "), num);
+
+ if (!(format & FMT_NOCOUNTS)) {
+ xtables_print_num(cs->counters.pcnt, format);
+ xtables_print_num(cs->counters.bcnt, format);
+ }
+
+ if (!(format & FMT_NOTARGET))
+ printf(FMT("%-9s ", "%s "), targname ? targname : "");
+
+ fputc(invflags & XT_INV_PROTO ? '!' : ' ', stdout);
+ {
+ const char *pname =
+ proto_to_name(proto, format&FMT_NUMERIC);
+ if (pname)
+ printf(FMT("%-5s", "%s "), pname);
+ else
+ printf(FMT("%-5hu", "%hu "), proto);
+ }
+}
+
+void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags,
+ unsigned int format)
+{
+ char iface[IFNAMSIZ+2];
+
+ if (!(format & FMT_VIA))
+ return;
+
+ if (invflags & IPT_INV_VIA_IN) {
+ iface[0] = '!';
+ iface[1] = '\0';
+ } else
+ iface[0] = '\0';
+
+ if (iniface[0] != '\0')
+ strcat(iface, iniface);
+ else if (format & FMT_NUMERIC)
+ strcat(iface, "*");
+ else
+ strcat(iface, "any");
+
+ printf(FMT(" %-6s ","in %s "), iface);
+
+ if (invflags & IPT_INV_VIA_OUT) {
+ iface[0] = '!';
+ iface[1] = '\0';
+ } else
+ iface[0] = '\0';
+
+ if (outiface[0] != '\0')
+ strcat(iface, outiface);
+ else if (format & FMT_NUMERIC)
+ strcat(iface, "*");
+ else
+ strcat(iface, "any");
+
+ printf(FMT("%-6s ","out %s "), iface);
+}
+
+static void
+print_iface(char letter, const char *iface, const unsigned char *mask, int inv)
+{
+ unsigned int i;
+
+ if (mask[0] == 0)
+ return;
+
+ printf("%s-%c ", inv ? "! " : "", letter);
+
+ for (i = 0; i < IFNAMSIZ; i++) {
+ if (mask[i] != 0) {
+ if (iface[i] != '\0')
+ printf("%c", iface[i]);
+ } else {
+ if (iface[i-1] != '\0')
+ printf("+");
+ break;
+ }
+ }
+
+ printf(" ");
+}
+
+void save_firewall_details(const struct iptables_command_state *cs,
+ uint8_t invflags, uint16_t proto,
+ const char *iniface,
+ unsigned const char *iniface_mask,
+ const char *outiface,
+ unsigned const char *outiface_mask,
+ unsigned int format)
+{
+ if (!(format & FMT_NOCOUNTS)) {
+ printf("-c ");
+ xtables_print_num(cs->counters.pcnt, format);
+ xtables_print_num(cs->counters.bcnt, format);
+ }
+
+ if (iniface != NULL) {
+ print_iface('i', iniface, iniface_mask,
+ invflags & IPT_INV_VIA_IN);
+ }
+ if (outiface != NULL) {
+ print_iface('o', outiface, outiface_mask,
+ invflags & IPT_INV_VIA_OUT);
+ }
+
+ if (proto > 0) {
+ const struct protoent *pent = getprotobynumber(proto);
+
+ if (invflags & XT_INV_PROTO)
+ printf("! ");
+
+ if (pent)
+ printf("-p %s ", pent->p_name);
+ else
+ printf("-p %u ", proto);
+ }
+}
+
+void save_matches_and_target(struct xtables_rule_match *m,
+ struct xtables_target *target,
+ const char *jumpto, uint8_t flags, const void *fw)
+{
+ struct xtables_rule_match *matchp;
+
+ for (matchp = m; matchp; matchp = matchp->next) {
+ if (matchp->match->alias) {
+ printf("-m %s",
+ matchp->match->alias(matchp->match->m));
+ } else
+ printf("-m %s", matchp->match->name);
+
+ if (matchp->match->save != NULL) {
+ /* cs->fw union makes the trick */
+ matchp->match->save(fw, matchp->match->m);
+ }
+ printf(" ");
+ }
+
+ if (target != NULL) {
+ if (target->alias) {
+ printf("-j %s", target->alias(target->t));
+ } else
+ printf("-j %s", jumpto);
+
+ if (target->save != NULL)
+ target->save(fw, target->t);
+ }
+}
+
+void print_matches_and_target(struct iptables_command_state *cs,
+ unsigned int format)
+{
+ struct xtables_rule_match *matchp;
+
+ for (matchp = cs->matches; matchp; matchp = matchp->next) {
+ if (matchp->match->print != NULL) {
+ matchp->match->print(&cs->fw, matchp->match->m,
+ format & FMT_NUMERIC);
+ }
+ }
+
+ if (cs->target != NULL) {
+ if (cs->target->print != NULL) {
+ cs->target->print(&cs->fw, cs->target->t,
+ format & FMT_NUMERIC);
+ }
+ }
+}
+
+struct nft_family_ops *nft_family_ops_lookup(int family)
+{
+ switch (family) {
+ case AF_INET:
+ return &nft_family_ops_ipv4;
+ case AF_INET6:
+ return &nft_family_ops_ipv6;
+ case NFPROTO_ARP:
+ return &nft_family_ops_arp;
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static bool
+compare_matches(struct xtables_rule_match *mt1, struct xtables_rule_match *mt2)
+{
+ struct xtables_rule_match *mp1;
+ struct xtables_rule_match *mp2;
+
+ for (mp1 = mt1, mp2 = mt2; mp1 && mp2; mp1 = mp1->next, mp2 = mp2->next) {
+ struct xt_entry_match *m1 = mp1->match->m;
+ struct xt_entry_match *m2 = mp2->match->m;
+
+ if (strcmp(m1->u.user.name, m2->u.user.name) != 0) {
+ DEBUGP("mismatching match name\n");
+ return false;
+ }
+
+ if (m1->u.user.match_size != m2->u.user.match_size) {
+ DEBUGP("mismatching match size\n");
+ return false;
+ }
+
+ if (memcmp(m1->data, m2->data,
+ mp1->match->userspacesize) != 0) {
+ DEBUGP("mismatch match data\n");
+ return false;
+ }
+ }
+
+ /* Both cursors should be NULL */
+ if (mp1 != mp2) {
+ DEBUGP("mismatch matches amount\n");
+ return false;
+ }
+
+ return true;
+}
+
+bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2)
+{
+ if (tg1 == NULL && tg2 == NULL)
+ return true;
+
+ if ((tg1 == NULL && tg2 != NULL) || (tg1 != NULL && tg2 == NULL))
+ return false;
+
+ if (strcmp(tg1->t->u.user.name, tg2->t->u.user.name) != 0)
+ return false;
+
+ if (memcmp(tg1->t->data, tg2->t->data, tg1->userspacesize) != 0)
+ return false;
+
+ return true;
+}
+
+bool nft_ipv46_rule_find(struct nft_family_ops *ops,
+ struct nft_rule *r, struct iptables_command_state *cs)
+{
+ struct iptables_command_state this = {};
+
+ nft_rule_to_iptables_command_state(r, &this);
+
+ DEBUGP("comparing with... ");
+#ifdef DEBUG_DEL
+ nft_rule_print_save(&this, r, NFT_RULE_APPEND, 0);
+#endif
+ if (!ops->is_same(cs, &this))
+ return false;
+
+ if (!compare_matches(cs->matches, this.matches)) {
+ DEBUGP("Different matches\n");
+ return false;
+ }
+
+ if (!compare_targets(cs->target, this.target)) {
+ DEBUGP("Different target\n");
+ return false;
+ }
+
+ if (strcmp(cs->jumpto, this.jumpto) != 0) {
+ DEBUGP("Different verdict\n");
+ return false;
+ }
+
+ return true;
+}
diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h
new file mode 100644
index 00000000..145f19d1
--- /dev/null
+++ b/iptables/nft-shared.h
@@ -0,0 +1,195 @@
+#ifndef _NFT_SHARED_H_
+#define _NFT_SHARED_H_
+
+#include <stdbool.h>
+
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+#include "xshared.h"
+
+#if 0
+#define DEBUGP(x, args...) fprintf(stdout, x, ## args)
+#define NLDEBUG
+#define DEBUG_DEL
+#else
+#define DEBUGP(x, args...)
+#endif
+
+/*
+ * 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))
+
+struct xtables_args;
+
+struct nft_family_ops {
+ int (*add)(struct nft_rule *r, void *data);
+ bool (*is_same)(const void *data_a,
+ const void *data_b);
+ void (*print_payload)(struct nft_rule_expr *e,
+ struct nft_rule_expr_iter *iter);
+ void (*parse_meta)(struct nft_rule_expr *e, uint8_t key,
+ void *data);
+ void (*parse_payload)(struct nft_rule_expr_iter *iter,
+ uint32_t offset, void *data);
+ void (*parse_immediate)(const char *jumpto, bool nft_goto, void *data);
+ void (*print_firewall)(struct nft_rule *r, unsigned int num,
+ unsigned int format);
+ void (*save_firewall)(const void *data, unsigned int format);
+ void (*proto_parse)(struct iptables_command_state *cs,
+ struct xtables_args *args);
+ void (*post_parse)(int command, struct iptables_command_state *cs,
+ struct xtables_args *args);
+ void (*parse_target)(struct xtables_target *t, void *data);
+ bool (*rule_find)(struct nft_family_ops *ops, struct nft_rule *r,
+ void *data);
+};
+
+void add_meta(struct nft_rule *r, uint32_t key);
+void add_payload(struct nft_rule *r, int offset, int len);
+void add_bitwise_u16(struct nft_rule *r, int mask, int xor);
+void add_cmp_ptr(struct nft_rule *r, uint32_t op, void *data, size_t len);
+void add_cmp_u8(struct nft_rule *r, uint8_t val, uint32_t op);
+void add_cmp_u16(struct nft_rule *r, uint16_t val, uint32_t op);
+void add_cmp_u32(struct nft_rule *r, uint32_t val, uint32_t op);
+void add_iniface(struct nft_rule *r, char *iface, int invflags);
+void add_outiface(struct nft_rule *r, char *iface, int invflags);
+void add_addr(struct nft_rule *r, int offset,
+ void *data, size_t len, int invflags);
+void add_proto(struct nft_rule *r, int offset, size_t len,
+ uint8_t proto, int invflags);
+void add_compat(struct nft_rule *r, uint32_t proto, bool inv);
+
+bool is_same_interfaces(const char *a_iniface, const char *a_outiface,
+ unsigned const char *a_iniface_mask,
+ unsigned const char *a_outiface_mask,
+ const char *b_iniface, const char *b_outiface,
+ unsigned const char *b_iniface_mask,
+ unsigned const char *b_outiface_mask);
+
+void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface,
+ unsigned char *iniface_mask, char *outiface,
+ unsigned char *outiface_mask, uint8_t *invflags);
+void print_proto(uint16_t proto, int invert);
+void get_cmp_data(struct nft_rule_expr_iter *iter,
+ void *data, size_t dlen, bool *inv);
+void nft_parse_target(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
+ int family, void *data);
+void nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
+ int family, void *data);
+void nft_parse_payload(struct nft_rule_expr *e,
+ struct nft_rule_expr_iter *iter,
+ int family, void *data);
+void nft_parse_counter(struct nft_rule_expr *e,
+ struct nft_rule_expr_iter *iter,
+ struct xt_counters *counters);
+void nft_parse_immediate(struct nft_rule_expr *e,
+ struct nft_rule_expr_iter *iter,
+ int family, void *data);
+void nft_rule_to_iptables_command_state(struct nft_rule *r,
+ struct iptables_command_state *cs);
+void print_firewall_details(const struct iptables_command_state *cs,
+ const char *targname, uint8_t flags,
+ uint8_t invflags, uint8_t proto,
+ unsigned int num, unsigned int format);
+void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags,
+ unsigned int format);
+void print_matches_and_target(struct iptables_command_state *cs,
+ unsigned int format);
+void save_firewall_details(const struct iptables_command_state *cs,
+ uint8_t invflags, uint16_t proto,
+ const char *iniface,
+ unsigned const char *iniface_mask,
+ const char *outiface,
+ unsigned const char *outiface_mask,
+ unsigned int format);
+void save_matches_and_target(struct xtables_rule_match *m,
+ struct xtables_target *target,
+ const char *jumpto,
+ uint8_t flags, const void *fw);
+
+struct nft_family_ops *nft_family_ops_lookup(int family);
+
+struct nft_handle;
+bool nft_ipv46_rule_find(struct nft_family_ops *ops, struct nft_rule *r,
+ struct iptables_command_state *cs);
+
+bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2);
+
+struct addr_mask {
+ union {
+ struct in_addr *v4;
+ struct in6_addr *v6;
+ } addr;
+
+ unsigned int naddrs;
+
+ union {
+ struct in_addr *v4;
+ struct in6_addr *v6;
+ } mask;
+};
+
+struct xtables_args {
+ int family;
+ uint16_t proto;
+ uint8_t flags;
+ uint8_t invflags;
+ char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
+ unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
+ bool goto_set;
+ const char *shostnetworkmask, *dhostnetworkmask;
+ const char *pcnt, *bcnt;
+ struct addr_mask s, d;
+ unsigned long long pcnt_cnt, bcnt_cnt;
+};
+
+#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
+
+/*
+ * ARP
+ */
+extern char *opcodes[];
+#define NUMOPCODES 9
+
+#include <linux/netfilter_arp/arp_tables.h>
+
+static inline struct xt_entry_target *nft_arp_get_target(struct arpt_entry *fw)
+{
+ struct xt_entry_target **target;
+
+ target = (void *) &fw->elems;
+
+ return *target;
+}
+
+#endif
diff --git a/iptables/nft.c b/iptables/nft.c
new file mode 100644
index 00000000..1237659f
--- /dev/null
+++ b/iptables/nft.c
@@ -0,0 +1,2542 @@
+/*
+ * (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 <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 <stdarg.h>
+#include <inttypes.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>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <netinet/ip6.h>
+
+#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 <libnftnl/table.h>
+#include <libnftnl/chain.h>
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+#include <netinet/in.h> /* inet_ntoa */
+#include <arpa/inet.h>
+
+#include "nft.h"
+#include "xshared.h" /* proto_to_name */
+#include "nft-shared.h"
+#include "xtables-config-parser.h"
+
+static void *nft_fn;
+
+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 LIST_HEAD(batch_page_list);
+static int batch_num_pages;
+
+struct batch_page {
+ struct list_head head;
+ struct mnl_nlmsg_batch *batch;
+};
+
+/* selected batch page is 256 Kbytes long to load ruleset of
+ * half a million rules without hitting -EMSGSIZE due to large
+ * iovec.
+ */
+#define BATCH_PAGE_SIZE getpagesize() * 32
+
+static struct mnl_nlmsg_batch *mnl_nft_batch_alloc(void)
+{
+ static char *buf;
+
+ /* libmnl needs higher buffer to handle batch overflows */
+ buf = malloc(BATCH_PAGE_SIZE + getpagesize());
+ if (buf == NULL)
+ return NULL;
+
+ return mnl_nlmsg_batch_start(buf, BATCH_PAGE_SIZE);
+}
+
+static struct mnl_nlmsg_batch *
+mnl_nft_batch_page_add(struct mnl_nlmsg_batch *batch)
+{
+ struct batch_page *batch_page;
+
+ batch_page = malloc(sizeof(struct batch_page));
+ if (batch_page == NULL)
+ return NULL;
+
+ batch_page->batch = batch;
+ list_add_tail(&batch_page->head, &batch_page_list);
+ batch_num_pages++;
+
+ return mnl_nft_batch_alloc();
+}
+
+static int nlbuffsiz;
+
+static void mnl_nft_set_sndbuffer(const struct mnl_socket *nl)
+{
+ int newbuffsiz;
+
+ if (batch_num_pages * BATCH_PAGE_SIZE <= nlbuffsiz)
+ return;
+
+ newbuffsiz = batch_num_pages * BATCH_PAGE_SIZE;
+
+ /* Rise sender buffer length to avoid hitting -EMSGSIZE */
+ if (setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUFFORCE,
+ &newbuffsiz, sizeof(socklen_t)) < 0)
+ return;
+
+ nlbuffsiz = newbuffsiz;
+}
+
+static ssize_t mnl_nft_socket_sendmsg(const struct mnl_socket *nl)
+{
+ static const struct sockaddr_nl snl = {
+ .nl_family = AF_NETLINK
+ };
+ struct iovec iov[batch_num_pages];
+ struct msghdr msg = {
+ .msg_name = (struct sockaddr *) &snl,
+ .msg_namelen = sizeof(snl),
+ .msg_iov = iov,
+ .msg_iovlen = batch_num_pages,
+ };
+ struct batch_page *batch_page, *next;
+ int i = 0;
+
+ mnl_nft_set_sndbuffer(nl);
+
+ list_for_each_entry_safe(batch_page, next, &batch_page_list, head) {
+ iov[i].iov_base = mnl_nlmsg_batch_head(batch_page->batch);
+ iov[i].iov_len = mnl_nlmsg_batch_size(batch_page->batch);
+ i++;
+#ifdef NL_DEBUG
+ mnl_nlmsg_fprintf(stdout,
+ mnl_nlmsg_batch_head(batch_page->batch),
+ mnl_nlmsg_batch_size(batch_page->batch),
+ sizeof(struct nfgenmsg));
+#endif
+ list_del(&batch_page->head);
+ free(batch_page->batch);
+ free(batch_page);
+ batch_num_pages--;
+ }
+
+ return sendmsg(mnl_socket_get_fd(nl), &msg, 0);
+}
+
+static int cb_err(const struct nlmsghdr *nlh, void *data)
+{
+ /* We can provide better error reporting than iptables-restore */
+ errno = EINVAL;
+ return MNL_CB_ERROR;
+}
+
+static mnl_cb_t cb_ctl_array[NLMSG_MIN_TYPE] = {
+ [NLMSG_ERROR] = cb_err,
+};
+
+static int mnl_nft_batch_talk(struct nft_handle *h)
+{
+ int ret, fd = mnl_socket_get_fd(h->nl);
+ char rcv_buf[MNL_SOCKET_BUFFER_SIZE];
+ fd_set readfds;
+ struct timeval tv = {
+ .tv_sec = 0,
+ .tv_usec = 0
+ };
+ int err = 0;
+
+ ret = mnl_nft_socket_sendmsg(h->nl);
+ if (ret == -1) {
+ perror("mnl_socket_sendmsg");
+ return -1;
+ }
+
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+
+ /* receive and digest all the acknowledgments from the kernel. */
+ ret = select(fd+1, &readfds, NULL, NULL, &tv);
+ if (ret == -1) {
+ perror("select");
+ return -1;
+ }
+ while (ret > 0 && FD_ISSET(fd, &readfds)) {
+ ret = mnl_socket_recvfrom(h->nl, rcv_buf, sizeof(rcv_buf));
+ if (ret == -1) {
+ perror("mnl_socket_recvfrom");
+ return -1;
+ }
+
+ ret = mnl_cb_run2(rcv_buf, ret, 0, h->portid,
+ NULL, NULL, cb_ctl_array,
+ MNL_ARRAY_SIZE(cb_ctl_array));
+ /* Continue on error, make sure we get all acknoledgments */
+ if (ret == -1)
+ err = errno;
+
+ ret = select(fd+1, &readfds, NULL, NULL, &tv);
+ if (ret == -1) {
+ perror("select");
+ return -1;
+ }
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+ }
+ return err ? -1 : 0;
+}
+
+static void mnl_nft_batch_put(struct mnl_nlmsg_batch *batch, int type,
+ uint32_t seq)
+{
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfg;
+
+ nlh = mnl_nlmsg_put_header(mnl_nlmsg_batch_current(batch));
+ nlh->nlmsg_type = type;
+ nlh->nlmsg_flags = NLM_F_REQUEST;
+ nlh->nlmsg_seq = seq;
+
+ nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+ nfg->nfgen_family = AF_INET;
+ nfg->version = NFNETLINK_V0;
+ nfg->res_id = NFNL_SUBSYS_NFTABLES;
+
+ if (!mnl_nlmsg_batch_next(batch))
+ mnl_nft_batch_page_add(batch);
+}
+
+static void mnl_nft_batch_begin(struct mnl_nlmsg_batch *batch, uint32_t seq)
+{
+ mnl_nft_batch_put(batch, NFNL_MSG_BATCH_BEGIN, seq);
+}
+
+static void mnl_nft_batch_end(struct mnl_nlmsg_batch *batch, uint32_t seq)
+{
+ mnl_nft_batch_put(batch, NFNL_MSG_BATCH_END, seq);
+}
+
+struct builtin_table xtables_ipv4[TABLES_MAX] = {
+ [RAW] = {
+ .name = "raw",
+ .chains = {
+ {
+ .name = "PREROUTING",
+ .type = "filter",
+ .prio = -300, /* NF_IP_PRI_RAW */
+ .hook = NF_INET_PRE_ROUTING,
+ },
+ {
+ .name = "OUTPUT",
+ .type = "filter",
+ .prio = -300, /* NF_IP_PRI_RAW */
+ .hook = NF_INET_LOCAL_OUT,
+ },
+ },
+ },
+ [MANGLE] = {
+ .name = "mangle",
+ .chains = {
+ {
+ .name = "PREROUTING",
+ .type = "filter",
+ .prio = -150, /* NF_IP_PRI_MANGLE */
+ .hook = NF_INET_PRE_ROUTING,
+ },
+ {
+ .name = "INPUT",
+ .type = "filter",
+ .prio = -150, /* NF_IP_PRI_MANGLE */
+ .hook = NF_INET_LOCAL_IN,
+ },
+ {
+ .name = "FORWARD",
+ .type = "filter",
+ .prio = -150, /* NF_IP_PRI_MANGLE */
+ .hook = NF_INET_FORWARD,
+ },
+ {
+ .name = "OUTPUT",
+ .type = "route",
+ .prio = -150, /* NF_IP_PRI_MANGLE */
+ .hook = NF_INET_LOCAL_OUT,
+ },
+ {
+ .name = "POSTROUTING",
+ .type = "filter",
+ .prio = -150, /* NF_IP_PRI_MANGLE */
+ .hook = NF_INET_POST_ROUTING,
+ },
+ },
+ },
+ [FILTER] = {
+ .name = "filter",
+ .chains = {
+ {
+ .name = "INPUT",
+ .type = "filter",
+ .prio = 0, /* NF_IP_PRI_FILTER */
+ .hook = NF_INET_LOCAL_IN,
+ },
+ {
+ .name = "FORWARD",
+ .type = "filter",
+ .prio = 0, /* NF_IP_PRI_FILTER */
+ .hook = NF_INET_FORWARD,
+ },
+ {
+ .name = "OUTPUT",
+ .type = "filter",
+ .prio = 0, /* NF_IP_PRI_FILTER */
+ .hook = NF_INET_LOCAL_OUT,
+ },
+ },
+ },
+ [SECURITY] = {
+ .name = "security",
+ .chains = {
+ {
+ .name = "INPUT",
+ .type = "filter",
+ .prio = 150, /* NF_IP_PRI_SECURITY */
+ .hook = NF_INET_LOCAL_IN,
+ },
+ {
+ .name = "FORWARD",
+ .type = "filter",
+ .prio = 150, /* NF_IP_PRI_SECURITY */
+ .hook = NF_INET_FORWARD,
+ },
+ {
+ .name = "OUTPUT",
+ .type = "filter",
+ .prio = 150, /* NF_IP_PRI_SECURITY */
+ .hook = NF_INET_LOCAL_OUT,
+ },
+ },
+ },
+ [NAT] = {
+ .name = "nat",
+ .chains = {
+ {
+ .name = "PREROUTING",
+ .type = "nat",
+ .prio = -100, /* NF_IP_PRI_NAT_DST */
+ .hook = NF_INET_PRE_ROUTING,
+ },
+ {
+ .name = "INPUT",
+ .type = "nat",
+ .prio = 100, /* NF_IP_PRI_NAT_SRC */
+ .hook = NF_INET_LOCAL_IN,
+ },
+ {
+ .name = "POSTROUTING",
+ .type = "nat",
+ .prio = 100, /* NF_IP_PRI_NAT_SRC */
+ .hook = NF_INET_POST_ROUTING,
+ },
+ {
+ .name = "OUTPUT",
+ .type = "nat",
+ .prio = -100, /* NF_IP_PRI_NAT_DST */
+ .hook = NF_INET_LOCAL_OUT,
+ },
+ },
+ },
+};
+
+#include <linux/netfilter_arp.h>
+
+struct builtin_table xtables_arp[TABLES_MAX] = {
+ [FILTER] = {
+ .name = "filter",
+ .chains = {
+ {
+ .name = "INPUT",
+ .type = "filter",
+ .prio = NF_IP_PRI_FILTER,
+ .hook = NF_ARP_IN,
+ },
+ {
+ .name = "FORWARD",
+ .type = "filter",
+ .prio = NF_IP_PRI_FILTER,
+ .hook = NF_ARP_FORWARD,
+ },
+ {
+ .name = "OUTPUT",
+ .type = "filter",
+ .prio = NF_IP_PRI_FILTER,
+ .hook = NF_ARP_OUT,
+ },
+ },
+ },
+};
+
+int
+nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t,
+ bool dormant)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nft_table *t;
+ int ret;
+
+ if (_t->initialized)
+ return 0;
+
+ t = nft_table_alloc();
+ if (t == NULL)
+ return -1;
+
+ nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)_t->name);
+ if (dormant) {
+ nft_table_attr_set_u32(t, NFT_TABLE_ATTR_FLAGS,
+ NFT_TABLE_F_DORMANT);
+ }
+
+ nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, h->family,
+ NLM_F_ACK|NLM_F_EXCL, h->seq);
+ nft_table_nlmsg_build_payload(nlh, t);
+ nft_table_free(t);
+
+#ifdef NLDEBUG
+ char tmp[1024];
+
+ nft_table_snprintf(tmp, sizeof(tmp), t, 0, 0);
+ printf("DEBUG: table: %s\n", tmp);
+ mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
+#endif
+
+ ret = mnl_talk(h, nlh, NULL, NULL);
+ if (ret == 0 || errno == EEXIST)
+ _t->initialized = true;
+
+ return ret;
+}
+
+struct nft_chain *
+nft_chain_builtin_alloc(struct builtin_table *table,
+ struct builtin_chain *chain, int policy)
+{
+ struct nft_chain *c;
+
+ c = nft_chain_alloc();
+ if (c == NULL)
+ return NULL;
+
+ 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, chain->prio);
+ nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_POLICY, policy);
+ nft_chain_attr_set(c, NFT_CHAIN_ATTR_TYPE, (char *)chain->type);
+
+ return c;
+}
+
+void
+nft_chain_builtin_add(struct nft_handle *h, struct builtin_table *table,
+ struct builtin_chain *chain, int policy)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nft_chain *c;
+
+ c = nft_chain_builtin_alloc(table, chain, policy);
+ if (c == NULL)
+ return;
+
+ /* NLM_F_CREATE requests module autoloading */
+ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family,
+ NLM_F_ACK|NLM_F_EXCL|NLM_F_CREATE,
+ h->seq);
+ nft_chain_nlmsg_build_payload(nlh, c);
+ nft_chain_free(c);
+
+ mnl_talk(h, nlh, NULL, NULL);
+}
+
+/* find if built-in table already exists */
+struct builtin_table *
+nft_table_builtin_find(struct nft_handle *h, const char *table)
+{
+ int i;
+ bool found = false;
+
+ for (i=0; i<TABLES_MAX; i++) {
+ if (h->tables[i].name == NULL)
+ break;
+
+ if (strcmp(h->tables[i].name, table) != 0)
+ continue;
+
+ found = true;
+ break;
+ }
+
+ return found ? &h->tables[i] : NULL;
+}
+
+/* find if built-in chain already exists */
+struct builtin_chain *
+nft_chain_builtin_find(struct builtin_table *t, const char *chain)
+{
+ int i;
+ bool found = false;
+
+ for (i=0; i<NF_IP_NUMHOOKS && t->chains[i].name != NULL; i++) {
+ if (strcmp(t->chains[i].name, chain) != 0)
+ continue;
+
+ found = true;
+ break;
+ }
+ return found ? &t->chains[i] : NULL;
+}
+
+static void
+__nft_chain_builtin_init(struct nft_handle *h,
+ struct builtin_table *table, const char *chain,
+ int policy)
+{
+ int i, default_policy;
+
+ /* Initialize all built-in chains. Exception, for e one received as
+ * parameter, set the default policy as requested.
+ */
+ for (i=0; i<NF_IP_NUMHOOKS && table->chains[i].name != NULL; i++) {
+ if (chain && strcmp(table->chains[i].name, chain) == 0)
+ default_policy = policy;
+ else
+ default_policy = NF_ACCEPT;
+
+ nft_chain_builtin_add(h, table, &table->chains[i],
+ default_policy);
+ }
+}
+
+int
+nft_chain_builtin_init(struct nft_handle *h, const char *table,
+ const char *chain, int policy)
+{
+ int ret = 0;
+ struct builtin_table *t;
+
+ t = nft_table_builtin_find(h, table);
+ if (t == NULL) {
+ ret = -1;
+ goto out;
+ }
+ if (nft_table_builtin_add(h, t, false) < 0) {
+ /* Built-in table already initialized, skip. */
+ if (errno == EEXIST)
+ goto out;
+ }
+ __nft_chain_builtin_init(h, t, chain, policy);
+out:
+ 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_init(struct nft_handle *h, struct builtin_table *t)
+{
+ 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);
+ h->tables = t;
+
+ INIT_LIST_HEAD(&h->rule_list);
+
+ h->batch = mnl_nft_batch_alloc();
+
+ return 0;
+}
+
+void nft_fini(struct nft_handle *h)
+{
+ mnl_socket_close(h->nl);
+ free(mnl_nlmsg_batch_head(h->batch));
+ mnl_nlmsg_batch_stop(h->batch);
+}
+
+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, h->family,
+ 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, h->family,
+ NLM_F_ACK|NLM_F_EXCL, h->seq);
+ nft_chain_nlmsg_build_payload(nlh, c);
+
+ return mnl_talk(h, nlh, NULL, NULL);
+}
+
+int nft_table_set_dormant(struct nft_handle *h, const char *table)
+{
+ int ret = 0, i;
+ struct builtin_table *t;
+
+ t = nft_table_builtin_find(h, table);
+ if (t == NULL) {
+ ret = -1;
+ goto out;
+ }
+ /* Add this table as dormant */
+ if (nft_table_builtin_add(h, t, true) < 0) {
+ /* Built-in table already initialized, skip. */
+ if (errno == EEXIST)
+ goto out;
+ }
+ for (i=0; t->chains[i].name != NULL && i<NF_INET_NUMHOOKS; i++)
+ __nft_chain_builtin_init(h, t, t->chains[i].name, NF_ACCEPT);
+out:
+ return ret;
+}
+
+int nft_table_wake_dormant(struct nft_handle *h, const char *table)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nft_table *t;
+
+ t = nft_table_alloc();
+ if (t == NULL)
+ return -1;
+
+ nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)table);
+ nft_table_attr_set_u32(t, NFT_TABLE_ATTR_FLAGS, 0);
+
+ nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, h->family,
+ NLM_F_ACK, h->seq);
+ nft_table_nlmsg_build_payload(nlh, t);
+ nft_table_free(t);
+
+ 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\n", 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;
+ struct builtin_table *_t;
+ struct builtin_chain *_c;
+
+ _t = nft_table_builtin_find(h, table);
+ /* if this built-in table does not exists, create it */
+ if (_t != NULL)
+ nft_table_builtin_add(h, _t, false);
+
+ _c = nft_chain_builtin_find(_t, chain);
+ if (_c != NULL) {
+ /* This is a built-in chain */
+ c = nft_chain_builtin_alloc(_t, _c, policy);
+ if (c == NULL)
+ return -1;
+ } else {
+ errno = ENOENT;
+ return -1;
+ }
+
+ 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, h->family,
+ h->restore ? NLM_F_ACK|NLM_F_CREATE :
+ NLM_F_ACK, h->seq);
+ nft_chain_nlmsg_build_payload(nlh, c);
+
+ nft_chain_print_debug(c, nlh);
+
+ nft_chain_free(c);
+
+ return mnl_talk(h, nlh, NULL, NULL);
+}
+
+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 int __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 -ENOMEM;
+
+ memcpy(info, m->data, m->u.match_size - sizeof(*m));
+ nft_rule_expr_set(e, NFT_EXPR_MT_INFO, info, m->u.match_size - sizeof(*m));
+
+ return 0;
+}
+
+int add_match(struct nft_rule *r, struct xt_entry_match *m)
+{
+ struct nft_rule_expr *expr;
+ int ret;
+
+ expr = nft_rule_expr_alloc("match");
+ if (expr == NULL)
+ return -ENOMEM;
+
+ ret = __add_match(expr, m);
+ nft_rule_add_expr(r, expr);
+
+ return ret;
+}
+
+static int __add_target(struct nft_rule_expr *e, struct xt_entry_target *t)
+{
+ void *info;
+
+ 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);
+
+ info = calloc(1, t->u.target_size);
+ if (info == NULL)
+ return -ENOMEM;
+
+ memcpy(info, t->data, t->u.target_size - sizeof(*t));
+ nft_rule_expr_set(e, NFT_EXPR_TG_INFO, info, t->u.target_size - sizeof(*t));
+
+ return 0;
+}
+
+int add_target(struct nft_rule *r, struct xt_entry_target *t)
+{
+ struct nft_rule_expr *expr;
+ int ret;
+
+ expr = nft_rule_expr_alloc("target");
+ if (expr == NULL)
+ return -ENOMEM;
+
+ ret = __add_target(expr, t);
+ nft_rule_add_expr(r, expr);
+
+ return ret;
+}
+
+int 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 -ENOMEM;
+
+ 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);
+
+ return 0;
+}
+
+int add_verdict(struct nft_rule *r, int verdict)
+{
+ struct nft_rule_expr *expr;
+
+ expr = nft_rule_expr_alloc("immediate");
+ if (expr == NULL)
+ return -ENOMEM;
+
+ 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);
+
+ return 0;
+}
+
+int add_action(struct nft_rule *r, struct iptables_command_state *cs,
+ bool goto_set)
+{
+ int ret = 0;
+
+ /* If no target at all, add nothing (default to continue) */
+ if (cs->target != NULL) {
+ /* Standard target? */
+ if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
+ ret = add_verdict(r, NF_ACCEPT);
+ else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
+ ret = add_verdict(r, NF_DROP);
+ else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
+ ret = add_verdict(r, NFT_RETURN);
+ else
+ ret = add_target(r, cs->target->t);
+ } else if (strlen(cs->jumpto) > 0) {
+ /* Not standard, then it's a go / jump to chain */
+ if (goto_set)
+ ret = add_jumpto(r, cs->jumpto, NFT_GOTO);
+ else
+ ret = add_jumpto(r, cs->jumpto, NFT_JUMP);
+ }
+ return ret;
+}
+
+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\n", tmp);
+ mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
+#endif
+}
+
+int 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 -ENOMEM;
+
+ 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);
+
+ return 0;
+}
+
+void add_compat(struct nft_rule *r, uint32_t proto, bool inv)
+{
+ nft_rule_attr_set_u32(r, NFT_RULE_ATTR_COMPAT_PROTO, proto);
+ nft_rule_attr_set_u32(r, NFT_RULE_ATTR_COMPAT_FLAGS,
+ inv ? NFT_RULE_COMPAT_F_INV : 0);
+}
+
+static struct nft_rule *
+nft_rule_new(struct nft_handle *h, const char *chain, const char *table,
+ void *data)
+{
+ struct nft_rule *r;
+
+ r = nft_rule_alloc();
+ if (r == NULL)
+ return NULL;
+
+ nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FAMILY, h->family);
+ nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table);
+ nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain);
+
+ if (h->ops->add(r, data) < 0)
+ goto err;
+
+ return r;
+err:
+ nft_rule_free(r);
+ return NULL;
+}
+
+enum rule_update_type {
+ NFT_DO_APPEND,
+ NFT_DO_INSERT,
+ NFT_DO_REPLACE,
+ NFT_DO_DELETE,
+ NFT_DO_FLUSH,
+ NFT_DO_COMMIT,
+ NFT_DO_ABORT,
+};
+
+struct rule_update {
+ struct list_head head;
+ enum rule_update_type type;
+ struct nft_rule *rule;
+};
+
+static int rule_update_add(struct nft_handle *h, enum rule_update_type type,
+ struct nft_rule *r)
+{
+ struct rule_update *rupd;
+
+ rupd = calloc(1, sizeof(struct rule_update));
+ if (rupd == NULL)
+ return -1;
+
+ rupd->rule = r;
+ rupd->type = type;
+ list_add_tail(&rupd->head, &h->rule_list);
+ h->rule_list_num++;
+
+ return 0;
+}
+
+int
+nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
+ void *data, uint64_t handle, bool verbose)
+{
+ struct nft_rule *r;
+ int type;
+
+ /* If built-in chains don't exist for this table, create them */
+ if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
+ nft_chain_builtin_init(h, table, chain, NF_ACCEPT);
+
+ nft_fn = nft_rule_append;
+
+ r = nft_rule_new(h, chain, table, data);
+ if (r == NULL)
+ return 0;
+
+ if (handle > 0) {
+ nft_rule_attr_set(r, NFT_RULE_ATTR_HANDLE, &handle);
+ type = NFT_DO_REPLACE;
+ } else
+ type = NFT_DO_APPEND;
+
+ if (rule_update_add(h, type, r) < 0)
+ nft_rule_free(r);
+
+ return 1;
+}
+
+void
+nft_rule_print_save(const void *data,
+ struct nft_rule *r, enum nft_rule_print type,
+ unsigned int format)
+{
+ const char *chain = nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN);
+ int family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY);
+ struct nft_family_ops *ops;
+
+ /* print chain name */
+ switch(type) {
+ case NFT_RULE_APPEND:
+ printf("-A %s ", chain);
+ break;
+ case NFT_RULE_DEL:
+ printf("-D %s ", chain);
+ break;
+ }
+
+ ops = nft_family_ops_lookup(family);
+
+ if (ops->save_firewall)
+ ops->save_firewall(data, format);
+
+}
+
+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_tail(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;
+ struct nft_chain_list *list;
+
+ list = nft_chain_list_alloc();
+ if (list == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family,
+ NLM_F_DUMP, h->seq);
+
+ mnl_talk(h, nlh, nft_chain_list_cb, list);
+
+ 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 [%"PRIu64":%"PRIu64"]\n", chain, policy_name[pol],
+ pkts, bytes);
+ } else
+ printf(":%s - [%"PRIu64":%"PRIu64"]\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)
+ 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;
+
+ basechain = nft_chain_builtin(c);
+ nft_chain_print_save(c, basechain);
+next:
+ c = nft_chain_list_iter_next(iter);
+ }
+
+ nft_chain_list_iter_destroy(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_tail(r, list);
+
+ return MNL_CB_OK;
+out:
+ nft_rule_free(r);
+ nft_rule_list_free(list);
+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)
+ return 0;
+
+ nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, h->family,
+ NLM_F_DUMP, h->seq);
+
+ ret = mnl_talk(h, nlh, nft_rule_list_cb, list);
+ if (ret < 0) {
+ 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)
+ return 0;
+
+ iter = nft_rule_list_iter_create(list);
+ if (iter == NULL)
+ 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);
+ struct iptables_command_state cs = {};
+
+ if (strcmp(table, rule_table) != 0)
+ goto next;
+
+ nft_rule_to_iptables_command_state(r, &cs);
+
+ nft_rule_print_save(&cs, r, NFT_RULE_APPEND,
+ counters ? 0 : FMT_NOCOUNTS);
+
+next:
+ r = nft_rule_list_iter_next(iter);
+ }
+
+ nft_rule_list_iter_destroy(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)
+{
+ 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);
+
+ if (rule_update_add(h, NFT_DO_FLUSH, r) < 0)
+ nft_rule_free(r);
+}
+
+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)
+ goto err;
+
+ 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);
+
+ if (chain != NULL)
+ break;
+next:
+ c = nft_chain_list_iter_next(iter);
+ }
+
+ nft_chain_list_iter_destroy(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;
+
+ /* If built-in chains don't exist for this table, create them */
+ if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
+ nft_chain_builtin_init(h, table, NULL, NF_ACCEPT);
+
+ c = nft_chain_alloc();
+ if (c == NULL)
+ return 0;
+
+ 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, h->family,
+ 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);
+
+ /* 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;
+
+ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELCHAIN, h->family,
+ NLM_F_ACK, h->seq);
+ nft_chain_nlmsg_build_payload(nlh, c);
+
+ return mnl_talk(h, nlh, NULL, 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)
+ goto err;
+
+ 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++;
+
+ if (chain != NULL)
+ break;
+next:
+ c = nft_chain_list_iter_next(iter);
+ }
+
+ nft_chain_list_iter_destroy(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;
+}
+
+struct nft_chain *
+nft_chain_list_find(struct nft_chain_list *list,
+ const char *table, const char *chain)
+{
+ struct nft_chain_list_iter *iter;
+ struct nft_chain *c;
+
+ iter = nft_chain_list_iter_create(list);
+ if (iter == NULL)
+ return NULL;
+
+ 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 (strcmp(chain, chain_name) != 0)
+ goto next;
+
+ nft_chain_list_iter_destroy(iter);
+ return c;
+next:
+ c = nft_chain_list_iter_next(iter);
+ }
+ nft_chain_list_iter_destroy(iter);
+ return NULL;
+}
+
+static struct nft_chain *
+nft_chain_find(struct nft_handle *h, const char *table, const char *chain)
+{
+ struct nft_chain_list *list;
+
+ list = nft_chain_list_get(h);
+ if (list == NULL)
+ return NULL;
+
+ return nft_chain_list_find(list, table, chain);
+}
+
+int nft_chain_user_rename(struct nft_handle *h,const char *chain,
+ const char *table, const char *newname)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nft_chain *c;
+ uint64_t handle;
+ int ret;
+
+ /* If built-in chains don't exist for this table, create them */
+ if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
+ nft_chain_builtin_init(h, table, NULL, NF_ACCEPT);
+
+ /* Find the old chain to be renamed */
+ c = nft_chain_find(h, table, chain);
+ if (c == NULL) {
+ errno = ENOENT;
+ return -1;
+ }
+ handle = nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_HANDLE);
+
+ /* Now prepare the new name for the chain */
+ 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 *)newname);
+ nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_HANDLE, handle);
+
+ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family,
+ NLM_F_ACK, h->seq);
+ nft_chain_nlmsg_build_payload(nlh, c);
+ nft_chain_free(c);
+
+ ret = mnl_talk(h, nlh, NULL, NULL);
+
+ /* the core expects 1 for success and 0 for error */
+ return ret == 0 ? 1 : 0;
+}
+
+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_tail(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;
+ struct nft_table_list *list;
+
+ list = nft_table_list_alloc();
+ if (list == NULL)
+ return 0;
+
+ nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, h->family,
+ NLM_F_DUMP, h->seq);
+
+ mnl_talk(h, nlh, nft_table_list_cb, list);
+
+ 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)
+ 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)
+ 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;
+}
+
+int nft_table_purge_chains(struct nft_handle *h, const char *this_table,
+ struct nft_chain_list *chain_list)
+{
+ struct nft_chain_list_iter *iter;
+ struct nft_chain *chain_obj;
+
+ iter = nft_chain_list_iter_create(chain_list);
+ if (iter == NULL)
+ return 0;
+
+ chain_obj = nft_chain_list_iter_next(iter);
+ while (chain_obj != NULL) {
+ const char *table =
+ nft_chain_attr_get_str(chain_obj, NFT_CHAIN_ATTR_TABLE);
+
+ if (strcmp(this_table, table) != 0)
+ goto next;
+
+ if (nft_chain_builtin(chain_obj))
+ goto next;
+
+ if ( __nft_chain_del(h, chain_obj) < 0) {
+ if (errno != EBUSY)
+ return -1;
+ }
+next:
+ chain_obj = nft_chain_list_iter_next(iter);
+ }
+ nft_chain_list_iter_destroy(iter);
+
+ return 0;
+}
+
+static int __nft_rule_del(struct nft_handle *h, struct nft_rule_list *list,
+ struct nft_rule *r)
+{
+ int ret;
+
+ nft_rule_list_del(r);
+
+ ret = rule_update_add(h, NFT_DO_DELETE, r);
+ if (ret < 0) {
+ nft_rule_free(r);
+ return -1;
+ }
+ return 1;
+}
+
+struct nft_rule_list *nft_rule_list_create(struct nft_handle *h)
+{
+ return nft_rule_list_get(h);
+}
+
+void nft_rule_list_destroy(struct nft_rule_list *list)
+{
+ nft_rule_list_free(list);
+}
+
+static struct nft_rule *
+nft_rule_find(struct nft_handle *h, struct nft_rule_list *list,
+ const char *chain, const char *table, void *data, int rulenum)
+{
+ struct nft_rule *r;
+ struct nft_rule_list_iter *iter;
+ int rule_ctr = 0;
+ bool found = false;
+
+ iter = nft_rule_list_iter_create(list);
+ if (iter == NULL)
+ 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);
+
+ 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)
+ goto next;
+ found = true;
+ break;
+ } else {
+ found = h->ops->rule_find(h->ops, r, data);
+ if (found)
+ break;
+ }
+next:
+ rule_ctr++;
+ r = nft_rule_list_iter_next(iter);
+ }
+
+ nft_rule_list_iter_destroy(iter);
+
+ return found ? r : NULL;
+}
+
+int nft_rule_check(struct nft_handle *h, const char *chain,
+ const char *table, void *data, bool verbose)
+{
+ struct nft_rule_list *list;
+ int ret;
+
+ nft_fn = nft_rule_check;
+
+ list = nft_rule_list_create(h);
+ if (list == NULL)
+ return 0;
+
+ ret = nft_rule_find(h, list, chain, table, data, -1) ? 1 : 0;
+ if (ret == 0)
+ errno = ENOENT;
+
+ nft_rule_list_destroy(list);
+
+ return ret;
+}
+
+int nft_rule_delete(struct nft_handle *h, const char *chain,
+ const char *table, void *data, bool verbose)
+{
+ int ret = 0;
+ struct nft_rule *r;
+ struct nft_rule_list *list;
+
+ nft_fn = nft_rule_delete;
+
+ list = nft_rule_list_create(h);
+ if (list == NULL)
+ return 0;
+
+ r = nft_rule_find(h, list, chain, table, data, -1);
+ if (r != NULL) {
+ ret =__nft_rule_del(h, list, r);
+ if (ret < 0)
+ errno = ENOMEM;
+ } else
+ errno = ENOENT;
+
+ nft_rule_list_destroy(list);
+
+ return ret;
+}
+
+static int
+nft_rule_add(struct nft_handle *h, const char *chain,
+ const char *table, struct iptables_command_state *cs,
+ uint64_t handle, bool verbose)
+{
+ struct nft_rule *r;
+
+ r = nft_rule_new(h, chain, table, cs);
+ if (r == NULL)
+ return 0;
+
+ if (handle > 0)
+ nft_rule_attr_set_u64(r, NFT_RULE_ATTR_POSITION, handle);
+
+ if (rule_update_add(h, NFT_DO_INSERT, r) < 0) {
+ nft_rule_free(r);
+ return 0;
+ }
+
+ return 1;
+}
+
+int nft_rule_insert(struct nft_handle *h, const char *chain,
+ const char *table, void *data, int rulenum, bool verbose)
+{
+ struct nft_rule_list *list;
+ struct nft_rule *r;
+ uint64_t handle = 0;
+
+ /* If built-in chains don't exist for this table, create them */
+ if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
+ nft_chain_builtin_init(h, table, chain, NF_ACCEPT);
+
+ nft_fn = nft_rule_insert;
+
+ if (rulenum > 0) {
+ list = nft_rule_list_create(h);
+ if (list == NULL)
+ goto err;
+
+ r = nft_rule_find(h, list, chain, table, data, rulenum);
+ if (r == NULL) {
+ errno = ENOENT;
+ goto err;
+ }
+
+ handle = nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE);
+ DEBUGP("adding after rule handle %"PRIu64"\n", handle);
+
+ nft_rule_list_destroy(list);
+ }
+
+ return nft_rule_add(h, chain, table, data, handle, verbose);
+err:
+ nft_rule_list_destroy(list);
+ return 0;
+}
+
+int nft_rule_delete_num(struct nft_handle *h, const char *chain,
+ const char *table, int rulenum, bool verbose)
+{
+ int ret = 0;
+ struct nft_rule *r;
+ struct nft_rule_list *list;
+
+ nft_fn = nft_rule_delete_num;
+
+ list = nft_rule_list_create(h);
+ if (list == NULL)
+ return 0;
+
+ r = nft_rule_find(h, list, chain, table, NULL, rulenum);
+ if (r != NULL) {
+ ret = 1;
+
+ DEBUGP("deleting rule by number %d\n", rulenum);
+ ret = __nft_rule_del(h, list, r);
+ if (ret < 0)
+ errno = ENOMEM;
+ } else
+ errno = ENOENT;
+
+ nft_rule_list_destroy(list);
+
+ return ret;
+}
+
+int nft_rule_replace(struct nft_handle *h, const char *chain,
+ const char *table, void *data, int rulenum, bool verbose)
+{
+ int ret = 0;
+ struct nft_rule *r;
+ struct nft_rule_list *list;
+
+ nft_fn = nft_rule_replace;
+
+ list = nft_rule_list_create(h);
+ if (list == NULL)
+ return 0;
+
+ r = nft_rule_find(h, list, chain, table, data, rulenum);
+ if (r != NULL) {
+ DEBUGP("replacing rule with handle=%llu\n",
+ (unsigned long long)
+ nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE));
+
+ ret = nft_rule_append(h, chain, table, data,
+ nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE),
+ verbose);
+ } else
+ errno = ENOENT;
+
+ nft_rule_list_destroy(list);
+
+ return ret;
+}
+
+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);
+ xtables_print_num(counters->pcnt, (format|FMT_NOTABLE));
+ fputs("packets, ", stdout);
+ xtables_print_num(counters->bcnt, (format|FMT_NOTABLE));
+ fputs("bytes", stdout);
+ }
+ printf(")\n");
+ } else {
+ 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 int
+__nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
+ int rulenum, unsigned int format,
+ void (*cb)(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;
+
+ list = nft_rule_list_get(h);
+ if (list == NULL)
+ return 0;
+
+ iter = nft_rule_list_iter_create(list);
+ if (iter == NULL)
+ goto err;
+
+ 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 && rule_ctr != rulenum) {
+ /* List by rule number case */
+ goto next;
+ }
+
+ cb(r, rule_ctr, format);
+ if (rulenum > 0 && rule_ctr == rulenum) {
+ ret = 1;
+ break;
+ }
+
+next:
+ r = nft_rule_list_iter_next(iter);
+ }
+
+ nft_rule_list_iter_destroy(iter);
+err:
+ 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)
+{
+ const struct nft_family_ops *ops;
+ struct nft_chain_list *list;
+ struct nft_chain_list_iter *iter;
+ struct nft_chain *c;
+ bool found = false;
+
+ /* If built-in chains don't exist for this table, create them */
+ if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
+ nft_chain_builtin_init(h, table, NULL, NF_ACCEPT);
+
+ ops = nft_family_ops_lookup(h->family);
+
+ if (chain && rulenum) {
+ __nft_rule_list(h, chain, table,
+ rulenum, format, ops->print_firewall);
+ return 1;
+ }
+
+ list = nft_chain_dump(h);
+
+ iter = nft_chain_list_iter_create(list);
+ if (iter == NULL)
+ goto err;
+
+ 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;
+
+ if (found)
+ printf("\n");
+
+ print_header(format, chain_name, policy_name[policy],
+ &ctrs, basechain, refs);
+
+ __nft_rule_list(h, chain_name, table,
+ rulenum, format, ops->print_firewall);
+
+ /* we printed the chain we wanted, stop processing. */
+ if (chain)
+ break;
+
+ found = true;
+
+next:
+ c = nft_chain_list_iter_next(iter);
+ }
+
+ nft_chain_list_iter_destroy(iter);
+err:
+ nft_chain_list_free(list);
+
+ return 1;
+}
+
+static void
+list_save(struct nft_rule *r, unsigned int num, unsigned int format)
+{
+ struct iptables_command_state cs = {};
+
+ nft_rule_to_iptables_command_state(r, &cs);
+
+ nft_rule_print_save(&cs, r, NFT_RULE_APPEND, !(format & FMT_NOCOUNTS));
+}
+
+static int
+nft_rule_list_chain_save(struct nft_handle *h, const char *chain,
+ 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)
+ 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 ||
+ (chain && strcmp(chain, chain_name) != 0))
+ goto next;
+
+ /* this is a base chain */
+ if (nft_chain_builtin(c)) {
+ printf("-P %s %s", chain_name, policy_name[policy]);
+
+ if (counters) {
+ printf(" -c %"PRIu64" %"PRIu64"\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);
+ }
+
+ nft_chain_list_iter_destroy(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;
+ int ret = 1;
+
+ list = nft_chain_dump(h);
+
+ /* Dump policies and custom chains first */
+ if (!rulenum)
+ nft_rule_list_chain_save(h, chain, table, list, counters);
+
+ /* Now dump out rules in this table */
+ iter = nft_chain_list_iter_create(list);
+ if (iter == NULL)
+ goto err;
+
+ 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;
+
+ ret = __nft_rule_list(h, chain_name, table, rulenum,
+ counters ? 0 : FMT_NOCOUNTS, list_save);
+
+ /* we printed the chain we wanted, stop processing. */
+ if (chain)
+ break;
+next:
+ c = nft_chain_list_iter_next(iter);
+ }
+
+ nft_chain_list_iter_destroy(iter);
+err:
+ nft_chain_list_free(list);
+
+ return ret;
+}
+
+int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
+ const char *table, int rulenum)
+{
+ struct iptables_command_state cs = {};
+ struct nft_rule_list *list;
+ struct nft_rule *r;
+ int ret = 0;
+
+ nft_fn = nft_rule_delete;
+
+ list = nft_rule_list_create(h);
+ if (list == NULL)
+ return 0;
+
+ r = nft_rule_find(h, list, chain, table, NULL, rulenum);
+ if (r == NULL) {
+ errno = ENOENT;
+ ret = 1;
+ goto error;
+ }
+
+ nft_rule_to_iptables_command_state(r, &cs);
+
+ cs.counters.pcnt = cs.counters.bcnt = 0;
+
+ ret = nft_rule_append(h, chain, table, &cs,
+ nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE),
+ false);
+
+error:
+ nft_rule_list_destroy(list);
+
+ return ret;
+}
+
+static int nft_action(struct nft_handle *h, int action)
+{
+ int flags = NLM_F_CREATE, type;
+ struct rule_update *n, *tmp;
+ struct nlmsghdr *nlh;
+ uint32_t seq = 1;
+ int ret;
+
+ mnl_nft_batch_begin(h->batch, seq++);
+
+ list_for_each_entry_safe(n, tmp, &h->rule_list, head) {
+ switch (n->type) {
+ case NFT_DO_APPEND:
+ type = NFT_MSG_NEWRULE;
+ flags |= NLM_F_APPEND;
+ break;
+ case NFT_DO_INSERT:
+ type = NFT_MSG_NEWRULE;
+ break;
+ case NFT_DO_REPLACE:
+ type = NFT_MSG_NEWRULE;
+ flags |= NLM_F_REPLACE;
+ break;
+ case NFT_DO_DELETE:
+ case NFT_DO_FLUSH:
+ type = NFT_MSG_DELRULE;
+ break;
+ default:
+ return 0;
+ }
+
+ nlh = nft_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
+ type, h->family, flags, seq++);
+ nft_rule_nlmsg_build_payload(nlh, n->rule);
+ nft_rule_print_debug(n->rule, nlh);
+
+ h->rule_list_num--;
+ list_del(&n->head);
+ nft_rule_free(n->rule);
+ free(n);
+
+ if (!mnl_nlmsg_batch_next(h->batch))
+ h->batch = mnl_nft_batch_page_add(h->batch);
+ }
+
+ switch (action) {
+ case NFT_DO_COMMIT:
+ mnl_nft_batch_end(h->batch, seq++);
+ break;
+ case NFT_DO_ABORT:
+ break;
+ }
+
+ if (!mnl_nlmsg_batch_is_empty(h->batch))
+ h->batch = mnl_nft_batch_page_add(h->batch);
+
+ ret = mnl_nft_batch_talk(h);
+ if (ret < 0)
+ perror("mnl_nft_batch_talk:");
+
+ mnl_nlmsg_batch_reset(h->batch);
+
+ return ret == 0 ? 1 : 0;
+}
+
+int nft_commit(struct nft_handle *h)
+{
+ return nft_action(h, NFT_DO_COMMIT);
+}
+
+int nft_abort(struct nft_handle *h)
+{
+ return nft_action(h, NFT_DO_ABORT);
+}
+
+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 ||
+ opt == IP6T_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);
+}
+
+static void xtables_config_perror(uint32_t flags, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ if (flags & NFT_LOAD_VERBOSE)
+ vfprintf(stderr, fmt, args);
+
+ va_end(args);
+}
+
+int nft_xtables_config_load(struct nft_handle *h, const char *filename,
+ uint32_t flags)
+{
+ 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 = NULL;
+ struct nft_chain_list_iter *citer = NULL;
+ struct nft_table *table;
+ struct nft_chain *chain;
+ uint32_t table_family, chain_family;
+ bool found = false;
+
+ if (h->restore)
+ return 0;
+
+ if (xtables_config_parse(filename, table_list, chain_list) < 0) {
+ if (errno == ENOENT) {
+ xtables_config_perror(flags,
+ "configuration file `%s' does not exists\n",
+ filename);
+ } else {
+ xtables_config_perror(flags,
+ "Fatal error parsing config file: %s\n",
+ strerror(errno));
+ }
+ goto err;
+ }
+
+ /* Stage 1) create tables */
+ titer = nft_table_list_iter_create(table_list);
+ while ((table = nft_table_list_iter_next(titer)) != NULL) {
+ table_family = nft_table_attr_get_u32(table,
+ NFT_TABLE_ATTR_FAMILY);
+ if (h->family != table_family)
+ continue;
+
+ found = true;
+
+ if (nft_table_add(h, table) < 0) {
+ if (errno == EEXIST) {
+ xtables_config_perror(flags,
+ "table `%s' already exists, skipping\n",
+ (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME));
+ } else {
+ xtables_config_perror(flags,
+ "table `%s' cannot be create, reason `%s'. Exitting\n",
+ (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME),
+ strerror(errno));
+ goto err;
+ }
+ continue;
+ }
+ xtables_config_perror(flags, "table `%s' has been created\n",
+ (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME));
+ }
+ nft_table_list_iter_destroy(titer);
+ nft_table_list_free(table_list);
+
+ if (!found)
+ goto err;
+
+ /* Stage 2) create chains */
+ citer = nft_chain_list_iter_create(chain_list);
+ while ((chain = nft_chain_list_iter_next(citer)) != NULL) {
+ chain_family = nft_chain_attr_get_u32(chain,
+ NFT_CHAIN_ATTR_TABLE);
+ if (h->family != chain_family)
+ continue;
+
+ if (nft_chain_add(h, chain) < 0) {
+ if (errno == EEXIST) {
+ xtables_config_perror(flags,
+ "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 {
+ xtables_config_perror(flags,
+ "chain `%s' cannot be create, reason `%s'. Exitting\n",
+ (char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME),
+ strerror(errno));
+ goto err;
+ }
+ continue;
+ }
+
+ xtables_config_perror(flags,
+ "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));
+ }
+ nft_chain_list_iter_destroy(citer);
+ nft_chain_list_free(chain_list);
+
+ return 0;
+
+err:
+ nft_table_list_free(table_list);
+ nft_chain_list_free(chain_list);
+
+ if (titer != NULL)
+ nft_table_list_iter_destroy(titer);
+ if (citer != NULL)
+ nft_chain_list_iter_destroy(citer);
+
+ return -1;
+}
+
+int nft_chain_zero_counters(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;
+ struct nlmsghdr *nlh;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ int ret = 0;
+
+ list = nft_chain_list_get(h);
+ if (list == NULL)
+ goto err;
+
+ iter = nft_chain_list_iter_create(list);
+ if (iter == NULL)
+ goto err;
+
+ c = nft_chain_list_iter_next(iter);
+ while (c != NULL) {
+ const char *chain_name =
+ nft_chain_attr_get(c, NFT_CHAIN_ATTR_NAME);
+ const char *chain_table =
+ nft_chain_attr_get(c, NFT_CHAIN_ATTR_TABLE);
+
+ if (strcmp(table, chain_table) != 0)
+ goto next;
+
+ if (chain != NULL && strcmp(chain, chain_name) != 0)
+ goto next;
+
+ nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_PACKETS, 0);
+ nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_BYTES, 0);
+
+ nft_chain_attr_unset(c, NFT_CHAIN_ATTR_HANDLE);
+
+ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN,
+ h->family, NLM_F_ACK, h->seq);
+
+ nft_chain_nlmsg_build_payload(nlh, c);
+
+ ret = mnl_talk(h, nlh, NULL, NULL);
+ if (ret < 0)
+ perror("mnl_talk:nft_chain_zero_counters");
+
+ if (chain != NULL)
+ break;
+next:
+ c = nft_chain_list_iter_next(iter);
+ }
+
+ nft_chain_list_iter_destroy(iter);
+
+err:
+ nft_chain_list_free(list);
+
+ /* the core expects 1 for success and 0 for error */
+ return ret == 0 ? 1 : 0;
+}
diff --git a/iptables/nft.h b/iptables/nft.h
new file mode 100644
index 00000000..c31371c0
--- /dev/null
+++ b/iptables/nft.h
@@ -0,0 +1,175 @@
+#ifndef _NFT_H_
+#define _NFT_H_
+
+#include "xshared.h"
+#include "nft-shared.h"
+#include <libiptc/linux_list.h>
+
+#define FILTER 0
+#define MANGLE 1
+#define RAW 2
+#define SECURITY 3
+#define NAT 4
+#define TABLES_MAX 5
+
+struct builtin_chain {
+ const char *name;
+ const char *type;
+ uint32_t prio;
+ uint32_t hook;
+};
+
+struct builtin_table {
+ const char *name;
+ struct builtin_chain chains[NF_INET_NUMHOOKS];
+ bool initialized;
+};
+
+struct nft_handle {
+ int family;
+ struct mnl_socket *nl;
+ uint32_t portid;
+ uint32_t seq;
+ struct list_head rule_list;
+ int rule_list_num;
+ struct mnl_nlmsg_batch *batch;
+ struct nft_family_ops *ops;
+ struct builtin_table *tables;
+ bool restore;
+};
+
+extern struct builtin_table xtables_ipv4[TABLES_MAX];
+extern struct builtin_table xtables_arp[TABLES_MAX];
+
+int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
+ int (*cb)(const struct nlmsghdr *nlh, void *data),
+ void *data);
+int nft_init(struct nft_handle *h, struct builtin_table *t);
+void nft_fini(struct nft_handle *h);
+
+/*
+ * Operations with tables.
+ */
+struct nft_table;
+struct nft_chain_list;
+
+int nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t, bool dormant);
+struct builtin_table *nft_table_builtin_find(struct nft_handle *h, const char *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);
+int nft_table_set_dormant(struct nft_handle *h, const char *table);
+int nft_table_wake_dormant(struct nft_handle *h, const char *table);
+int nft_table_purge_chains(struct nft_handle *h, const char *table, struct nft_chain_list *list);
+
+/*
+ * Operations with chains.
+ */
+struct nft_chain;
+
+struct nft_chain *nft_chain_builtin_alloc(struct builtin_table *table, struct builtin_chain *chain, int policy);
+void nft_chain_builtin_add(struct nft_handle *h, struct builtin_table *table, struct builtin_chain *chain, int policy);
+struct builtin_chain *nft_chain_builtin_find(struct builtin_table *t, const char *chain);
+int nft_chain_builtin_init(struct nft_handle *h, const char *table, const char *chain, int policy);
+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);
+struct nft_chain *nft_chain_list_find(struct nft_chain_list *list, const char *table, const char *chain);
+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);
+int nft_chain_zero_counters(struct nft_handle *h, const char *chain, const char *table);
+
+/*
+ * Operations with rule-set.
+ */
+struct nft_rule;
+
+int nft_rule_append(struct nft_handle *h, const char *chain, const char *table, void *data, uint64_t handle, bool verbose);
+int nft_rule_insert(struct nft_handle *h, const char *chain, const char *table, void *data, int rulenum, bool verbose);
+int nft_rule_check(struct nft_handle *h, const char *chain, const char *table, void *data, bool verbose);
+int nft_rule_delete(struct nft_handle *h, const char *chain, const char *table, void *data, 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, void *data, 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);
+int nft_rule_zero_counters(struct nft_handle *h, const char *chain, const char *table, int rulenum);
+
+struct nft_rule_list *nft_rule_list_create(struct nft_handle *h);
+void nft_rule_list_destroy(struct nft_rule_list *list);
+
+/*
+ * Operations used in userspace tools
+ */
+int add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes);
+int add_verdict(struct nft_rule *r, int verdict);
+int add_match(struct nft_rule *r, struct xt_entry_match *m);
+int add_target(struct nft_rule *r, struct xt_entry_target *t);
+int add_jumpto(struct nft_rule *r, const char *name, int verdict);
+int add_action(struct nft_rule *r, struct iptables_command_state *cs, bool goto_set);
+
+enum nft_rule_print {
+ NFT_RULE_APPEND,
+ NFT_RULE_DEL,
+};
+
+void nft_rule_print_save(const void *data,
+ struct nft_rule *r, enum nft_rule_print type,
+ unsigned int format);
+
+/*
+ * global commit and abort
+ */
+int nft_commit(struct nft_handle *h);
+int nft_abort(struct nft_handle *h);
+
+/*
+ * revision compatibility.
+ */
+int nft_compatible_revision(const char *name, uint8_t rev, int opt);
+
+/*
+ * Error reporting.
+ */
+const char *nft_strerror(int err);
+
+/* For xtables.c */
+int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table, bool restore);
+/* For xtables-arptables.c */
+int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table);
+
+/*
+ * Parse config for tables and chain helper functions
+ */
+#define XTABLES_CONFIG_DEFAULT "/etc/xtables.conf"
+
+struct nft_table_list;
+struct nft_chain_list;
+
+extern int xtables_config_parse(const char *filename, struct nft_table_list *table_list, struct nft_chain_list *chain_list);
+
+enum {
+ NFT_LOAD_VERBOSE = (1 << 0),
+};
+
+int nft_xtables_config_load(struct nft_handle *h, const char *filename, uint32_t flags);
+
+/*
+ * ARP
+ */
+
+struct arpt_entry;
+
+int nft_arp_rule_append(struct nft_handle *h, const char *chain,
+ const char *table, struct arpt_entry *fw,
+ bool verbose);
+int nft_arp_rule_insert(struct nft_handle *h, const char *chain,
+ const char *table, struct arpt_entry *fw,
+ int rulenum, bool verbose);
+
+void nft_rule_to_arpt_entry(struct nft_rule *r, struct arpt_entry *fw);
+
+#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-arp-standalone.c b/iptables/xtables-arp-standalone.c
new file mode 100644
index 00000000..23b6bcb4
--- /dev/null
+++ b/iptables/xtables-arp-standalone.c
@@ -0,0 +1,88 @@
+/*
+ * 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>
+ *
+ * arptables -- IP firewall administration for kernels with
+ * firewall table (aimed for the 2.3 kernels)
+ *
+ * See the accompanying manual page arptables(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 <xtables.h>
+#include "nft.h"
+#include <linux/netfilter_arp/arp_tables.h>
+
+#include "xtables-multi.h"
+
+extern struct xtables_globals xtables_globals;
+extern const char *program_version, *program_name;
+
+static const struct xtables_afinfo afinfo_arp = {
+ .kmod = "arp_tables",
+ .proc_exists = "/proc/net/arp_tables_names",
+ .libprefix = "libarp_",
+ .family = NFPROTO_ARP,
+ .ipproto = IPPROTO_IP,
+ .so_rev_match = -1,
+ .so_rev_target = -1,
+};
+
+int xtables_arp_main(int argc, char *argv[])
+{
+ int ret;
+ char *table = "filter";
+ struct nft_handle h = {
+ .family = NFPROTO_ARP,
+ };
+
+ xtables_globals.program_name = "arptables";
+ /* This code below could be replaced by xtables_init_all, which
+ * doesn't support NFPROTO_ARP yet.
+ */
+ xtables_init();
+ afinfo = &afinfo_arp;
+ ret = xtables_set_params(&xtables_globals);
+ if (ret < 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();
+#endif
+
+ ret = do_commandarp(&h, argc, argv, &table);
+ if (ret)
+ ret = nft_commit(&h);
+
+ exit(!ret);
+}
diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c
new file mode 100644
index 00000000..0c79a387
--- /dev/null
+++ b/iptables/xtables-arp.c
@@ -0,0 +1,1502 @@
+/* Code to take an arptables-style command line and do it. */
+
+/*
+ * arptables:
+ * Author: Bart De Schuymer <bdschuym@pandora.be>, but
+ * almost all code is from the iptables userspace program, which has main
+ * authors: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * 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.
+ */
+
+/*
+ Currently, only support for specifying hardware addresses for Ethernet
+ is available.
+ This tool is not luser-proof: you can specify an Ethernet source address
+ and set hardware length to something different than 6, f.e.
+*/
+
+#include <getopt.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <dlfcn.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <net/if.h>
+#include <netinet/ether.h>
+#include <xtables.h>
+
+#include "xshared.h"
+
+#include "nft.h"
+#include <linux/netfilter_arp/arp_tables.h>
+
+typedef char arpt_chainlabel[32];
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* XXX: command defined by nft-shared.h do not overlap with these two */
+#undef CMD_CHECK
+#undef CMD_RENAME_CHAIN
+
+#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_CHECK 0x0800U
+#define CMD_RENAME_CHAIN 0x1000U
+#define NUMBER_OF_CMD 13
+static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
+ 'N', 'X', 'P', 'E' };
+
+#define OPTION_OFFSET 256
+
+#define OPT_NONE 0x00000U
+#define OPT_NUMERIC 0x00001U
+#define OPT_S_IP 0x00002U
+#define OPT_D_IP 0x00004U
+#define OPT_S_MAC 0x00008U
+#define OPT_D_MAC 0x00010U
+#define OPT_H_LENGTH 0x00020U
+#define OPT_P_LENGTH 0x00040U
+#define OPT_OPCODE 0x00080U
+#define OPT_H_TYPE 0x00100U
+#define OPT_P_TYPE 0x00200U
+#define OPT_JUMP 0x00400U
+#define OPT_VERBOSE 0x00800U
+#define OPT_VIANAMEIN 0x01000U
+#define OPT_VIANAMEOUT 0x02000U
+#define OPT_LINENUMBERS 0x04000U
+#define OPT_COUNTERS 0x08000U
+#define NUMBER_OF_OPT 16
+static const char optflags[NUMBER_OF_OPT]
+= { 'n', 's', 'd', 2, 3, 7, 8, 4, 5, 6, 'j', 'v', 'i', 'o', '0', 'c'};
+
+static struct option original_opts[] = {
+ { "append", 1, 0, 'A' },
+ { "delete", 1, 0, 'D' },
+ { "insert", 1, 0, 'I' },
+ { "replace", 1, 0, 'R' },
+ { "list", 2, 0, 'L' },
+ { "flush", 2, 0, 'F' },
+ { "zero", 2, 0, 'Z' },
+ { "new-chain", 1, 0, 'N' },
+ { "delete-chain", 2, 0, 'X' },
+ { "rename-chain", 1, 0, 'E' },
+ { "policy", 1, 0, 'P' },
+ { "source-ip", 1, 0, 's' },
+ { "destination-ip", 1, 0, 'd' },
+ { "src-ip", 1, 0, 's' },
+ { "dst-ip", 1, 0, 'd' },
+ { "source-mac", 1, 0, 2},
+ { "destination-mac", 1, 0, 3},
+ { "src-mac", 1, 0, 2},
+ { "dst-mac", 1, 0, 3},
+ { "h-length", 1, 0, 'l' },
+ { "p-length", 1, 0, 8 },
+ { "opcode", 1, 0, 4 },
+ { "h-type", 1, 0, 5 },
+ { "proto-type", 1, 0, 6 },
+ { "in-interface", 1, 0, 'i' },
+ { "jump", 1, 0, 'j' },
+ { "table", 1, 0, 't' },
+ { "match", 1, 0, 'm' },
+ { "numeric", 0, 0, 'n' },
+ { "out-interface", 1, 0, 'o' },
+ { "verbose", 0, 0, 'v' },
+ { "exact", 0, 0, 'x' },
+ { "version", 0, 0, 'V' },
+ { "help", 2, 0, 'h' },
+ { "line-numbers", 0, 0, '0' },
+ { "modprobe", 1, 0, 'M' },
+ { 0 }
+};
+
+int RUNTIME_NF_ARP_NUMHOOKS = 3;
+
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+
+extern void xtables_exit_error(enum xtables_exittype status, const char *msg, ...);
+extern struct xtables_globals xtables_globals;
+
+/* 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 char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
+/* Well, it's better than "Re: Linux vs FreeBSD" */
+{
+ /* -n -s -d -p -j -v -x -i -o -f --line */
+/*INSERT*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*DELETE*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*DELETE_NUM*/{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*REPLACE*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*APPEND*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*LIST*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*FLUSH*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*ZERO*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*NEW_CHAIN*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*DEL_CHAIN*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*SET_POLICY*/{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*CHECK*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*RENAME*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}
+};
+
+static int inverse_for_options[NUMBER_OF_OPT] =
+{
+/* -n */ 0,
+/* -s */ ARPT_INV_SRCIP,
+/* -d */ ARPT_INV_TGTIP,
+/* 2 */ ARPT_INV_SRCDEVADDR,
+/* 3 */ ARPT_INV_TGTDEVADDR,
+/* -l */ ARPT_INV_ARPHLN,
+/* 8 */ 0,
+/* 4 */ ARPT_INV_ARPOP,
+/* 5 */ ARPT_INV_ARPHRD,
+/* 6 */ ARPT_INV_ARPPRO,
+/* -j */ 0,
+/* -v */ 0,
+/* -i */ ARPT_INV_VIA_IN,
+/* -o */ ARPT_INV_VIA_OUT,
+/*--line*/ 0,
+/* -c */ 0,
+};
+
+const char *program_version = XTABLES_VERSION;
+const char *program_name = "arptables";
+
+/* A few hardcoded protocols for 'all' and in case the user has no
+ /etc/protocols */
+struct pprot {
+ char *name;
+ u_int8_t num;
+};
+
+/* Primitive headers... */
+/* defined in netinet/in.h */
+#if 0
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH 51
+#endif
+#endif
+
+/***********************************************/
+/* ARPTABLES SPECIFIC NEW FUNCTIONS ADDED HERE */
+/***********************************************/
+
+unsigned char mac_type_unicast[ETH_ALEN] = {0,0,0,0,0,0};
+unsigned char msk_type_unicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+
+/*
+ * put the mac address into 6 (ETH_ALEN) bytes
+ */
+static int getmac_and_mask(char *from, char *to, char *mask)
+{
+ char *p;
+ int i;
+ struct ether_addr *addr;
+
+ if (strcasecmp(from, "Unicast") == 0) {
+ memcpy(to, mac_type_unicast, ETH_ALEN);
+ memcpy(mask, msk_type_unicast, ETH_ALEN);
+ return 0;
+ }
+ if (strcasecmp(from, "Multicast") == 0) {
+ memcpy(to, mac_type_multicast, ETH_ALEN);
+ memcpy(mask, msk_type_multicast, ETH_ALEN);
+ return 0;
+ }
+ if (strcasecmp(from, "Broadcast") == 0) {
+ memcpy(to, mac_type_broadcast, ETH_ALEN);
+ memcpy(mask, msk_type_broadcast, ETH_ALEN);
+ return 0;
+ }
+ if ( (p = strrchr(from, '/')) != NULL) {
+ *p = '\0';
+ if (!(addr = ether_aton(p + 1)))
+ return -1;
+ memcpy(mask, addr, ETH_ALEN);
+ } else
+ memset(mask, 0xff, ETH_ALEN);
+ if (!(addr = ether_aton(from)))
+ return -1;
+ memcpy(to, addr, ETH_ALEN);
+ for (i = 0; i < ETH_ALEN; i++)
+ to[i] &= mask[i];
+ return 0;
+}
+
+static int getlength_and_mask(char *from, uint8_t *to, uint8_t *mask)
+{
+ char *p, *buffer;
+ int i;
+
+ if ( (p = strrchr(from, '/')) != NULL) {
+ *p = '\0';
+ i = strtol(p+1, &buffer, 10);
+ if (*buffer != '\0' || i < 0 || i > 255)
+ return -1;
+ *mask = (uint8_t)i;
+ } else
+ *mask = 255;
+ i = strtol(from, &buffer, 10);
+ if (*buffer != '\0' || i < 0 || i > 255)
+ return -1;
+ *to = (uint8_t)i;
+ return 0;
+}
+
+static int get16_and_mask(char *from, uint16_t *to, uint16_t *mask, int base)
+{
+ char *p, *buffer;
+ int i;
+
+ if ( (p = strrchr(from, '/')) != NULL) {
+ *p = '\0';
+ i = strtol(p+1, &buffer, base);
+ if (*buffer != '\0' || i < 0 || i > 65535)
+ return -1;
+ *mask = htons((uint16_t)i);
+ } else
+ *mask = 65535;
+ i = strtol(from, &buffer, base);
+ if (*buffer != '\0' || i < 0 || i > 65535)
+ return -1;
+ *to = htons((uint16_t)i);
+ return 0;
+}
+
+static int
+string_to_number(const char *s, unsigned int min, unsigned int max,
+ unsigned int *ret)
+{
+ long number;
+ char *end;
+
+ /* Handle hex, octal, etc. */
+ errno = 0;
+ number = strtol(s, &end, 0);
+ if (*end == '\0' && end != s) {
+ /* we parsed a number, let's see if we want this */
+ if (errno != ERANGE && min <= number && number <= max) {
+ *ret = number;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/*********************************************/
+/* ARPTABLES SPECIFIC NEW FUNCTIONS END HERE */
+/*********************************************/
+
+static struct in_addr *
+dotted_to_addr(const char *dotted)
+{
+ static struct in_addr addr;
+ unsigned char *addrp;
+ char *p, *q;
+ unsigned int onebyte;
+ int i;
+ char buf[20];
+
+ /* copy dotted string, because we need to modify it */
+ strncpy(buf, dotted, sizeof(buf) - 1);
+ addrp = (unsigned char *) &(addr.s_addr);
+
+ p = buf;
+ for (i = 0; i < 3; i++) {
+ if ((q = strchr(p, '.')) == NULL)
+ return (struct in_addr *) NULL;
+
+ *q = '\0';
+ if (string_to_number(p, 0, 255, &onebyte) == -1)
+ return (struct in_addr *) NULL;
+
+ addrp[i] = (unsigned char) onebyte;
+ p = q + 1;
+ }
+
+ /* we've checked 3 bytes, now we check the last one */
+ if (string_to_number(p, 0, 255, &onebyte) == -1)
+ return (struct in_addr *) NULL;
+
+ addrp[3] = (unsigned char) onebyte;
+
+ return &addr;
+}
+
+static struct in_addr *
+network_to_addr(const char *name)
+{
+ struct netent *net;
+ static struct in_addr addr;
+
+ if ((net = getnetbyname(name)) != NULL) {
+ if (net->n_addrtype != AF_INET)
+ return (struct in_addr *) NULL;
+ addr.s_addr = htonl((unsigned long) net->n_net);
+ return &addr;
+ }
+
+ return (struct in_addr *) NULL;
+}
+
+static void
+inaddrcpy(struct in_addr *dst, struct in_addr *src)
+{
+ /* memcpy(dst, src, sizeof(struct in_addr)); */
+ dst->s_addr = src->s_addr;
+}
+
+static void
+exit_tryhelp(int status)
+{
+ fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+ program_name, program_name );
+ exit(status);
+}
+
+static void
+exit_printhelp(void)
+{
+ struct xtables_target *t = NULL;
+ int i;
+
+ printf("%s v%s\n\n"
+"Usage: %s -[AD] chain rule-specification [options]\n"
+" %s -[RI] chain rulenum rule-specification [options]\n"
+" %s -D chain rulenum [options]\n"
+" %s -[LFZ] [chain] [options]\n"
+" %s -[NX] chain\n"
+" %s -E old-chain-name new-chain-name\n"
+" %s -P chain target [options]\n"
+" %s -h (print this help information)\n\n",
+ program_name, program_version, program_name, program_name,
+ program_name, program_name, program_name, program_name,
+ program_name, program_name);
+
+ printf(
+"Commands:\n"
+"Either long or short options are allowed.\n"
+" --append -A chain Append to chain\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] List the rules in a chain or all chains\n"
+" --flush -F [chain] Delete all rules in chain or all chains\n"
+" --zero -Z [chain] Zero counters in chain or all chains\n"
+" --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"
+" --source-ip -s [!] address[/mask]\n"
+" source specification\n"
+" --destination-ip -d [!] address[/mask]\n"
+" destination specification\n"
+" --source-mac [!] address[/mask]\n"
+" --destination-mac [!] address[/mask]\n"
+" --h-length -l length[/mask] hardware length (nr of bytes)\n"
+" --opcode code[/mask] operation code (2 bytes)\n"
+" --h-type type[/mask] hardware type (2 bytes, hexadecimal)\n"
+" --proto-type type[/mask] protocol type (2 bytes)\n"
+" --in-interface -i [!] input name[+]\n"
+" network interface name ([+] for wildcard)\n"
+" --out-interface -o [!] output name[+]\n"
+" network interface name ([+] for wildcard)\n"
+" --jump -j target\n"
+" target for rule (may load target extension)\n"
+" --match -m match\n"
+" extended match (may load extension)\n"
+" --numeric -n numeric output of addresses and ports\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"
+" --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");
+ printf(" opcode strings: \n");
+ for (i = 0; i < NUMOPCODES; i++)
+ printf(" %d = %s\n", i + 1, opcodes[i]);
+ printf(
+" hardware type string: 1 = Ethernet\n"
+" protocol type string: 0x800 = IPv4\n");
+
+ /* Print out any special helps. A user might like to be able
+ to add a --help to the commandline, and see expected
+ results. So we call help for all matches & targets */
+ for (t = xtables_targets; t; t = t->next) {
+ if (strcmp(t->name, "CLASSIFY") && strcmp(t->name, "mangle"))
+ continue;
+ printf("\n");
+ t->help();
+ }
+ 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 unsigned int othercmds, int invert)
+{
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM, "unexpected ! flag");
+ if (*cmd & (~othercmds))
+ xtables_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
+ cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
+ *cmd |= newcmd;
+}
+
+static int
+check_inverse(const char option[], int *invert, int *optidx, int argc)
+{
+ if (option && strcmp(option, "!") == 0) {
+ if (*invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "Multiple `!' flags not allowed");
+ *invert = TRUE;
+ if (optidx) {
+ *optidx = *optidx+1;
+ if (argc && *optidx > argc)
+ xtables_error(PARAMETER_PROBLEM,
+ "no argument following `!'");
+ }
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static struct in_addr *
+host_to_addr(const char *name, unsigned int *naddr)
+{
+ struct hostent *host;
+ struct in_addr *addr;
+ unsigned int i;
+
+ *naddr = 0;
+ if ((host = gethostbyname(name)) != NULL) {
+ if (host->h_addrtype != AF_INET ||
+ host->h_length != sizeof(struct in_addr))
+ return (struct in_addr *) NULL;
+
+ while (host->h_addr_list[*naddr] != (char *) NULL)
+ (*naddr)++;
+ addr = xtables_calloc(*naddr, sizeof(struct in_addr));
+ for (i = 0; i < *naddr; i++)
+ inaddrcpy(&(addr[i]),
+ (struct in_addr *) host->h_addr_list[i]);
+ return addr;
+ }
+
+ return (struct in_addr *) NULL;
+}
+
+/*
+ * 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.
+*/
+
+static struct in_addr *
+parse_hostnetwork(const char *name, unsigned int *naddrs)
+{
+ struct in_addr *addrp, *addrptmp;
+
+ if ((addrptmp = dotted_to_addr(name)) != NULL ||
+ (addrptmp = network_to_addr(name)) != NULL) {
+ addrp = xtables_malloc(sizeof(struct in_addr));
+ inaddrcpy(addrp, addrptmp);
+ *naddrs = 1;
+ return addrp;
+ }
+ if ((addrp = host_to_addr(name, naddrs)) != NULL)
+ return addrp;
+
+ xtables_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
+}
+
+static struct in_addr *
+parse_mask(char *mask)
+{
+ static struct in_addr maskaddr;
+ struct in_addr *addrp;
+ unsigned int bits;
+
+ if (mask == NULL) {
+ /* no mask at all defaults to 32 bits */
+ maskaddr.s_addr = 0xFFFFFFFF;
+ return &maskaddr;
+ }
+ if ((addrp = dotted_to_addr(mask)) != NULL)
+ /* dotted_to_addr already returns a network byte order addr */
+ return addrp;
+ if (string_to_number(mask, 0, 32, &bits) == -1)
+ xtables_error(PARAMETER_PROBLEM,
+ "invalid mask `%s' specified", mask);
+ if (bits != 0) {
+ maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
+ return &maskaddr;
+ }
+
+ maskaddr.s_addr = 0L;
+ return &maskaddr;
+}
+
+static void
+parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
+ struct in_addr *maskp, unsigned int *naddrs)
+{
+ struct in_addr *addrp;
+ char buf[256];
+ char *p;
+ int i, j, k, n;
+
+ strncpy(buf, name, sizeof(buf) - 1);
+ if ((p = strrchr(buf, '/')) != NULL) {
+ *p = '\0';
+ addrp = parse_mask(p + 1);
+ } else
+ addrp = parse_mask(NULL);
+ inaddrcpy(maskp, addrp);
+
+ /* if a null mask is given, the name is ignored, like in "any/0" */
+ if (maskp->s_addr == 0L)
+ strcpy(buf, "0.0.0.0");
+
+ addrp = *addrpp = parse_hostnetwork(buf, naddrs);
+ n = *naddrs;
+ for (i = 0, j = 0; i < n; i++) {
+ addrp[j++].s_addr &= maskp->s_addr;
+ for (k = 0; k < j - 1; k++) {
+ if (addrp[k].s_addr == addrp[j - 1].s_addr) {
+ (*naddrs)--;
+ j--;
+ break;
+ }
+ }
+ }
+}
+
+static void
+parse_interface(const char *arg, char *vianame, unsigned char *mask)
+{
+ int vialen = strlen(arg);
+ unsigned int i;
+
+ memset(mask, 0, IFNAMSIZ);
+ memset(vianame, 0, IFNAMSIZ);
+
+ if (vialen + 1 > IFNAMSIZ)
+ xtables_error(PARAMETER_PROBLEM,
+ "interface name `%s' must be shorter than IFNAMSIZ"
+ " (%i)", arg, IFNAMSIZ-1);
+
+ strcpy(vianame, arg);
+ if (vialen == 0)
+ memset(mask, 0, IFNAMSIZ);
+ else if (vianame[vialen - 1] == '+') {
+ memset(mask, 0xFF, vialen - 1);
+ memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
+ /* Don't remove `+' here! -HW */
+ } else {
+ /* Include nul-terminator in match */
+ memset(mask, 0xFF, vialen + 1);
+ memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
+ for (i = 0; vianame[i]; i++) {
+ if (!isalnum(vianame[i])
+ && vianame[i] != '_'
+ && vianame[i] != '.') {
+ printf("Warning: wierd character in interface"
+ " `%s' (No aliases, :, ! or *).\n",
+ vianame);
+ break;
+ }
+ }
+ }
+}
+
+/* Can't be zero. */
+static int
+parse_rulenumber(const char *rule)
+{
+ unsigned int rulenum;
+
+ if (!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)+1 > sizeof(arpt_chainlabel))
+ xtables_error(PARAMETER_PROBLEM,
+ "Invalid target name `%s' (%zu chars max)",
+ targetname, sizeof(arpt_chainlabel)-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, u_int16_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
+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;
+
+ return nft_rule_list(h, chain, table, rulenum, format);
+}
+
+static struct arpt_entry *
+generate_entry(const struct arpt_entry *fw,
+ struct arpt_entry_target *target)
+{
+ struct arpt_entry_target **t;
+ struct arpt_entry *e;
+ unsigned int size;
+
+
+ size = sizeof(struct arpt_entry);
+
+ e = xtables_malloc(size);
+ *e = *fw;
+ e->target_offset = offsetof(struct arpt_entry, elems);
+ e->next_offset = e->target_offset + target->u.target_size;
+
+ t = (void *) &e->elems;
+ *t = target;
+
+ return e;
+}
+
+static struct xtables_target *command_jump(struct arpt_entry *fw,
+ const char *jumpto)
+{
+ struct xtables_target *target;
+ size_t size;
+
+ /* XTF_TRY_LOAD (may be chain name) */
+ target = xtables_find_target(jumpto, XTF_TRY_LOAD);
+
+ if (!target)
+ return NULL;
+
+ size = XT_ALIGN(sizeof(struct xt_entry_target))
+ + target->size;
+
+ target->t = xtables_calloc(1, size);
+ target->t->u.target_size = size;
+ strncpy(target->t->u.user.name, jumpto, sizeof(target->t->u.user.name));
+ target->t->u.user.name[sizeof(target->t->u.user.name)-1] = '\0';
+ target->t->u.user.revision = target->revision;
+
+ xs_init_target(target);
+
+ if (target->x6_options != NULL)
+ opts = xtables_options_xfrm(xtables_globals.orig_opts,
+ opts, target->x6_options,
+ &target->option_offset);
+ else
+ opts = xtables_merge_options(xtables_globals.orig_opts,
+ opts, target->extra_opts,
+ &target->option_offset);
+
+ return target;
+}
+
+static int
+append_entry(struct nft_handle *h,
+ const char *chain,
+ const char *table,
+ struct arpt_entry *fw,
+ int rulenum,
+ unsigned int nsaddrs,
+ const struct in_addr saddrs[],
+ unsigned int ndaddrs,
+ const struct in_addr daddrs[],
+ bool verbose, bool append)
+{
+ unsigned int i, j;
+ int ret = 1;
+
+ for (i = 0; i < nsaddrs; i++) {
+ fw->arp.src.s_addr = saddrs[i].s_addr;
+ for (j = 0; j < ndaddrs; j++) {
+ fw->arp.tgt.s_addr = daddrs[j].s_addr;
+ if (append) {
+ ret = nft_rule_append(h, chain, table, fw, 0,
+ verbose);
+ } else {
+ ret = nft_rule_insert(h, chain, table, fw,
+ rulenum, verbose);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int
+replace_entry(const char *chain,
+ const char *table,
+ struct arpt_entry *fw,
+ unsigned int rulenum,
+ const struct in_addr *saddr,
+ const struct in_addr *daddr,
+ bool verbose, struct nft_handle *h)
+{
+ fw->arp.src.s_addr = saddr->s_addr;
+ fw->arp.tgt.s_addr = daddr->s_addr;
+
+ return nft_rule_replace(h, chain, table, fw, rulenum, verbose);
+}
+
+static int
+delete_entry(const char *chain,
+ const char *table,
+ struct arpt_entry *fw,
+ unsigned int nsaddrs,
+ const struct in_addr saddrs[],
+ unsigned int ndaddrs,
+ const struct in_addr daddrs[],
+ bool verbose, struct nft_handle *h)
+{
+ unsigned int i, j;
+ int ret = 1;
+
+ for (i = 0; i < nsaddrs; i++) {
+ fw->arp.src.s_addr = saddrs[i].s_addr;
+ for (j = 0; j < ndaddrs; j++) {
+ fw->arp.tgt.s_addr = daddrs[j].s_addr;
+ ret = nft_rule_delete(h, chain, table, fw, verbose);
+ }
+ }
+
+ return ret;
+}
+
+int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
+{
+ struct arpt_entry fw, *e = NULL;
+ int invert = 0;
+ unsigned int nsaddrs = 0, ndaddrs = 0;
+ struct in_addr *saddrs = NULL, *daddrs = NULL;
+
+ int c, verbose = 0;
+ const char *chain = NULL;
+ const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
+ const char *policy = NULL, *newname = NULL;
+ unsigned int rulenum = 0, options = 0, command = 0;
+ const char *pcnt = NULL, *bcnt = NULL;
+ int ret = 1;
+ struct xtables_target *target = NULL;
+ struct xtables_target *t;
+
+ const char *jumpto = "";
+
+ memset(&fw, 0, sizeof(fw));
+ opts = original_opts;
+ global_option_offset = 0;
+
+ xtables_globals.orig_opts = original_opts;
+
+ /* re-set optind to 0 in case do_command gets called
+ * a second time */
+ optind = 0;
+
+ for (t = xtables_targets; t; t = t->next) {
+ t->tflags = 0;
+ t->used = 0;
+ }
+
+ /* Suppress error messages: we may add new options if we
+ demand-load a protocol. */
+ opterr = 0;
+
+ while ((c = getopt_long(argc, argv,
+ "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:l:i:vnt:m:c:",
+ opts, NULL)) != -1) {
+ switch (c) {
+ /*
+ * Command selection
+ */
+ case 'A':
+ add_command(&command, CMD_APPEND, CMD_NONE,
+ invert);
+ chain = optarg;
+ break;
+
+ case 'D':
+ add_command(&command, CMD_DELETE, CMD_NONE,
+ 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,
+ 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,
+ 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,
+ invert);
+ if (optarg) chain = optarg;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ chain = argv[optind++];
+ break;
+
+ case 'F':
+ add_command(&command, CMD_FLUSH, CMD_NONE,
+ 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,
+ invert);
+ if (optarg) chain = optarg;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ chain = argv[optind++];
+ break;
+
+ case 'N':
+ if (optarg && *optarg == '-')
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name not allowed to start "
+ "with `-'\n");
+ 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,
+ invert);
+ chain = optarg;
+ break;
+
+ case 'X':
+ add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
+ 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,
+ 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,
+ 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];
+
+ exit_printhelp();
+ break;
+ case 's':
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_S_IP, &fw.arp.invflags,
+ invert);
+ shostnetworkmask = argv[optind-1];
+ break;
+
+ case 'd':
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_D_IP, &fw.arp.invflags,
+ invert);
+ dhostnetworkmask = argv[optind-1];
+ break;
+
+ case 2:/* src-mac */
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_S_MAC, &fw.arp.invflags,
+ invert);
+ if (getmac_and_mask(argv[optind - 1],
+ fw.arp.src_devaddr.addr, fw.arp.src_devaddr.mask))
+ xtables_error(PARAMETER_PROBLEM, "Problem with specified "
+ "source mac");
+ break;
+
+ case 3:/* dst-mac */
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_D_MAC, &fw.arp.invflags,
+ invert);
+
+ if (getmac_and_mask(argv[optind - 1],
+ fw.arp.tgt_devaddr.addr, fw.arp.tgt_devaddr.mask))
+ xtables_error(PARAMETER_PROBLEM, "Problem with specified "
+ "destination mac");
+ break;
+
+ case 'l':/* hardware length */
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_H_LENGTH, &fw.arp.invflags,
+ invert);
+ getlength_and_mask(argv[optind - 1], &fw.arp.arhln,
+ &fw.arp.arhln_mask);
+
+ if (fw.arp.arhln != 6) {
+ xtables_error(PARAMETER_PROBLEM,
+ "Only harware address length of"
+ " 6 is supported currently.");
+ }
+
+ break;
+
+ case 8:/* protocol length */
+ xtables_error(PARAMETER_PROBLEM, "not supported");
+/*
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_P_LENGTH, &fw.arp.invflags,
+ invert);
+
+ getlength_and_mask(argv[optind - 1], &fw.arp.arpln,
+ &fw.arp.arpln_mask);
+ break;
+*/
+
+ case 4:/* opcode */
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_OPCODE, &fw.arp.invflags,
+ invert);
+ if (get16_and_mask(argv[optind - 1], &fw.arp.arpop, &fw.arp.arpop_mask, 10)) {
+ int i;
+
+ for (i = 0; i < NUMOPCODES; i++)
+ if (!strcasecmp(opcodes[i], optarg))
+ break;
+ if (i == NUMOPCODES)
+ xtables_error(PARAMETER_PROBLEM, "Problem with specified opcode");
+ fw.arp.arpop = htons(i+1);
+ }
+ break;
+
+ case 5:/* h-type */
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_H_TYPE, &fw.arp.invflags,
+ invert);
+ if (get16_and_mask(argv[optind - 1], &fw.arp.arhrd, &fw.arp.arhrd_mask, 16)) {
+ if (strcasecmp(argv[optind-1], "Ethernet"))
+ xtables_error(PARAMETER_PROBLEM, "Problem with specified hardware type");
+ fw.arp.arhrd = htons(1);
+ }
+ break;
+
+ case 6:/* proto-type */
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_P_TYPE, &fw.arp.invflags,
+ invert);
+ if (get16_and_mask(argv[optind - 1], &fw.arp.arpro, &fw.arp.arpro_mask, 0)) {
+ if (strcasecmp(argv[optind-1], "ipv4"))
+ xtables_error(PARAMETER_PROBLEM, "Problem with specified protocol type");
+ fw.arp.arpro = htons(0x800);
+ }
+ break;
+
+ case 'j':
+ set_option(&options, OPT_JUMP, &fw.arp.invflags,
+ invert);
+ jumpto = parse_target(optarg);
+ target = command_jump(&fw, jumpto);
+ break;
+
+ case 'i':
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_VIANAMEIN, &fw.arp.invflags,
+ invert);
+ parse_interface(argv[optind-1],
+ fw.arp.iniface,
+ fw.arp.iniface_mask);
+/* fw.nfcache |= NFC_IP_IF_IN; */
+ break;
+
+ case 'o':
+ check_inverse(optarg, &invert, &optind, argc);
+ set_option(&options, OPT_VIANAMEOUT, &fw.arp.invflags,
+ invert);
+ parse_interface(argv[optind-1],
+ fw.arp.outiface,
+ fw.arp.outiface_mask);
+ /* fw.nfcache |= NFC_IP_IF_OUT; */
+ break;
+
+ case 'v':
+ if (!verbose)
+ set_option(&options, OPT_VERBOSE,
+ &fw.arp.invflags, invert);
+ verbose++;
+ break;
+
+ case 'm': /*{
+ size_t size;
+
+ if (invert)
+ exit_error(PARAMETER_PROBLEM,
+ "unexpected ! flag before --match");
+
+ m = find_match(optarg, LOAD_MUST_SUCCEED);
+ size = ARPT_ALIGN(sizeof(struct arpt_entry_match))
+ + m->size;
+ m->m = fw_calloc(1, size);
+ m->m->u.match_size = size;
+ strcpy(m->m->u.user.name, m->name);
+ m->init(m->m, &fw.nfcache);
+ opts = merge_options(opts, m->extra_opts, &m->option_offset);
+ }*/
+ break;
+
+ case 'n':
+ set_option(&options, OPT_NUMERIC, &fw.arp.invflags,
+ invert);
+ break;
+
+ case 't':
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "unexpected ! flag before --table");
+ *table = argv[optind-1];
+ break;
+
+ case 'V':
+ if (invert)
+ printf("Not %s ;-)\n", program_version);
+ else
+ printf("%s v%s\n",
+ program_name, program_version);
+ exit(0);
+
+ case '0':
+ set_option(&options, OPT_LINENUMBERS, &fw.arp.invflags,
+ invert);
+ break;
+
+ case 'M':
+ //modprobe = optarg;
+ break;
+
+ case 'c':
+
+ set_option(&options, OPT_COUNTERS, &fw.arp.invflags,
+ invert);
+ pcnt = optarg;
+ if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ bcnt = argv[optind++];
+ else
+ xtables_error(PARAMETER_PROBLEM,
+ "-%c requires packet and byte counter",
+ opt2char(OPT_COUNTERS));
+
+ if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "-%c packet counter not numeric",
+ opt2char(OPT_COUNTERS));
+
+ if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "-%c byte counter not numeric",
+ opt2char(OPT_COUNTERS));
+
+ break;
+
+
+ case 1: /* non option */
+ if (optarg[0] == '!' && optarg[1] == '\0') {
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "multiple consecutive ! not"
+ " allowed");
+ invert = TRUE;
+ optarg[0] = '\0';
+ continue;
+ }
+ printf("Bad argument `%s'\n", optarg);
+ exit_tryhelp(2);
+
+ default:
+ if (target) {
+ xtables_option_tpcall(c, argv,
+ invert, target, &fw);
+ }
+ break;
+ }
+ invert = FALSE;
+ }
+
+ if (target)
+ xtables_option_tfcall(target);
+
+ if (optind < argc)
+ xtables_error(PARAMETER_PROBLEM,
+ "unknown arguments found on commandline");
+ if (!command)
+ xtables_error(PARAMETER_PROBLEM, "no command specified");
+ if (invert)
+ xtables_error(PARAMETER_PROBLEM,
+ "nothing appropriate following !");
+
+ if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
+ if (!(options & OPT_D_IP))
+ dhostnetworkmask = "0.0.0.0/0";
+ if (!(options & OPT_S_IP))
+ shostnetworkmask = "0.0.0.0/0";
+ }
+
+ if (shostnetworkmask)
+ parse_hostnetworkmask(shostnetworkmask, &saddrs,
+ &(fw.arp.smsk), &nsaddrs);
+
+ if (dhostnetworkmask)
+ parse_hostnetworkmask(dhostnetworkmask, &daddrs,
+ &(fw.arp.tmsk), &ndaddrs);
+
+ if ((nsaddrs > 1 || ndaddrs > 1) &&
+ (fw.arp.invflags & (ARPT_INV_SRCIP | ARPT_INV_TGTIP)))
+ 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, options);
+
+ if (chain && strlen(chain) > ARPT_FUNCTION_MAXNAMELEN)
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name `%s' too long (must be under %i chars)",
+ chain, ARPT_FUNCTION_MAXNAMELEN);
+
+ if (nft_init(h, xtables_arp) < 0)
+ xtables_error(OTHER_PROBLEM,
+ "Could not initialize nftables layer.");
+
+ h->ops = nft_family_ops_lookup(h->family);
+ if (h->ops == NULL)
+ xtables_error(PARAMETER_PROBLEM, "Unknown family");
+
+ if (command == CMD_APPEND
+ || command == CMD_DELETE
+ || command == CMD_INSERT
+ || command == CMD_REPLACE) {
+ if (strcmp(chain, "PREROUTING") == 0
+ || strcmp(chain, "INPUT") == 0) {
+ /* -o not valid with incoming packets. */
+ if (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 (options & OPT_VIANAMEIN)
+ xtables_error(PARAMETER_PROBLEM,
+ "Can't use -%c with %s\n",
+ opt2char(OPT_VIANAMEIN),
+ chain);
+ }
+
+ if (!target && strlen(jumpto) != 0) {
+ size_t size;
+
+ target = xtables_find_target(XT_STANDARD_TARGET,
+ XTF_LOAD_MUST_SUCCEED);
+ size = sizeof(struct arpt_entry_target) + target->size;
+ target->t = xtables_calloc(1, size);
+ target->t->u.target_size = size;
+ strcpy(target->t->u.user.name, jumpto);
+ }
+
+ if (!target) {
+ xtables_error(PARAMETER_PROBLEM,
+ "No target provided or"
+ " initalization failed");
+ }
+
+ e = generate_entry(&fw, target->t);
+ }
+
+ switch (command) {
+ case CMD_APPEND:
+ ret = append_entry(h, chain, *table, e, 0,
+ nsaddrs, saddrs, ndaddrs, daddrs,
+ options&OPT_VERBOSE, true);
+ break;
+ case CMD_DELETE:
+ ret = delete_entry(chain, *table, e,
+ nsaddrs, saddrs, ndaddrs, daddrs,
+ options&OPT_VERBOSE, h);
+ break;
+ case CMD_DELETE_NUM:
+ ret = nft_rule_delete_num(h, chain, *table, rulenum - 1, verbose);
+ break;
+ case CMD_REPLACE:
+ ret = replace_entry(chain, *table, e, rulenum - 1,
+ saddrs, daddrs, options&OPT_VERBOSE, h);
+ break;
+ case CMD_INSERT:
+ ret = append_entry(h, chain, *table, e, rulenum - 1,
+ nsaddrs, saddrs, ndaddrs, daddrs,
+ options&OPT_VERBOSE, false);
+ break;
+ case CMD_LIST:
+ ret = list_entries(h, chain, *table,
+ rulenum,
+ options&OPT_VERBOSE,
+ options&OPT_NUMERIC,
+ /*options&OPT_EXPANDED*/0,
+ options&OPT_LINENUMBERS);
+ break;
+ case CMD_FLUSH:
+ ret = nft_rule_flush(h, chain, *table);
+ break;
+ case CMD_ZERO:
+ ret = nft_chain_zero_counters(h, chain, *table);
+ break;
+ case CMD_LIST|CMD_ZERO:
+ ret = list_entries(h, chain, *table, rulenum,
+ options&OPT_VERBOSE,
+ options&OPT_NUMERIC,
+ /*options&OPT_EXPANDED*/0,
+ options&OPT_LINENUMBERS);
+ if (ret)
+ ret = nft_chain_zero_counters(h, chain, *table);
+ 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:
+ 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);*/
+
+ return ret;
+}
diff --git a/iptables/xtables-compat-multi.c b/iptables/xtables-compat-multi.c
new file mode 100644
index 00000000..47810524
--- /dev/null
+++ b/iptables/xtables-compat-multi.c
@@ -0,0 +1,39 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "xshared.h"
+
+#include "xtables-multi.h"
+
+static const struct subcommand multi_subcommands[] = {
+ {"iptables-xml", iptables_xml_main},
+ {"xml", iptables_xml_main},
+ {"iptables", xtables_ip4_main},
+ {"iptables-compat", xtables_ip4_main},
+ {"main4", xtables_ip4_main},
+ {"save4", xtables_ip4_save_main},
+ {"restore4", xtables_ip4_restore_main},
+ {"iptables-save", xtables_ip4_save_main},
+ {"iptables-restore", xtables_ip4_restore_main},
+ {"iptables-compat-save", xtables_ip4_save_main},
+ {"iptables-compat-restore", xtables_ip4_restore_main},
+ {"ip6tables", xtables_ip6_main},
+ {"ip6tables-compat", xtables_ip6_main},
+ {"main6", xtables_ip6_main},
+ {"save6", xtables_ip6_save_main},
+ {"restore6", xtables_ip6_restore_main},
+ {"ip6tables-save", xtables_ip6_save_main},
+ {"ip6tables-restore", xtables_ip6_restore_main},
+ {"ip6tables-compat-save", xtables_ip6_save_main},
+ {"ip6tables-compat-restore", xtables_ip6_restore_main},
+ {"arptables", xtables_arp_main},
+ {"arptables-compat", xtables_arp_main},
+ {"xtables-config", xtables_config_main},
+ {"xtables-events", xtables_events_main},
+ {NULL},
+};
+
+int main(int argc, char **argv)
+{
+ return subcmd_main(argc, argv, multi_subcommands);
+}
diff --git a/iptables/xtables-config-parser.y b/iptables/xtables-config-parser.y
new file mode 100644
index 00000000..2770a1b9
--- /dev/null
+++ b/iptables/xtables-config-parser.y
@@ -0,0 +1,250 @@
+%{
+/*
+ * (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 <libnftnl/table.h>
+#include <libnftnl/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));
+}
+
+static void stack_free(struct stack_elem *e)
+{
+ free(e);
+}
+
+%}
+
+%union {
+ int val;
+ char *string;
+}
+
+%token T_FAMILY
+%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 : family
+ ;
+
+family : T_FAMILY T_STRING '{' tables '}'
+ {
+ void *data = stack_push(T_FAMILY, strlen($2)+1);
+ stack_put_str(data, $2);
+ }
+ ;
+
+tables : table
+ | tables table
+ ;
+
+table : T_TABLE T_STRING '{' chains '}'
+ {
+ /* added in reverse order to pop it in order */
+ void *data = stack_push(T_TABLE, strlen($2)+1);
+ stack_put_str(data, $2);
+ }
+ ;
+
+chains : chain
+ | chains chain
+ ;
+
+chain : T_CHAIN T_STRING T_HOOK T_STRING T_PRIO T_INTEGER
+ {
+ /* added in reverse order to pop it in order */
+ void *data = stack_push(T_PRIO, sizeof(int32_t));
+ stack_put_i32(data, $6);
+ data = stack_push(T_HOOK, strlen($4)+1);
+ stack_put_str(data, $4);
+ data = stack_push(T_CHAIN, strlen($2)+1);
+ 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;
+}
+
+static int32_t familytonumber(const char *family)
+{
+ if (strcmp(family, "ipv4") == 0)
+ return AF_INET;
+ else if (strcmp(family, "ipv6") == 0)
+ return AF_INET6;
+
+ 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;
+ int32_t family = 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_FAMILY:
+ family = familytonumber(e->data);
+ if (family == -1)
+ return -1;
+ break;
+ case T_TABLE:
+ table = nft_table_alloc();
+ if (table == NULL) {
+ perror("nft_table_alloc");
+ return -1;
+ }
+ nft_table_attr_set_u32(table, NFT_TABLE_ATTR_FAMILY, family);
+ nft_table_attr_set(table, NFT_TABLE_ATTR_NAME, e->data);
+ /* This is intentionally prepending, instead of
+ * appending, since the elements in the stack are in
+ * the reverse order that chains appear in the
+ * configuration file.
+ */
+ nft_table_list_add(table, table_list);
+ break;
+ case T_PRIO:
+ memcpy(&prio, e->data, sizeof(int32_t));
+ 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_u32(chain, NFT_CHAIN_ATTR_FAMILY,
+ nft_table_attr_get_u32(table, NFT_TABLE_ATTR_FAMILY));
+ nft_chain_attr_set_s32(chain, NFT_CHAIN_ATTR_PRIO, prio);
+ nft_chain_attr_set(chain, NFT_CHAIN_ATTR_NAME, e->data);
+ /* Intentionally prepending, instead of appending */
+ nft_chain_list_add(chain, chain_list);
+ break;
+ case T_HOOK:
+ nft_chain_attr_set_u32(chain, NFT_CHAIN_ATTR_HOOKNUM,
+ hooknametonum(e->data));
+ 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..a895c8bc
--- /dev/null
+++ b/iptables/xtables-config-syntax.l
@@ -0,0 +1,54 @@
+%{
+/*
+ * (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\.\-\_]*
+
+%%
+"family" { return T_FAMILY; }
+"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..b7cf6094
--- /dev/null
+++ b/iptables/xtables-config.c
@@ -0,0 +1,46 @@
+/*
+ * (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 "xtables-multi.h"
+#include "nft.h"
+
+int xtables_config_main(int argc, char *argv[])
+{
+ struct nft_handle h = {
+ .family = AF_INET,
+ };
+ const char *filename = NULL;
+
+ 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 (nft_init(&h, xtables_ipv4) < 0) {
+ fprintf(stderr, "Failed to initialize nft: %s\n",
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ return nft_xtables_config_load(&h, filename, NFT_LOAD_VERBOSE) == 0 ?
+ EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/iptables/xtables-events.c b/iptables/xtables-events.c
new file mode 100644
index 00000000..4be8ab8c
--- /dev/null
+++ b/iptables/xtables-events.c
@@ -0,0 +1,217 @@
+/*
+ * (C) 2012-2013 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 <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <getopt.h>
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftnl/table.h>
+#include <libnftnl/chain.h>
+#include <libnftnl/rule.h>
+
+#include <include/xtables.h>
+#include "iptables.h" /* for xtables_globals */
+#include "xtables-multi.h"
+#include "nft.h"
+
+static int table_cb(const struct nlmsghdr *nlh, int type)
+{
+ struct nft_table *t;
+ char buf[4096];
+
+ t = nft_table_alloc();
+ if (t == NULL) {
+ perror("OOM");
+ goto err;
+ }
+
+ if (nft_table_nlmsg_parse(nlh, t) < 0) {
+ perror("nft_table_nlmsg_parse");
+ goto err_free;
+ }
+
+ nft_table_snprintf(buf, sizeof(buf), t, NFT_OUTPUT_DEFAULT, 0);
+ /* FIXME: define syntax to represent table events */
+ printf("# [table: %s]\t%s\n", type == NFT_MSG_NEWTABLE ? "NEW" : "DEL", buf);
+
+err_free:
+ nft_table_free(t);
+err:
+ return MNL_CB_OK;
+}
+
+static bool counters;
+
+static int rule_cb(const struct nlmsghdr *nlh, int type)
+{
+ struct iptables_command_state cs = {};
+ struct nft_rule *r;
+
+ 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 err_free;
+ }
+
+ nft_rule_to_iptables_command_state(r, &cs);
+
+ switch(nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY)) {
+ case AF_INET:
+ printf("-4 ");
+ break;
+ case AF_INET6:
+ printf("-6 ");
+ break;
+ default:
+ break;
+ }
+
+
+ nft_rule_print_save(&cs, r,
+ type == NFT_MSG_NEWRULE ? NFT_RULE_APPEND :
+ NFT_RULE_DEL,
+ counters ? 0 : FMT_NOCOUNTS);
+err_free:
+ nft_rule_free(r);
+err:
+ return MNL_CB_OK;
+}
+
+static int chain_cb(const struct nlmsghdr *nlh, int type)
+{
+ struct nft_chain *t;
+ char buf[4096];
+
+ t = nft_chain_alloc();
+ if (t == NULL) {
+ perror("OOM");
+ goto err;
+ }
+
+ if (nft_chain_nlmsg_parse(nlh, t) < 0) {
+ perror("nft_chain_nlmsg_parse");
+ goto err_free;
+ }
+
+ nft_chain_snprintf(buf, sizeof(buf), t, NFT_OUTPUT_DEFAULT, 0);
+ /* FIXME: define syntax to represent chain events */
+ printf("# [chain: %s]\t%s\n", type == NFT_MSG_NEWCHAIN ? "NEW" : "DEL", buf);
+
+err_free:
+ nft_chain_free(t);
+err:
+ return MNL_CB_OK;
+}
+
+static int events_cb(const struct nlmsghdr *nlh, void *data)
+{
+ int ret = MNL_CB_OK;
+ int type = nlh->nlmsg_type & 0xFF;
+
+ switch(type) {
+ case NFT_MSG_NEWTABLE:
+ case NFT_MSG_DELTABLE:
+ ret = table_cb(nlh, type);
+ break;
+ case NFT_MSG_NEWCHAIN:
+ case NFT_MSG_DELCHAIN:
+ ret = chain_cb(nlh, type);
+ break;
+ case NFT_MSG_NEWRULE:
+ case NFT_MSG_DELRULE:
+ ret = rule_cb(nlh, type);
+ break;
+ }
+
+ return ret;
+}
+
+static const struct option options[] = {
+ {.name = "counters", .has_arg = false, .val = 'c'},
+ {NULL},
+};
+
+static void print_usage(const char *name, const char *version)
+{
+ fprintf(stderr, "Usage: %s [-c]\n"
+ " [ --counters ]\n", name);
+ exit(EXIT_FAILURE);
+}
+
+int xtables_events_main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ int ret, c;
+
+ xtables_globals.program_name = "xtables-events";
+ /* 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
+
+ opterr = 0;
+ while ((c = getopt_long(argc, argv, "c", options, NULL)) != -1) {
+ switch (c) {
+ case 'c':
+ counters = true;
+ break;
+ default:
+ print_usage(argv[0], XTABLES_VERSION);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, (1 << (NFNLGRP_NFTABLES-1)), MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, 0, 0, events_cb, NULL);
+ if (ret <= 0)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+ mnl_socket_close(nl);
+
+ return EXIT_SUCCESS;
+}
diff --git a/iptables/xtables-multi.c b/iptables/xtables-multi.c
index 8014d5fb..5f487355 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,14 @@ 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},
+ {"xtables-events", xtables_events_main},
+ {"xtables-arp", xtables_arp_main},
+#endif
{NULL},
};
diff --git a/iptables/xtables-multi.h b/iptables/xtables-multi.h
index 615724b1..e706894b 100644
--- a/iptables/xtables-multi.h
+++ b/iptables/xtables-multi.h
@@ -2,5 +2,16 @@
#define _XTABLES_MULTI_H 1
extern int iptables_xml_main(int, char **);
+#ifdef ENABLE_NFTABLES
+extern int xtables_ip4_main(int, char **);
+extern int xtables_ip4_save_main(int, char **);
+extern int xtables_ip4_restore_main(int, char **);
+extern int xtables_ip6_main(int, char **);
+extern int xtables_ip6_save_main(int, char **);
+extern int xtables_ip6_restore_main(int, char **);
+extern int xtables_arp_main(int, char **);
+extern int xtables_config_main(int, char **);
+extern int xtables_events_main(int, char **);
+#endif
#endif /* _XTABLES_MULTI_H */
diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
new file mode 100644
index 00000000..f7850bb2
--- /dev/null
+++ b/iptables/xtables-restore.c
@@ -0,0 +1,488 @@
+/* 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"
+#include <libnftnl/chain.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'},
+ {.name = "ipv4", .has_arg = false, .val = '4'},
+ {.name = "ipv6", .has_arg = false, .val = '6'},
+ {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!");
+ }
+ }
+}
+
+static const struct xtc_ops xtc_ops = {
+ .strerror = nft_strerror,
+};
+
+static int
+xtables_restore_main(int family, const char *progname, int argc, char *argv[])
+{
+ struct nft_handle h = {
+ .family = family,
+ .restore = true,
+ };
+ 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 = &xtc_ops;
+ struct nft_chain_list *chain_list;
+ struct nft_chain *chain_obj;
+
+ line = 0;
+
+ xtables_globals.program_name = progname;
+ c = xtables_init_all(&xtables_globals, family);
+ 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
+
+ if (nft_init(&h, xtables_ipv4) < 0) {
+ fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
+ xtables_globals.program_name,
+ xtables_globals.program_version,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ while ((c = getopt_long(argc, argv, "bcvthnM:T:46", 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;
+ case '4':
+ h.family = AF_INET;
+ break;
+ case '6':
+ h.family = AF_INET6;
+ xtables_set_nfproto(AF_INET6);
+ 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;
+
+ chain_list = nft_chain_dump(&h);
+ if (chain_list == NULL)
+ xtables_error(OTHER_PROBLEM, "cannot retrieve chain list\n");
+
+ /* 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)) {
+ if (!testing) {
+ /* Commit per table, although we support
+ * global commit at once, stick by now to
+ * the existing behaviour.
+ */
+ DEBUGP("Calling commit\n");
+ ret = nft_commit(&h);
+ } else {
+ DEBUGP("Not calling commit, testing\n");
+ ret = nft_abort(&h);
+ }
+ in_table = 0;
+
+ /* Purge out unused chains in this table */
+ if (!testing)
+ nft_table_purge_chains(&h, curtable, chain_list);
+
+ } 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);
+ }
+
+ 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);
+ }
+
+ chain_obj = nft_chain_list_find(chain_list,
+ curtable, chain);
+ /* This chain has been found, delete from list. Later
+ * on, unvisited chains will be purged out.
+ */
+ if (chain_obj != NULL)
+ nft_chain_list_del(chain_obj);
+
+ 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);
+
+ }
+ 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));
+ }
+ DEBUGP("Setting policy of chain %s to %s\n",
+ chain, policy);
+ ret = 1;
+
+ } else {
+ if (nft_chain_user_add(&h, chain, curtable) < 0) {
+ if (errno == EEXIST)
+ continue;
+
+ xtables_error(PARAMETER_PROBLEM,
+ "cannot create chain "
+ "'%s' (%s)\n", chain,
+ strerror(errno));
+ }
+ continue;
+ }
+
+ } 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], true);
+ if (ret < 0) {
+ ret = nft_abort(&h);
+ if (ret < 0) {
+ fprintf(stderr, "failed to abort "
+ "commit operation\n");
+ }
+ exit(1);
+ }
+
+ 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;
+}
+
+int xtables_ip4_restore_main(int argc, char *argv[])
+{
+ return xtables_restore_main(NFPROTO_IPV4, "iptables-restore",
+ argc, argv);
+}
+
+int xtables_ip6_restore_main(int argc, char *argv[])
+{
+ return xtables_restore_main(NFPROTO_IPV6, "ip6tables-restore",
+ argc, argv);
+}
diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c
new file mode 100644
index 00000000..42d29071
--- /dev/null
+++ b/iptables/xtables-save.c
@@ -0,0 +1,154 @@
+/* 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 <libnftnl/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'},
+ {.name = "ipv4", .has_arg = false, .val = '4'},
+ {.name = "ipv6", .has_arg = false, .val = '6'},
+ {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
+ */
+static int
+xtables_save_main(int family, const char *progname, int argc, char *argv[])
+{
+ const char *tablename = NULL;
+ bool dump = false;
+ struct nft_handle h = {
+ .family = family,
+ };
+ int c;
+
+ xtables_globals.program_name = progname;
+ c = xtables_init_all(&xtables_globals, family);
+ 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
+ if (nft_init(&h, xtables_ipv4) < 0) {
+ fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
+ xtables_globals.program_name,
+ xtables_globals.program_version,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ while ((c = getopt_long(argc, argv, "bcdt:46", 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':
+ dump = true;
+ break;
+ case '4':
+ h.family = AF_INET;
+ break;
+ case '6':
+ h.family = AF_INET6;
+ xtables_set_nfproto(AF_INET6);
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr, "Unknown arguments found on commandline\n");
+ exit(1);
+ }
+
+ if (dump) {
+ do_output(&h, tablename, show_counters);
+ exit(0);
+ }
+
+ return !do_output(&h, tablename, show_counters);
+}
+
+int xtables_ip4_save_main(int argc, char *argv[])
+{
+ return xtables_save_main(NFPROTO_IPV4, "iptables-save", argc, argv);
+}
+
+int xtables_ip6_save_main(int argc, char *argv[])
+{
+ return xtables_save_main(NFPROTO_IPV6, "ip6tables-save", argc, argv);
+}
diff --git a/iptables/xtables-standalone.c b/iptables/xtables-standalone.c
new file mode 100644
index 00000000..355a4460
--- /dev/null
+++ b/iptables/xtables-standalone.c
@@ -0,0 +1,104 @@
+/*
+ * 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"
+
+static int
+xtables_main(int family, const char *progname, int argc, char *argv[])
+{
+ int ret;
+ char *table = "filter";
+ struct nft_handle h = {
+ .family = family,
+ };
+
+ xtables_globals.program_name = progname;
+ ret = xtables_init_all(&xtables_globals, family);
+ if (ret < 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
+
+ if (nft_init(&h, xtables_ipv4) < 0) {
+ fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
+ xtables_globals.program_name,
+ xtables_globals.program_version,
+ strerror(errno));
+ nft_fini(&h);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = do_commandx(&h, argc, argv, &table, false);
+ if (ret)
+ ret = nft_commit(&h);
+
+ nft_fini(&h);
+
+ 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);
+}
+
+int xtables_ip4_main(int argc, char *argv[])
+{
+ return xtables_main(NFPROTO_IPV4, "iptables", argc, argv);
+}
+
+int xtables_ip6_main(int argc, char *argv[])
+{
+ return xtables_main(NFPROTO_IPV6, "ip6tables", argc, argv);
+}
diff --git a/iptables/xtables.c b/iptables/xtables.c
new file mode 100644
index 00000000..45a5ac63
--- /dev/null
+++ b/iptables/xtables.c
@@ -0,0 +1,1261 @@
+/* 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-shared.h"
+#include "nft.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#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 xtables_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 = xtables_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
+
+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);
+}
+
+void
+xtables_exit_error(enum xtables_exittype status, const char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ fprintf(stderr, "%s v%s: ", prog_name, prog_vers);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ if (status == PARAMETER_PROBLEM)
+ exit_tryhelp(status);
+ if (status == VERSION_PROBLEM)
+ fprintf(stderr,
+ "Perhaps iptables or your kernel needs to be upgraded.\n");
+ /* On error paths, make sure that we don't leak memory */
+ xtables_free_opts(1);
+ exit(status);
+}
+
+static void
+generic_opt_check(int command, int options)
+{
+ 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,
+ int rulenum, int family,
+ const struct addr_mask s,
+ const struct addr_mask d,
+ bool verbose, struct nft_handle *h, bool append)
+{
+ unsigned int i, j;
+ int ret = 1;
+
+ for (i = 0; i < s.naddrs; i++) {
+ if (family == AF_INET) {
+ cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr;
+ cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr;
+ for (j = 0; j < d.naddrs; j++) {
+ cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr;
+ cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr;
+
+ if (append) {
+ ret = nft_rule_append(h, chain, table,
+ cs, 0,
+ verbose);
+ } else {
+ ret = nft_rule_insert(h, chain, table,
+ cs, rulenum,
+ verbose);
+ }
+ }
+ } else if (family == AF_INET6) {
+ memcpy(&cs->fw6.ipv6.src,
+ &s.addr.v6[i], sizeof(struct in6_addr));
+ memcpy(&cs->fw6.ipv6.smsk,
+ &s.mask.v6[i], sizeof(struct in6_addr));
+ for (j = 0; j < d.naddrs; j++) {
+ memcpy(&cs->fw6.ipv6.dst,
+ &d.addr.v6[j], sizeof(struct in6_addr));
+ memcpy(&cs->fw6.ipv6.dmsk,
+ &d.mask.v6[j], sizeof(struct in6_addr));
+ if (append) {
+ ret = nft_rule_append(h, chain, table,
+ cs, 0,
+ verbose);
+ } else {
+ ret = nft_rule_insert(h, chain, table,
+ cs, rulenum,
+ verbose);
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int
+replace_entry(const char *chain, const char *table,
+ struct iptables_command_state *cs,
+ unsigned int rulenum,
+ int family,
+ const struct addr_mask s,
+ const struct addr_mask d,
+ bool verbose, struct nft_handle *h)
+{
+ if (family == AF_INET) {
+ cs->fw.ip.src.s_addr = s.addr.v4->s_addr;
+ cs->fw.ip.dst.s_addr = d.addr.v4->s_addr;
+ cs->fw.ip.smsk.s_addr = s.mask.v4->s_addr;
+ cs->fw.ip.dmsk.s_addr = d.mask.v4->s_addr;
+ } else if (family == AF_INET6) {
+ memcpy(&cs->fw6.ipv6.src, s.addr.v6, sizeof(struct in6_addr));
+ memcpy(&cs->fw6.ipv6.dst, d.addr.v6, sizeof(struct in6_addr));
+ memcpy(&cs->fw6.ipv6.smsk, s.mask.v6, sizeof(struct in6_addr));
+ memcpy(&cs->fw6.ipv6.dmsk, d.mask.v6, sizeof(struct in6_addr));
+ } else
+ return 1;
+
+ 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,
+ int family,
+ const struct addr_mask s,
+ const struct addr_mask d,
+ bool verbose,
+ struct nft_handle *h)
+{
+ unsigned int i, j;
+ int ret = 1;
+
+ for (i = 0; i < s.naddrs; i++) {
+ if (family == AF_INET) {
+ cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr;
+ cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr;
+ for (j = 0; j < d.naddrs; j++) {
+ cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr;
+ cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr;
+ ret = nft_rule_delete(h, chain,
+ table, cs, verbose);
+ }
+ } else if (family == AF_INET6) {
+ memcpy(&cs->fw6.ipv6.src,
+ &s.addr.v6[i], sizeof(struct in6_addr));
+ memcpy(&cs->fw6.ipv6.smsk,
+ &s.mask.v6[i], sizeof(struct in6_addr));
+ for (j = 0; j < d.naddrs; j++) {
+ memcpy(&cs->fw6.ipv6.dst,
+ &d.addr.v6[j], sizeof(struct in6_addr));
+ memcpy(&cs->fw6.ipv6.dmsk,
+ &d.mask.v6[j], sizeof(struct in6_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,
+ int family,
+ const struct addr_mask s,
+ const struct addr_mask d,
+ bool verbose, struct nft_handle *h)
+{
+ unsigned int i, j;
+ int ret = 1;
+
+ for (i = 0; i < s.naddrs; i++) {
+ if (family == AF_INET) {
+ cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr;
+ cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr;
+ for (j = 0; j < d.naddrs; j++) {
+ cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr;
+ cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr;
+ ret = nft_rule_check(h, chain,
+ table, cs, verbose);
+ }
+ } else if (family == AF_INET6) {
+ memcpy(&cs->fw6.ipv6.src,
+ &s.addr.v6[i], sizeof(struct in6_addr));
+ memcpy(&cs->fw6.ipv6.smsk,
+ &s.mask.v6[i], sizeof(struct in6_addr));
+ for (j = 0; j < d.naddrs; j++) {
+ memcpy(&cs->fw6.ipv6.dst,
+ &d.addr.v6[j], sizeof(struct in6_addr));
+ memcpy(&cs->fw6.ipv6.dmsk,
+ &d.mask.v6[j], sizeof(struct in6_addr));
+ ret = nft_rule_check(h, chain,
+ table, cs, verbose);
+ }
+ }
+ }
+
+ return ret;
+}
+
+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;
+
+ 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);
+
+ /* iptables does not return error if rule number not found */
+ return 1;
+}
+
+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(xtables_globals.orig_opts, opts,
+ cs->target->x6_options,
+ &cs->target->option_offset);
+ else
+ opts = xtables_merge_options(xtables_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(xtables_globals.orig_opts, opts,
+ m->x6_options, &m->option_offset);
+ else if (m->extra_opts != NULL)
+ opts = xtables_merge_options(xtables_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,
+ bool restore)
+{
+ struct iptables_command_state cs;
+ int verbose = 0;
+ const char *chain = NULL;
+ const char *policy = NULL, *newname = NULL;
+ unsigned int rulenum = 0, command = 0;
+ int ret = 1;
+ struct xtables_match *m;
+ struct xtables_rule_match *matchp;
+ struct xtables_target *t;
+ struct xtables_args args = {
+ .family = h->family,
+ };
+
+ 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;
+
+ h->ops = nft_family_ops_lookup(h->family);
+ if (h->ops == NULL)
+ xtables_error(PARAMETER_PROBLEM, "Unknown family");
+
+ 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, &args.invflags,
+ cs.invert);
+
+ /* Canonicalize into lower case */
+ for (cs.protocol = optarg; *cs.protocol; cs.protocol++)
+ *cs.protocol = tolower(*cs.protocol);
+
+ cs.protocol = optarg;
+ args.proto = xtables_parse_protocol(cs.protocol);
+
+ if (args.proto == 0 && (args.invflags & XT_INV_PROTO))
+ xtables_error(PARAMETER_PROBLEM,
+ "rule would never match protocol");
+
+ /* This needs to happen here to parse extensions */
+ h->ops->proto_parse(&cs, &args);
+ break;
+
+ case 's':
+ set_option(&cs.options, OPT_SOURCE, &args.invflags,
+ cs.invert);
+ args.shostnetworkmask = optarg;
+ break;
+
+ case 'd':
+ set_option(&cs.options, OPT_DESTINATION,
+ &args.invflags, cs.invert);
+ args.dhostnetworkmask = optarg;
+ break;
+
+#ifdef IPT_F_GOTO
+ case 'g':
+ set_option(&cs.options, OPT_JUMP, &args.invflags,
+ cs.invert);
+ args.goto_set = true;
+ 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, &args.invflags,
+ cs.invert);
+ xtables_parse_interface(optarg,
+ args.iniface,
+ args.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, &args.invflags,
+ cs.invert);
+ xtables_parse_interface(optarg,
+ args.outiface,
+ args.outiface_mask);
+ break;
+
+ case 'f':
+ if (args.family == AF_INET6) {
+ xtables_error(PARAMETER_PROBLEM,
+ "`-f' is not supported in IPv6, "
+ "use -m frag instead");
+ }
+ set_option(&cs.options, OPT_FRAGMENT, &args.invflags,
+ cs.invert);
+ args.flags |= IPT_F_FRAG;
+ break;
+
+ case 'v':
+ if (!verbose)
+ set_option(&cs.options, OPT_VERBOSE,
+ &args.invflags, cs.invert);
+ verbose++;
+ break;
+
+ case 'm':
+ command_match(&cs);
+ break;
+
+ case 'n':
+ set_option(&cs.options, OPT_NUMERIC, &args.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, &args.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 'w':
+ if (restore) {
+ xtables_error(PARAMETER_PROBLEM,
+ "You cannot use `-w' from "
+ "iptables-restore");
+ }
+ break;
+
+ case '0':
+ set_option(&cs.options, OPT_LINENUMBERS,
+ &args.invflags, cs.invert);
+ break;
+
+ case 'M':
+ xtables_modprobe_program = optarg;
+ break;
+
+ case 'c':
+
+ set_option(&cs.options, OPT_COUNTERS, &args.invflags,
+ cs.invert);
+ args.pcnt = optarg;
+ args.bcnt = strchr(args.pcnt + 1, ',');
+ if (args.bcnt)
+ args.bcnt++;
+ if (!args.bcnt && optind < argc &&
+ argv[optind][0] != '-' &&
+ argv[optind][0] != '!')
+ args.bcnt = argv[optind++];
+ if (!args.bcnt)
+ xtables_error(PARAMETER_PROBLEM,
+ "-%c requires packet and byte counter",
+ opt2char(OPT_COUNTERS));
+
+ if (sscanf(args.pcnt, "%llu", &args.pcnt_cnt) != 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "-%c packet counter not numeric",
+ opt2char(OPT_COUNTERS));
+
+ if (sscanf(args.bcnt, "%llu", &args.bcnt_cnt) != 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "-%c byte counter not numeric",
+ opt2char(OPT_COUNTERS));
+ break;
+
+ case '4':
+ if (args.family != AF_INET)
+ exit_tryhelp(2);
+
+ h->ops = nft_family_ops_lookup(args.family);
+ break;
+
+ case '6':
+ args.family = AF_INET6;
+ xtables_set_nfproto(AF_INET6);
+
+ h->ops = nft_family_ops_lookup(args.family);
+ if (h->ops == NULL)
+ xtables_error(PARAMETER_PROBLEM,
+ "Unknown family");
+ break;
+
+ 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, &xtables_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 !");
+
+ /* Set only if required, needed by xtables-restore */
+ if (h->family == AF_UNSPEC)
+ h->family = args.family;
+
+ h->ops->post_parse(command, &cs, &args);
+
+ if (command == CMD_REPLACE &&
+ (args.s.naddrs != 1 || args.d.naddrs != 1))
+ xtables_error(PARAMETER_PROBLEM, "Replacement rule does not "
+ "specify a unique address");
+
+ generic_opt_check(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, 0, h->family,
+ args.s, args.d, cs.options&OPT_VERBOSE,
+ h, true);
+ break;
+ case CMD_DELETE:
+ ret = delete_entry(chain, *table, &cs, h->family,
+ args.s, args.d, 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, h->family,
+ args.s, args.d, cs.options&OPT_VERBOSE, h);
+ break;
+ case CMD_REPLACE:
+ ret = replace_entry(chain, *table, &cs, rulenum - 1,
+ h->family, args.s, args.d,
+ cs.options&OPT_VERBOSE, h);
+ break;
+ case CMD_INSERT:
+ ret = add_entry(chain, *table, &cs, rulenum - 1, h->family,
+ args.s, args.d, cs.options&OPT_VERBOSE, h,
+ false);
+ break;
+ case CMD_FLUSH:
+ ret = nft_rule_flush(h, chain, *table);
+ break;
+ case CMD_ZERO:
+ ret = nft_chain_zero_counters(h, chain, *table);
+ break;
+ case CMD_ZERO_NUM:
+ ret = nft_rule_zero_counters(h, chain, *table, rulenum - 1);
+ break;
+ case CMD_LIST:
+ case CMD_LIST|CMD_ZERO:
+ case CMD_LIST|CMD_ZERO_NUM:
+ 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 = nft_chain_zero_counters(h, chain, *table);
+ if (ret && (command & CMD_ZERO_NUM))
+ ret = nft_rule_zero_counters(h, chain, *table,
+ rulenum - 1);
+ break;
+ case CMD_LIST_RULES:
+ case CMD_LIST_RULES|CMD_ZERO:
+ case CMD_LIST_RULES|CMD_ZERO_NUM:
+ ret = list_rules(h, chain, *table, rulenum, cs.options&OPT_VERBOSE);
+ if (ret && (command & CMD_ZERO))
+ ret = nft_chain_zero_counters(h, chain, *table);
+ if (ret && (command & CMD_ZERO_NUM))
+ ret = nft_rule_zero_counters(h, chain, *table,
+ rulenum - 1);
+ 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:
+ 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); */
+
+ xtables_rule_matches_free(&cs.matches);
+
+ if (h->family == AF_INET) {
+ free(args.s.addr.v4);
+ free(args.s.mask.v4);
+ free(args.d.addr.v4);
+ free(args.d.mask.v4);
+ } else if (h->family == AF_INET6) {
+ free(args.s.addr.v6);
+ free(args.s.mask.v6);
+ free(args.d.addr.v6);
+ free(args.d.mask.v6);
+ }
+ xtables_free_opts(1);
+
+ return ret;
+}
diff --git a/libxtables/xtables.c b/libxtables/xtables.c
index fb60c01b..a511c08c 100644
--- a/libxtables/xtables.c
+++ b/libxtables/xtables.c
@@ -743,7 +743,7 @@ xtables_find_target(const char *name, enum xtables_tryload tryload)
return ptr;
}
-static int compatible_revision(const char *name, uint8_t revision, int opt)
+int xtables_compatible_revision(const char *name, uint8_t revision, int opt)
{
struct xt_get_revision rev;
socklen_t s = sizeof(rev);
@@ -799,12 +799,12 @@ static int compatible_revision(const char *name, uint8_t revision, int opt)
static int compatible_match_revision(const char *name, uint8_t revision)
{
- return compatible_revision(name, revision, afinfo->so_rev_match);
+ return xt_params->compat_rev(name, revision, afinfo->so_rev_match);
}
static int compatible_target_revision(const char *name, uint8_t revision)
{
- return compatible_revision(name, revision, afinfo->so_rev_target);
+ return xt_params->compat_rev(name, revision, afinfo->so_rev_target);
}
static void xtables_check_options(const char *name, const struct option *opt)