summaryrefslogtreecommitdiffstats
path: root/iptables
diff options
context:
space:
mode:
Diffstat (limited to 'iptables')
-rw-r--r--iptables/.gitignore3
-rw-r--r--iptables/Makefile.am37
-rw-r--r--iptables/ip6tables-restore.c8
-rw-r--r--iptables/ip6tables-save.c9
-rw-r--r--iptables/ip6tables-standalone.c2
-rw-r--r--iptables/ip6tables.c67
-rw-r--r--iptables/iptables-restore.8.in8
-rw-r--r--iptables/iptables-restore.c8
-rw-r--r--iptables/iptables-save.8.in4
-rw-r--r--iptables/iptables-save.c9
-rw-r--r--iptables/iptables-xml.c5
-rw-r--r--iptables/iptables.8.in20
-rw-r--r--iptables/iptables.c68
-rw-r--r--iptables/nft-arp.c720
-rw-r--r--iptables/nft-ipv4.c425
-rw-r--r--iptables/nft-ipv6.c370
-rw-r--r--iptables/nft-shared.c810
-rw-r--r--iptables/nft-shared.h222
-rw-r--r--iptables/nft.c2581
-rw-r--r--iptables/nft.h173
-rw-r--r--iptables/xshared.c8
-rw-r--r--iptables/xshared.h3
-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.y248
-rw-r--r--iptables/xtables-config-syntax.l54
-rw-r--r--iptables/xtables-config.c46
-rw-r--r--iptables/xtables-events.c212
-rw-r--r--iptables/xtables-multi.c12
-rw-r--r--iptables/xtables-multi.h11
-rw-r--r--iptables/xtables-restore.c486
-rw-r--r--iptables/xtables-save.c157
-rw-r--r--iptables/xtables-standalone.c104
-rw-r--r--iptables/xtables.c1272
35 files changed, 9709 insertions, 82 deletions
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..aaaade85 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} ../extensions/libext4.a ../extensions/libext6.a
+# 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-restore.c b/iptables/ip6tables-restore.c
index b8b9e0dd..0f4dd97a 100644
--- a/iptables/ip6tables-restore.c
+++ b/iptables/ip6tables-restore.c
@@ -25,11 +25,10 @@
#define DEBUGP(x, args...)
#endif
-static int binary = 0, counters = 0, verbose = 0, noflush = 0;
+static int 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'},
@@ -44,8 +43,7 @@ static void print_usage(const char *name, const char *version) __attribute__((no
static void print_usage(const char *name, const char *version)
{
- fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n"
- " [ --binary ]\n"
+ fprintf(stderr, "Usage: %s [-c] [-v] [-t] [-h]\n"
" [ --counters ]\n"
" [ --verbose ]\n"
" [ --test ]\n"
@@ -207,7 +205,7 @@ int ip6tables_restore_main(int argc, char *argv[])
while ((c = getopt_long(argc, argv, "bcvthnM:T:", options, NULL)) != -1) {
switch (c) {
case 'b':
- binary = 1;
+ fprintf(stderr, "-b/--binary option is not implemented\n");
break;
case 'c':
counters = 1;
diff --git a/iptables/ip6tables-save.c b/iptables/ip6tables-save.c
index d819b30b..56e5afb3 100644
--- a/iptables/ip6tables-save.c
+++ b/iptables/ip6tables-save.c
@@ -18,10 +18,6 @@
#include "ip6tables.h"
#include "ip6tables-multi.h"
-#ifndef NO_SHARED_LIBS
-#include <dlfcn.h>
-#endif
-
static int show_counters = 0;
static const struct option options[] = {
@@ -141,8 +137,11 @@ int ip6tables_save_main(int argc, char *argv[])
init_extensions6();
#endif
- while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "bcdt:M:", options, NULL)) != -1) {
switch (c) {
+ case 'b':
+ fprintf(stderr, "-b/--binary option is not implemented\n");
+ break;
case 'c':
show_counters = 1;
break;
diff --git a/iptables/ip6tables-standalone.c b/iptables/ip6tables-standalone.c
index 656e08d5..35d2d9a5 100644
--- a/iptables/ip6tables-standalone.c
+++ b/iptables/ip6tables-standalone.c
@@ -73,6 +73,8 @@ ip6tables_main(int argc, char *argv[])
fprintf(stderr, "ip6tables: %s.\n",
ip6tc_strerror(errno));
}
+ if (errno == EAGAIN)
+ exit(RESOURCE_PROBLEM);
}
exit(!ret);
diff --git a/iptables/ip6tables.c b/iptables/ip6tables.c
index 76de3672..8db13b45 100644
--- a/iptables/ip6tables.c
+++ b/iptables/ip6tables.c
@@ -102,7 +102,7 @@ static struct option original_opts[] = {
{.name = "numeric", .has_arg = 0, .val = 'n'},
{.name = "out-interface", .has_arg = 1, .val = 'o'},
{.name = "verbose", .has_arg = 0, .val = 'v'},
- {.name = "wait", .has_arg = 0, .val = 'w'},
+ {.name = "wait", .has_arg = 2, .val = 'w'},
{.name = "exact", .has_arg = 0, .val = 'x'},
{.name = "version", .has_arg = 0, .val = 'V'},
{.name = "help", .has_arg = 2, .val = 'h'},
@@ -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
@@ -258,7 +259,7 @@ exit_printhelp(const struct xtables_rule_match *matches)
" network interface name ([+] for wildcard)\n"
" --table -t table table to manipulate (default: `filter')\n"
" --verbose -v verbose mode\n"
-" --wait -w wait for the xtables lock\n"
+" --wait -w [seconds] wait for the xtables lock\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"*/
@@ -387,6 +388,32 @@ parse_rulenumber(const char *rule)
return rulenum;
}
+static void
+parse_chain(const char *chainname)
+{
+ const char *ptr;
+
+ if (strlen(chainname) >= XT_EXTENSION_MAXNAMELEN)
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name `%s' too long (must be under %u chars)",
+ chainname, XT_EXTENSION_MAXNAMELEN);
+
+ if (*chainname == '-' || *chainname == '!')
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name not allowed to start "
+ "with `%c'\n", *chainname);
+
+ if (xtables_find_target(chainname, XTF_TRY_LOAD))
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name may not clash "
+ "with target name\n");
+
+ for (ptr = chainname; *ptr; ptr++)
+ if (isspace(*ptr))
+ xtables_error(PARAMETER_PROBLEM,
+ "Invalid chain name `%s'", chainname);
+}
+
static const char *
parse_target(const char *targetname)
{
@@ -1296,7 +1323,7 @@ int do_command6(int argc, char *argv[], char **table,
struct in6_addr *smasks = NULL, *dmasks = NULL;
int verbose = 0;
- bool wait = false;
+ int wait = 0;
const char *chain = NULL;
const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
const char *policy = NULL, *newname = NULL;
@@ -1332,7 +1359,7 @@ int do_command6(int argc, char *argv[], char **table,
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:bvwnt:m:xc:g:46",
+ "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:bvw::nt:m:xc:g:46",
opts, NULL)) != -1) {
switch (cs.c) {
/*
@@ -1432,14 +1459,7 @@ int do_command6(int argc, char *argv[], char **table,
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");
+ parse_chain(optarg);
add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
cs.invert);
chain = optarg;
@@ -1583,7 +1603,16 @@ int do_command6(int argc, char *argv[], char **table,
"You cannot use `-w' from "
"ip6tables-restore");
}
- wait = true;
+ wait = -1;
+ if (optarg) {
+ if (sscanf(optarg, "%i", &wait) != 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "wait seconds not numeric");
+ } else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ if (sscanf(argv[optind++], "%i", &wait) != 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "wait seconds not numeric");
break;
case 'm':
@@ -1732,15 +1761,13 @@ int do_command6(int argc, char *argv[], char **table,
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);
-
/* Attempt to acquire the xtables lock */
if (!restore && !xtables_lock(wait)) {
- fprintf(stderr, "Another app is currently holding the xtables lock. "
- "Perhaps you want to use the -w option?\n");
+ fprintf(stderr, "Another app is currently holding the xtables lock. ");
+ if (wait == 0)
+ fprintf(stderr, "Perhaps you want to use the -w option?\n");
+ else
+ fprintf(stderr, "Stopped waiting after %ds.\n", wait);
xtables_free_opts(1);
exit(RESOURCE_PROBLEM);
}
diff --git a/iptables/iptables-restore.8.in b/iptables/iptables-restore.8.in
index feb57efa..7a286b91 100644
--- a/iptables/iptables-restore.8.in
+++ b/iptables/iptables-restore.8.in
@@ -24,16 +24,18 @@ iptables-restore \(em Restore IP Tables
ip6tables-restore \(em Restore IPv6 Tables
.SH SYNOPSIS
\fBiptables\-restore\fP [\fB\-chntv\fP] [\fB\-M\fP \fImodprobe\fP]
+[\fB\-T\fP \fIname\fP] [\fBfile\fP]
.P
\fBip6tables\-restore\fP [\fB\-chntv\fP] [\fB\-M\fP \fImodprobe\fP]
-[\fB\-T\fP \fIname\fP]
+[\fB\-T\fP \fIname\fP] [\fBfile\fP]
.SH DESCRIPTION
.PP
.B iptables-restore
and
.B ip6tables-restore
-are used to restore IP and IPv6 Tables from data specified on STDIN. Use
-I/O redirection provided by your shell to read from a file
+are used to restore IP and IPv6 Tables from data specified on STDIN or in
+\fIfile\fP. Use I/O redirection provided by your shell to read from a file or
+specify \fIfile\fP as an argument.
.TP
\fB\-c\fR, \fB\-\-counters\fR
restore the values of all packet and byte counters
diff --git a/iptables/iptables-restore.c b/iptables/iptables-restore.c
index 8c942ff6..261822dd 100644
--- a/iptables/iptables-restore.c
+++ b/iptables/iptables-restore.c
@@ -22,11 +22,10 @@
#define DEBUGP(x, args...)
#endif
-static int binary = 0, counters = 0, verbose = 0, noflush = 0;
+static int 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'},
@@ -43,8 +42,7 @@ static void print_usage(const char *name, const char *version) __attribute__((no
static void print_usage(const char *name, const char *version)
{
- fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n"
- " [ --binary ]\n"
+ fprintf(stderr, "Usage: %s [-c] [-v] [-t] [-h]\n"
" [ --counters ]\n"
" [ --verbose ]\n"
" [ --test ]\n"
@@ -207,7 +205,7 @@ iptables_restore_main(int argc, char *argv[])
while ((c = getopt_long(argc, argv, "bcvthnM:T:", options, NULL)) != -1) {
switch (c) {
case 'b':
- binary = 1;
+ fprintf(stderr, "-b/--binary option is not implemented\n");
break;
case 'c':
counters = 1;
diff --git a/iptables/iptables-save.8.in b/iptables/iptables-save.8.in
index 5e939b1a..7f99d8a3 100644
--- a/iptables/iptables-save.8.in
+++ b/iptables/iptables-save.8.in
@@ -27,7 +27,7 @@ ip6tables-save \(em dump iptables rules to stdout
[\fB\-t\fP \fItable\fP]
.P
\fBip6tables\-save\fP [\fB\-M\fP \fImodprobe\fP] [\fB\-c\fP]
-[\fB\-t\fP \fItable\fP
+[\fB\-t\fP \fItable\fP]
.SH DESCRIPTION
.PP
.B iptables-save
@@ -36,7 +36,7 @@ and
are used to dump the contents of IP or IPv6 Table in easily parseable format
to STDOUT. Use I/O-redirection provided by your shell to write to a file.
.TP
-\fB\-M\fP \fImodprobe_program\fP
+\fB\-M\fR, \fB\-\-modprobe\fR \fImodprobe_program\fP
Specify the path to the modprobe program. By default, iptables-save will
inspect /proc/sys/kernel/modprobe to determine the executable's path.
.TP
diff --git a/iptables/iptables-save.c b/iptables/iptables-save.c
index e599fcec..bcf88ae1 100644
--- a/iptables/iptables-save.c
+++ b/iptables/iptables-save.c
@@ -17,10 +17,6 @@
#include "iptables.h"
#include "iptables-multi.h"
-#ifndef NO_SHARED_LIBS
-#include <dlfcn.h>
-#endif
-
static int show_counters = 0;
static const struct option options[] = {
@@ -140,8 +136,11 @@ iptables_save_main(int argc, char *argv[])
init_extensions4();
#endif
- while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "bcdt:M:", options, NULL)) != -1) {
switch (c) {
+ case 'b':
+ fprintf(stderr, "-b/--binary option is not implemented\n");
+ break;
case 'c':
show_counters = 1;
break;
diff --git a/iptables/iptables-xml.c b/iptables/iptables-xml.c
index 96284476..c7615abb 100644
--- a/iptables/iptables-xml.c
+++ b/iptables/iptables-xml.c
@@ -845,6 +845,11 @@ iptables_xml_main(int argc, char *argv[])
for (a = 0; a < newargc; a++)
DEBUGP("argv[%u]: %s\n", a, newargv[a]);
+ if (!chain) {
+ fprintf(stderr, "%s: line %u failed - no chain found\n",
+ prog_name, line);
+ exit(1);
+ }
needChain(chain);// Should we explicitly look for -A
do_rule(pcnt, bcnt, newargc, newargv, newargvattr);
diff --git a/iptables/iptables.8.in b/iptables/iptables.8.in
index 155c97e9..ceba5dc2 100644
--- a/iptables/iptables.8.in
+++ b/iptables/iptables.8.in
@@ -224,10 +224,8 @@ any rules. If no argument is given, it will attempt to delete every
non-builtin chain in the table.
.TP
\fB\-P\fP, \fB\-\-policy\fP \fIchain target\fP
-Set the policy for the chain to the given target. See the section \fBTARGETS\fP
-for the legal targets. Only built-in (non-user-defined) chains can have
-policies, and neither built-in nor user-defined chains can be policy
-targets.
+Set the policy for the built-in (non-user-defined) chain to the given target.
+The policy target must be either \fBACCEPT\fP or \fBDROP\fP.
.TP
\fB\-E\fP, \fB\-\-rename\-chain\fP \fIold\-chain new\-chain\fP
Rename the user specified chain to the user supplied name. This is
@@ -361,12 +359,13 @@ For appending, insertion, deletion and replacement, this causes
detailed information on the rule or rules to be printed. \fB\-v\fP may be
specified multiple times to possibly emit more detailed debug statements.
.TP
-\fB\-w\fP, \fB\-\-wait\fP
+\fB\-w\fP, \fB\-\-wait\fP [\fIseconds\fP]
Wait for the xtables lock.
To prevent multiple instances of the program from running concurrently,
an attempt will be made to obtain an exclusive lock at launch. By default,
the program will exit if the lock cannot be obtained. This option will
-make the program wait until the exclusive lock can be obtained.
+make the program wait (indefinitely or for optional \fIseconds\fP) until
+the exclusive lock can be obtained.
.TP
\fB\-n\fP, \fB\-\-numeric\fP
Numeric output.
@@ -453,9 +452,10 @@ Jozsef Kadlecsik wrote the REJECT target.
.PP
Harald Welte wrote the ULOG and NFQUEUE target, the new libiptc, as well as the TTL, DSCP, ECN matches and targets.
.PP
-The Netfilter Core Team is: Marc Boucher, Martin Josefsson, Yasuyuki Kozakai,
-Jozsef Kadlecsik, Patrick McHardy, James Morris, Pablo Neira Ayuso,
-Harald Welte and Rusty Russell.
+The Netfilter Core Team is: Jozsef Kadlecsik, Patrick McHardy, Pablo Neira
+Ayuso, Eric Leblond and Florian Westphal. Emeritus Core Team members are: Marc
+Boucher, Martin Josefsson, Yasuyuki Kozakai, James Morris, Harald Welte and
+Rusty Russell.
.PP
Man page originally written by Herve Eychenne <rv@wallfire.org>.
.\" .. and did I mention that we are incredibly cool people?
@@ -464,4 +464,4 @@ Man page originally written by Herve Eychenne <rv@wallfire.org>.
.\" .. and most of all, modest ..
.SH VERSION
.PP
-This manual page applies to iptables/ip6tables @PACKAGE_AND_VERSION@.
+This manual page applies to iptables/ip6tables @PACKAGE_VERSION@.
diff --git a/iptables/iptables.c b/iptables/iptables.c
index d3899bcb..88953c47 100644
--- a/iptables/iptables.c
+++ b/iptables/iptables.c
@@ -99,7 +99,7 @@ static struct option original_opts[] = {
{.name = "numeric", .has_arg = 0, .val = 'n'},
{.name = "out-interface", .has_arg = 1, .val = 'o'},
{.name = "verbose", .has_arg = 0, .val = 'v'},
- {.name = "wait", .has_arg = 0, .val = 'w'},
+ {.name = "wait", .has_arg = 2, .val = 'w'},
{.name = "exact", .has_arg = 0, .val = 'x'},
{.name = "fragments", .has_arg = 0, .val = 'f'},
{.name = "version", .has_arg = 0, .val = 'V'},
@@ -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
@@ -252,7 +253,7 @@ exit_printhelp(const struct xtables_rule_match *matches)
" network interface name ([+] for wildcard)\n"
" --table -t table table to manipulate (default: `filter')\n"
" --verbose -v verbose mode\n"
-" --wait -w wait for the xtables lock\n"
+" --wait -w [seconds] wait for the xtables lock\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"
@@ -373,6 +374,32 @@ parse_rulenumber(const char *rule)
return rulenum;
}
+static void
+parse_chain(const char *chainname)
+{
+ const char *ptr;
+
+ if (strlen(chainname) >= XT_EXTENSION_MAXNAMELEN)
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name `%s' too long (must be under %u chars)",
+ chainname, XT_EXTENSION_MAXNAMELEN);
+
+ if (*chainname == '-' || *chainname == '!')
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name not allowed to start "
+ "with `%c'\n", *chainname);
+
+ if (xtables_find_target(chainname, XTF_TRY_LOAD))
+ xtables_error(PARAMETER_PROBLEM,
+ "chain name may not clash "
+ "with target name\n");
+
+ for (ptr = chainname; *ptr; ptr++)
+ if (isspace(*ptr))
+ xtables_error(PARAMETER_PROBLEM,
+ "Invalid chain name `%s'", chainname);
+}
+
static const char *
parse_target(const char *targetname)
{
@@ -1292,7 +1319,7 @@ int do_command4(int argc, char *argv[], char **table,
struct in_addr *daddrs = NULL, *dmasks = NULL;
int verbose = 0;
- bool wait = false;
+ int wait = 0;
const char *chain = NULL;
const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
const char *policy = NULL, *newname = NULL;
@@ -1325,10 +1352,9 @@ int do_command4(int argc, char *argv[], char **table,
/* Suppress error messages: we may add new options if we
demand-load a protocol. */
opterr = 0;
-
opts = xt_params->orig_opts;
while ((cs.c = getopt_long(argc, argv,
- "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvwnt:m:xc:g:46",
+ "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvw::nt:m:xc:g:46",
opts, NULL)) != -1) {
switch (cs.c) {
/*
@@ -1428,14 +1454,7 @@ int do_command4(int argc, char *argv[], char **table,
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");
+ parse_chain(optarg);
add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
cs.invert);
chain = optarg;
@@ -1577,7 +1596,16 @@ int do_command4(int argc, char *argv[], char **table,
"You cannot use `-w' from "
"iptables-restore");
}
- wait = true;
+ wait = -1;
+ if (optarg) {
+ if (sscanf(optarg, "%i", &wait) != 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "wait seconds not numeric");
+ } else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ if (sscanf(argv[optind++], "%i", &wait) != 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "wait seconds not numeric");
break;
case 'm':
@@ -1729,15 +1757,13 @@ int do_command4(int argc, char *argv[], char **table,
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);
-
/* Attempt to acquire the xtables lock */
if (!restore && !xtables_lock(wait)) {
- fprintf(stderr, "Another app is currently holding the xtables lock. "
- "Perhaps you want to use the -w option?\n");
+ fprintf(stderr, "Another app is currently holding the xtables lock. ");
+ if (wait == 0)
+ fprintf(stderr, "Perhaps you want to use the -w option?\n");
+ else
+ fprintf(stderr, "Stopped waiting after %ds.\n", wait);
xtables_free_opts(1);
exit(RESOURCE_PROBLEM);
}
diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c
new file mode 100644
index 00000000..bb4bab29
--- /dev/null
+++ b/iptables/nft-arp.c
@@ -0,0 +1,720 @@
+/*
+ * (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;
+
+ 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, &fw->arp.smsk.s_addr,
+ sizeof(struct in_addr), 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, &fw->arp.tmsk.s_addr,
+ sizeof(struct in_addr), 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;
+
+ return result;
+}
+
+static void nft_arp_parse_meta(struct nft_xt_ctx *ctx, struct nft_rule_expr *e,
+ void *data)
+{
+ struct arpt_entry *fw = data;
+ uint8_t flags = 0;
+
+ parse_meta(e, ctx->meta.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 parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask)
+{
+ mask->s_addr = ctx->bitwise.mask[0];
+}
+
+static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
+ struct nft_rule_expr *e, 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 (ctx->payload.offset) {
+ case offsetof(struct arphdr, ar_hrd):
+ get_cmp_data(e, &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(e, &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(e, &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(e, &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 < 0)
+ break;
+
+ if (ctx->payload.offset == sizeof(struct arphdr) +
+ fw->arp.arhln) {
+ get_cmp_data(e, &addr, sizeof(addr), &inv);
+ fw->arp.src.s_addr = addr.s_addr;
+ if (ctx->flags & NFT_XT_CTX_BITWISE)
+ parse_mask_ipv4(ctx, &fw->arp.smsk);
+ else
+ fw->arp.smsk.s_addr = 0xffffffff;
+
+ if (inv)
+ fw->arp.invflags |= ARPT_INV_SRCIP;
+ } else if (ctx->payload.offset == sizeof(struct arphdr) +
+ fw->arp.arhln +
+ sizeof(struct in_addr)) {
+ get_cmp_data(e, &addr, sizeof(addr), &inv);
+ fw->arp.tgt.s_addr = addr.s_addr;
+ if (ctx->flags & NFT_XT_CTX_BITWISE)
+ parse_mask_ipv4(ctx, &fw->arp.tmsk);
+ else
+ 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_u32(r, NFT_RULE_ATTR_FAMILY);
+ struct nft_xt_ctx ctx = {
+ .state.fw = fw,
+ .family = family,
+ };
+
+ iter = nft_rule_expr_iter_create(r);
+ if (iter == NULL)
+ return;
+
+ ctx.iter = iter;
+ 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, &ctx.state.fw->counters);
+ else if (strcmp(name, "payload") == 0)
+ nft_parse_payload(&ctx, expr);
+ else if (strcmp(name, "meta") == 0)
+ nft_parse_meta(&ctx, expr);
+ else if (strcmp(name, "bitwise") == 0)
+ nft_parse_bitwise(&ctx, expr);
+ else if (strcmp(name, "cmp") == 0)
+ nft_parse_cmp(&ctx, expr);
+ else if (strcmp(name, "immediate") == 0)
+ nft_parse_immediate(&ctx, expr);
+ else if (strcmp(name, "target") == 0)
+ nft_parse_target(&ctx, expr);
+
+ expr = nft_rule_expr_iter_next(iter);
+ }
+
+ nft_rule_expr_iter_destroy(iter);
+}
+
+static struct xtables_target
+*get_target(struct arpt_entry *fw, unsigned int format)
+{
+ const char *targname;
+ struct xtables_target *target = NULL;
+ const struct xt_entry_target *t;
+
+ if (!fw->target_offset)
+ return NULL;
+
+ 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);
+
+ return target;
+}
+
+static void print_fw_details(struct arpt_entry *fw, unsigned int format)
+{
+ char buf[BUFSIZ];
+ char iface[IFNAMSIZ+2];
+ int print_iface = 0;
+ int i;
+
+ 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
+
+ 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(" ");
+ }
+}
+
+static void
+nft_arp_print_firewall(struct nft_rule *r, unsigned int num,
+ unsigned int format)
+{
+ struct arpt_entry fw = {};
+ struct xtables_target *target = NULL;
+ const struct xt_entry_target *t = NULL;
+
+ nft_rule_to_arpt_entry(r, &fw);
+
+ if (format & FMT_LINENUMBERS)
+ printf("%u ", num);
+
+ target = get_target(&fw, format);
+ print_fw_details(&fw, format);
+
+ 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 void nft_arp_save_firewall(const void *data,
+ unsigned int format)
+{
+ const struct arpt_entry *fw = data;
+ struct xtables_target *target = NULL;
+ const struct xt_entry_target *t = NULL;
+
+ print_fw_details((struct arpt_entry *)fw, format);
+
+ target = get_target((struct arpt_entry *)fw, format);
+
+ if (target) {
+ if (target->print)
+ /* Print the target information. */
+ target->print(&fw->arp, t, format & FMT_NUMERIC);
+ }
+ printf("\n");
+}
+
+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.iniface,
+ a->arp.outiface,
+ (unsigned char *)a->arp.iniface_mask,
+ (unsigned char *)a->arp.outiface_mask,
+ b->arp.iniface,
+ b->arp.outiface,
+ (unsigned char *)b->arp.iniface_mask,
+ (unsigned char *)b->arp.outiface_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;
+}
+
+static void nft_arp_save_counters(const void *data)
+{
+ const struct arpt_entry *fw = data;
+
+ save_counters(fw->counters.pcnt, fw->counters.bcnt);
+}
+
+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,
+ .save_firewall = nft_arp_save_firewall,
+ .save_counters = nft_arp_save_counters,
+ .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..cb1d45b1
--- /dev/null
+++ b/iptables/nft-ipv4.c
@@ -0,0 +1,425 @@
+/*
+ * (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.proto != 0)
+ add_proto(r, offsetof(struct iphdr, protocol), 1,
+ cs->fw.ip.proto, 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, &cs->fw.ip.smsk.s_addr,
+ sizeof(struct in_addr), 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, &cs->fw.ip.dmsk.s_addr,
+ sizeof(struct in_addr), 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_xt_ctx *ctx, struct nft_rule_expr *e, bool *inv)
+{
+ uint8_t op;
+
+ /* we assume correct mask and xor */
+ if (!(ctx->flags & NFT_XT_CTX_BITWISE))
+ return;
+
+ /* we assume correct data */
+ 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_xt_ctx *ctx, struct nft_rule_expr *e,
+ void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ parse_meta(e, ctx->meta.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 parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask)
+{
+ mask->s_addr = ctx->bitwise.mask[0];
+}
+
+static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx,
+ struct nft_rule_expr *e, void *data)
+{
+ struct iptables_command_state *cs = data;
+ struct in_addr addr;
+ uint8_t proto;
+ bool inv;
+
+ switch(ctx->payload.offset) {
+ case offsetof(struct iphdr, saddr):
+ get_cmp_data(e, &addr, sizeof(addr), &inv);
+ cs->fw.ip.src.s_addr = addr.s_addr;
+ if (ctx->flags & NFT_XT_CTX_BITWISE)
+ parse_mask_ipv4(ctx, &cs->fw.ip.smsk);
+ else
+ 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(e, &addr, sizeof(addr), &inv);
+ cs->fw.ip.dst.s_addr = addr.s_addr;
+ if (ctx->flags & NFT_XT_CTX_BITWISE)
+ parse_mask_ipv4(ctx, &cs->fw.ip.dmsk);
+ else
+ 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(e, &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(ctx, e, &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);
+
+ 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);
+}
+
+static void nft_ipv4_save_counters(const void *data)
+{
+ const struct iptables_command_state *cs = data;
+
+ save_counters(cs->counters.pcnt, cs->counters.bcnt);
+}
+
+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,
+ .save_counters = nft_ipv4_save_counters,
+ .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..a70afcca
--- /dev/null
+++ b/iptables/nft-ipv6.c
@@ -0,0 +1,370 @@
+/*
+ * (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 (cs->fw6.ipv6.proto != 0)
+ add_proto(r, offsetof(struct ip6_hdr, ip6_nxt), 1,
+ cs->fw6.ipv6.proto, 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, &cs->fw6.ipv6.smsk,
+ sizeof(struct in6_addr), 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, &cs->fw6.ipv6.dmsk,
+ sizeof(struct in6_addr), 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_xt_ctx *ctx, struct nft_rule_expr *e,
+ void *data)
+{
+ struct iptables_command_state *cs = data;
+
+ parse_meta(e, ctx->meta.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 parse_mask_ipv6(struct nft_xt_ctx *ctx, struct in6_addr *mask)
+{
+ memcpy(mask, ctx->bitwise.mask, sizeof(struct in6_addr));
+}
+
+static void nft_ipv6_parse_payload(struct nft_xt_ctx *ctx,
+ struct nft_rule_expr *e, void *data)
+{
+ struct iptables_command_state *cs = data;
+ struct in6_addr addr;
+ uint8_t proto;
+ bool inv;
+
+ switch (ctx->payload.offset) {
+ case offsetof(struct ip6_hdr, ip6_src):
+ get_cmp_data(e, &addr, sizeof(addr), &inv);
+ memcpy(cs->fw6.ipv6.src.s6_addr, &addr, sizeof(addr));
+ if (ctx->flags & NFT_XT_CTX_BITWISE)
+ parse_mask_ipv6(ctx, &cs->fw6.ipv6.smsk);
+ else
+ memset(&cs->fw.ip.smsk, 0xff, sizeof(struct in6_addr));
+
+ if (inv)
+ cs->fw6.ipv6.invflags |= IPT_INV_SRCIP;
+ break;
+ case offsetof(struct ip6_hdr, ip6_dst):
+ get_cmp_data(e, &addr, sizeof(addr), &inv);
+ memcpy(cs->fw6.ipv6.dst.s6_addr, &addr, sizeof(addr));
+ if (ctx->flags & NFT_XT_CTX_BITWISE)
+ parse_mask_ipv6(ctx, &cs->fw6.ipv6.dmsk);
+ else
+ memset(&cs->fw.ip.dmsk, 0xff, sizeof(struct in6_addr));
+
+ if (inv)
+ cs->fw6.ipv6.invflags |= IPT_INV_DSTIP;
+ break;
+ case offsetof(struct ip6_hdr, ip6_nxt):
+ get_cmp_data(e, &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);
+
+ 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);
+}
+
+static void nft_ipv6_save_counters(const void *data)
+{
+ const struct iptables_command_state *cs = data;
+
+ save_counters(cs->counters.pcnt, cs->counters.bcnt);
+}
+
+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,
+ .save_counters = nft_ipv6_save_counters,
+ .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..c22e83d3
--- /dev/null
+++ b/iptables/nft-shared.c
@@ -0,0 +1,810 @@
+/*
+ * (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);
+}
+
+static void add_bitwise(struct nft_rule *r, uint8_t *mask, size_t len)
+{
+ struct nft_rule_expr *expr;
+ uint32_t xor[4] = { 0 };
+
+ 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, len);
+ nft_rule_expr_set(expr, NFT_EXPR_BITWISE_MASK, mask, len);
+ nft_rule_expr_set(expr, NFT_EXPR_BITWISE_XOR, &xor, len);
+
+ 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, void *mask, size_t len, int invflags)
+{
+ uint32_t op;
+
+ add_payload(r, offset, len);
+ add_bitwise(r, mask, 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;
+ }
+}
+
+static void *nft_get_data(struct nft_xt_ctx *ctx)
+{
+ switch(ctx->family) {
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ return ctx->state.cs;
+ case NFPROTO_ARP:
+ return ctx->state.fw;
+ default:
+ /* Should not happen */
+ return NULL;
+ }
+}
+
+void nft_parse_target(struct nft_xt_ctx *ctx, struct nft_rule_expr *e)
+{
+ 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;
+ size_t size;
+ struct nft_family_ops *ops;
+ void *data = nft_get_data(ctx);
+
+ 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(ctx->family);
+ ops->parse_target(target, data);
+}
+
+static void nft_parse_match(struct nft_xt_ctx *ctx, struct nft_rule_expr *e)
+{
+ 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, &ctx->state.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 *e, void *data, size_t dlen, bool *inv)
+{
+ uint32_t len;
+ uint8_t op;
+
+ 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_xt_ctx *ctx, struct nft_rule_expr *e)
+{
+ ctx->reg = nft_rule_expr_get_u32(e, NFT_EXPR_META_DREG);
+ ctx->meta.key = nft_rule_expr_get_u32(e, NFT_EXPR_META_KEY);
+ ctx->flags |= NFT_XT_CTX_META;
+}
+
+void nft_parse_payload(struct nft_xt_ctx *ctx, struct nft_rule_expr *e)
+{
+ ctx->reg = nft_rule_expr_get_u32(e, NFT_EXPR_META_DREG);
+ ctx->payload.offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET);
+ ctx->flags |= NFT_XT_CTX_PAYLOAD;
+}
+
+void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nft_rule_expr *e)
+{
+ uint32_t reg, len;
+ const void *data;
+
+ reg = nft_rule_expr_get_u32(e, NFT_EXPR_BITWISE_SREG);
+ if (ctx->reg && reg != ctx->reg)
+ return;
+
+ data = nft_rule_expr_get(e, NFT_EXPR_BITWISE_XOR, &len);
+ memcpy(ctx->bitwise.xor, data, len);
+ data = nft_rule_expr_get(e, NFT_EXPR_BITWISE_MASK, &len);
+ memcpy(ctx->bitwise.mask, data, len);
+ ctx->flags |= NFT_XT_CTX_BITWISE;
+}
+
+void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nft_rule_expr *e)
+{
+ struct nft_family_ops *ops = nft_family_ops_lookup(ctx->family);
+ void *data = nft_get_data(ctx);
+ uint32_t reg;
+
+ reg = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_SREG);
+ if (ctx->reg && reg != ctx->reg)
+ return;
+
+ if (ctx->flags & NFT_XT_CTX_META)
+ ops->parse_meta(ctx, e, data);
+ /* bitwise context is interpreted from payload */
+ if (ctx->flags & NFT_XT_CTX_PAYLOAD);
+ ops->parse_payload(ctx, e, data);
+}
+
+void nft_parse_counter(struct nft_rule_expr *e, 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_xt_ctx *ctx, struct nft_rule_expr *e)
+{
+ 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;
+ void *data = nft_get_data(ctx);
+
+ /* 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(ctx->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_u32(r, NFT_RULE_ATTR_FAMILY);
+ struct nft_xt_ctx ctx = {
+ .state.cs = cs,
+ .family = family,
+ };
+
+ iter = nft_rule_expr_iter_create(r);
+ if (iter == NULL)
+ return;
+
+ ctx.iter = iter;
+ 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, &ctx.state.cs->counters);
+ else if (strcmp(name, "payload") == 0)
+ nft_parse_payload(&ctx, expr);
+ else if (strcmp(name, "meta") == 0)
+ nft_parse_meta(&ctx, expr);
+ else if (strcmp(name, "bitwise") == 0)
+ nft_parse_bitwise(&ctx, expr);
+ else if (strcmp(name, "cmp") == 0)
+ nft_parse_cmp(&ctx, expr);
+ else if (strcmp(name, "immediate") == 0)
+ nft_parse_immediate(&ctx, expr);
+ else if (strcmp(name, "match") == 0)
+ nft_parse_match(&ctx, expr);
+ else if (strcmp(name, "target") == 0)
+ nft_parse_target(&ctx, expr);
+
+ 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)
+{
+ 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_counters(uint64_t pcnt, uint64_t bcnt)
+{
+ printf("[%llu:%llu] ", (unsigned long long)pcnt,
+ (unsigned long long)bcnt);
+}
+
+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..c3832929
--- /dev/null
+++ b/iptables/nft-shared.h
@@ -0,0 +1,222 @@
+#ifndef _NFT_SHARED_H_
+#define _NFT_SHARED_H_
+
+#include <stdbool.h>
+
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+#include <linux/netfilter_arp/arp_tables.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;
+
+enum {
+ NFT_XT_CTX_PAYLOAD = (1 << 0),
+ NFT_XT_CTX_META = (1 << 1),
+ NFT_XT_CTX_BITWISE = (1 << 2),
+};
+
+struct nft_xt_ctx {
+ union {
+ struct iptables_command_state *cs;
+ struct arpt_entry *fw;
+ } state;
+ struct nft_rule_expr_iter *iter;
+ int family;
+ uint32_t flags;
+
+ uint32_t reg;
+ struct {
+ uint32_t offset;
+ uint32_t len;
+ } payload;
+ struct {
+ uint32_t key;
+ } meta;
+ struct {
+ uint32_t mask[4];
+ uint32_t xor[4];
+ } bitwise;
+};
+
+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_xt_ctx *ctx, struct nft_rule_expr *e,
+ void *data);
+ void (*parse_payload)(struct nft_xt_ctx *ctx, struct nft_rule_expr *e,
+ void *data);
+ void (*parse_bitwise)(struct nft_xt_ctx *ctx, struct nft_rule_expr *e,
+ void *data);
+ void (*parse_cmp)(struct nft_xt_ctx *ctx, struct nft_rule_expr *e,
+ 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 (*save_counters)(const void *data);
+ 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, void *mask, 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 *e, void *data, size_t dlen, bool *inv);
+void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nft_rule_expr *e);
+void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nft_rule_expr *e);
+void nft_parse_target(struct nft_xt_ctx *ctx, struct nft_rule_expr *e);
+void nft_parse_meta(struct nft_xt_ctx *ctx, struct nft_rule_expr *e);
+void nft_parse_payload(struct nft_xt_ctx *ctx, struct nft_rule_expr *e);
+void nft_parse_counter(struct nft_rule_expr *e, struct xt_counters *counters);
+void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nft_rule_expr *e);
+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);
+void save_counters(uint64_t pcnt, uint64_t bcnt);
+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
+
+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..91e91333
--- /dev/null
+++ b/iptables/nft.c
@@ -0,0 +1,2581 @@
+/*
+ * (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 <libnftnl/set.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)
+ 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 void mnl_nft_batch_reset(void)
+{
+ struct batch_page *batch_page, *next;
+
+ list_for_each_entry_safe(batch_page, next, &batch_page_list, head) {
+ list_del(&batch_page->head);
+ free(batch_page->batch);
+ free(batch_page);
+ batch_num_pages--;
+ }
+}
+
+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;
+ int i = 0, ret;
+
+ mnl_nft_set_sndbuffer(nl);
+
+ list_for_each_entry(batch_page, &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
+ }
+
+ ret = sendmsg(mnl_socket_get_fd(nl), &msg, 0);
+ mnl_nft_batch_reset();
+
+ return ret;
+}
+
+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)
+ 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)
+ return -1;
+
+ while (ret > 0 && FD_ISSET(fd, &readfds)) {
+ ret = mnl_socket_recvfrom(h->nl, rcv_buf, sizeof(rcv_buf));
+ if (ret == -1)
+ 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)
+ return -1;
+
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+ }
+ return err ? -1 : 0;
+}
+
+static void mnl_nft_batch_begin(struct mnl_nlmsg_batch *batch, uint32_t seq)
+{
+ nft_batch_begin(mnl_nlmsg_batch_current(batch), seq);
+ if (!mnl_nlmsg_batch_next(batch))
+ mnl_nft_batch_page_add(batch);
+}
+
+static void mnl_nft_batch_end(struct mnl_nlmsg_batch *batch, uint32_t seq)
+{
+ nft_batch_end(mnl_nlmsg_batch_current(batch), seq);
+ if (!mnl_nlmsg_batch_next(batch))
+ mnl_nft_batch_page_add(batch);
+}
+
+enum obj_update_type {
+ NFT_COMPAT_TABLE_ADD,
+ NFT_COMPAT_CHAIN_ADD,
+ NFT_COMPAT_CHAIN_UPDATE,
+ NFT_COMPAT_RULE_APPEND,
+ NFT_COMPAT_RULE_INSERT,
+ NFT_COMPAT_RULE_REPLACE,
+ NFT_COMPAT_RULE_DELETE,
+ NFT_COMPAT_RULE_FLUSH,
+};
+
+enum obj_action {
+ NFT_COMPAT_COMMIT,
+ NFT_COMPAT_ABORT,
+};
+
+struct obj_update {
+ struct list_head head;
+ enum obj_update_type type;
+ union {
+ struct nft_table *table;
+ struct nft_chain *chain;
+ struct nft_rule *rule;
+ void *ptr;
+ };
+};
+
+static int batch_add(struct nft_handle *h, enum obj_update_type type, void *ptr)
+{
+ struct obj_update *obj;
+
+ obj = calloc(1, sizeof(struct obj_update));
+ if (obj == NULL)
+ return -1;
+
+ obj->ptr = ptr;
+ obj->type = type;
+ list_add_tail(&obj->head, &h->obj_list);
+ h->obj_list_num++;
+
+ return 0;
+}
+
+static int batch_table_add(struct nft_handle *h, enum obj_update_type type,
+ struct nft_table *t)
+{
+ return batch_add(h, type, t);
+}
+
+static int batch_chain_add(struct nft_handle *h, enum obj_update_type type,
+ struct nft_chain *c)
+{
+ return batch_add(h, type, c);
+}
+
+static int batch_rule_add(struct nft_handle *h, enum obj_update_type type,
+ struct nft_rule *r)
+{
+ return batch_add(h, type, r);
+}
+
+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_add(struct nft_handle *h, struct nft_table *t, uint16_t flags)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ int ret;
+
+ nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, h->family,
+ NLM_F_ACK|flags, 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);
+
+ return (ret == 0 || (ret == -1 && errno == EEXIST)) ? 0 : -1;
+}
+
+static int nft_table_builtin_add(struct nft_handle *h,
+ struct builtin_table *_t)
+{
+ 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 (h->batch_support)
+ ret = batch_table_add(h, NFT_COMPAT_TABLE_ADD, t);
+ else
+ ret = nft_table_add(h, t, NLM_F_EXCL);
+
+ if (ret == 0)
+ _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;
+}
+
+int nft_chain_add(struct nft_handle *h, struct nft_chain *c, uint16_t flags)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ /* NLM_F_CREATE requests module autoloading */
+ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family,
+ NLM_F_ACK|flags|NLM_F_CREATE,
+ h->seq);
+ nft_chain_nlmsg_build_payload(nlh, c);
+ nft_chain_free(c);
+
+#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
+
+ return mnl_talk(h, nlh, NULL, NULL);
+}
+
+void nft_chain_builtin_add(struct nft_handle *h, struct builtin_table *table,
+ struct builtin_chain *chain, int policy)
+{
+ struct nft_chain *c;
+
+ c = nft_chain_builtin_alloc(table, chain, policy);
+ if (c == NULL)
+ return;
+
+ if (h->batch_support)
+ batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, c);
+ else
+ nft_chain_add(h, c, NLM_F_EXCL);
+}
+
+/* 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) < 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;
+}
+
+static bool mnl_batch_supported(struct nft_handle *h)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ uint32_t seq = 1;
+ int ret;
+
+ mnl_nft_batch_begin(h->batch, seq++);
+
+ nft_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
+ NFT_MSG_NEWSET, AF_INET,
+ NLM_F_ACK, seq++);
+ mnl_nlmsg_batch_next(h->batch);
+
+ mnl_nft_batch_end(h->batch, seq++);
+
+ ret = mnl_socket_sendto(h->nl, mnl_nlmsg_batch_head(h->batch),
+ mnl_nlmsg_batch_size(h->batch));
+ if (ret < 0)
+ goto err;
+
+ mnl_nlmsg_batch_reset(h->batch);
+
+ ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(h->nl),
+ NULL, NULL);
+ if (ret <= 0)
+ break;
+
+ ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
+ }
+
+ /* We're sending an incomplete message to see if the kernel supports
+ * set messages in batches. EINVAL means that we sent an incomplete
+ * message with missing attributes. The kernel just ignores messages
+ * that we cannot include in the batch.
+ */
+ return (ret == -1 && errno == EINVAL) ? true : false;
+err:
+ mnl_nlmsg_batch_reset(h->batch);
+ return ret;
+}
+
+int nft_init(struct nft_handle *h, struct builtin_table *t)
+{
+ h->nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (h->nl == NULL)
+ return -1;
+
+ if (mnl_socket_bind(h->nl, 0, MNL_SOCKET_AUTOPID) < 0)
+ return -1;
+
+ h->portid = mnl_socket_get_portid(h->nl);
+ h->tables = t;
+
+ INIT_LIST_HEAD(&h->obj_list);
+
+ h->batch = mnl_nft_batch_alloc();
+ h->batch_support = mnl_batch_supported(h);
+
+ 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);
+}
+
+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 struct nft_chain *nft_chain_new(struct nft_handle *h,
+ const char *table, const char *chain,
+ int policy,
+ const struct xt_counters *counters)
+{
+ 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);
+
+ _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 NULL;
+ } else {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ 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);
+ }
+
+ return 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 *c = NULL;
+ int ret;
+
+ nft_fn = nft_chain_set;
+
+ if (strcmp(policy, "DROP") == 0)
+ c = nft_chain_new(h, table, chain, NF_DROP, counters);
+ else if (strcmp(policy, "ACCEPT") == 0)
+ c = nft_chain_new(h, table, chain, NF_ACCEPT, counters);
+
+ if (c == NULL)
+ return 0;
+
+ if (h->batch_support)
+ ret = batch_chain_add(h, NFT_COMPAT_CHAIN_UPDATE, c);
+ else
+ ret = nft_chain_add(h, c, 0);
+
+ /* 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_PACKETS, packets);
+ nft_rule_expr_set_u64(expr, NFT_EXPR_CTR_BYTES, 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;
+}
+
+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_COMPAT_RULE_REPLACE;
+ } else
+ type = NFT_COMPAT_RULE_APPEND;
+
+ if (batch_rule_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_u32(r, NFT_RULE_ATTR_FAMILY);
+ struct nft_family_ops *ops;
+
+ ops = nft_family_ops_lookup(family);
+
+ if (!(format & FMT_NOCOUNTS) && ops->save_counters)
+ ops->save_counters(data);
+
+ /* print chain name */
+ switch(type) {
+ case NFT_RULE_APPEND:
+ printf("-A %s ", chain);
+ break;
+ case NFT_RULE_DEL:
+ printf("-D %s ", chain);
+ break;
+ }
+
+ 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)
+ goto err;
+
+ if (nft_chain_nlmsg_parse(nlh, c) < 0)
+ 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)
+ goto err;
+
+ if (nft_rule_nlmsg_parse(nlh, r) < 0)
+ 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 (batch_rule_add(h, NFT_COMPAT_RULE_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)
+ goto err;
+
+ if (nft_table_nlmsg_parse(nlh, t) < 0)
+ 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 = batch_rule_add(h, NFT_COMPAT_RULE_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 (batch_rule_add(h, NFT_COMPAT_RULE_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 void nft_compat_table_batch_add(struct nft_handle *h, uint16_t type,
+ uint16_t flags, uint32_t seq,
+ struct nft_table *table)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = nft_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
+ type, h->family, flags, seq);
+ nft_table_nlmsg_build_payload(nlh, table);
+ nft_table_free(table);
+}
+
+static void nft_compat_chain_batch_add(struct nft_handle *h, uint16_t type,
+ uint16_t flags, uint32_t seq,
+ struct nft_chain *chain)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = nft_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
+ type, h->family, flags, seq);
+ nft_chain_nlmsg_build_payload(nlh, chain);
+ nft_chain_print_debug(chain, nlh);
+ nft_chain_free(chain);
+}
+
+static void nft_compat_rule_batch_add(struct nft_handle *h, uint16_t type,
+ uint16_t flags, uint32_t seq,
+ struct nft_rule *rule)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = nft_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(h->batch),
+ type, h->family, flags, seq);
+ nft_rule_nlmsg_build_payload(nlh, rule);
+ nft_rule_print_debug(rule, nlh);
+ nft_rule_free(rule);
+}
+
+static int nft_action(struct nft_handle *h, int action)
+{
+ struct obj_update *n, *tmp;
+ uint32_t seq = 1;
+ int ret = 0;
+
+ mnl_nft_batch_begin(h->batch, seq++);
+
+ list_for_each_entry_safe(n, tmp, &h->obj_list, head) {
+ switch (n->type) {
+ case NFT_COMPAT_TABLE_ADD:
+ nft_compat_table_batch_add(h, NFT_MSG_NEWTABLE,
+ NLM_F_CREATE, seq++,
+ n->table);
+ break;
+ case NFT_COMPAT_CHAIN_ADD:
+ nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN,
+ NLM_F_CREATE, seq++,
+ n->chain);
+ break;
+ case NFT_COMPAT_CHAIN_UPDATE:
+ nft_compat_chain_batch_add(h, NFT_MSG_NEWCHAIN,
+ h->restore ?
+ NLM_F_CREATE : 0,
+ seq++, n->chain);
+ break;
+ case NFT_COMPAT_RULE_APPEND:
+ nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
+ NLM_F_CREATE | NLM_F_APPEND,
+ seq++, n->rule);
+ break;
+ case NFT_COMPAT_RULE_INSERT:
+ nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
+ NLM_F_CREATE, seq++,
+ n->rule);
+ break;
+ case NFT_COMPAT_RULE_REPLACE:
+ nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
+ NLM_F_CREATE | NLM_F_REPLACE,
+ seq++, n->rule);
+ break;
+ case NFT_COMPAT_RULE_DELETE:
+ case NFT_COMPAT_RULE_FLUSH:
+ nft_compat_rule_batch_add(h, NFT_MSG_DELRULE, 0,
+ seq++, n->rule);
+ break;
+ }
+
+ h->obj_list_num--;
+ list_del(&n->head);
+ free(n);
+
+ if (!mnl_nlmsg_batch_next(h->batch))
+ h->batch = mnl_nft_batch_page_add(h->batch);
+ }
+
+ switch (action) {
+ case NFT_COMPAT_COMMIT:
+ mnl_nft_batch_end(h->batch, seq++);
+ break;
+ case NFT_COMPAT_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);
+
+ mnl_nlmsg_batch_reset(h->batch);
+
+ return ret == 0 ? 1 : 0;
+}
+
+int nft_commit(struct nft_handle *h)
+{
+ return nft_action(h, NFT_COMPAT_COMMIT);
+}
+
+int nft_abort(struct nft_handle *h)
+{
+ return nft_action(h, NFT_COMPAT_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)
+ return 0;
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
+ goto err;
+
+ portid = mnl_socket_get_portid(nl);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
+ goto err;
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret == -1)
+ goto err;
+
+ ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
+ if (ret == -1)
+ 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 (batch_table_add(h, NFT_COMPAT_TABLE_ADD, 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 (batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, 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 (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..339d7bcd
--- /dev/null
+++ b/iptables/nft.h
@@ -0,0 +1,173 @@
+#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 obj_list;
+ int obj_list_num;
+ struct mnl_nlmsg_batch *batch;
+ struct nft_family_ops *ops;
+ struct builtin_table *tables;
+ bool restore;
+ bool batch_support;
+};
+
+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;
+
+struct builtin_table *nft_table_builtin_find(struct nft_handle *h, const char *table);
+int nft_table_add(struct nft_handle *h, struct nft_table *t, uint16_t flags);
+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_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, struct nft_chain *c, uint16_t flags);
+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.c b/iptables/xshared.c
index 6c9992ed..b18022ee 100644
--- a/iptables/xshared.c
+++ b/iptables/xshared.c
@@ -243,10 +243,11 @@ void xs_init_match(struct xtables_match *match)
match->init(match->m);
}
-bool xtables_lock(bool wait)
+bool xtables_lock(int wait)
{
int i = 0, ret, xt_socket;
struct sockaddr_un xt_addr;
+ int waited = 0;
memset(&xt_addr, 0, sizeof(xt_addr));
xt_addr.sun_family = AF_UNIX;
@@ -261,11 +262,12 @@ bool xtables_lock(bool wait)
offsetof(struct sockaddr_un, sun_path)+XT_SOCKET_LEN);
if (ret == 0)
return true;
- else if (wait == false)
+ else if (wait >= 0 && waited >= wait)
return false;
if (++i % 2 == 0)
fprintf(stderr, "Another app is currently holding the xtables lock; "
- "waiting for it to exit...\n");
+ "waiting (%ds) for it to exit...\n", waited);
+ waited++;
sleep(1);
}
}
diff --git a/iptables/xshared.h b/iptables/xshared.h
index 1e2b9b8e..40dd9154 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;
@@ -84,7 +85,7 @@ extern struct xtables_match *load_proto(struct iptables_command_state *);
extern int subcmd_main(int, char **, const struct subcommand *);
extern void xs_init_target(struct xtables_target *);
extern void xs_init_match(struct xtables_match *);
-extern bool xtables_lock(bool wait);
+extern bool xtables_lock(int wait);
extern const struct xtables_afinfo *afinfo;
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..c8116c3e
--- /dev/null
+++ b/iptables/xtables-config-parser.y
@@ -0,0 +1,248 @@
+%{
+/*
+ * (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)
+ 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)
+ 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..1e0b1752
--- /dev/null
+++ b/iptables/xtables-events.c
@@ -0,0 +1,212 @@
+/*
+ * (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)
+ goto err;
+
+ if (nft_table_nlmsg_parse(nlh, t) < 0)
+ 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 arpt_entry fw_arp = {};
+ struct nft_rule *r;
+ void *fw = NULL;
+ uint8_t family;
+
+ r = nft_rule_alloc();
+ if (r == NULL)
+ goto err;
+
+ if (nft_rule_nlmsg_parse(nlh, r) < 0)
+ goto err_free;
+
+ family = nft_rule_attr_get_u32(r, NFT_RULE_ATTR_FAMILY);
+ switch (family) {
+ case AF_INET:
+ case AF_INET6:
+ printf("-%c ", family == AF_INET ? '4' : '6');
+ nft_rule_to_iptables_command_state(r, &cs);
+ fw = &cs;
+ break;
+ case NFPROTO_ARP:
+ printf("-0 ");
+ nft_rule_to_arpt_entry(r, &fw_arp);
+ fw = &fw_arp;
+ break;
+ default:
+ goto err_free;
+ }
+
+
+ nft_rule_print_save(fw, 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)
+ goto err;
+
+ if (nft_chain_nlmsg_parse(nlh, t) < 0)
+ 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("cannot open nfnetlink socket");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, (1 << (NFNLGRP_NFTABLES-1)), MNL_SOCKET_AUTOPID) < 0) {
+ perror("cannot bind to nfnetlink socket");
+ 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("cannot receive from nfnetlink socket");
+ 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..3cb095fe
--- /dev/null
+++ b/iptables/xtables-restore.c
@@ -0,0 +1,486 @@
+/* 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 counters = 0, verbose = 0, noflush = 0;
+
+/* Keeping track of external matches and targets. */
+static const struct option options[] = {
+ {.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 [-c] [-v] [-t] [-h]\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':
+ fprintf(stderr, "-b/--binary option is not implemented\n");
+ 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..d51cdcf7
--- /dev/null
+++ b/iptables/xtables-save.c
@@ -0,0 +1,157 @@
+/* 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:M:46", options, NULL)) != -1) {
+ switch (c) {
+ case 'b':
+ fprintf(stderr, "-b/--binary option is not implemented\n");
+ break;
+ 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..d661dd16
--- /dev/null
+++ b/iptables/xtables.c
@@ -0,0 +1,1272 @@
+/* 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 = "wait", .has_arg = 2, .val = 'w'},
+ {.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;
+ int wait = 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:fbvw::nt: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");
+ }
+ if (optarg) {
+ if (sscanf(optarg, "%i", &wait) != 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "wait seconds not numeric");
+ } else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ if (sscanf(argv[optind++], "%i", &wait) != 1)
+ xtables_error(PARAMETER_PROBLEM,
+ "wait seconds not numeric");
+ 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;
+}