summaryrefslogtreecommitdiffstats
path: root/iptables
diff options
context:
space:
mode:
Diffstat (limited to 'iptables')
-rw-r--r--iptables/.gitignore5
-rw-r--r--iptables/Makefile.am109
-rw-r--r--iptables/arptables-nft-restore.84
-rw-r--r--iptables/arptables-nft-save.82
-rw-r--r--iptables/arptables-nft.8156
-rw-r--r--iptables/ebtables-nft.8176
-rw-r--r--iptables/ip6tables-apply.81
-rw-r--r--iptables/ip6tables-restore.81
-rw-r--r--iptables/ip6tables-save.81
-rw-r--r--iptables/ip6tables.81
-rw-r--r--iptables/ip6tables.c66
-rwxr-xr-xiptables/iptables-apply16
-rw-r--r--iptables/iptables-apply.8.in4
-rw-r--r--iptables/iptables-restore.8.in25
-rw-r--r--iptables/iptables-restore.c60
-rw-r--r--iptables/iptables-save.8.in18
-rw-r--r--iptables/iptables-save.c5
-rw-r--r--iptables/iptables-xml.c26
-rw-r--r--iptables/iptables.8.in43
-rw-r--r--iptables/iptables.c66
-rw-r--r--iptables/nft-arp.c370
-rw-r--r--iptables/nft-bridge.c625
-rw-r--r--iptables/nft-bridge.h14
-rw-r--r--iptables/nft-cache.c45
-rw-r--r--iptables/nft-cache.h2
-rw-r--r--iptables/nft-cmd.c30
-rw-r--r--iptables/nft-cmd.h10
-rw-r--r--iptables/nft-ipv4.c177
-rw-r--r--iptables/nft-ipv6.c151
-rw-r--r--iptables/nft-ruleparse-arp.c174
-rw-r--r--iptables/nft-ruleparse-bridge.c422
-rw-r--r--iptables/nft-ruleparse-ipv4.c133
-rw-r--r--iptables/nft-ruleparse-ipv6.c110
-rw-r--r--iptables/nft-ruleparse.c1177
-rw-r--r--iptables/nft-ruleparse.h136
-rw-r--r--iptables/nft-shared.c1045
-rw-r--r--iptables/nft-shared.h78
-rw-r--r--iptables/nft.c269
-rw-r--r--iptables/nft.h20
-rwxr-xr-xiptables/tests/shell/run-tests.sh7
-rwxr-xr-xiptables/tests/shell/testcases/chain/0003rename_040
-rwxr-xr-xiptables/tests/shell/testcases/chain/0003rename_112
-rwxr-xr-xiptables/tests/shell/testcases/chain/0006rename-segfault_019
-rwxr-xr-xiptables/tests/shell/testcases/chain/0007counters_078
-rwxr-xr-xiptables/tests/shell/testcases/chain/0008rename-segfault2_032
-rwxr-xr-xiptables/tests/shell/testcases/ebtables/0001-ebtables-basic_04
-rwxr-xr-xiptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_020
-rwxr-xr-xiptables/tests/shell/testcases/ebtables/0003-ebtables-restore-defaults_02
-rwxr-xr-xiptables/tests/shell/testcases/ebtables/0006-flush_047
-rwxr-xr-xiptables/tests/shell/testcases/ebtables/0008-ebtables-among_0106
-rwxr-xr-xiptables/tests/shell/testcases/ebtables/0009-broute-bug_025
-rwxr-xr-xiptables/tests/shell/testcases/ebtables/0010-change-counters_045
-rwxr-xr-xiptables/tests/shell/testcases/ip6tables/0002-verbose-output_012
-rwxr-xr-xiptables/tests/shell/testcases/ip6tables/0003-list-rules_06
-rwxr-xr-xiptables/tests/shell/testcases/ip6tables/0005-rule-check_017
-rwxr-xr-xiptables/tests/shell/testcases/ipt-restore/0003-restore-ordering_016
-rwxr-xr-xiptables/tests/shell/testcases/ipt-restore/0008-restore-counters_07
-rwxr-xr-xiptables/tests/shell/testcases/ipt-restore/0011-noflush-empty-line_02
-rwxr-xr-xiptables/tests/shell/testcases/ipt-save/0001load-dumps_01
-rwxr-xr-xiptables/tests/shell/testcases/iptables/0002-verbose-output_04
-rwxr-xr-xiptables/tests/shell/testcases/iptables/0003-list-rules_06
-rwxr-xr-xiptables/tests/shell/testcases/iptables/0004-return-codes_01
-rwxr-xr-xiptables/tests/shell/testcases/iptables/0009-unknown-arg_031
-rwxr-xr-xiptables/tests/shell/testcases/iptables/0010-wait_055
-rwxr-xr-xiptables/tests/shell/testcases/nft-only/0007-mid-restore-flush_010
-rwxr-xr-xiptables/tests/shell/testcases/nft-only/0009-needless-bitwise_02
-rw-r--r--iptables/tests/shell/testcases/nft-only/0010-iptables-nft-save.txt26
-rwxr-xr-xiptables/tests/shell/testcases/nft-only/0010-native-delinearize_09
-rw-r--r--iptables/tests/shell/testcases/nft-only/0010-nft-native.txt41
-rwxr-xr-xiptables/tests/shell/testcases/nft-only/0011-zero-needs-compat_012
-rw-r--r--iptables/xshared.c745
-rw-r--r--iptables/xshared.h73
-rw-r--r--iptables/xtables-eb-translate.c522
-rw-r--r--iptables/xtables-eb.c1016
-rw-r--r--iptables/xtables-monitor.8.in2
-rw-r--r--iptables/xtables-monitor.c2
-rw-r--r--iptables/xtables-multi.h2
-rw-r--r--iptables/xtables-nft-multi.c1
-rw-r--r--iptables/xtables-nft.816
-rw-r--r--iptables/xtables-restore.c47
-rw-r--r--iptables/xtables-translate.849
-rw-r--r--iptables/xtables-translate.c85
-rw-r--r--iptables/xtables.c7
83 files changed, 5031 insertions, 4004 deletions
diff --git a/iptables/.gitignore b/iptables/.gitignore
index cd7d87b1..8141e34d 100644
--- a/iptables/.gitignore
+++ b/iptables/.gitignore
@@ -1,6 +1,11 @@
+/ebtables-translate.8
/ip6tables
+/ip6tables.8
+/ip6tables-apply.8
/ip6tables-save
+/ip6tables-save.8
/ip6tables-restore
+/ip6tables-restore.8
/ip6tables-static
/ip6tables-translate.8
/ip6tables-restore-translate.8
diff --git a/iptables/Makefile.am b/iptables/Makefile.am
index 23f8352d..2007cd10 100644
--- a/iptables/Makefile.am
+++ b/iptables/Makefile.am
@@ -1,51 +1,67 @@
# -*- Makefile -*-
AM_CFLAGS = ${regular_CFLAGS}
-AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include -I${top_srcdir} ${kinclude_CPPFLAGS} ${libmnl_CFLAGS} ${libnftnl_CFLAGS} ${libnetfilter_conntrack_CFLAGS}
+AM_CPPFLAGS = ${regular_CPPFLAGS} \
+ -I${top_builddir}/include \
+ -I${top_srcdir}/include \
+ -I${top_srcdir} \
+ ${kinclude_CPPFLAGS} \
+ ${libmnl_CFLAGS} \
+ ${libnftnl_CFLAGS} \
+ ${libnetfilter_conntrack_CFLAGS}
AM_LDFLAGS = ${regular_LDFLAGS}
BUILT_SOURCES =
-xtables_legacy_multi_SOURCES = xtables-legacy-multi.c iptables-xml.c
-xtables_legacy_multi_CFLAGS = ${AM_CFLAGS}
-xtables_legacy_multi_LDADD = ../extensions/libext.a
+common_sources = iptables-xml.c xtables-multi.h xshared.c xshared.h
+common_ldadd = ../extensions/libext.a ../libxtables/libxtables.la -lm
+common_cflags = ${AM_CFLAGS}
if ENABLE_STATIC
-xtables_legacy_multi_CFLAGS += -DALL_INCLUSIVE
+common_cflags += -DALL_INCLUSIVE
endif
+
+xtables_legacy_multi_SOURCES = ${common_sources} xtables-legacy-multi.c \
+ iptables-restore.c iptables-save.c
+xtables_legacy_multi_CFLAGS = ${common_cflags}
+xtables_legacy_multi_LDADD = ${common_ldadd}
if ENABLE_IPV4
-xtables_legacy_multi_SOURCES += iptables-standalone.c iptables.c
+xtables_legacy_multi_SOURCES += iptables-standalone.c iptables.c iptables-multi.h
xtables_legacy_multi_CFLAGS += -DENABLE_IPV4
xtables_legacy_multi_LDADD += ../libiptc/libip4tc.la ../extensions/libext4.a
endif
if ENABLE_IPV6
-xtables_legacy_multi_SOURCES += ip6tables-standalone.c ip6tables.c
+xtables_legacy_multi_SOURCES += ip6tables-standalone.c ip6tables.c ip6tables-multi.h
xtables_legacy_multi_CFLAGS += -DENABLE_IPV6
xtables_legacy_multi_LDADD += ../libiptc/libip6tc.la ../extensions/libext6.a
endif
-xtables_legacy_multi_SOURCES += xshared.c iptables-restore.c iptables-save.c
-xtables_legacy_multi_LDADD += ../libxtables/libxtables.la -lm
# iptables using nf_tables api
if ENABLE_NFTABLES
-xtables_nft_multi_SOURCES = xtables-nft-multi.c iptables-xml.c
-xtables_nft_multi_CFLAGS = ${AM_CFLAGS}
-xtables_nft_multi_LDADD = ../extensions/libext.a ../extensions/libext_ebt.a
-if ENABLE_STATIC
-xtables_nft_multi_CFLAGS += -DALL_INCLUSIVE
-endif
+xtables_nft_multi_SOURCES = ${common_sources} xtables-nft-multi.c
+xtables_nft_multi_CFLAGS = ${common_cflags}
+xtables_nft_multi_LDADD = ${common_ldadd} \
+ ../extensions/libext_arpt.a \
+ ../extensions/libext_ebt.a \
+ ../extensions/libext4.a \
+ ../extensions/libext6.a \
+ ${libmnl_LIBS} ${libnftnl_LIBS} \
+ ${libnetfilter_conntrack_LIBS}
xtables_nft_multi_CFLAGS += -DENABLE_NFTABLES -DENABLE_IPV4 -DENABLE_IPV6
-xtables_nft_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-monitor.c nft-cache.c \
- xtables-arp.c \
- nft-bridge.c nft-cmd.c nft-chain.c \
- xtables-eb-standalone.c xtables-eb.c \
- xtables-eb-translate.c \
- xtables-translate.c
-xtables_nft_multi_LDADD += ${libmnl_LIBS} ${libnftnl_LIBS} ${libnetfilter_conntrack_LIBS} ../extensions/libext4.a ../extensions/libext6.a ../extensions/libext_ebt.a ../extensions/libext_arpt.a
-xtables_nft_multi_SOURCES += xshared.c
-xtables_nft_multi_LDADD += ../libxtables/libxtables.la -lm
+xtables_nft_multi_SOURCES += nft.c nft.h \
+ nft-arp.c nft-ipv4.c nft-ipv6.c \
+ nft-bridge.c nft-bridge.h \
+ nft-cache.c nft-cache.h \
+ nft-chain.c nft-chain.h \
+ nft-cmd.c nft-cmd.h \
+ nft-ruleparse.c nft-ruleparse.h \
+ nft-ruleparse-arp.c nft-ruleparse-bridge.c \
+ nft-ruleparse-ipv4.c nft-ruleparse-ipv6.c \
+ nft-shared.c nft-shared.h \
+ xtables-monitor.c \
+ xtables.c xtables-arp.c xtables-eb.c \
+ xtables-standalone.c xtables-eb-standalone.c \
+ xtables-translate.c xtables-eb-translate.c \
+ xtables-save.c xtables-restore.c
endif
sbin_PROGRAMS = xtables-legacy-multi
@@ -57,22 +73,21 @@ man_MANS = iptables.8 iptables-restore.8 iptables-save.8 \
ip6tables-save.8 iptables-extensions.8 \
iptables-apply.8 ip6tables-apply.8
-sbin_SCRIPTS = iptables-apply
+dist_sbin_SCRIPTS = iptables-apply
+dist_pkgdata_DATA = iptables.xslt
+
+xlate_man_links = iptables-translate.8 ip6tables-translate.8 \
+ iptables-restore-translate.8 ip6tables-restore-translate.8 \
+ ebtables-translate.8 arptables-translate.8
if ENABLE_NFTABLES
-man_MANS += xtables-nft.8 xtables-translate.8 xtables-legacy.8 \
- iptables-translate.8 ip6tables-translate.8 \
- iptables-restore-translate.8 ip6tables-restore-translate.8 \
- xtables-monitor.8 \
- arptables-nft.8 arptables-nft-restore.8 arptables-nft-save.8 \
- ebtables-nft.8
+man_MANS += ${xlate_man_links} xtables-monitor.8
+
+dist_man_MANS = xtables-nft.8 xtables-translate.8 xtables-legacy.8 \
+ arptables-nft.8 arptables-nft-restore.8 arptables-nft-save.8 \
+ ebtables-nft.8
endif
-CLEANFILES = iptables.8 xtables-monitor.8 \
- iptables-xml.1 iptables-apply.8 \
- iptables-extensions.8 iptables-extensions.8.tmpl \
- iptables-restore.8 iptables-save.8 \
- iptables-restore-translate.8 ip6tables-restore-translate.8 \
- iptables-translate.8 ip6tables-translate.8
+CLEANFILES = ${man_MANS} iptables-extensions.8.tmpl
vx_bin_links = iptables-xml
if ENABLE_IPV4
@@ -86,11 +101,12 @@ endif
if ENABLE_NFTABLES
x_sbin_links = iptables-nft iptables-nft-restore iptables-nft-save \
ip6tables-nft ip6tables-nft-restore ip6tables-nft-save \
- iptables-translate ip6tables-translate \
+ iptables-translate ip6tables-translate ebtables-translate \
iptables-restore-translate ip6tables-restore-translate \
arptables-nft arptables \
arptables-nft-restore arptables-restore \
arptables-nft-save arptables-save \
+ arptables-translate \
ebtables-nft ebtables \
ebtables-nft-restore ebtables-restore \
ebtables-nft-save ebtables-save \
@@ -98,12 +114,15 @@ x_sbin_links = iptables-nft iptables-nft-restore iptables-nft-save \
endif
iptables-extensions.8: iptables-extensions.8.tmpl ../extensions/matches.man ../extensions/targets.man
- ${AM_VERBOSE_GEN} sed \
+ ${AM_V_GEN} sed \
-e '/@MATCH@/ r ../extensions/matches.man' \
-e '/@TARGET@/ r ../extensions/targets.man' $< >$@;
-iptables-translate.8 ip6tables-translate.8 iptables-restore-translate.8 ip6tables-restore-translate.8:
- ${AM_VERBOSE_GEN} echo '.so man8/xtables-translate.8' >$@
+${xlate_man_links}:
+ ${AM_V_GEN} echo '.so man8/xtables-translate.8' >$@
+
+ip6tables.8 ip6tables-apply.8 ip6tables-restore.8 ip6tables-save.8:
+ ${AM_V_GEN} echo "$@" | sed 's|^ip6|.so man8/ip|' >$@
pkgconfig_DATA = xtables.pc
@@ -139,3 +158,5 @@ uninstall-hook:
); \
( cd "$$dir" && rm -f ip6tables-apply ); \
}
+
+EXTRA_DIST = tests
diff --git a/iptables/arptables-nft-restore.8 b/iptables/arptables-nft-restore.8
index 09d9082c..596ca1c9 100644
--- a/iptables/arptables-nft-restore.8
+++ b/iptables/arptables-nft-restore.8
@@ -20,9 +20,9 @@
.\"
.\"
.SH NAME
-arptables-restore \- Restore ARP Tables (nft-based)
+arptables-restore \(em Restore ARP Tables (nft-based)
.SH SYNOPSIS
-\fBarptables\-restore
+\fBarptables\-restore\fP
.SH DESCRIPTION
.PP
.B arptables-restore
diff --git a/iptables/arptables-nft-save.8 b/iptables/arptables-nft-save.8
index 905e5985..e9171d5d 100644
--- a/iptables/arptables-nft-save.8
+++ b/iptables/arptables-nft-save.8
@@ -20,7 +20,7 @@
.\"
.\"
.SH NAME
-arptables-save \- dump arptables rules to stdout (nft-based)
+arptables-save \(em dump arptables rules to stdout (nft-based)
.SH SYNOPSIS
\fBarptables\-save\fP [\fB\-M\fP \fImodprobe\fP] [\fB\-c\fP]
.P
diff --git a/iptables/arptables-nft.8 b/iptables/arptables-nft.8
index ea31e084..c48a2cc2 100644
--- a/iptables/arptables-nft.8
+++ b/iptables/arptables-nft.8
@@ -22,22 +22,36 @@
.\"
.\"
.SH NAME
-arptables \- ARP table administration (nft-based)
+arptables \(em ARP table administration (nft-based)
.SH SYNOPSIS
-.BR "arptables " [ "-t table" ] " -" [ AD ] " chain rule-specification " [ options ]
-.br
-.BR "arptables " [ "-t table" ] " -" [ RI ] " chain rulenum rule-specification " [ options ]
-.br
-.BR "arptables " [ "-t table" ] " -D chain rulenum " [ options ]
-.br
-.BR "arptables " [ "-t table" ] " -" [ "LFZ" ] " " [ chain ] " " [ options ]
-.br
-.BR "arptables " [ "-t table" ] " -" [ "NX" ] " chain"
-.br
-.BR "arptables " [ "-t table" ] " -E old-chain-name new-chain-name"
-.br
-.BR "arptables " [ "-t table" ] " -P chain target " [ options ]
-
+\fBarptables\fP [\fB\-t\fP \fItable\fP] {\fB\-A|\-D\fP} \fIchain\fP
+\fIrule-specification\fP [options...]
+.PP
+\fBarptables\fP [\fB\-t\fP \fItable\fP] \fB\-I\fP \fIchain\fP [\fIrulenum\fP]
+\fIrule-specification\fP
+.PP
+\fBarptables\fP [\fB\-t\fP \fItable\fP] \fB\-R\fP \fIchain rulenum
+rule-specification\fP
+.PP
+\fBarptables\fP [\fB\-t\fP \fItable\fP] \fB\-D\fP \fIchain rulenum\fP
+.PP
+\fBarptables\fP [\fB\-t\fP \fItable\fP] {\fB\-F\fP|\fB\-L\fP|\fB\-Z\fP}
+[\fIchain\fP [\fIrulenum\fP]] [\fIoptions...\fP]
+.PP
+\fBarptables\fP [\fB\-t\fP \fItable\fP] \fB\-N\fP \fIchain\fP
+.PP
+\fBarptables\fP [\fB\-t\fP \fItable\fP] \fB\-X\fP [\fIchain\fP]
+.PP
+\fBarptables\fP [\fB\-t\fP \fItable\fP] \fB\-P\fP \fIchain policy\fP
+.PP
+\fBarptables\fP [\fB\-t\fP \fItable\fP] \fB\-E\fP \fIold-chain-name
+new-chain-name\fP
+.PP
+rule-specification := [matches...] [target]
+.PP
+match := \fB\-m\fP \fImatchname\fP [per-match-options]
+.PP
+target := \fB\-j\fP \fItargetname\fP [per-target-options]
.SH DESCRIPTION
.B arptables
is a user space tool, it is used to set up and maintain the
@@ -88,11 +102,11 @@ section of this man page.
There is only one ARP table in the Linux
kernel. The table is
.BR filter.
-You can drop the '-t filter' argument to the arptables command.
-The -t argument must be the
+You can drop the '\-t filter' argument to the arptables command.
+The \-t argument must be the
first argument on the arptables command line, if used.
.TP
-.B "-t, --table"
+.B "\-t, \-\-table"
.br
.BR filter ,
is the only table and contains two built-in chains:
@@ -109,79 +123,79 @@ are commands, miscellaneous commands, rule-specifications, match-extensions,
and watcher-extensions.
.SS COMMANDS
The arptables command arguments specify the actions to perform on the table
-defined with the -t argument. If you do not use the -t argument to name
+defined with the \-t argument. If you do not use the \-t argument to name
a table, the commands apply to the default filter table.
With the exception of the
-.B "-Z"
+.B "\-Z"
command, only one command may be used on the command line at a time.
.TP
-.B "-A, --append"
+.B "\-A, \-\-append"
Append a rule to the end of the selected chain.
.TP
-.B "-D, --delete"
+.B "\-D, \-\-delete"
Delete the specified rule from the selected chain. There are two ways to
use this command. The first is by specifying an interval of rule numbers
to delete, syntax: start_nr[:end_nr]. Using negative numbers is allowed, for more
-details about using negative numbers, see the -I command. The second usage is by
+details about using negative numbers, see the \-I command. The second usage is by
specifying the complete rule as it would have been specified when it was added.
.TP
-.B "-I, --insert"
+.B "\-I, \-\-insert"
Insert the specified rule into the selected chain at the specified rule number.
If the current number of rules equals N, then the specified number can be
-between -N and N+1. For a positive number i, it holds that i and i-N-1 specify the
+between \-N and N+1. For a positive number i, it holds that i and i\-N\-1 specify the
same place in the chain where the rule should be inserted. The number 0 specifies
the place past the last rule in the chain and using this number is therefore
-equivalent with using the -A command.
+equivalent with using the \-A command.
.TP
-.B "-R, --replace"
+.B "\-R, \-\-replace"
Replaces the specified rule into the selected chain at the specified rule number.
If the current number of rules equals N, then the specified number can be
between 1 and N. i specifies the place in the chain where the rule should be replaced.
.TP
-.B "-P, --policy"
+.B "\-P, \-\-policy"
Set the policy for the chain to the given target. The policy can be
.BR ACCEPT ", " DROP " or " RETURN .
.TP
-.B "-F, --flush"
+.B "\-F, \-\-flush"
Flush the selected chain. If no chain is selected, then every chain will be
flushed. Flushing the chain does not change the policy of the
chain, however.
.TP
-.B "-Z, --zero"
+.B "\-Z, \-\-zero"
Set the counters of the selected chain to zero. If no chain is selected, all the counters
are set to zero. The
-.B "-Z"
+.B "\-Z"
command can be used in conjunction with the
-.B "-L"
+.B "\-L"
command.
When both the
-.B "-Z"
+.B "\-Z"
and
-.B "-L"
+.B "\-L"
commands are used together in this way, the rule counters are printed on the screen
before they are set to zero.
.TP
-.B "-L, --list"
+.B "\-L, \-\-list"
List all rules in the selected chain. If no chain is selected, all chains
are listed.
.TP
-.B "-N, --new-chain"
+.B "\-N, \-\-new-chain"
Create a new user-defined chain with the given name. The number of
user-defined chains is unlimited. A user-defined chain name has maximum
length of 31 characters.
.TP
-.B "-X, --delete-chain"
+.B "\-X, \-\-delete-chain"
Delete the specified user-defined chain. There must be no remaining references
to the specified chain, otherwise
.B arptables
will refuse to delete it. If no chain is specified, all user-defined
chains that aren't referenced will be removed.
.TP
-.B "-E, --rename-chain"
+.B "\-E, \-\-rename\-chain"
Rename the specified chain to a new name. Besides renaming a user-defined
chain, you may rename a standard chain name to a name that suits your
taste. For example, if you like PREBRIDGING more than PREROUTING,
-then you can use the -E command to rename the PREROUTING chain. If you do
+then you can use the \-E command to rename the PREROUTING chain. If you do
rename one of the standard
.B arptables
chain names, please be sure to mention
@@ -195,15 +209,15 @@ of the
.B arptables
kernel table.
-.SS MISCELLANOUS COMMANDS
+.SS MISCELLANEOUS COMMANDS
.TP
-.B "-V, --version"
+.B "\-V, \-\-version"
Show the version of the arptables userspace program.
.TP
-.B "-h, --help"
+.B "\-h, \-\-help"
Give a brief description of the command syntax.
.TP
-.BR "-j, --jump " "\fItarget\fP"
+.BR "\-j, \-\-jump " "\fItarget\fP"
The target of the rule. This is one of the following values:
.BR ACCEPT ,
.BR DROP ,
@@ -213,7 +227,7 @@ a target extension (see
.BR "TARGET EXTENSIONS" ")"
or a user-defined chain name.
.TP
-.BI "-c, --set-counters " "PKTS BYTES"
+.BI "\-c, \-\-set-counters " "PKTS BYTES"
This enables the administrator to initialize the packet and byte
counters of a rule (during
.B INSERT,
@@ -227,38 +241,38 @@ in the add and delete commands). A "!" option before the specification
inverts the test for that specification. Apart from these standard rule
specifications there are some other command line arguments of interest.
.TP
-.BR "-s, --source-ip " "[!] \fIaddress\fP[/\fImask]\fP"
+.BR "\-s, \-\-source\-ip " "[!] \fIaddress\fP[/\fImask]\fP"
The Source IP specification.
.TP
-.BR "-d, --destination-ip " "[!] \fIaddress\fP[/\fImask]\fP"
+.BR "\-d, \-\-destination\-ip " "[!] \fIaddress\fP[/\fImask]\fP"
The Destination IP specification.
.TP
-.BR "--source-mac " "[!] \fIaddress\fP[/\fImask\fP]"
+.BR "\-\-source\-mac " "[!] \fIaddress\fP[/\fImask\fP]"
The source mac address. Both mask and address are written as 6 hexadecimal
numbers separated by colons.
.TP
-.BR "--destination-mac " "[!] \fIaddress\fP[/\fImask\fP]"
+.BR "\-\-destination\-mac " "[!] \fIaddress\fP[/\fImask\fP]"
The destination mac address. Both mask and address are written as 6 hexadecimal
numbers separated by colons.
.TP
-.BR "-i, --in-interface " "[!] \fIname\fP"
+.BR "\-i, \-\-in\-interface " "[!] \fIname\fP"
The interface via which a frame is received (for the
.B INPUT
chain). The flag
-.B --in-if
+.B \-\-in\-if
is an alias for this option.
.TP
-.BR "-o, --out-interface " "[!] \fIname\fP"
+.BR "\-o, \-\-out-interface " "[!] \fIname\fP"
The interface via which a frame is going to be sent (for the
.B OUTPUT
chain). The flag
-.B --out-if
+.B \-\-out\-if
is an alias for this option.
.TP
-.BR "-l, --h-length " "\fIlength\fP[/\fImask\fP]"
+.BR "\-l, \-\-h\-length " "\fIlength\fP[/\fImask\fP]"
The hardware length (nr of bytes)
.TP
-.BR "--opcode " "\fIcode\fP[/\fImask\fP]
+.BR "\-\-opcode " "\fIcode\fP[/\fImask\fP]
The operation code (2 bytes). Available values are:
.BR 1 = Request
.BR 2 = Reply
@@ -270,63 +284,63 @@ The operation code (2 bytes). Available values are:
.BR 8 = InARP_Request
.BR 9 = ARP_NAK .
.TP
-.BR "--h-type " "\fItype\fP[/\fImask\fP]"
+.BR "\-\-h\-type " "\fItype\fP[/\fImask\fP]"
The hardware type (2 bytes, hexadecimal). Available values are:
.BR 1 = Ethernet .
.TP
-.BR "--proto-type " "\fItype\fP[/\fImask\fP]"
+.BR "\-\-proto\-type " "\fItype\fP[/\fImask\fP]"
The protocol type (2 bytes). Available values are:
.BR 0x800 = IPv4 .
.SS TARGET-EXTENSIONS
.B arptables
extensions are precompiled into the userspace tool. So there is no need
-to explicitly load them with a -m option like in
+to explicitly load them with a \-m option like in
.BR iptables .
However, these
extensions deal with functionality supported by supplemental kernel modules.
.SS mangle
.TP
-.BR "--mangle-ip-s IP address"
+.BR "\-\-mangle\-ip\-s IP address"
Mangles Source IP Address to given value.
.TP
-.BR "--mangle-ip-d IP address"
+.BR "\-\-mangle\-ip\-d IP address"
Mangles Destination IP Address to given value.
.TP
-.BR "--mangle-mac-s MAC address"
+.BR "\-\-mangle\-mac\-s MAC address"
Mangles Source MAC Address to given value.
.TP
-.BR "--mangle-mac-d MAC address"
+.BR "\-\-mangle\-mac\-d MAC address"
Mangles Destination MAC Address to given value.
.TP
-.BR "--mangle-target target "
+.BR "\-\-mangle\-target target "
Target of ARP mangle operation
-.BR "" ( DROP ", " CONTINUE " or " ACCEPT " -- default is " ACCEPT ).
+.BR "" ( DROP ", " CONTINUE " or " ACCEPT " \(em default is " ACCEPT ).
.SS CLASSIFY
-This module allows you to set the skb->priority value (and thus clas-
-sify the packet into a specific CBQ class).
+This module allows you to set the skb\->priority value (and thus
+classify the packet into a specific CBQ class).
.TP
-.BR "--set-class major:minor"
+.BR "\-\-set\-class major:minor"
Set the major and minor class value. The values are always
interpreted as hexadecimal even if no 0x prefix is given.
.SS MARK
-This module allows you to set the skb->mark value (and thus classify
+This module allows you to set the skb\->mark value (and thus classify
the packet by the mark in u32)
.TP
-.BR "--set-mark mark"
+.BR "\-\-set\-mark mark"
Set the mark value. The values are always
interpreted as hexadecimal even if no 0x prefix is given
.TP
-.BR "--and-mark mark"
+.BR "\-\-and\-mark mark"
Binary AND the mark with bits.
.TP
-.BR "--or-mark mark"
+.BR "\-\-or\-mark mark"
Binary OR the mark with bits.
.SH NOTES
@@ -343,6 +357,6 @@ chain in
.SH MAILINGLISTS
.BR "" "See " http://netfilter.org/mailinglists.html
.SH SEE ALSO
-.BR xtables-nft "(8), " iptables "(8), " ebtables "(8), " ip (8)
+.BR xtables\-nft "(8), " iptables "(8), " ebtables "(8), " ip (8)
.PP
.BR "" "See " https://wiki.nftables.org
diff --git a/iptables/ebtables-nft.8 b/iptables/ebtables-nft.8
index d75aae24..29c7d9fa 100644
--- a/iptables/ebtables-nft.8
+++ b/iptables/ebtables-nft.8
@@ -24,7 +24,7 @@
.\"
.\"
.SH NAME
-ebtables \- Ethernet bridge frame table administration (nft-based)
+ebtables \(em Ethernet bridge frame table administration (nft-based)
.SH SYNOPSIS
.BR "ebtables " [ -t " table ] " - [ ACDI "] chain rule specification [match extensions] [watcher extensions] target"
.br
@@ -55,7 +55,7 @@ It is analogous to the
application, but less complicated, due to the fact that the Ethernet protocol
is much simpler than the IP protocol.
.SS CHAINS
-There are two ebtables tables with built-in chains in the
+There are three ebtables tables with built-in chains in the
Linux kernel. These tables are used to divide functionality into
different sets of rules. Each set of rules is called a chain.
Each chain is an ordered list of rules that can match Ethernet frames. If a
@@ -81,7 +81,10 @@ an 'extension' (see below) or a jump to a user-defined chain.
.B ACCEPT
means to let the frame through.
.B DROP
-means the frame has to be dropped.
+means the frame has to be dropped. In the
+.BR BROUTING " chain however, the " ACCEPT " and " DROP " target have different"
+meanings (see the info provided for the
+.BR -t " option)."
.B CONTINUE
means the next rule has to be checked. This can be handy, f.e., to know how many
frames pass a certain point in the chain, to log those frames or to apply multiple
@@ -93,17 +96,13 @@ For the extension targets please refer to the
.B "TARGET EXTENSIONS"
section of this man page.
.SS TABLES
-As stated earlier, there are two ebtables tables in the Linux
-kernel. The table names are
-.BR filter " and " nat .
-Of these two tables,
+As stated earlier, the table names are
+.BR filter ", " nat " and " broute .
+Of these tables,
the filter table is the default table that the command operates on.
-If you are working with the filter table, then you can drop the '-t filter'
-argument to the ebtables command. However, you will need to provide
-the -t argument for
-.B nat
-table. Moreover, the -t argument must be the
-first argument on the ebtables command line, if used.
+If you are working with a table other than filter, you will need to provide
+the -t argument. Moreover, the -t argument must be the
+first argument on the ebtables command line, if used.
.TP
.B "-t, --table"
.br
@@ -131,6 +130,23 @@ iptables world to ebtables it is easier to have the same names. Note that you
can change the name
.BR "" ( -E )
if you don't like the default.
+.br
+.br
+.B broute
+is used to make a brouter, it has one built-in chain:
+.BR BROUTING .
+The targets
+.BR DROP " and " ACCEPT
+have a special meaning in the broute table (these names are used for
+compatibility reasons with ebtables-legacy).
+.B DROP
+actually means the frame has to be routed, while
+.B ACCEPT
+means the frame has to be bridged. The
+.B BROUTING
+chain is traversed very early.
+Normally those frames
+would be bridged, but you can decide otherwise here.
.SH EBTABLES COMMAND LINE ARGUMENTS
After the initial ebtables '-t table' command line argument, the remaining
arguments can be divided into several groups. These groups
@@ -305,7 +321,7 @@ of the ebtables kernel table.
.TP
.B "--init-table"
Replace the current table data by the initial table data.
-.SS MISCELLANOUS COMMANDS
+.SS MISCELLANEOUS COMMANDS
.TP
.B "-v, --verbose"
Verbose mode.
@@ -356,7 +372,7 @@ and the
.BR "WATCHER EXTENSIONS"
below.
.TP
-.BR "-p, --protocol " "[!] \fIprotocol\fP"
+.RB [ ! ] " -p" , " --protocol " \fIprotocol\fP
The protocol that was responsible for creating the frame. This can be a
hexadecimal number, above
.IR 0x0600 ,
@@ -386,7 +402,7 @@ See that file for more information. The flag
.B --proto
is an alias for this option.
.TP
-.BR "-i, --in-interface " "[!] \fIname\fP"
+.RB [ ! ] " -i" , " --in-interface " \fIname\fP
The interface (bridge port) via which a frame is received (this option is useful in the
.BR INPUT ,
.BR FORWARD ,
@@ -397,7 +413,7 @@ The flag
.B --in-if
is an alias for this option.
.TP
-.BR "--logical-in " "[!] \fIname\fP"
+.RB [ ! ] " --logical-in " \fIname\fP
The (logical) bridge interface via which a frame is received (this option is useful in the
.BR INPUT ,
.BR FORWARD ,
@@ -406,7 +422,7 @@ chains).
If the interface name ends with '+', then
any interface name that begins with this name (disregarding '+') will match.
.TP
-.BR "-o, --out-interface " "[!] \fIname\fP"
+.RB [ ! ] " -o" , " --out-interface " \fIname\fP
The interface (bridge port) via which a frame is going to be sent (this option is useful in the
.BR OUTPUT ,
.B FORWARD
@@ -418,7 +434,7 @@ The flag
.B --out-if
is an alias for this option.
.TP
-.BR "--logical-out " "[!] \fIname\fP"
+.RB [ ! ] " --logical-out " \fIname\fP
The (logical) bridge interface via which a frame is going to be sent (this option
is useful in the
.BR OUTPUT ,
@@ -429,7 +445,7 @@ chains).
If the interface name ends with '+', then
any interface name that begins with this name (disregarding '+') will match.
.TP
-.BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]"
+.RB [ ! ] " -s" , " --source " \fIaddress\fP[ / \fImask\fP]
The source MAC address. Both mask and address are written as 6 hexadecimal
numbers separated by colons. Alternatively one can specify Unicast,
Multicast, Broadcast or BGA (Bridge Group Address):
@@ -443,7 +459,7 @@ address will also match the multicast specification. The flag
.B --src
is an alias for this option.
.TP
-.BR "-d, --destination " "[!] \fIaddress\fP[/\fImask\fP]"
+.RB [ ! ] " -d" , " --destination " \fIaddress\fP[ / \fImask\fP]
The destination MAC address. See
.B -s
(above) for more details on MAC addresses. The flag
@@ -468,11 +484,11 @@ the core ebtables code.
Specify 802.3 DSAP/SSAP fields or SNAP type. The protocol must be specified as
.IR "LENGTH " "(see the option " " -p " above).
.TP
-.BR "--802_3-sap " "[!] \fIsap\fP"
+.RB [ ! ] " --802_3-sap " \fIsap\fP
DSAP and SSAP are two one byte 802.3 fields. The bytes are always
equal, so only one byte (hexadecimal) is needed as an argument.
.TP
-.BR "--802_3-type " "[!] \fItype\fP"
+.RB [ ! ] " --802_3-type " \fItype\fP
If the 802.3 DSAP and SSAP values are 0xaa then the SNAP type field must
be consulted to determine the payload protocol. This is a two byte
(hexadecimal) argument. Only 802.3 frames with DSAP/SSAP 0xaa are
@@ -487,88 +503,88 @@ the MAC address is optional. Multiple MAC/IP address pairs with the same MAC add
but different IP address (and vice versa) can be specified. If the MAC address doesn't
match any entry from the list, the frame doesn't match the rule (unless "!" was used).
.TP
-.BR "--among-dst " "[!] \fIlist\fP"
+.RB [ ! ] " --among-dst " \fIlist\fP
Compare the MAC destination to the given list. If the Ethernet frame has type
.IR IPv4 " or " ARP ,
then comparison with MAC/IP destination address pairs from the
list is possible.
.TP
-.BR "--among-src " "[!] \fIlist\fP"
+.RB [ ! ] " --among-src " \fIlist\fP
Compare the MAC source to the given list. If the Ethernet frame has type
.IR IPv4 " or " ARP ,
then comparison with MAC/IP source address pairs from the list
is possible.
.TP
-.BR "--among-dst-file " "[!] \fIfile\fP"
+.RB [ ! ] " --among-dst-file " \fIfile\fP
Same as
.BR --among-dst " but the list is read in from the specified file."
.TP
-.BR "--among-src-file " "[!] \fIfile\fP"
+.RB [ ! ] " --among-src-file " \fIfile\fP
Same as
.BR --among-src " but the list is read in from the specified file."
.SS arp
Specify (R)ARP fields. The protocol must be specified as
.IR ARP " or " RARP .
.TP
-.BR "--arp-opcode " "[!] \fIopcode\fP"
+.RB [ ! ] " --arp-opcode " \fIopcode\fP
The (R)ARP opcode (decimal or a string, for more details see
.BR "ebtables -h arp" ).
.TP
-.BR "--arp-htype " "[!] \fIhardware type\fP"
+.RB [ ! ] " --arp-htype " \fIhardware-type\fP
The hardware type, this can be a decimal or the string
.I Ethernet
(which sets
.I type
to 1). Most (R)ARP packets have Eternet as hardware type.
.TP
-.BR "--arp-ptype " "[!] \fIprotocol type\fP"
+.RB [ ! ] " --arp-ptype " \fIprotocol-type\fP
The protocol type for which the (r)arp is used (hexadecimal or the string
.IR IPv4 ,
denoting 0x0800).
Most (R)ARP packets have protocol type IPv4.
.TP
-.BR "--arp-ip-src " "[!] \fIaddress\fP[/\fImask\fP]"
+.RB [ ! ] " --arp-ip-src " \fIaddress\fP[ / \fImask\fP]
The (R)ARP IP source address specification.
.TP
-.BR "--arp-ip-dst " "[!] \fIaddress\fP[/\fImask\fP]"
+.RB [ ! ] " --arp-ip-dst " \fIaddress\fP[ / \fImask\fP]
The (R)ARP IP destination address specification.
.TP
-.BR "--arp-mac-src " "[!] \fIaddress\fP[/\fImask\fP]"
+.RB [ ! ] " --arp-mac-src " \fIaddress\fP[ / \fImask\fP]
The (R)ARP MAC source address specification.
.TP
-.BR "--arp-mac-dst " "[!] \fIaddress\fP[/\fImask\fP]"
+.RB [ ! ] " --arp-mac-dst " \fIaddress\fP[ / \fImask\fP]
The (R)ARP MAC destination address specification.
.TP
-.BR "" "[!]" " --arp-gratuitous"
+.RB [ ! ] " --arp-gratuitous"
Checks for ARP gratuitous packets: checks equality of IPv4 source
address and IPv4 destination address inside the ARP header.
.SS ip
Specify IPv4 fields. The protocol must be specified as
.IR IPv4 .
.TP
-.BR "--ip-source " "[!] \fIaddress\fP[/\fImask\fP]"
+.RB [ ! ] " --ip-source " \fIaddress\fP[ / \fImask\fP]
The source IP address.
The flag
.B --ip-src
is an alias for this option.
.TP
-.BR "--ip-destination " "[!] \fIaddress\fP[/\fImask\fP]"
+.RB [ ! ] " --ip-destination " \fIaddress\fP[ / \fImask\fP]
The destination IP address.
The flag
.B --ip-dst
is an alias for this option.
.TP
-.BR "--ip-tos " "[!] \fItos\fP"
+.RB [ ! ] " --ip-tos " \fItos\fP
The IP type of service, in hexadecimal numbers.
.BR IPv4 .
.TP
-.BR "--ip-protocol " "[!] \fIprotocol\fP"
+.RB [ ! ] " --ip-protocol " \fIprotocol\fP
The IP protocol.
The flag
.B --ip-proto
is an alias for this option.
.TP
-.BR "--ip-source-port " "[!] \fIport1\fP[:\fIport2\fP]"
+.RB [ ! ] " --ip-source-port " \fIport1\fP[ : \fIport2\fP]
The source port or port range for the IP protocols 6 (TCP), 17
(UDP), 33 (DCCP) or 132 (SCTP). The
.B --ip-protocol
@@ -580,7 +596,7 @@ The flag
.B --ip-sport
is an alias for this option.
.TP
-.BR "--ip-destination-port " "[!] \fIport1\fP[:\fIport2\fP]"
+.RB [ ! ] " --ip-destination-port " \fIport1\fP[ : \fIport2\fP]
The destination port or port range for ip protocols 6 (TCP), 17
(UDP), 33 (DCCP) or 132 (SCTP). The
.B --ip-protocol
@@ -595,28 +611,28 @@ is an alias for this option.
Specify IPv6 fields. The protocol must be specified as
.IR IPv6 .
.TP
-.BR "--ip6-source " "[!] \fIaddress\fP[/\fImask\fP]"
+.RB [ ! ] " --ip6-source " \fIaddress\fP[ / \fImask\fP]
The source IPv6 address.
The flag
.B --ip6-src
is an alias for this option.
.TP
-.BR "--ip6-destination " "[!] \fIaddress\fP[/\fImask\fP]"
+.RB [ ! ] " --ip6-destination " \fIaddress\fP[ / \fImask\fP]
The destination IPv6 address.
The flag
.B --ip6-dst
is an alias for this option.
.TP
-.BR "--ip6-tclass " "[!] \fItclass\fP"
+.RB [ ! ] " --ip6-tclass " \fItclass\fP
The IPv6 traffic class, in hexadecimal numbers.
.TP
-.BR "--ip6-protocol " "[!] \fIprotocol\fP"
+.RB [ ! ] " --ip6-protocol " \fIprotocol\fP
The IP protocol.
The flag
.B --ip6-proto
is an alias for this option.
.TP
-.BR "--ip6-source-port " "[!] \fIport1\fP[:\fIport2\fP]"
+.RB [ ! ] " --ip6-source-port " \fIport1\fP[ : \fIport2\fP]
The source port or port range for the IPv6 protocols 6 (TCP), 17
(UDP), 33 (DCCP) or 132 (SCTP). The
.B --ip6-protocol
@@ -628,7 +644,7 @@ The flag
.B --ip6-sport
is an alias for this option.
.TP
-.BR "--ip6-destination-port " "[!] \fIport1\fP[:\fIport2\fP]"
+.RB [ ! ] " --ip6-destination-port " \fIport1\fP[ : \fIport2\fP]
The destination port or port range for IPv6 protocols 6 (TCP), 17
(UDP), 33 (DCCP) or 132 (SCTP). The
.B --ip6-protocol
@@ -640,7 +656,7 @@ The flag
.B --ip6-dport
is an alias for this option.
.TP
-.BR "--ip6-icmp-type " "[!] {\fItype\fP[:\fItype\fP]/\fIcode\fP[:\fIcode\fP]|\fItypename\fP}"
+.RB [ ! ] " --ip6-icmp-type " {\fItype\fP[ : \fItype\fP] / \fIcode\fP[ : \fIcode\fP]|\fItypename\fP}
Specify ipv6\-icmp type and code to match.
Ranges for both type and code are supported. Type and code are
separated by a slash. Valid numbers for type and range are 0 to 255.
@@ -669,7 +685,7 @@ number; the default is
.IR 5 .
.SS mark_m
.TP
-.BR "--mark " "[!] [\fIvalue\fP][/\fImask\fP]"
+.RB [ ! ] " --mark " [\fIvalue\fP][ / \fImask\fP]
Matches frames with the given unsigned mark value. If a
.IR value " and " mask " are specified, the logical AND of the mark value of the frame and"
the user-specified
@@ -688,7 +704,7 @@ non-zero. Only specifying a
.IR mask " is useful to match multiple mark values."
.SS pkttype
.TP
-.BR "--pkttype-type " "[!] \fItype\fP"
+.RB [ ! ] " --pkttype-type " \fItype\fP
Matches on the Ethernet "class" of the frame, which is determined by the
generic networking code. Possible values:
.IR broadcast " (MAC destination is the broadcast address),"
@@ -705,47 +721,47 @@ if the lower bound is omitted (but the colon is not), then the lowest possible l
for that option is used, while if the upper bound is omitted (but the colon again is not), the
highest possible upper bound for that option is used.
.TP
-.BR "--stp-type " "[!] \fItype\fP"
-The BPDU type (0-255), recognized non-numerical types are
+.RB [ ! ] " --stp-type " \fItype\fP
+The BPDU type (0\(en255), recognized non-numerical types are
.IR config ", denoting a configuration BPDU (=0), and"
.IR tcn ", denothing a topology change notification BPDU (=128)."
.TP
-.BR "--stp-flags " "[!] \fIflag\fP"
-The BPDU flag (0-255), recognized non-numerical flags are
+.RB [ ! ] " --stp-flags " \fIflag\fP
+The BPDU flag (0\(en255), recognized non-numerical flags are
.IR topology-change ", denoting the topology change flag (=1), and"
.IR topology-change-ack ", denoting the topology change acknowledgement flag (=128)."
.TP
-.BR "--stp-root-prio " "[!] [\fIprio\fP][:\fIprio\fP]"
-The root priority (0-65535) range.
+.RB [ ! ] " --stp-root-prio " [\fIprio\fP][ : \fIprio\fP]
+The root priority (0\(en65535) range.
.TP
-.BR "--stp-root-addr " "[!] [\fIaddress\fP][/\fImask\fP]"
+.RB [ ! ] " --stp-root-addr " [\fIaddress\fP][ / \fImask\fP]
The root mac address, see the option
.BR -s " for more details."
.TP
-.BR "--stp-root-cost " "[!] [\fIcost\fP][:\fIcost\fP]"
-The root path cost (0-4294967295) range.
+.RB [ ! ] " --stp-root-cost " [\fIcost\fP][ : \fIcost\fP]
+The root path cost (0\(en4294967295) range.
.TP
-.BR "--stp-sender-prio " "[!] [\fIprio\fP][:\fIprio\fP]"
-The BPDU's sender priority (0-65535) range.
+.RB [ ! ] " --stp-sender-prio " [\fIprio\fP][ : \fIprio\fP]
+The BPDU's sender priority (0\(en65535) range.
.TP
-.BR "--stp-sender-addr " "[!] [\fIaddress\fP][/\fImask\fP]"
+.RB [ ! ] " --stp-sender-addr " [\fIaddress\fP][ / \fImask\fP]
The BPDU's sender mac address, see the option
.BR -s " for more details."
.TP
-.BR "--stp-port " "[!] [\fIport\fP][:\fIport\fP]"
-The port identifier (0-65535) range.
+.RB [ ! ] " --stp-port " [\fIport\fP][ : \fIport\fP]
+The port identifier (0\(en65535) range.
.TP
-.BR "--stp-msg-age " "[!] [\fIage\fP][:\fIage\fP]"
-The message age timer (0-65535) range.
+.RB [ ! ] " --stp-msg-age " [\fIage\fP][ : \fIage\fP]
+The message age timer (0\(en65535) range.
.TP
-.BR "--stp-max-age " "[!] [\fIage\fP][:\fIage\fP]"
-The max age timer (0-65535) range.
+.RB [ ! ] " --stp-max-age " [\fIage\fP][ : \fIage\fP]
+The max age timer (0\(en65535) range.
.TP
-.BR "--stp-hello-time " "[!] [\fItime\fP][:\fItime\fP]"
-The hello time timer (0-65535) range.
+.RB [ ! ] " --stp-hello-time " [\fItime\fP][ : \fItime\fP]
+The hello time timer (0\(en65535) range.
.TP
-.BR "--stp-forward-delay " "[!] [\fIdelay\fP][:\fIdelay\fP]"
-The forward delay timer (0-65535) range.
+.RB [ ! ] " --stp-forward-delay " [\fIdelay\fP][ : \fIdelay\fP]
+The forward delay timer (0\(en65535) range.
.\" .SS string
.\" This module matches on a given string using some pattern matching strategy.
.\" .TP
@@ -758,10 +774,10 @@ The forward delay timer (0-65535) range.
.\" .BR "--string-to " "\fIoffset\fP"
.\" The highest offset from which a match can start. (default: size of frame)
.\" .TP
-.\" .BR "--string " "[!] \fIpattern\fP"
+.\" .RB [ ! ] " --string " \fIpattern\fP
.\" Matches the given pattern.
.\" .TP
-.\" .BR "--string-hex " "[!] \fIpattern\fP"
+.\" .RB [ ! ] " --string-hex " \fIpattern\fP
.\" Matches the given pattern in hex notation, e.g. '|0D 0A|', '|0D0A|', 'www|09|netfilter|03|org|00|'
.\" .TP
.\" .BR "--string-icase"
@@ -771,15 +787,15 @@ Specify 802.1Q Tag Control Information fields.
The protocol must be specified as
.IR 802_1Q " (0x8100)."
.TP
-.BR "--vlan-id " "[!] \fIid\fP"
+.RB [ ! ] " --vlan-id " \fIid\fP
The VLAN identifier field (VID). Decimal number from 0 to 4095.
.TP
-.BR "--vlan-prio " "[!] \fIprio\fP"
+.RB [ ! ] " --vlan-prio " \fIprio\fP
The user priority field, a decimal number from 0 to 7.
The VID should be set to 0 ("null VID") or unspecified
(in the latter case the VID is deliberately set to 0).
.TP
-.BR "--vlan-encap " "[!] \fItype\fP"
+.RB [ ! ] " --vlan-encap " \fItype\fP
The encapsulated Ethernet frame type/length.
Specified as a hexadecimal
number from 0x0000 to 0xFFFF or as a symbolic name
@@ -796,7 +812,7 @@ The log watcher writes descriptive data about a frame to the syslog.
.TP
.B "--log"
.br
-Log with the default loggin options: log-level=
+Log with the default logging options: log-level=
.IR info ,
log-prefix="", no ip logging, no arp logging.
.TP
@@ -842,7 +858,7 @@ Log with the default logging options
.TP
.B --nflog-group "\fInlgroup\fP"
.br
-The netlink group (1 - 2^32-1) to which packets are (only applicable for
+The netlink group (1\(en2\(ha32\-1) to which packets are (only applicable for
nfnetlink_log). The default value is 1.
.TP
.B --nflog-prefix "\fIprefix\fP"
@@ -1059,8 +1075,6 @@ arp message and the hardware address length in the arp header is 6 bytes.
.BR "" "See " http://netfilter.org/mailinglists.html
.SH BUGS
The version of ebtables this man page ships with does not support the
-.B broute
-table. Also there is no support for
.B string
match. Further, support for atomic-options
.RB ( --atomic-file ", " --atomic-init ", " --atomic-save ", " --atomic-commit )
diff --git a/iptables/ip6tables-apply.8 b/iptables/ip6tables-apply.8
deleted file mode 100644
index 994b487a..00000000
--- a/iptables/ip6tables-apply.8
+++ /dev/null
@@ -1 +0,0 @@
-.so man8/iptables-apply.8
diff --git a/iptables/ip6tables-restore.8 b/iptables/ip6tables-restore.8
deleted file mode 100644
index cf4ea3e7..00000000
--- a/iptables/ip6tables-restore.8
+++ /dev/null
@@ -1 +0,0 @@
-.so man8/iptables-restore.8
diff --git a/iptables/ip6tables-save.8 b/iptables/ip6tables-save.8
deleted file mode 100644
index 182f55c1..00000000
--- a/iptables/ip6tables-save.8
+++ /dev/null
@@ -1 +0,0 @@
-.so man8/iptables-save.8
diff --git a/iptables/ip6tables.8 b/iptables/ip6tables.8
deleted file mode 100644
index 0dee41ad..00000000
--- a/iptables/ip6tables.8
+++ /dev/null
@@ -1 +0,0 @@
-.so man8/iptables.8
diff --git a/iptables/ip6tables.c b/iptables/ip6tables.c
index 75984cc1..f9ae18ae 100644
--- a/iptables/ip6tables.c
+++ b/iptables/ip6tables.c
@@ -122,6 +122,9 @@ print_match(const struct xt_entry_match *m,
printf("%s%s ", match->name, unsupported_rev);
else
printf("%s ", match->name);
+
+ if (match->next == match)
+ free(match);
} else {
if (name[0])
printf("UNKNOWN match `%s' ", name);
@@ -179,6 +182,9 @@ print_firewall(const struct ip6t_entry *fw,
tg->print(&fw->ipv6, t, format & FMT_NUMERIC);
else if (target->print)
printf(" %s%s", target->name, unsupported_rev);
+
+ if (target->next == target)
+ free(target);
} else if (t->u.target_size != sizeof(*t))
printf("[%u bytes of unknown target data] ",
(unsigned int)(t->u.target_size - sizeof(*t)));
@@ -277,40 +283,6 @@ insert_entry(const xt_chainlabel chain,
return ret;
}
-static unsigned char *
-make_delete_mask(const struct xtables_rule_match *matches,
- const struct xtables_target *target)
-{
- /* Establish mask for comparison */
- unsigned int size;
- const struct xtables_rule_match *matchp;
- unsigned char *mask, *mptr;
-
- size = sizeof(struct ip6t_entry);
- for (matchp = matches; matchp; matchp = matchp->next)
- size += XT_ALIGN(sizeof(struct xt_entry_match)) + matchp->match->size;
-
- mask = xtables_calloc(1, size
- + XT_ALIGN(sizeof(struct xt_entry_target))
- + target->size);
-
- memset(mask, 0xFF, sizeof(struct ip6t_entry));
- mptr = mask + sizeof(struct ip6t_entry);
-
- for (matchp = matches; matchp; matchp = matchp->next) {
- memset(mptr, 0xFF,
- XT_ALIGN(sizeof(struct xt_entry_match))
- + matchp->match->userspacesize);
- mptr += XT_ALIGN(sizeof(struct xt_entry_match)) + matchp->match->size;
- }
-
- memset(mptr, 0xFF,
- XT_ALIGN(sizeof(struct xt_entry_target))
- + target->userspacesize);
-
- return mask;
-}
-
static int
delete_entry(const xt_chainlabel chain,
struct ip6t_entry *fw,
@@ -329,7 +301,7 @@ delete_entry(const xt_chainlabel chain,
int ret = 1;
unsigned char *mask;
- mask = make_delete_mask(matches, target);
+ mask = make_delete_mask(matches, target, sizeof(*fw));
for (i = 0; i < nsaddrs; i++) {
fw->ipv6.src = saddrs[i];
fw->ipv6.smsk = smasks[i];
@@ -359,7 +331,7 @@ check_entry(const xt_chainlabel chain, struct ip6t_entry *fw,
int ret = 1;
unsigned char *mask;
- mask = make_delete_mask(matches, target);
+ mask = make_delete_mask(matches, target, sizeof(*fw));
for (i = 0; i < nsaddrs; i++) {
fw->ipv6.src = saddrs[i];
fw->ipv6.smsk = smasks[i];
@@ -537,8 +509,7 @@ void print_rule6(const struct ip6t_entry *e,
save_ipv6_addr('d', &e->ipv6.dst, &e->ipv6.dmsk,
e->ipv6.invflags & IP6T_INV_DSTIP);
- save_rule_details(e->ipv6.iniface, e->ipv6.iniface_mask,
- e->ipv6.outiface, e->ipv6.outiface_mask,
+ save_rule_details(e->ipv6.iniface, e->ipv6.outiface,
e->ipv6.proto, 0, e->ipv6.invflags);
#if 0
@@ -697,6 +668,10 @@ int do_command6(int argc, char *argv[], char **table,
struct xt_cmd_parse_ops cmd_parse_ops = {
.proto_parse = ipv6_proto_parse,
.post_parse = ipv6_post_parse,
+ .option_name = ip46t_option_name,
+ .option_invert = ip46t_option_invert,
+ .command_default = command_default,
+ .print_help = xtables_printhelp,
};
struct xt_cmd_parse p = {
.table = *table,
@@ -740,6 +715,9 @@ int do_command6(int argc, char *argv[], char **table,
smasks = args.s.mask.v6;
dmasks = args.d.mask.v6;
+ iface_to_mask(cs.fw6.ipv6.iniface, cs.fw6.ipv6.iniface_mask);
+ iface_to_mask(cs.fw6.ipv6.outiface, cs.fw6.ipv6.outiface_mask);
+
/* Attempt to acquire the xtables lock */
if (!restore)
xtables_lock_or_exit(wait);
@@ -800,13 +778,12 @@ int do_command6(int argc, char *argv[], char **table,
#ifdef IP6T_F_GOTO
if (cs.fw6.ipv6.flags & IP6T_F_GOTO)
xtables_error(PARAMETER_PROBLEM,
- "goto '%s' is not a chain\n",
- cs.jumpto);
+ "goto '%s' is not a chain",
+ cs.jumpto);
#endif
xtables_find_target(cs.jumpto, XTF_LOAD_MUST_SUCCEED);
} else {
e = generate_entry(&cs.fw6, cs.matches, cs.target->t);
- free(cs.target->t);
}
}
@@ -908,17 +885,14 @@ int do_command6(int argc, char *argv[], char **table,
if (verbose > 1)
dump_entries6(*handle);
- xtables_rule_matches_free(&cs.matches);
+ xtables_clear_iptables_command_state(&cs);
if (e != NULL) {
free(e);
e = NULL;
}
- free(saddrs);
- free(smasks);
- free(daddrs);
- free(dmasks);
+ xtables_clear_args(&args);
xtables_free_opts(1);
return ret;
diff --git a/iptables/iptables-apply b/iptables/iptables-apply
index 3a7df5e3..c603fb21 100755
--- a/iptables/iptables-apply
+++ b/iptables/iptables-apply
@@ -141,9 +141,9 @@ for opt in $OPTS; do
;;
(*)
case "${OPT_STATE:-}" in
- (SET_TIMEOUT) eval TIMEOUT=$opt;;
+ (SET_TIMEOUT) eval TIMEOUT="$opt";;
(SET_SAVEFILE)
- eval SAVEFILE=$opt
+ eval SAVEFILE="$opt"
[ -z "$SAVEFILE" ] && SAVEFILE="$DEF_SAVEFILE"
;;
esac
@@ -163,13 +163,13 @@ done
# Validate parameters
if [ "$TIMEOUT" -ge 0 ] 2>/dev/null; then
- TIMEOUT=$(($TIMEOUT))
+ TIMEOUT=$((TIMEOUT))
else
echo "Error: timeout must be a positive number" >&2
exit 1
fi
-if [ -n "$SAVEFILE" -a -e "$SAVEFILE" -a ! -w "$SAVEFILE" ]; then
+if [ -n "$SAVEFILE" ] && [ -e "$SAVEFILE" ] && [ ! -w "$SAVEFILE" ]; then
echo "Error: savefile not writable: $SAVEFILE" >&2
exit 8
fi
@@ -205,8 +205,8 @@ esac
### Begin work
# Store old iptables rules to temporary file
-TMPFILE=`mktemp /tmp/$PROGNAME-XXXXXXXX`
-trap "rm -f $TMPFILE" EXIT HUP INT QUIT ILL TRAP ABRT BUS \
+TMPFILE=$(mktemp "/tmp/$PROGNAME-XXXXXXXX")
+trap 'rm -f $TMPFILE' EXIT HUP INT QUIT ILL TRAP ABRT BUS \
FPE USR1 SEGV USR2 PIPE ALRM TERM
if ! "$SAVE" >"$TMPFILE"; then
@@ -257,13 +257,13 @@ esac
# Prompt user for confirmation
echo -n "Can you establish NEW connections to the machine? (y/N) "
-read -n1 -t "$TIMEOUT" ret 2>&1 || :
+read -r -n1 -t "$TIMEOUT" ret 2>&1 || :
case "${ret:-}" in
(y*|Y*)
# Success
echo
- if [ ! -z "$SAVEFILE" ]; then
+ if [ -n "$SAVEFILE" ]; then
# Write successfully applied rules to the savefile
echo "Writing successfully applied rules to '$SAVEFILE'..."
if ! "$SAVE" >"$SAVEFILE"; then
diff --git a/iptables/iptables-apply.8.in b/iptables/iptables-apply.8.in
index f0ed4e5f..33fd79fe 100644
--- a/iptables/iptables-apply.8.in
+++ b/iptables/iptables-apply.8.in
@@ -3,10 +3,8 @@
.\" Date: May 10, 2010
.\"
.TH IPTABLES\-APPLY 8 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@"
-.\" disable hyphenation
-.nh
.SH NAME
-iptables-apply \- a safer way to update iptables remotely
+iptables-apply \(em a safer way to update iptables remotely
.SH SYNOPSIS
\fBiptables\-apply\fP [\-\fBhV\fP] [\fB-t\fP \fItimeout\fP] [\fB-w\fP \fIsavefile\fP] {[\fIrulesfile]|-c [runcmd]}\fP
.SH "DESCRIPTION"
diff --git a/iptables/iptables-restore.8.in b/iptables/iptables-restore.8.in
index 20216842..aa816f79 100644
--- a/iptables/iptables-restore.8.in
+++ b/iptables/iptables-restore.8.in
@@ -23,13 +23,13 @@ iptables-restore \(em Restore IP Tables
.P
ip6tables-restore \(em Restore IPv6 Tables
.SH SYNOPSIS
-\fBiptables\-restore\fP [\fB\-chntvV\fP] [\fB\-w\fP \fIsecs\fP]
-[\fB\-W\fP \fIusecs\fP] [\fB\-M\fP \fImodprobe\fP] [\fB\-T\fP \fIname\fP]
-[\fBfile\fP]
+\fBiptables\-restore\fP [\fB\-chntvV\fP] [\fB\-w\fP \fIseconds\fP]
+[\fB\-M\fP \fImodprobe\fP] [\fB\-T\fP \fIname\fP]
+[\fIfile\fP]
.P
-\fBip6tables\-restore\fP [\fB\-chntvV\fP] [\fB\-w\fP \fIsecs\fP]
-[\fB\-W\fP \fIusecs\fP] [\fB\-M\fP \fImodprobe\fP] [\fB\-T\fP \fIname\fP]
-[\fBfile\fP]
+\fBip6tables\-restore\fP [\fB\-chntvV\fP] [\fB\-w\fP \fIseconds\fP]
+[\fB\-M\fP \fImodprobe\fP] [\fB\-T\fP \fIname\fP]
+[\fIfile\fP]
.SH DESCRIPTION
.PP
.B iptables-restore
@@ -40,13 +40,13 @@ are used to restore IP and IPv6 Tables from data specified on STDIN or in
specify \fIfile\fP as an argument.
.TP
\fB\-c\fR, \fB\-\-counters\fR
-restore the values of all packet and byte counters
+Restore the values of all packet and byte counters.
.TP
\fB\-h\fP, \fB\-\-help\fP
Print a short option summary.
.TP
\fB\-n\fR, \fB\-\-noflush\fR
-don't flush the previous contents of the table. If not specified,
+Don't flush the previous contents of the table. If not specified,
both commands flush (delete) all previous contents of the respective table.
.TP
\fB\-t\fP, \fB\-\-test\fP
@@ -67,9 +67,10 @@ the program will exit if the lock cannot be obtained. This option will
make the program wait (indefinitely or for optional \fIseconds\fP) until
the exclusive lock can be obtained.
.TP
-\fB\-M\fP, \fB\-\-modprobe\fP \fImodprobe_program\fP
-Specify the path to the modprobe program. By default, iptables-restore will
-inspect /proc/sys/kernel/modprobe to determine the executable's path.
+\fB\-M\fP, \fB\-\-modprobe\fP \fImodprobe\fP
+Specify the path to the modprobe(8) program. By default,
+iptables-restore will inspect \fI/proc/sys/kernel/modprobe\fP to
+determine the executable's path.
.TP
\fB\-T\fP, \fB\-\-table\fP \fIname\fP
Restore only the named table even if the input stream contains other ones.
@@ -81,7 +82,7 @@ from Rusty Russell.
.br
Andras Kis-Szabo <kisza@sch.bme.hu> contributed ip6tables-restore.
.SH SEE ALSO
-\fBiptables\-apply\fP(8),\fBiptables\-save\fP(8), \fBiptables\fP(8)
+\fBiptables\-apply\fP(8), \fBiptables\-save\fP(8), \fBiptables\fP(8)
.PP
The iptables-HOWTO, which details more iptables usage, the NAT-HOWTO,
which details NAT, and the netfilter-hacking-HOWTO which details the
diff --git a/iptables/iptables-restore.c b/iptables/iptables-restore.c
index 4410a587..53029738 100644
--- a/iptables/iptables-restore.c
+++ b/iptables/iptables-restore.c
@@ -78,8 +78,9 @@ create_handle(const struct iptables_restore_cb *cb, const char *tablename)
}
if (!handle)
- xtables_error(PARAMETER_PROBLEM, "%s: unable to initialize "
- "table '%s'\n", xt_params->program_name, tablename);
+ xtables_error(PARAMETER_PROBLEM,
+ "%s: unable to initialize table '%s'",
+ xt_params->program_name, tablename);
return handle;
}
@@ -184,12 +185,12 @@ ip46tables_restore_main(const struct iptables_restore_cb *cb,
if (!testing) {
DEBUGP("Calling commit\n");
ret = cb->ops->commit(handle);
- cb->ops->free(handle);
- handle = NULL;
} else {
DEBUGP("Not calling commit, testing\n");
ret = 1;
}
+ cb->ops->free(handle);
+ handle = NULL;
/* Done with the current table, release the lock. */
if (lock >= 0) {
@@ -209,8 +210,8 @@ ip46tables_restore_main(const struct iptables_restore_cb *cb,
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);
+ "%s: line %u table name invalid",
+ xt_params->program_name, line);
strncpy(curtable, table, XT_TABLE_MAXNAMELEN);
curtable[XT_TABLE_MAXNAMELEN] = '\0';
@@ -222,8 +223,6 @@ ip46tables_restore_main(const struct iptables_restore_cb *cb,
}
continue;
}
- if (handle)
- cb->ops->free(handle);
handle = create_handle(cb, table);
if (noflush == 0) {
@@ -249,8 +248,8 @@ ip46tables_restore_main(const struct iptables_restore_cb *cb,
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);
+ "%s: line %u chain name invalid",
+ xt_params->program_name, line);
if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
xtables_error(PARAMETER_PROBLEM,
@@ -263,16 +262,14 @@ ip46tables_restore_main(const struct iptables_restore_cb *cb,
DEBUGP("Flushing existing user defined chain '%s'\n", chain);
if (!cb->ops->flush_entries(chain, handle))
xtables_error(PARAMETER_PROBLEM,
- "error flushing chain "
- "'%s':%s\n", chain,
- strerror(errno));
+ "error flushing chain '%s':%s",
+ chain, strerror(errno));
} else {
DEBUGP("Creating new chain '%s'\n", chain);
if (!cb->ops->create_chain(chain, handle))
xtables_error(PARAMETER_PROBLEM,
- "error creating chain "
- "'%s':%s\n", chain,
- strerror(errno));
+ "error creating chain '%s':%s",
+ chain, strerror(errno));
}
}
@@ -280,32 +277,29 @@ ip46tables_restore_main(const struct iptables_restore_cb *cb,
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);
+ "%s: line %u policy invalid",
+ xt_params->program_name, line);
if (strcmp(policy, "-") != 0) {
+ char *ctrs = strtok(NULL, " \t\n");
struct xt_counters count = {};
- 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 ((!ctrs && counters) ||
+ (ctrs && !parse_counters(ctrs, &count)))
+ xtables_error(PARAMETER_PROBLEM,
+ "invalid policy counters for chain '%s'",
+ chain);
DEBUGP("Setting policy of chain %s to %s\n",
chain, policy);
- if (!cb->ops->set_policy(chain, policy, &count,
- handle))
+ if (!cb->ops->set_policy(chain, policy,
+ counters ? &count : NULL,
+ handle))
xtables_error(OTHER_PROBLEM,
- "Can't set policy `%s'"
- " on `%s' line %u: %s\n",
- policy, chain, line,
- cb->ops->strerror(errno));
+ "Can't set policy `%s' on `%s' line %u: %s",
+ policy, chain, line,
+ cb->ops->strerror(errno));
}
xtables_announce_chain(chain);
diff --git a/iptables/iptables-save.8.in b/iptables/iptables-save.8.in
index 7683fd37..65c1f28c 100644
--- a/iptables/iptables-save.8.in
+++ b/iptables/iptables-save.8.in
@@ -36,23 +36,27 @@ and
are used to dump the contents of IP or IPv6 Table in easily parseable format
either to STDOUT or to a specified file.
.TP
-\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.
+\fB\-M\fR, \fB\-\-modprobe\fR \fImodprobe\fP
+Specify the path to the modprobe(8) program. By default,
+iptables-save will inspect \fI/proc/sys/kernel/modprobe\fP to determine
+the executable's path.
.TP
\fB\-f\fR, \fB\-\-file\fR \fIfilename\fP
Specify a filename to log the output to. If not specified, iptables-save
will log to STDOUT.
.TP
\fB\-c\fR, \fB\-\-counters\fR
-include the current values of all packet and byte counters in the output
+Include the current values of all packet and byte counters in the output.
.TP
\fB\-t\fR, \fB\-\-table\fR \fItablename\fP
-restrict output to only one table. If the kernel is configured with automatic
+Restrict output to only one table. If the kernel is configured with automatic
module loading, an attempt will be made to load the appropriate module for
that table if it is not already there.
.br
-If not specified, output includes all available tables.
+If not specified, output includes all available tables. No module loading takes
+place, so in order to include a specific table in the output, the respective
+module (something like \fBiptable_mangle\fP or \fBip6table_raw\fP) must be
+loaded first.
.SH BUGS
None known as of iptables-1.2.1 release
.SH AUTHORS
@@ -62,7 +66,7 @@ Rusty Russell <rusty@rustcorp.com.au>
.br
Andras Kis-Szabo <kisza@sch.bme.hu> contributed ip6tables-save.
.SH SEE ALSO
-\fBiptables\-apply\fP(8),\fBiptables\-restore\fP(8), \fBiptables\fP(8)
+\fBiptables\-apply\fP(8), \fBiptables\-restore\fP(8), \fBiptables\fP(8)
.PP
The iptables-HOWTO, which details more iptables usage, the NAT-HOWTO,
which details NAT, and the netfilter-hacking-HOWTO which details the
diff --git a/iptables/iptables-save.c b/iptables/iptables-save.c
index a8dded63..094adf22 100644
--- a/iptables/iptables-save.c
+++ b/iptables/iptables-save.c
@@ -61,8 +61,7 @@ for_each_table(int (*func)(struct iptables_save_cb *cb, const char *tablename),
while (fgets(tablename, sizeof(tablename), procfile)) {
if (tablename[strlen(tablename) - 1] != '\n')
xtables_error(OTHER_PROBLEM,
- "Badly formed tablename `%s'\n",
- tablename);
+ "Badly formed tablename `%s'", tablename);
tablename[strlen(tablename) - 1] = '\0';
ret &= func(cb, tablename);
}
@@ -85,7 +84,7 @@ static int do_output(struct iptables_save_cb *cb, const char *tablename)
h = cb->ops->init(tablename);
}
if (!h)
- xtables_error(OTHER_PROBLEM, "Cannot initialize: %s\n",
+ xtables_error(OTHER_PROBLEM, "Cannot initialize: %s",
cb->ops->strerror(errno));
time_t now = time(NULL);
diff --git a/iptables/iptables-xml.c b/iptables/iptables-xml.c
index 6cf059fb..396c0a12 100644
--- a/iptables/iptables-xml.c
+++ b/iptables/iptables-xml.c
@@ -210,8 +210,8 @@ saveChain(char *chain, char *policy, struct xt_counters *ctr)
{
if (nextChain >= maxChains)
xtables_error(PARAMETER_PROBLEM,
- "%s: line %u chain name invalid\n",
- prog_name, line);
+ "%s: line %u chain name invalid",
+ prog_name, line);
chains[nextChain].chain = xtables_strdup(chain);
chains[nextChain].policy = xtables_strdup(policy);
@@ -225,13 +225,13 @@ finishChains(void)
{
int c;
- for (c = 0; c < nextChain; c++)
- if (!chains[c].created) {
+ for (c = 0; c < nextChain; c++) {
+ if (!chains[c].created)
openChain(chains[c].chain, chains[c].policy,
&(chains[c].count), '/');
- free(chains[c].chain);
- free(chains[c].policy);
- }
+ free(chains[c].chain);
+ free(chains[c].policy);
+ }
nextChain = 0;
}
@@ -610,8 +610,8 @@ iptables_xml_main(int argc, char *argv[])
DEBUGP("line %u, table '%s'\n", line, table);
if (!table)
xtables_error(PARAMETER_PROBLEM,
- "%s: line %u table name invalid\n",
- prog_name, line);
+ "%s: line %u table name invalid",
+ prog_name, line);
openTable(table);
@@ -626,8 +626,8 @@ iptables_xml_main(int argc, char *argv[])
DEBUGP("line %u, chain '%s'\n", line, chain);
if (!chain)
xtables_error(PARAMETER_PROBLEM,
- "%s: line %u chain name invalid\n",
- prog_name, line);
+ "%s: line %u chain name invalid",
+ prog_name, line);
DEBUGP("Creating new chain '%s'\n", chain);
@@ -635,8 +635,8 @@ iptables_xml_main(int argc, char *argv[])
DEBUGP("line %u, policy '%s'\n", line, policy);
if (!policy)
xtables_error(PARAMETER_PROBLEM,
- "%s: line %u policy invalid\n",
- prog_name, line);
+ "%s: line %u policy invalid",
+ prog_name, line);
ctrs = strtok(NULL, " \t\n");
parse_counters(ctrs, &count);
diff --git a/iptables/iptables.8.in b/iptables/iptables.8.in
index f81c632f..21fb891d 100644
--- a/iptables/iptables.8.in
+++ b/iptables/iptables.8.in
@@ -45,15 +45,15 @@ iptables/ip6tables \(em administration tool for IPv4/IPv6 packet filtering and N
.PP
\fBiptables\fP [\fB\-t\fP \fItable\fP] \fB\-X\fP [\fIchain\fP]
.PP
-\fBiptables\fP [\fB\-t\fP \fItable\fP] \fB\-P\fP \fIchain target\fP
+\fBiptables\fP [\fB\-t\fP \fItable\fP] \fB\-P\fP \fIchain policy\fP
.PP
\fBiptables\fP [\fB\-t\fP \fItable\fP] \fB\-E\fP \fIold-chain-name new-chain-name\fP
.PP
-rule-specification = [\fImatches...\fP] [\fItarget\fP]
+rule-specification := [matches...] [target]
.PP
-match = \fB\-m\fP \fImatchname\fP [\fIper-match-options\fP]
+match := \fB\-m\fP \fImatchname\fP [per-match-options]
.PP
-target = \fB\-j\fP \fItargetname\fP [\fIper\-target\-options\fP]
+target := \fB\-j\fP \fItargetname\fP [per-target-options]
.SH DESCRIPTION
\fBIptables\fP and \fBip6tables\fP are used to set up, maintain, and inspect the
tables of IPv4 and IPv6 packet
@@ -125,8 +125,8 @@ This table is used mainly for configuring exemptions from connection
tracking in combination with the NOTRACK target. It registers at the netfilter
hooks with higher priority and is thus called before ip_conntrack, or any other
IP tables. It provides the following built-in chains: \fBPREROUTING\fP
-(for packets arriving via any network interface) \fBOUTPUT\fP
-(for packets generated by local processes)
+(for packets arriving via any network interface) and \fBOUTPUT\fP
+(for packets generated by local processes).
.TP
\fBsecurity\fP:
This table is used for Mandatory Access Control (MAC) networking rules, such
@@ -244,13 +244,13 @@ add, delete, insert, replace and append commands).
\fB\-4\fP, \fB\-\-ipv4\fP
This option has no effect in iptables and iptables-restore.
If a rule using the \fB\-4\fP option is inserted with (and only with)
-ip6tables-restore, it will be silently ignored. Any other uses will throw an
+\fBip6tables\-restore\fP, it will be silently ignored. Any other uses will throw an
error. This option allows IPv4 and IPv6 rules in a single rule file
for use with both iptables-restore and ip6tables-restore.
.TP
\fB\-6\fP, \fB\-\-ipv6\fP
If a rule using the \fB\-6\fP option is inserted with (and only with)
-iptables-restore, it will be silently ignored. Any other uses will throw an
+\fBiptables\-restore\fP, it will be silently ignored. Any other uses will throw an
error. This option allows IPv4 and IPv6 rules in a single rule file
for use with both iptables-restore and ip6tables-restore.
This option has no effect in ip6tables and ip6tables-restore.
@@ -258,9 +258,9 @@ This option has no effect in ip6tables and ip6tables-restore.
[\fB!\fP] \fB\-p\fP, \fB\-\-protocol\fP \fIprotocol\fP
The protocol of the rule or of the packet to check.
The specified protocol can be one of \fBtcp\fP, \fBudp\fP, \fBudplite\fP,
-\fBicmp\fP, \fBicmpv6\fP,\fBesp\fP, \fBah\fP, \fBsctp\fP, \fBmh\fP or the special keyword "\fBall\fP",
+\fBicmp\fP, \fBicmpv6\fP, \fBesp\fP, \fBah\fP, \fBsctp\fP, \fBmh\fP or the special keyword "\fBall\fP",
or it can be a numeric value, representing one of these protocols or a
-different one. A protocol name from /etc/protocols is also allowed.
+different one. A protocol name from \fI/etc/protocols\fP is also allowed.
A "!" argument before the protocol inverts the
test. The number zero is equivalent to \fBall\fP. "\fBall\fP"
will match with all protocols and is taken as default when this
@@ -307,8 +307,8 @@ false, evaluation will stop.
This specifies the target of the rule; i.e., what to do if the packet
matches it. The target can be a user-defined chain (other than the
one this rule is in), one of the special builtin targets which decide
-the fate of the packet immediately, or an extension (see \fBEXTENSIONS\fP
-below). If this
+the fate of the packet immediately, or an extension (see \fBMATCH AND TARGET
+EXTENSIONS\fP below). If this
option is omitted in a rule (and \fB\-g\fP
is not used), then matching the rule will have no
effect on the packet's fate, but the counters on the rule will be
@@ -316,7 +316,7 @@ incremented.
.TP
\fB\-g\fP, \fB\-\-goto\fP \fIchain\fP
This specifies that the processing should continue in a user
-specified chain. Unlike the \-\-jump option return will not continue
+specified chain. Unlike with the \-\-jump option, \fBRETURN\fP will not continue
processing in this chain but instead in the chain that called us via
\-\-jump.
.TP
@@ -386,7 +386,7 @@ network names, or services (whenever applicable).
\fB\-x\fP, \fB\-\-exact\fP
Expand numbers.
Display the exact value of the packet and byte counters,
-instead of only the rounded number in K's (multiples of 1000)
+instead of only the rounded number in K's (multiples of 1000),
M's (multiples of 1000K) or G's (multiples of 1000M). This option is
only relevant for the \fB\-L\fP command.
.TP
@@ -410,13 +410,16 @@ the default setting.
iptables can use extended packet matching and target modules.
A list of these is available in the \fBiptables\-extensions\fP(8) manpage.
.SH DIAGNOSTICS
-Various error messages are printed to standard error. The exit code
-is 0 for correct functioning. Errors which appear to be caused by
-invalid or abused command line parameters cause an exit code of 2, and
+Various error messages are printed to standard error. The exit code is 0 for
+correct functioning. Errors which appear to be caused by invalid or abused
+command line parameters cause an exit code of 2. Errors which indicate an
+incompatibility between kernel and user space cause an exit code of 3. Errors
+which indicate a resource problem, such as a busy lock, failing memory
+allocation or error messages from kernel cause an exit code of 4. Finally,
other errors cause an exit code of 1.
.SH BUGS
Bugs? What's this? ;-)
-Well, you might want to have a look at http://bugzilla.netfilter.org/
+Well, you might want to have a look at https://bugzilla.netfilter.org/
\fBiptables\fP will exit immediately with an error code of 111 if it finds
that it was called as a setuid-to-root program.
iptables cannot be used safely in this manner because it trusts
@@ -438,7 +441,7 @@ entering the \fBFORWARD\fP chain.
.PP
The various forms of NAT have been separated out; \fBiptables\fP
is a pure packet filter when using the default `filter' table, with
-optional extension modules. This should simplify much of the previous
+optional extension modules. This should avoid much of the
confusion over the combination of IP masquerading and packet filtering
seen previously. So the following options are handled differently:
.nf
@@ -460,7 +463,7 @@ not in the standard distribution,
and the netfilter-hacking-HOWTO details the netfilter internals.
.br
See
-.BR "http://www.netfilter.org/" .
+.BR "https://www.netfilter.org/" .
.SH AUTHORS
Rusty Russell originally wrote iptables, in early consultation with Michael
Neuling.
diff --git a/iptables/iptables.c b/iptables/iptables.c
index e5207ba1..8eb043e9 100644
--- a/iptables/iptables.c
+++ b/iptables/iptables.c
@@ -122,6 +122,9 @@ print_match(const struct xt_entry_match *m,
printf("%s%s ", match->name, unsupported_rev);
else
printf("%s ", match->name);
+
+ if (match->next == match)
+ free(match);
} else {
if (name[0])
printf("UNKNOWN match `%s' ", name);
@@ -178,6 +181,9 @@ print_firewall(const struct ipt_entry *fw,
tg->print(&fw->ip, t, format & FMT_NUMERIC);
else if (target->print)
printf(" %s%s", target->name, unsupported_rev);
+
+ if (target->next == target)
+ free(target);
} else if (t->u.target_size != sizeof(*t))
printf("[%u bytes of unknown target data] ",
(unsigned int)(t->u.target_size - sizeof(*t)));
@@ -276,40 +282,6 @@ insert_entry(const xt_chainlabel chain,
return ret;
}
-static unsigned char *
-make_delete_mask(const struct xtables_rule_match *matches,
- const struct xtables_target *target)
-{
- /* Establish mask for comparison */
- unsigned int size;
- const struct xtables_rule_match *matchp;
- unsigned char *mask, *mptr;
-
- size = sizeof(struct ipt_entry);
- for (matchp = matches; matchp; matchp = matchp->next)
- size += XT_ALIGN(sizeof(struct xt_entry_match)) + matchp->match->size;
-
- mask = xtables_calloc(1, size
- + XT_ALIGN(sizeof(struct xt_entry_target))
- + target->size);
-
- memset(mask, 0xFF, sizeof(struct ipt_entry));
- mptr = mask + sizeof(struct ipt_entry);
-
- for (matchp = matches; matchp; matchp = matchp->next) {
- memset(mptr, 0xFF,
- XT_ALIGN(sizeof(struct xt_entry_match))
- + matchp->match->userspacesize);
- mptr += XT_ALIGN(sizeof(struct xt_entry_match)) + matchp->match->size;
- }
-
- memset(mptr, 0xFF,
- XT_ALIGN(sizeof(struct xt_entry_target))
- + target->userspacesize);
-
- return mask;
-}
-
static int
delete_entry(const xt_chainlabel chain,
struct ipt_entry *fw,
@@ -328,7 +300,7 @@ delete_entry(const xt_chainlabel chain,
int ret = 1;
unsigned char *mask;
- mask = make_delete_mask(matches, target);
+ mask = make_delete_mask(matches, target, sizeof(*fw));
for (i = 0; i < nsaddrs; i++) {
fw->ip.src.s_addr = saddrs[i].s_addr;
fw->ip.smsk.s_addr = smasks[i].s_addr;
@@ -358,7 +330,7 @@ check_entry(const xt_chainlabel chain, struct ipt_entry *fw,
int ret = 1;
unsigned char *mask;
- mask = make_delete_mask(matches, target);
+ mask = make_delete_mask(matches, target, sizeof(*fw));
for (i = 0; i < nsaddrs; i++) {
fw->ip.src.s_addr = saddrs[i].s_addr;
fw->ip.smsk.s_addr = smasks[i].s_addr;
@@ -544,8 +516,7 @@ void print_rule4(const struct ipt_entry *e,
save_ipv4_addr('d', &e->ip.dst, &e->ip.dmsk,
e->ip.invflags & IPT_INV_DSTIP);
- save_rule_details(e->ip.iniface, e->ip.iniface_mask,
- e->ip.outiface, e->ip.outiface_mask,
+ save_rule_details(e->ip.iniface, e->ip.outiface,
e->ip.proto, e->ip.flags & IPT_F_FRAG,
e->ip.invflags);
@@ -691,6 +662,10 @@ int do_command4(int argc, char *argv[], char **table,
struct xt_cmd_parse_ops cmd_parse_ops = {
.proto_parse = ipv4_proto_parse,
.post_parse = ipv4_post_parse,
+ .option_name = ip46t_option_name,
+ .option_invert = ip46t_option_invert,
+ .command_default = command_default,
+ .print_help = xtables_printhelp,
};
struct xt_cmd_parse p = {
.table = *table,
@@ -733,6 +708,9 @@ int do_command4(int argc, char *argv[], char **table,
smasks = args.s.mask.v4;
dmasks = args.d.mask.v4;
+ iface_to_mask(cs.fw.ip.iniface, cs.fw.ip.iniface_mask);
+ iface_to_mask(cs.fw.ip.outiface, cs.fw.ip.outiface_mask);
+
/* Attempt to acquire the xtables lock */
if (!restore)
xtables_lock_or_exit(wait);
@@ -795,13 +773,12 @@ int do_command4(int argc, char *argv[], char **table,
#ifdef IPT_F_GOTO
if (cs.fw.ip.flags & IPT_F_GOTO)
xtables_error(PARAMETER_PROBLEM,
- "goto '%s' is not a chain\n",
- cs.jumpto);
+ "goto '%s' is not a chain",
+ cs.jumpto);
#endif
xtables_find_target(cs.jumpto, XTF_LOAD_MUST_SUCCEED);
} else {
e = generate_entry(&cs.fw, cs.matches, cs.target->t);
- free(cs.target->t);
}
}
@@ -903,17 +880,14 @@ int do_command4(int argc, char *argv[], char **table,
if (verbose > 1)
dump_entries(*handle);
- xtables_rule_matches_free(&cs.matches);
+ xtables_clear_iptables_command_state(&cs);
if (e != NULL) {
free(e);
e = NULL;
}
- free(saddrs);
- free(smasks);
- free(daddrs);
- free(dmasks);
+ xtables_clear_args(&args);
xtables_free_opts(1);
return ret;
diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c
index e6e4d2d8..5d66e271 100644
--- a/iptables/nft-arp.c
+++ b/iptables/nft-arp.c
@@ -18,6 +18,7 @@
#include <xtables.h>
#include <libiptc/libxtc.h>
+#include <arpa/inet.h>
#include <net/if_arp.h>
#include <netinet/if_ether.h>
@@ -40,8 +41,8 @@ static bool need_devaddr(struct arpt_devaddr_info *info)
return false;
}
-static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r,
- struct iptables_command_state *cs)
+static int nft_arp_add(struct nft_handle *h, struct nft_rule_ctx *ctx,
+ struct nftnl_rule *r, struct iptables_command_state *cs)
{
struct arpt_entry *fw = &cs->arp;
uint32_t op;
@@ -49,50 +50,65 @@ static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r,
if (fw->arp.iniface[0] != '\0') {
op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_VIA_IN);
- add_iniface(h, r, fw->arp.iniface, op);
+ add_iface(h, r, fw->arp.iniface, NFT_META_IIFNAME, op);
}
if (fw->arp.outiface[0] != '\0') {
op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_VIA_OUT);
- add_outiface(h, r, fw->arp.outiface, op);
+ add_iface(h, r, fw->arp.outiface, NFT_META_OIFNAME, op);
}
if (fw->arp.arhrd != 0 ||
+ fw->arp.arhrd_mask != 0xffff ||
fw->arp.invflags & IPT_INV_ARPHRD) {
uint8_t reg;
op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_ARPHRD);
add_payload(h, r, offsetof(struct arphdr, ar_hrd), 2,
NFT_PAYLOAD_NETWORK_HEADER, &reg);
+ if (fw->arp.arhrd_mask != 0xffff)
+ add_bitwise_u16(h, r, fw->arp.arhrd_mask, 0, reg, &reg);
add_cmp_u16(r, fw->arp.arhrd, op, reg);
}
if (fw->arp.arpro != 0 ||
+ fw->arp.arpro_mask != 0xffff ||
fw->arp.invflags & IPT_INV_PROTO) {
uint8_t reg;
op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_PROTO);
add_payload(h, r, offsetof(struct arphdr, ar_pro), 2,
NFT_PAYLOAD_NETWORK_HEADER, &reg);
+ if (fw->arp.arpro_mask != 0xffff)
+ add_bitwise_u16(h, r, fw->arp.arpro_mask, 0, reg, &reg);
add_cmp_u16(r, fw->arp.arpro, op, reg);
}
if (fw->arp.arhln != 0 ||
+ fw->arp.arhln_mask != 255 ||
fw->arp.invflags & IPT_INV_ARPHLN) {
+ uint8_t reg;
+
op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_ARPHLN);
- add_proto(h, r, offsetof(struct arphdr, ar_hln), 1,
- fw->arp.arhln, op);
+ add_payload(h, r, offsetof(struct arphdr, ar_hln), 1,
+ NFT_PAYLOAD_NETWORK_HEADER, &reg);
+ if (fw->arp.arhln_mask != 255)
+ add_bitwise(h, r, &fw->arp.arhln_mask, 1, reg, &reg);
+ add_cmp_u8(r, fw->arp.arhln, op, reg);
}
add_proto(h, r, offsetof(struct arphdr, ar_pln), 1, 4, NFT_CMP_EQ);
if (fw->arp.arpop != 0 ||
+ fw->arp.arpop_mask != 0xffff ||
fw->arp.invflags & IPT_INV_ARPOP) {
uint8_t reg;
op = nft_invflags2cmp(fw->arp.invflags, IPT_INV_ARPOP);
add_payload(h, r, offsetof(struct arphdr, ar_op), 2,
NFT_PAYLOAD_NETWORK_HEADER, &reg);
+ if (fw->arp.arpop_mask != 0xffff)
+ add_bitwise_u16(h, r, fw->arp.arpop_mask, 0, reg, &reg);
add_cmp_u16(r, fw->arp.arpop, op, reg);
}
@@ -160,134 +176,6 @@ static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r,
return ret;
}
-static void nft_arp_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
- struct iptables_command_state *cs)
-{
- struct arpt_entry *fw = &cs->arp;
- uint8_t flags = 0;
-
- parse_meta(ctx, e, ctx->meta.key, fw->arp.iniface, fw->arp.iniface_mask,
- fw->arp.outiface, fw->arp.outiface_mask,
- &flags);
-
- fw->arp.invflags |= flags;
-}
-
-static void parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask)
-{
- mask->s_addr = ctx->bitwise.mask[0];
-}
-
-static bool nft_arp_parse_devaddr(struct nft_xt_ctx *ctx,
- struct nftnl_expr *e,
- struct arpt_devaddr_info *info)
-{
- uint32_t hlen;
- bool inv;
-
- nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &hlen);
-
- if (hlen != ETH_ALEN)
- return false;
-
- get_cmp_data(e, info->addr, ETH_ALEN, &inv);
-
- if (ctx->flags & NFT_XT_CTX_BITWISE) {
- memcpy(info->mask, ctx->bitwise.mask, ETH_ALEN);
- ctx->flags &= ~NFT_XT_CTX_BITWISE;
- } else {
- memset(info->mask, 0xff,
- min(ctx->payload.len, ETH_ALEN));
- }
-
- return inv;
-}
-
-static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
- struct nftnl_expr *e,
- struct iptables_command_state *cs)
-{
- struct arpt_entry *fw = &cs->arp;
- struct in_addr addr;
- uint16_t ar_hrd, ar_pro, ar_op;
- uint8_t 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 |= IPT_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 |= IPT_INV_PROTO;
- 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 |= IPT_INV_ARPOP;
- break;
- case offsetof(struct arphdr, ar_hln):
- get_cmp_data(e, &ar_hln, sizeof(ar_hln), &inv);
- fw->arp.arhln = ar_hln;
- fw->arp.arhln_mask = 0xff;
- if (inv)
- fw->arp.invflags |= IPT_INV_ARPOP;
- break;
- default:
- if (ctx->payload.offset == sizeof(struct arphdr)) {
- if (nft_arp_parse_devaddr(ctx, e, &fw->arp.src_devaddr))
- fw->arp.invflags |= IPT_INV_SRCDEVADDR;
- } else 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);
- ctx->flags &= ~NFT_XT_CTX_BITWISE;
- } else {
- memset(&fw->arp.smsk, 0xff,
- min(ctx->payload.len,
- sizeof(struct in_addr)));
- }
-
- if (inv)
- fw->arp.invflags |= IPT_INV_SRCIP;
- } else if (ctx->payload.offset == sizeof(struct arphdr) +
- fw->arp.arhln +
- sizeof(struct in_addr)) {
- if (nft_arp_parse_devaddr(ctx, e, &fw->arp.tgt_devaddr))
- fw->arp.invflags |= IPT_INV_TGTDEVADDR;
- } else if (ctx->payload.offset == sizeof(struct arphdr) +
- fw->arp.arhln +
- sizeof(struct in_addr) +
- fw->arp.arhln) {
- 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);
- ctx->flags &= ~NFT_XT_CTX_BITWISE;
- } else {
- memset(&fw->arp.tmsk, 0xff,
- min(ctx->payload.len,
- sizeof(struct in_addr)));
- }
-
- if (inv)
- fw->arp.invflags |= IPT_INV_DSTIP;
- }
- break;
- }
-}
-
static void nft_arp_print_header(unsigned int format, const char *chain,
const char *pol,
const struct xt_counters *counters,
@@ -401,7 +289,8 @@ after_devsrc:
after_devdst:
- if (fw->arp.arhln_mask != 255 || fw->arp.arhln != 6) {
+ if (fw->arp.arhln_mask != 255 || fw->arp.arhln != 6 ||
+ fw->arp.invflags & IPT_INV_ARPHLN) {
printf("%s%s", sep, fw->arp.invflags & IPT_INV_ARPHLN
? "! " : "");
printf("--h-length %d", fw->arp.arhln);
@@ -425,7 +314,8 @@ after_devdst:
sep = " ";
}
- if (fw->arp.arhrd_mask != 65535 || fw->arp.arhrd != htons(1)) {
+ if (fw->arp.arhrd_mask != 65535 || fw->arp.arhrd != htons(1) ||
+ fw->arp.invflags & IPT_INV_ARPHRD) {
uint16_t tmp = ntohs(fw->arp.arhrd);
printf("%s%s", sep, fw->arp.invflags & IPT_INV_ARPHRD
@@ -433,9 +323,9 @@ after_devdst:
if (tmp == 1 && !(format & FMT_NUMERIC))
printf("--h-type %s", "Ethernet");
else
- printf("--h-type %u", tmp);
+ printf("--h-type 0x%x", tmp);
if (fw->arp.arhrd_mask != 65535)
- printf("/%d", ntohs(fw->arp.arhrd_mask));
+ printf("/0x%x", ntohs(fw->arp.arhrd_mask));
sep = " ";
}
@@ -449,7 +339,7 @@ after_devdst:
else
printf("--proto-type 0x%x", tmp);
if (fw->arp.arpro_mask != 65535)
- printf("/%x", ntohs(fw->arp.arpro_mask));
+ printf("/0x%x", ntohs(fw->arp.arpro_mask));
sep = " ";
}
}
@@ -490,7 +380,7 @@ nft_arp_print_rule(struct nft_handle *h, struct nftnl_rule *r,
if (!(format & FMT_NONEWLINE))
fputc('\n', stdout);
- nft_clear_iptables_command_state(&cs);
+ xtables_clear_iptables_command_state(&cs);
}
static bool nft_arp_is_same(const struct iptables_command_state *cs_a,
@@ -616,7 +506,7 @@ static void nft_arp_post_parse(int command,
&args->d.naddrs);
if ((args->s.naddrs > 1 || args->d.naddrs > 1) &&
- (cs->arp.arp.invflags & (ARPT_INV_SRCIP | ARPT_INV_TGTIP)))
+ (cs->arp.arp.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))
xtables_error(PARAMETER_PROBLEM,
"! not allowed with multiple"
" source or destination IP addresses");
@@ -639,7 +529,7 @@ static void nft_arp_post_parse(int command,
if (cs->arp.arp.arhln != 6)
xtables_error(PARAMETER_PROBLEM,
- "Only harware address length of 6 is supported currently.");
+ "Only hardware address length of 6 is supported currently.");
}
if (args->arp_opcode) {
if (get16_and_mask(args->arp_opcode, &cs->arp.arp.arpop,
@@ -682,6 +572,8 @@ static void nft_arp_init_cs(struct iptables_command_state *cs)
cs->arp.arp.arhln_mask = 255;
cs->arp.arp.arhrd = htons(ARPHRD_ETHER);
cs->arp.arp.arhrd_mask = 65535;
+ cs->arp.arp.arpop_mask = 65535;
+ cs->arp.arp.arpro_mask = 65535;
}
static int
@@ -701,7 +593,7 @@ nft_arp_add_entry(struct nft_handle *h,
cs->arp.arp.tgt.s_addr = args->d.addr.v4[j].s_addr;
cs->arp.arp.tmsk.s_addr = args->d.mask.v4[j].s_addr;
if (append) {
- ret = nft_cmd_rule_append(h, chain, table, cs, NULL,
+ ret = nft_cmd_rule_append(h, chain, table, cs,
verbose);
} else {
ret = nft_cmd_rule_insert(h, chain, table, cs,
@@ -772,23 +664,207 @@ nft_arp_replace_entry(struct nft_handle *h,
return nft_cmd_rule_replace(h, chain, table, cs, rulenum, verbose);
}
+static void nft_arp_xlate_mac_and_mask(const struct arpt_devaddr_info *devaddr,
+ const char *addr,
+ bool invert,
+ struct xt_xlate *xl)
+{
+ unsigned int i;
+
+ for (i = 0; i < 6; ++i) {
+ if (devaddr->mask[i])
+ break;
+ }
+
+ if (i == 6)
+ return;
+
+ xt_xlate_add(xl, "arp %s ether ", addr);
+ if (invert)
+ xt_xlate_add(xl, "!= ");
+
+ xt_xlate_add(xl, "%02x", (uint8_t)devaddr->addr[0]);
+ for (i = 1; i < 6; ++i)
+ xt_xlate_add(xl, ":%02x", (uint8_t)devaddr->addr[i]);
+
+ for (i = 0; i < 6; ++i) {
+ int j;
+
+ if ((uint8_t)devaddr->mask[i] == 0xff)
+ continue;
+
+ xt_xlate_add(xl, "/%02x", (uint8_t)devaddr->mask[0]);
+
+ for (j = 1; j < 6; ++j)
+ xt_xlate_add(xl, ":%02x", (uint8_t)devaddr->mask[j]);
+ return;
+ }
+}
+
+static void nft_arp_xlate16(uint16_t v, uint16_t m, const char *what,
+ bool hex, bool inverse,
+ struct xt_xlate *xl)
+{
+ const char *fmt = hex ? "0x%x " : "%d ";
+
+ if (m) {
+ xt_xlate_add(xl, "arp %s ", what);
+ if (inverse)
+ xt_xlate_add(xl, " !=");
+ if (m != 0xffff) {
+ xt_xlate_add(xl, "& ");
+ xt_xlate_add(xl, fmt, ntohs(m));;
+
+ }
+ xt_xlate_add(xl, fmt, ntohs(v));
+ }
+}
+
+static void nft_arp_xlate_ipv4_addr(const char *what, const struct in_addr *addr,
+ const struct in_addr *mask,
+ bool inv, struct xt_xlate *xl)
+{
+ char mbuf[INET_ADDRSTRLEN], abuf[INET_ADDRSTRLEN];
+ const char *op = inv ? "!= " : "";
+ int cidr;
+
+ if (!inv && !addr->s_addr && !mask->s_addr)
+ return;
+
+ inet_ntop(AF_INET, addr, abuf, sizeof(abuf));
+
+ cidr = xtables_ipmask_to_cidr(mask);
+ switch (cidr) {
+ case -1:
+ xt_xlate_add(xl, "arp %s ip & %s %s %s ", what,
+ inet_ntop(AF_INET, mask, mbuf, sizeof(mbuf)),
+ inv ? "!=" : "==", abuf);
+ break;
+ case 32:
+ xt_xlate_add(xl, "arp %s ip %s%s ", what, op, abuf);
+ break;
+ default:
+ xt_xlate_add(xl, "arp %s ip %s%s/%d ", what, op, abuf, cidr);
+ }
+}
+
+static int nft_arp_xlate(const struct iptables_command_state *cs,
+ struct xt_xlate *xl)
+{
+ const struct arpt_entry *fw = &cs->arp;
+ int ret;
+
+ xlate_ifname(xl, "iifname", fw->arp.iniface,
+ fw->arp.invflags & IPT_INV_VIA_IN);
+ xlate_ifname(xl, "oifname", fw->arp.outiface,
+ fw->arp.invflags & IPT_INV_VIA_OUT);
+
+ if (fw->arp.arhrd ||
+ fw->arp.arhrd_mask != 0xffff ||
+ fw->arp.invflags & IPT_INV_ARPHRD)
+ nft_arp_xlate16(fw->arp.arhrd, fw->arp.arhrd_mask,
+ "htype", false,
+ fw->arp.invflags & IPT_INV_ARPHRD, xl);
+
+ if (fw->arp.arhln_mask != 255 || fw->arp.arhln ||
+ fw->arp.invflags & IPT_INV_ARPHLN) {
+ xt_xlate_add(xl, "arp hlen ");
+ if (fw->arp.invflags & IPT_INV_ARPHLN)
+ xt_xlate_add(xl, " !=");
+ if (fw->arp.arhln_mask != 255)
+ xt_xlate_add(xl, "& %d ", fw->arp.arhln_mask);
+ xt_xlate_add(xl, "%d ", fw->arp.arhln);
+ }
+
+ /* added implicitly by arptables-nft */
+ xt_xlate_add(xl, "arp plen %d", 4);
+
+ if (fw->arp.arpop_mask != 65535 ||
+ fw->arp.arpop != 0 ||
+ fw->arp.invflags & IPT_INV_ARPOP)
+ nft_arp_xlate16(fw->arp.arpop, fw->arp.arpop_mask,
+ "operation", false,
+ fw->arp.invflags & IPT_INV_ARPOP, xl);
+
+ if (fw->arp.arpro_mask != 65535 ||
+ fw->arp.invflags & IPT_INV_PROTO ||
+ fw->arp.arpro)
+ nft_arp_xlate16(fw->arp.arpro, fw->arp.arpro_mask,
+ "ptype", true,
+ fw->arp.invflags & IPT_INV_PROTO, xl);
+
+ if (fw->arp.smsk.s_addr != 0L)
+ nft_arp_xlate_ipv4_addr("saddr", &fw->arp.src, &fw->arp.smsk,
+ fw->arp.invflags & IPT_INV_SRCIP, xl);
+
+ if (fw->arp.tmsk.s_addr != 0L)
+ nft_arp_xlate_ipv4_addr("daddr", &fw->arp.tgt, &fw->arp.tmsk,
+ fw->arp.invflags & IPT_INV_DSTIP, xl);
+
+ nft_arp_xlate_mac_and_mask(&fw->arp.src_devaddr, "saddr",
+ fw->arp.invflags & IPT_INV_SRCDEVADDR, xl);
+ nft_arp_xlate_mac_and_mask(&fw->arp.tgt_devaddr, "daddr",
+ fw->arp.invflags & IPT_INV_TGTDEVADDR, xl);
+
+ ret = xlate_matches(cs, xl);
+ if (!ret)
+ return ret;
+
+ /* Always add counters per rule, as in iptables */
+ xt_xlate_add(xl, "counter");
+ return xlate_action(cs, false, xl);
+}
+
+static const char *nft_arp_option_name(int option)
+{
+ switch (option) {
+ default: return ip46t_option_name(option);
+ /* different name than iptables */
+ case OPT_SOURCE: return "--source-ip";
+ case OPT_DESTINATION: return "--destination-ip";
+ /* arptables specific ones */
+ case OPT_S_MAC: return "--source-mac";
+ case OPT_D_MAC: return "--destination-mac";
+ case OPT_H_LENGTH: return "--h-length";
+ case OPT_OPCODE: return "--opcode";
+ case OPT_H_TYPE: return "--h-type";
+ case OPT_P_TYPE: return "--proto-type";
+ }
+}
+
+static int nft_arp_option_invert(int option)
+{
+ switch (option) {
+ case OPT_S_MAC: return IPT_INV_SRCDEVADDR;
+ case OPT_D_MAC: return IPT_INV_TGTDEVADDR;
+ case OPT_H_LENGTH: return IPT_INV_ARPHLN;
+ case OPT_OPCODE: return IPT_INV_ARPOP;
+ case OPT_H_TYPE: return IPT_INV_ARPHRD;
+ case OPT_P_TYPE: return IPT_INV_PROTO;
+ default: return ip46t_option_invert(option);
+ }
+}
+
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,
.print_header = nft_arp_print_header,
.print_rule = nft_arp_print_rule,
.save_rule = nft_arp_save_rule,
.save_chain = nft_arp_save_chain,
+ .rule_parse = &nft_ruleparse_ops_arp,
.cmd_parse = {
.post_parse = nft_arp_post_parse,
+ .option_name = nft_arp_option_name,
+ .option_invert = nft_arp_option_invert,
+ .command_default = command_default,
+ .print_help = xtables_printhelp,
},
.rule_to_cs = nft_rule_to_iptables_command_state,
.init_cs = nft_arp_init_cs,
- .clear_cs = nft_clear_iptables_command_state,
- .parse_target = nft_ipv46_parse_target,
+ .clear_cs = xtables_clear_iptables_command_state,
+ .xlate = nft_arp_xlate,
.add_entry = nft_arp_add_entry,
.delete_entry = nft_arp_delete_entry,
.check_entry = nft_arp_check_entry,
diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c
index 106bcc72..922ce983 100644
--- a/iptables/nft-bridge.c
+++ b/iptables/nft-bridge.c
@@ -65,42 +65,68 @@ static void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char
xtables_print_mac_and_mask(mac, mask);
}
-static void add_logical_iniface(struct nft_handle *h, struct nftnl_rule *r,
- char *iface, uint32_t op)
+static int add_meta_broute(struct nftnl_rule *r)
{
- int iface_len;
- uint8_t reg;
+ struct nftnl_expr *expr;
- iface_len = strlen(iface);
+ expr = nftnl_expr_alloc("immediate");
+ if (expr == NULL)
+ return -1;
- add_meta(h, r, NFT_META_BRI_IIFNAME, &reg);
- if (iface[iface_len - 1] == '+')
- add_cmp_ptr(r, op, iface, iface_len - 1, reg);
- else
- add_cmp_ptr(r, op, iface, iface_len + 1, reg);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG32_01);
+ nftnl_expr_set_u8(expr, NFTNL_EXPR_IMM_DATA, 1);
+ nftnl_rule_add_expr(r, expr);
+
+ expr = nftnl_expr_alloc("meta");
+ if (expr == NULL)
+ return -1;
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_KEY, NFT_META_BRI_BROUTE);
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_META_SREG, NFT_REG32_01);
+
+ nftnl_rule_add_expr(r, expr);
+ return 0;
}
-static void add_logical_outiface(struct nft_handle *h, struct nftnl_rule *r,
- char *iface, uint32_t op)
+static int _add_action(struct nftnl_rule *r, struct iptables_command_state *cs)
{
- int iface_len;
- uint8_t reg;
+ const char *table = nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
- iface_len = strlen(iface);
+ if (cs->target &&
+ table && strcmp(table, "broute") == 0) {
+ if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0) {
+ int ret = add_meta_broute(r);
- add_meta(h, r, NFT_META_BRI_OIFNAME, &reg);
- if (iface[iface_len - 1] == '+')
- add_cmp_ptr(r, op, iface, iface_len - 1, reg);
- else
- add_cmp_ptr(r, op, iface, iface_len + 1, reg);
+ if (ret)
+ return ret;
+
+ cs->jumpto = "ACCEPT";
+ }
+ }
+
+ return add_action(r, cs, false);
}
-static int _add_action(struct nftnl_rule *r, struct iptables_command_state *cs)
+static int
+nft_bridge_add_match(struct nft_handle *h, const struct ebt_entry *fw,
+ struct nft_rule_ctx *ctx, struct nftnl_rule *r,
+ struct xt_entry_match *m)
{
- return add_action(r, cs, false);
+ if (!strcmp(m->u.user.name, "802_3") && !(fw->bitmask & EBT_802_3))
+ xtables_error(PARAMETER_PROBLEM,
+ "For 802.3 DSAP/SSAP filtering the protocol must be LENGTH");
+
+ if (!strcmp(m->u.user.name, "ip") && fw->ethproto != htons(ETH_P_IP))
+ xtables_error(PARAMETER_PROBLEM,
+ "For IP filtering the protocol must be specified as IPv4.");
+
+ if (!strcmp(m->u.user.name, "ip6") && fw->ethproto != htons(ETH_P_IPV6))
+ xtables_error(PARAMETER_PROBLEM,
+ "For IPv6 filtering the protocol must be specified as IPv6.");
+
+ return add_match(h, ctx, r, m);
}
-static int nft_bridge_add(struct nft_handle *h,
+static int nft_bridge_add(struct nft_handle *h, struct nft_rule_ctx *ctx,
struct nftnl_rule *r,
struct iptables_command_state *cs)
{
@@ -108,54 +134,61 @@ static int nft_bridge_add(struct nft_handle *h,
struct ebt_entry *fw = &cs->eb;
uint32_t op;
+ if (fw->bitmask & EBT_SOURCEMAC) {
+ op = nft_invflags2cmp(fw->invflags, EBT_ISOURCE);
+ add_addr(h, r, NFT_PAYLOAD_LL_HEADER,
+ offsetof(struct ethhdr, h_source),
+ fw->sourcemac, fw->sourcemsk, ETH_ALEN, op);
+ }
+
+ if (fw->bitmask & EBT_DESTMAC) {
+ op = nft_invflags2cmp(fw->invflags, EBT_IDEST);
+ add_addr(h, r, NFT_PAYLOAD_LL_HEADER,
+ offsetof(struct ethhdr, h_dest),
+ fw->destmac, fw->destmsk, ETH_ALEN, op);
+ }
+
if (fw->in[0] != '\0') {
op = nft_invflags2cmp(fw->invflags, EBT_IIN);
- add_iniface(h, r, fw->in, op);
+ add_iface(h, r, fw->in, NFT_META_IIFNAME, op);
}
if (fw->out[0] != '\0') {
op = nft_invflags2cmp(fw->invflags, EBT_IOUT);
- add_outiface(h, r, fw->out, op);
+ add_iface(h, r, fw->out, NFT_META_OIFNAME, op);
}
if (fw->logical_in[0] != '\0') {
op = nft_invflags2cmp(fw->invflags, EBT_ILOGICALIN);
- add_logical_iniface(h, r, fw->logical_in, op);
+ add_iface(h, r, fw->logical_in, NFT_META_BRI_IIFNAME, op);
}
if (fw->logical_out[0] != '\0') {
op = nft_invflags2cmp(fw->invflags, EBT_ILOGICALOUT);
- add_logical_outiface(h, r, fw->logical_out, op);
- }
-
- if (fw->bitmask & EBT_ISOURCE) {
- op = nft_invflags2cmp(fw->invflags, EBT_ISOURCE);
- add_addr(h, r, NFT_PAYLOAD_LL_HEADER,
- offsetof(struct ethhdr, h_source),
- fw->sourcemac, fw->sourcemsk, ETH_ALEN, op);
- }
-
- if (fw->bitmask & EBT_IDEST) {
- op = nft_invflags2cmp(fw->invflags, EBT_IDEST);
- add_addr(h, r, NFT_PAYLOAD_LL_HEADER,
- offsetof(struct ethhdr, h_dest),
- fw->destmac, fw->destmsk, ETH_ALEN, op);
+ add_iface(h, r, fw->logical_out, NFT_META_BRI_OIFNAME, op);
}
if ((fw->bitmask & EBT_NOPROTO) == 0) {
+ uint16_t ethproto = fw->ethproto;
uint8_t reg;
op = nft_invflags2cmp(fw->invflags, EBT_IPROTO);
add_payload(h, r, offsetof(struct ethhdr, h_proto), 2,
NFT_PAYLOAD_LL_HEADER, &reg);
- add_cmp_u16(r, fw->ethproto, op, reg);
+
+ if (fw->bitmask & EBT_802_3) {
+ op = (op == NFT_CMP_EQ ? NFT_CMP_LT : NFT_CMP_GTE);
+ ethproto = htons(0x0600);
+ }
+
+ add_cmp_u16(r, ethproto, op, reg);
}
add_compat(r, fw->ethproto, fw->invflags & EBT_IPROTO);
for (iter = cs->match_list; iter; iter = iter->next) {
if (iter->ismatch) {
- if (add_match(h, r, iter->u.match->m))
+ if (nft_bridge_add_match(h, fw, ctx, r, iter->u.match->m))
break;
} else {
if (add_target(r, iter->u.watcher->t))
@@ -169,358 +202,18 @@ static int nft_bridge_add(struct nft_handle *h,
return _add_action(r, cs);
}
-static void nft_bridge_parse_meta(struct nft_xt_ctx *ctx,
- struct nftnl_expr *e,
- struct iptables_command_state *cs)
-{
- struct ebt_entry *fw = &cs->eb;
- uint8_t invflags = 0;
- char iifname[IFNAMSIZ] = {}, oifname[IFNAMSIZ] = {};
-
- parse_meta(ctx, e, ctx->meta.key, iifname, NULL, oifname, NULL, &invflags);
-
- switch (ctx->meta.key) {
- case NFT_META_BRI_IIFNAME:
- if (invflags & IPT_INV_VIA_IN)
- cs->eb.invflags |= EBT_ILOGICALIN;
- snprintf(fw->logical_in, sizeof(fw->logical_in), "%s", iifname);
- break;
- case NFT_META_IIFNAME:
- if (invflags & IPT_INV_VIA_IN)
- cs->eb.invflags |= EBT_IIN;
- snprintf(fw->in, sizeof(fw->in), "%s", iifname);
- break;
- case NFT_META_BRI_OIFNAME:
- if (invflags & IPT_INV_VIA_OUT)
- cs->eb.invflags |= EBT_ILOGICALOUT;
- snprintf(fw->logical_out, sizeof(fw->logical_out), "%s", oifname);
- break;
- case NFT_META_OIFNAME:
- if (invflags & IPT_INV_VIA_OUT)
- cs->eb.invflags |= EBT_IOUT;
- snprintf(fw->out, sizeof(fw->out), "%s", oifname);
- break;
- default:
- break;
- }
-}
-
-static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx,
- struct nftnl_expr *e,
- struct iptables_command_state *cs)
-{
- struct ebt_entry *fw = &cs->eb;
- unsigned char addr[ETH_ALEN];
- unsigned short int ethproto;
- bool inv;
- int i;
-
- switch (ctx->payload.offset) {
- case offsetof(struct ethhdr, h_dest):
- get_cmp_data(e, addr, sizeof(addr), &inv);
- for (i = 0; i < ETH_ALEN; i++)
- fw->destmac[i] = addr[i];
- if (inv)
- fw->invflags |= EBT_IDEST;
-
- if (ctx->flags & NFT_XT_CTX_BITWISE) {
- memcpy(fw->destmsk, ctx->bitwise.mask, ETH_ALEN);
- ctx->flags &= ~NFT_XT_CTX_BITWISE;
- } else {
- memset(&fw->destmsk, 0xff,
- min(ctx->payload.len, ETH_ALEN));
- }
- fw->bitmask |= EBT_IDEST;
- break;
- case offsetof(struct ethhdr, h_source):
- get_cmp_data(e, addr, sizeof(addr), &inv);
- for (i = 0; i < ETH_ALEN; i++)
- fw->sourcemac[i] = addr[i];
- if (inv)
- fw->invflags |= EBT_ISOURCE;
- if (ctx->flags & NFT_XT_CTX_BITWISE) {
- memcpy(fw->sourcemsk, ctx->bitwise.mask, ETH_ALEN);
- ctx->flags &= ~NFT_XT_CTX_BITWISE;
- } else {
- memset(&fw->sourcemsk, 0xff,
- min(ctx->payload.len, ETH_ALEN));
- }
- fw->bitmask |= EBT_ISOURCE;
- break;
- case offsetof(struct ethhdr, h_proto):
- get_cmp_data(e, &ethproto, sizeof(ethproto), &inv);
- fw->ethproto = ethproto;
- if (inv)
- fw->invflags |= EBT_IPROTO;
- fw->bitmask &= ~EBT_NOPROTO;
- break;
- }
-}
-
-/* return 0 if saddr, 1 if daddr, -1 on error */
-static int
-lookup_check_ether_payload(uint32_t base, uint32_t offset, uint32_t len)
-{
- if (base != 0 || len != ETH_ALEN)
- return -1;
-
- switch (offset) {
- case offsetof(struct ether_header, ether_dhost):
- return 1;
- case offsetof(struct ether_header, ether_shost):
- return 0;
- default:
- return -1;
- }
-}
-
-/* return 0 if saddr, 1 if daddr, -1 on error */
-static int
-lookup_check_iphdr_payload(uint32_t base, uint32_t offset, uint32_t len)
-{
- if (base != 1 || len != 4)
- return -1;
-
- switch (offset) {
- case offsetof(struct iphdr, daddr):
- return 1;
- case offsetof(struct iphdr, saddr):
- return 0;
- default:
- return -1;
- }
-}
-
-/* Make sure previous payload expression(s) is/are consistent and extract if
- * matching on source or destination address and if matching on MAC and IP or
- * only MAC address. */
-static int lookup_analyze_payloads(const struct nft_xt_ctx *ctx,
- bool *dst, bool *ip)
-{
- int val, val2 = -1;
-
- if (ctx->flags & NFT_XT_CTX_PREV_PAYLOAD) {
- val = lookup_check_ether_payload(ctx->prev_payload.base,
- ctx->prev_payload.offset,
- ctx->prev_payload.len);
- if (val < 0) {
- DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
- ctx->prev_payload.base, ctx->prev_payload.offset,
- ctx->prev_payload.len);
- return -1;
- }
- if (!(ctx->flags & NFT_XT_CTX_PAYLOAD)) {
- DEBUGP("Previous but no current payload?\n");
- return -1;
- }
- val2 = lookup_check_iphdr_payload(ctx->payload.base,
- ctx->payload.offset,
- ctx->payload.len);
- if (val2 < 0) {
- DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
- ctx->payload.base, ctx->payload.offset,
- ctx->payload.len);
- return -1;
- } else if (val != val2) {
- DEBUGP("mismatching payload match offsets\n");
- return -1;
- }
- } else if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
- val = lookup_check_ether_payload(ctx->payload.base,
- ctx->payload.offset,
- ctx->payload.len);
- if (val < 0) {
- DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
- ctx->payload.base, ctx->payload.offset,
- ctx->payload.len);
- return -1;
- }
- } else {
- DEBUGP("unknown LHS of lookup expression\n");
- return -1;
- }
-
- if (dst)
- *dst = (val == 1);
- if (ip)
- *ip = (val2 != -1);
- return 0;
-}
-
-static int set_elems_to_among_pairs(struct nft_among_pair *pairs,
- const struct nftnl_set *s, int cnt)
-{
- struct nftnl_set_elems_iter *iter = nftnl_set_elems_iter_create(s);
- struct nftnl_set_elem *elem;
- size_t tmpcnt = 0;
- const void *data;
- uint32_t datalen;
- int ret = -1;
-
- if (!iter) {
- fprintf(stderr, "BUG: set elems iter allocation failed\n");
- return ret;
- }
-
- while ((elem = nftnl_set_elems_iter_next(iter))) {
- data = nftnl_set_elem_get(elem, NFTNL_SET_ELEM_KEY, &datalen);
- if (!data) {
- fprintf(stderr, "BUG: set elem without key\n");
- goto err;
- }
- if (datalen > sizeof(*pairs)) {
- fprintf(stderr, "BUG: overlong set elem\n");
- goto err;
- }
- nft_among_insert_pair(pairs, &tmpcnt, data);
- }
- ret = 0;
-err:
- nftnl_set_elems_iter_destroy(iter);
- return ret;
-}
-
-static struct nftnl_set *set_from_lookup_expr(struct nft_xt_ctx *ctx,
- const struct nftnl_expr *e)
-{
- const char *set_name = nftnl_expr_get_str(e, NFTNL_EXPR_LOOKUP_SET);
- uint32_t set_id = nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_SET_ID);
- struct nftnl_set_list *slist;
- struct nftnl_set *set;
-
- slist = nft_set_list_get(ctx->h, ctx->table, set_name);
- if (slist) {
- set = nftnl_set_list_lookup_byname(slist, set_name);
- if (set)
- return set;
-
- set = nft_set_batch_lookup_byid(ctx->h, set_id);
- if (set)
- return set;
- }
-
- return NULL;
-}
-
-static void nft_bridge_parse_lookup(struct nft_xt_ctx *ctx,
- struct nftnl_expr *e)
-{
- struct xtables_match *match = NULL;
- struct nft_among_data *among_data;
- bool is_dst, have_ip, inv;
- struct ebt_match *ematch;
- struct nftnl_set *s;
- size_t poff, size;
- uint32_t cnt;
-
- if (lookup_analyze_payloads(ctx, &is_dst, &have_ip))
- return;
-
- s = set_from_lookup_expr(ctx, e);
- if (!s)
- xtables_error(OTHER_PROBLEM,
- "BUG: lookup expression references unknown set");
-
- cnt = nftnl_set_get_u32(s, NFTNL_SET_DESC_SIZE);
-
- for (ematch = ctx->cs->match_list; ematch; ematch = ematch->next) {
- if (!ematch->ismatch || strcmp(ematch->u.match->name, "among"))
- continue;
-
- match = ematch->u.match;
- among_data = (struct nft_among_data *)match->m->data;
-
- size = cnt + among_data->src.cnt + among_data->dst.cnt;
- size *= sizeof(struct nft_among_pair);
-
- size += XT_ALIGN(sizeof(struct xt_entry_match)) +
- sizeof(struct nft_among_data);
-
- match->m = xtables_realloc(match->m, size);
- break;
- }
- if (!match) {
- match = xtables_find_match("among", XTF_TRY_LOAD,
- &ctx->cs->matches);
-
- size = cnt * sizeof(struct nft_among_pair);
- size += XT_ALIGN(sizeof(struct xt_entry_match)) +
- sizeof(struct nft_among_data);
-
- match->m = xtables_calloc(1, size);
- strcpy(match->m->u.user.name, match->name);
- match->m->u.user.revision = match->revision;
- xs_init_match(match);
-
- if (ctx->h->ops->parse_match != NULL)
- ctx->h->ops->parse_match(match, ctx->cs);
- }
- if (!match)
- return;
-
- match->m->u.match_size = size;
-
- inv = !!(nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_FLAGS) &
- NFT_LOOKUP_F_INV);
-
- among_data = (struct nft_among_data *)match->m->data;
- poff = nft_among_prepare_data(among_data, is_dst, cnt, inv, have_ip);
- if (set_elems_to_among_pairs(among_data->pairs + poff, s, cnt))
- xtables_error(OTHER_PROBLEM,
- "ebtables among pair parsing failed");
-
- ctx->flags &= ~(NFT_XT_CTX_PAYLOAD | NFT_XT_CTX_PREV_PAYLOAD);
-}
-
-static void parse_watcher(void *object, struct ebt_match **match_list,
- bool ismatch)
-{
- struct ebt_match *m = xtables_calloc(1, sizeof(struct ebt_match));
-
- if (ismatch)
- m->u.match = object;
- else
- m->u.watcher = object;
-
- m->ismatch = ismatch;
- if (*match_list == NULL)
- *match_list = m;
- else
- (*match_list)->next = m;
-}
-
-static void nft_bridge_parse_match(struct xtables_match *m,
- struct iptables_command_state *cs)
-{
- parse_watcher(m, &cs->match_list, true);
-}
-
-static void nft_bridge_parse_target(struct xtables_target *t,
- struct iptables_command_state *cs)
-{
- /* harcoded names :-( */
- if (strcmp(t->name, "log") == 0 ||
- strcmp(t->name, "nflog") == 0) {
- parse_watcher(t, &cs->match_list, false);
- return;
- }
-
- cs->target = t;
- cs->jumpto = t->name;
-}
-
-static void nft_rule_to_ebtables_command_state(struct nft_handle *h,
+static bool nft_rule_to_ebtables_command_state(struct nft_handle *h,
const struct nftnl_rule *r,
struct iptables_command_state *cs)
{
cs->eb.bitmask = EBT_NOPROTO;
- nft_rule_to_iptables_command_state(h, r, cs);
+ return nft_rule_to_iptables_command_state(h, r, cs);
}
static void print_iface(const char *option, const char *name, bool invert)
{
if (*name)
- printf("%s%s %s ", option, invert ? " !" : "", name);
+ printf("%s%s %s ", invert ? "! " : "", option, name);
}
static void nft_bridge_print_table_header(const char *tablename)
@@ -565,9 +258,7 @@ static void print_mac(char option, const unsigned char *mac,
const unsigned char *mask,
bool invert)
{
- printf("-%c ", option);
- if (invert)
- printf("! ");
+ printf("%s-%c ", invert ? "! " : "", option);
ebt_print_mac_and_mask(mac, mask);
printf(" ");
}
@@ -582,12 +273,10 @@ static void print_protocol(uint16_t ethproto, bool invert, unsigned int bitmask)
if (bitmask & EBT_NOPROTO)
return;
- printf("-p ");
- if (invert)
- printf("! ");
+ printf("%s-p ", invert ? "! " : "");
if (bitmask & EBT_802_3) {
- printf("length ");
+ printf("Length ");
return;
}
@@ -601,7 +290,7 @@ static void print_protocol(uint16_t ethproto, bool invert, unsigned int bitmask)
static void __nft_bridge_save_rule(const struct iptables_command_state *cs,
unsigned int format)
{
- if (cs->eb.ethproto)
+ if (!(cs->eb.bitmask & EBT_NOPROTO))
print_protocol(cs->eb.ethproto, cs->eb.invflags & EBT_IPROTO,
cs->eb.bitmask);
if (cs->eb.bitmask & EBT_ISOURCE)
@@ -661,7 +350,7 @@ static void nft_bridge_print_rule(struct nft_handle *h, struct nftnl_rule *r,
struct iptables_command_state cs = {};
if (format & FMT_LINENUMBERS)
- printf("%d ", num);
+ printf("%d. ", num);
nft_rule_to_ebtables_command_state(h, r, &cs);
__nft_bridge_save_rule(&cs, format);
@@ -732,7 +421,6 @@ static int xlate_ebmatches(const struct iptables_command_state *cs, struct xt_xl
struct xt_xlate_mt_params mt_params = {
.ip = (const void *)&cs->eb,
.numeric = numeric,
- .escape_quotes = false,
.match = matchp->m,
};
@@ -745,7 +433,6 @@ static int xlate_ebmatches(const struct iptables_command_state *cs, struct xt_xl
struct xt_xlate_tg_params wt_params = {
.ip = (const void *)&cs->eb,
.numeric = numeric,
- .escape_quotes = false,
.target = watcherp->t,
};
@@ -776,7 +463,6 @@ static int xlate_ebaction(const struct iptables_command_state *cs, struct xt_xla
else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
xt_xlate_add(xl, " return");
else if (cs->target->xlate) {
- xt_xlate_add(xl, " ");
struct xt_xlate_tg_params params = {
.ip = (const void *)&cs->eb,
.target = cs->target->t,
@@ -810,20 +496,18 @@ static void nft_bridge_xlate_mac(struct xt_xlate *xl, const char *type, bool inv
xt_xlate_add(xl, "ether %s %s", type, invert ? "!= " : "");
- xlate_mac(xl, mac);
-
if (memcmp(mask, one_msk, ETH_ALEN)) {
int i;
- xt_xlate_add(xl, " and ");
+ xt_xlate_add(xl, "and");
xlate_mac(xl, mask);
xt_xlate_add(xl, " == %02x", mac[0] & mask[0]);
for (i=1; i < ETH_ALEN; i++)
xt_xlate_add(xl, ":%02x", mac[i] & mask[i]);
+ } else {
+ xlate_mac(xl, mac);
}
-
- xt_xlate_add(xl, " ");
}
static int nft_bridge_xlate(const struct iptables_command_state *cs,
@@ -840,7 +524,10 @@ static int nft_bridge_xlate(const struct iptables_command_state *cs,
xlate_ifname(xl, "meta obrname", cs->eb.logical_out,
cs->eb.invflags & EBT_ILOGICALOUT);
- if ((cs->eb.bitmask & EBT_NOPROTO) == 0) {
+ if (cs->eb.bitmask & EBT_802_3) {
+ xt_xlate_add(xl, "ether type %s 0x0600 ",
+ cs->eb.invflags & EBT_IPROTO ? ">=" : "<");
+ } else if ((cs->eb.bitmask & EBT_NOPROTO) == 0) {
const char *implicit = NULL;
switch (ntohs(cs->eb.ethproto)) {
@@ -863,9 +550,6 @@ static int nft_bridge_xlate(const struct iptables_command_state *cs,
ntohs(cs->eb.ethproto));
}
- if (cs->eb.bitmask & EBT_802_3)
- return 0;
-
if (cs->eb.bitmask & EBT_ISOURCE)
nft_bridge_xlate_mac(xl, "saddr", cs->eb.invflags & EBT_ISOURCE,
cs->eb.sourcemac, cs->eb.sourcemsk);
@@ -883,15 +567,132 @@ static int nft_bridge_xlate(const struct iptables_command_state *cs,
return ret;
}
+static const char *nft_bridge_option_name(int option)
+{
+ switch (option) {
+ /* ebtables specific ones */
+ case OPT_LOGICALIN: return "--logical-in";
+ case OPT_LOGICALOUT: return "--logical-out";
+ case OPT_LINENUMBERS: return "--Ln";
+ case OPT_LIST_C: return "--Lc";
+ case OPT_LIST_X: return "--Lx";
+ case OPT_LIST_MAC2: return "--Lmac2";
+ default: return ip46t_option_name(option);
+ }
+}
+
+static int nft_bridge_option_invert(int option)
+{
+ switch (option) {
+ case OPT_SOURCE: return EBT_ISOURCE;
+ case OPT_DESTINATION: return EBT_IDEST;
+ case OPT_PROTOCOL: return EBT_IPROTO;
+ case OPT_VIANAMEIN: return EBT_IIN;
+ case OPT_VIANAMEOUT: return EBT_IOUT;
+ case OPT_LOGICALIN: return EBT_ILOGICALIN;
+ case OPT_LOGICALOUT: return EBT_ILOGICALOUT;
+ default: return -1;
+ }
+}
+
+static void nft_bridge_proto_parse(struct iptables_command_state *cs,
+ struct xtables_args *args)
+{
+ char *buffer;
+ int i;
+
+ cs->eb.bitmask &= ~((unsigned int)EBT_NOPROTO);
+
+ i = strtol(cs->protocol, &buffer, 16);
+ if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
+ xtables_error(PARAMETER_PROBLEM,
+ "Problem with the specified protocol");
+ if (*buffer != '\0') {
+ struct xt_ethertypeent *ent;
+
+ if (!strcmp(cs->protocol, "length")) {
+ cs->eb.bitmask |= EBT_802_3;
+ return;
+ }
+ ent = xtables_getethertypebyname(cs->protocol);
+ if (!ent)
+ xtables_error(PARAMETER_PROBLEM,
+ "Problem with the specified Ethernet protocol '%s', perhaps "XT_PATH_ETHERTYPES " is missing",
+ cs->protocol);
+ cs->eb.ethproto = ent->e_ethertype;
+ } else
+ cs->eb.ethproto = i;
+
+ if (cs->eb.ethproto < 0x0600)
+ xtables_error(PARAMETER_PROBLEM,
+ "Sorry, protocols have values above or equal to 0x0600");
+}
+
+static void nft_bridge_post_parse(int command,
+ struct iptables_command_state *cs,
+ struct xtables_args *args)
+{
+ struct ebt_match *match;
+
+ cs->eb.invflags = args->invflags;
+
+ memcpy(cs->eb.in, args->iniface, IFNAMSIZ);
+ memcpy(cs->eb.out, args->outiface, IFNAMSIZ);
+ memcpy(cs->eb.logical_in, args->bri_iniface, IFNAMSIZ);
+ memcpy(cs->eb.logical_out, args->bri_outiface, IFNAMSIZ);
+
+ cs->counters.pcnt = args->pcnt_cnt;
+ cs->counters.bcnt = args->bcnt_cnt;
+
+ if (args->shostnetworkmask) {
+ if (xtables_parse_mac_and_mask(args->shostnetworkmask,
+ cs->eb.sourcemac,
+ cs->eb.sourcemsk))
+ xtables_error(PARAMETER_PROBLEM,
+ "Problem with specified source mac '%s'",
+ args->shostnetworkmask);
+ cs->eb.bitmask |= EBT_SOURCEMAC;
+ }
+ if (args->dhostnetworkmask) {
+ if (xtables_parse_mac_and_mask(args->dhostnetworkmask,
+ cs->eb.destmac,
+ cs->eb.destmsk))
+ xtables_error(PARAMETER_PROBLEM,
+ "Problem with specified destination mac '%s'",
+ args->dhostnetworkmask);
+ cs->eb.bitmask |= EBT_DESTMAC;
+ }
+
+ if ((cs->options & (OPT_LIST_X | OPT_LINENUMBERS)) ==
+ (OPT_LIST_X | OPT_LINENUMBERS))
+ xtables_error(PARAMETER_PROBLEM,
+ "--Lx is not compatible with --Ln");
+
+ /* So, the extensions can work with the host endian.
+ * The kernel does not have to do this of course */
+ cs->eb.ethproto = htons(cs->eb.ethproto);
+
+ for (match = cs->match_list; match; match = match->next) {
+ if (match->ismatch)
+ continue;
+
+ xtables_option_tfcall(match->u.watcher);
+ }
+}
+
struct nft_family_ops nft_family_ops_bridge = {
.add = nft_bridge_add,
.is_same = nft_bridge_is_same,
.print_payload = NULL,
- .parse_meta = nft_bridge_parse_meta,
- .parse_payload = nft_bridge_parse_payload,
- .parse_lookup = nft_bridge_parse_lookup,
- .parse_match = nft_bridge_parse_match,
- .parse_target = nft_bridge_parse_target,
+ .rule_parse = &nft_ruleparse_ops_bridge,
+ .cmd_parse = {
+ .proto_parse = nft_bridge_proto_parse,
+ .post_parse = nft_bridge_post_parse,
+ .option_name = nft_bridge_option_name,
+ .option_invert = nft_bridge_option_invert,
+ .command_default = ebt_command_default,
+ .print_help = nft_bridge_print_help,
+ },
.print_table_header = nft_bridge_print_table_header,
.print_header = nft_bridge_print_header,
.print_rule = nft_bridge_print_rule,
diff --git a/iptables/nft-bridge.h b/iptables/nft-bridge.h
index eb1b3928..13b077fc 100644
--- a/iptables/nft-bridge.h
+++ b/iptables/nft-bridge.h
@@ -8,13 +8,6 @@
#include <net/ethernet.h>
#include <libiptc/libxtc.h>
-/* We use replace->flags, so we can't use the following values:
- * 0x01 == OPT_COMMAND, 0x02 == OPT_TABLE, 0x100 == OPT_ZERO */
-#define LIST_N 0x04
-#define LIST_C 0x08
-#define LIST_X 0x10
-#define LIST_MAC2 0x20
-
extern unsigned char eb_mac_type_unicast[ETH_ALEN];
extern unsigned char eb_msk_type_unicast[ETH_ALEN];
extern unsigned char eb_mac_type_multicast[ETH_ALEN];
@@ -115,12 +108,12 @@ static inline const char *ebt_target_name(unsigned int verdict)
}) \
void ebt_cs_clean(struct iptables_command_state *cs);
-void ebt_load_match_extensions(void);
void ebt_add_match(struct xtables_match *m,
struct iptables_command_state *cs);
void ebt_add_watcher(struct xtables_target *watcher,
struct iptables_command_state *cs);
-int ebt_command_default(struct iptables_command_state *cs);
+int ebt_command_default(struct iptables_command_state *cs,
+ struct xtables_globals *unused, bool ebt_invert);
struct nft_among_pair {
struct ether_addr ether;
@@ -178,4 +171,7 @@ nft_among_insert_pair(struct nft_among_pair *pairs,
(*pcount)++;
}
+/* from xtables-eb.c */
+void nft_bridge_print_help(struct iptables_command_state *cs);
+
#endif
diff --git a/iptables/nft-cache.c b/iptables/nft-cache.c
index 608e42a7..91d29670 100644
--- a/iptables/nft-cache.c
+++ b/iptables/nft-cache.c
@@ -26,6 +26,14 @@
#include "nft-cache.h"
#include "nft-chain.h"
+/* users may define NDEBUG */
+static void assert_nft_restart(struct nft_handle *h)
+{
+ int rc = nft_restart(h);
+
+ assert(rc >= 0);
+}
+
static void cache_chain_list_insert(struct list_head *list, const char *name)
{
struct cache_chain *pos = NULL, *new;
@@ -105,7 +113,8 @@ static void mnl_genid_get(struct nft_handle *h, uint32_t *genid)
return;
xtables_error(RESOURCE_PROBLEM,
- "Could not fetch rule set generation id: %s\n", nft_strerror(errno));
+ "Could not fetch rule set generation id: %s",
+ nft_strerror(errno));
}
static int nftnl_table_list_cb(const struct nlmsghdr *nlh, void *data)
@@ -141,12 +150,12 @@ static int fetch_table_cache(struct nft_handle *h)
char buf[16536];
int i, ret;
- nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, h->family,
- NLM_F_DUMP, h->seq);
+ nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, h->family,
+ NLM_F_DUMP, h->seq);
ret = mnl_talk(h, nlh, nftnl_table_list_cb, h);
if (ret < 0 && errno == EINTR)
- assert(nft_restart(h) >= 0);
+ assert_nft_restart(h);
for (i = 0; i < NFT_TABLE_MAX; i++) {
enum nft_table_type type = h->tables[i].type;
@@ -416,6 +425,7 @@ static int set_fetch_elem_cb(struct nftnl_set *s, void *data)
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nft_handle *h = data;
struct nlmsghdr *nlh;
+ int ret;
if (set_has_elements(s))
return 0;
@@ -424,7 +434,14 @@ static int set_fetch_elem_cb(struct nftnl_set *s, void *data)
NLM_F_DUMP, h->seq);
nftnl_set_elems_nlmsg_build_payload(nlh, s);
- return mnl_talk(h, nlh, set_elem_cb, s);
+ ret = mnl_talk(h, nlh, set_elem_cb, s);
+
+ if (!ret && h->verbose > 1) {
+ fprintf(stdout, "set ");
+ nftnl_set_fprintf(stdout, s, 0, 0);
+ fprintf(stdout, "\n");
+ }
+ return ret;
}
static int fetch_set_cache(struct nft_handle *h,
@@ -453,8 +470,8 @@ static int fetch_set_cache(struct nft_handle *h,
}
}
- nlh = nftnl_set_nlmsg_build_hdr(buf, NFT_MSG_GETSET,
- h->family, flags, h->seq);
+ nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSET,
+ h->family, flags, h->seq);
if (s) {
nftnl_set_nlmsg_build_payload(nlh, s);
@@ -463,7 +480,7 @@ static int fetch_set_cache(struct nft_handle *h,
ret = mnl_talk(h, nlh, nftnl_set_list_cb, &d);
if (ret < 0 && errno == EINTR) {
- assert(nft_restart(h) >= 0);
+ assert_nft_restart(h);
return ret;
}
@@ -496,14 +513,14 @@ static int __fetch_chain_cache(struct nft_handle *h,
struct nlmsghdr *nlh;
int ret;
- nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family,
- c ? NLM_F_ACK : NLM_F_DUMP, h->seq);
+ nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family,
+ c ? NLM_F_ACK : NLM_F_DUMP, h->seq);
if (c)
nftnl_chain_nlmsg_build_payload(nlh, c);
ret = mnl_talk(h, nlh, nftnl_chain_list_cb, &d);
if (ret < 0 && errno == EINTR)
- assert(nft_restart(h) >= 0);
+ assert_nft_restart(h);
return ret;
}
@@ -591,13 +608,13 @@ static int nft_rule_list_update(struct nft_chain *nc, void *data)
nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN,
nftnl_chain_get_str(c, NFTNL_CHAIN_NAME));
- nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, h->family,
- NLM_F_DUMP, h->seq);
+ nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, h->family,
+ NLM_F_DUMP, h->seq);
nftnl_rule_nlmsg_build_payload(nlh, rule);
ret = mnl_talk(h, nlh, nftnl_rule_list_cb, &rld);
if (ret < 0 && errno == EINTR)
- assert(nft_restart(h) >= 0);
+ assert_nft_restart(h);
nftnl_rule_free(rule);
diff --git a/iptables/nft-cache.h b/iptables/nft-cache.h
index 58a01526..29ec6b5c 100644
--- a/iptables/nft-cache.h
+++ b/iptables/nft-cache.h
@@ -1,6 +1,8 @@
#ifndef _NFT_CACHE_H_
#define _NFT_CACHE_H_
+#include <libnftnl/chain.h>
+
struct nft_handle;
struct nft_chain;
struct nft_cmd;
diff --git a/iptables/nft-cmd.c b/iptables/nft-cmd.c
index fcd01bd0..b38da9bd 100644
--- a/iptables/nft-cmd.c
+++ b/iptables/nft-cmd.c
@@ -14,16 +14,21 @@
#include <xtables.h>
#include "nft.h"
#include "nft-cmd.h"
+#include <libnftnl/set.h>
struct nft_cmd *nft_cmd_new(struct nft_handle *h, int command,
const char *table, const char *chain,
struct iptables_command_state *state,
int rulenum, bool verbose)
{
+ struct nft_rule_ctx ctx = {
+ .command = command,
+ };
struct nftnl_rule *rule;
struct nft_cmd *cmd;
cmd = xtables_calloc(1, sizeof(struct nft_cmd));
+ cmd->error.lineno = h->error.lineno;
cmd->command = command;
cmd->table = xtables_strdup(table);
if (chain)
@@ -32,7 +37,7 @@ struct nft_cmd *nft_cmd_new(struct nft_handle *h, int command,
cmd->verbose = verbose;
if (state) {
- rule = nft_rule_new(h, chain, table, state);
+ rule = nft_rule_new(h, &ctx, chain, table, state);
if (!rule) {
nft_cmd_free(cmd);
return NULL;
@@ -60,6 +65,7 @@ void nft_cmd_free(struct nft_cmd *cmd)
switch (cmd->command) {
case NFT_COMPAT_RULE_CHECK:
case NFT_COMPAT_RULE_DELETE:
+ case NFT_COMPAT_RULE_CHANGE_COUNTERS:
if (cmd->obj.rule)
nftnl_rule_free(cmd->obj.rule);
break;
@@ -91,7 +97,7 @@ static void nft_cmd_rule_bridge(struct nft_handle *h, const struct nft_cmd *cmd)
int nft_cmd_rule_append(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *state,
- void *ref, bool verbose)
+ bool verbose)
{
struct nft_cmd *cmd;
@@ -395,3 +401,23 @@ int ebt_cmd_user_chain_policy(struct nft_handle *h, const char *table,
return 1;
}
+
+int nft_cmd_rule_change_counters(struct nft_handle *h,
+ const char *chain, const char *table,
+ struct iptables_command_state *cs,
+ int rule_nr, uint8_t counter_op, bool verbose)
+{
+ struct nft_cmd *cmd;
+
+ cmd = nft_cmd_new(h, NFT_COMPAT_RULE_CHANGE_COUNTERS, table, chain,
+ rule_nr == -1 ? cs : NULL, rule_nr, verbose);
+ if (!cmd)
+ return 0;
+
+ cmd->counter_op = counter_op;
+ cmd->counters = cs->counters;
+
+ nft_cache_level_set(h, NFT_CL_RULES, cmd);
+
+ return 1;
+}
diff --git a/iptables/nft-cmd.h b/iptables/nft-cmd.h
index b5a99ef7..00ecc802 100644
--- a/iptables/nft-cmd.h
+++ b/iptables/nft-cmd.h
@@ -22,8 +22,12 @@ struct nft_cmd {
} obj;
const char *policy;
struct xt_counters counters;
+ uint8_t counter_op;
const char *rename;
int counters_save;
+ struct {
+ unsigned int lineno;
+ } error;
};
struct nft_cmd *nft_cmd_new(struct nft_handle *h, int command,
@@ -34,7 +38,7 @@ void nft_cmd_free(struct nft_cmd *cmd);
int nft_cmd_rule_append(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *state,
- void *ref, bool verbose);
+ bool verbose);
int nft_cmd_rule_insert(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *state,
int rulenum, bool verbose);
@@ -74,6 +78,10 @@ int nft_cmd_rule_list_save(struct nft_handle *h, const char *chain,
const char *table, int rulenum, int counters);
int ebt_cmd_user_chain_policy(struct nft_handle *h, const char *table,
const char *chain, const char *policy);
+int nft_cmd_rule_change_counters(struct nft_handle *h,
+ const char *chain, const char *table,
+ struct iptables_command_state *cs,
+ int rule_nr, uint8_t counter_op, bool verbose);
void nft_cmd_table_new(struct nft_handle *h, const char *table);
#endif /* _NFT_CMD_H_ */
diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c
index 59c4a41f..0ce8477f 100644
--- a/iptables/nft-ipv4.c
+++ b/iptables/nft-ipv4.c
@@ -26,28 +26,13 @@
#include "nft.h"
#include "nft-shared.h"
-static int nft_ipv4_add(struct nft_handle *h, struct nftnl_rule *r,
- struct iptables_command_state *cs)
+static int nft_ipv4_add(struct nft_handle *h, struct nft_rule_ctx *ctx,
+ struct nftnl_rule *r, struct iptables_command_state *cs)
{
struct xtables_rule_match *matchp;
uint32_t op;
int ret;
- if (cs->fw.ip.iniface[0] != '\0') {
- op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_VIA_IN);
- add_iniface(h, r, cs->fw.ip.iniface, op);
- }
-
- if (cs->fw.ip.outiface[0] != '\0') {
- op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_VIA_OUT);
- add_outiface(h, r, cs->fw.ip.outiface, op);
- }
-
- if (cs->fw.ip.proto != 0) {
- op = nft_invflags2cmp(cs->fw.ip.invflags, XT_INV_PROTO);
- add_l4proto(h, r, cs->fw.ip.proto, op);
- }
-
if (cs->fw.ip.src.s_addr || cs->fw.ip.smsk.s_addr || cs->fw.ip.invflags & IPT_INV_SRCIP) {
op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_SRCIP);
add_addr(h, r, NFT_PAYLOAD_NETWORK_HEADER,
@@ -55,6 +40,7 @@ static int nft_ipv4_add(struct nft_handle *h, struct nftnl_rule *r,
&cs->fw.ip.src.s_addr, &cs->fw.ip.smsk.s_addr,
sizeof(struct in_addr), op);
}
+
if (cs->fw.ip.dst.s_addr || cs->fw.ip.dmsk.s_addr || cs->fw.ip.invflags & IPT_INV_DSTIP) {
op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_DSTIP);
add_addr(h, r, NFT_PAYLOAD_NETWORK_HEADER,
@@ -62,6 +48,23 @@ static int nft_ipv4_add(struct nft_handle *h, struct nftnl_rule *r,
&cs->fw.ip.dst.s_addr, &cs->fw.ip.dmsk.s_addr,
sizeof(struct in_addr), op);
}
+
+ if (cs->fw.ip.iniface[0] != '\0') {
+ op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_VIA_IN);
+ add_iface(h, r, cs->fw.ip.iniface, NFT_META_IIFNAME, op);
+ }
+
+ if (cs->fw.ip.outiface[0] != '\0') {
+ op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_VIA_OUT);
+ add_iface(h, r, cs->fw.ip.outiface, NFT_META_OIFNAME, op);
+ }
+
+ if (cs->fw.ip.proto != 0) {
+ op = nft_invflags2cmp(cs->fw.ip.invflags, XT_INV_PROTO);
+ add_proto(h, r, offsetof(struct iphdr, protocol),
+ sizeof(uint8_t), cs->fw.ip.proto, op);
+ }
+
if (cs->fw.ip.flags & IPT_F_FRAG) {
uint8_t reg;
@@ -81,7 +84,7 @@ static int nft_ipv4_add(struct nft_handle *h, struct nftnl_rule *r,
add_compat(r, cs->fw.ip.proto, cs->fw.ip.invflags & XT_INV_PROTO);
for (matchp = cs->matches; matchp; matchp = matchp->next) {
- ret = add_match(h, r, matchp->match->m);
+ ret = add_match(h, ctx, r, matchp->match->m);
if (ret < 0)
return ret;
}
@@ -115,103 +118,6 @@ static bool nft_ipv4_is_same(const struct iptables_command_state *a,
b->fw.ip.iniface_mask, b->fw.ip.outiface_mask);
}
-static void get_frag(struct nft_xt_ctx *ctx, struct nftnl_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 = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
- if (op == NFT_CMP_EQ)
- *inv = true;
- else
- *inv = false;
-
- ctx->flags &= ~NFT_XT_CTX_BITWISE;
-}
-
-static void nft_ipv4_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
- struct iptables_command_state *cs)
-{
- switch (ctx->meta.key) {
- case NFT_META_L4PROTO:
- cs->fw.ip.proto = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
- if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
- cs->fw.ip.invflags |= XT_INV_PROTO;
- return;
- default:
- break;
- }
-
- parse_meta(ctx, 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 nftnl_expr *e,
- struct iptables_command_state *cs)
-{
- 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);
- ctx->flags &= ~NFT_XT_CTX_BITWISE;
- } else {
- memset(&cs->fw.ip.smsk, 0xff,
- min(ctx->payload.len, sizeof(struct in_addr)));
- }
-
- 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);
- ctx->flags &= ~NFT_XT_CTX_BITWISE;
- } else {
- memset(&cs->fw.ip.dmsk, 0xff,
- min(ctx->payload.len, sizeof(struct in_addr)));
- }
-
- 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;
- inv = false;
- get_frag(ctx, e, &inv);
- if (inv)
- cs->fw.ip.invflags |= IPT_INV_FRAG;
- break;
- default:
- DEBUGP("unknown payload offset %d\n", ctx->payload.offset);
- break;
- }
-}
-
static void nft_ipv4_set_goto_flag(struct iptables_command_state *cs)
{
cs->fw.ip.flags |= IPT_F_GOTO;
@@ -244,7 +150,7 @@ static void nft_ipv4_print_rule(struct nft_handle *h, struct nftnl_rule *r,
if (!(format & FMT_NONEWLINE))
fputc('\n', stdout);
- nft_clear_iptables_command_state(&cs);
+ xtables_clear_iptables_command_state(&cs);
}
static void nft_ipv4_save_rule(const struct iptables_command_state *cs,
@@ -255,8 +161,7 @@ static void nft_ipv4_save_rule(const struct iptables_command_state *cs,
save_ipv4_addr('d', &cs->fw.ip.dst, &cs->fw.ip.dmsk,
cs->fw.ip.invflags & IPT_INV_DSTIP);
- save_rule_details(cs->fw.ip.iniface, cs->fw.ip.iniface_mask,
- cs->fw.ip.outiface, cs->fw.ip.outiface_mask,
+ save_rule_details(cs->fw.ip.iniface, cs->fw.ip.outiface,
cs->fw.ip.proto, cs->fw.ip.flags & IPT_F_FRAG,
cs->fw.ip.invflags);
@@ -309,20 +214,16 @@ static int nft_ipv4_xlate(const struct iptables_command_state *cs,
}
if (cs->fw.ip.proto != 0) {
- const struct protoent *pent =
- getprotobynumber(cs->fw.ip.proto);
- char protonum[sizeof("65535")];
- const char *name = protonum;
-
- snprintf(protonum, sizeof(protonum), "%u",
- cs->fw.ip.proto);
-
- if (!pent || !xlate_find_match(cs, pent->p_name)) {
- if (pent)
- name = pent->p_name;
- xt_xlate_add(xl, "ip protocol %s%s ",
- cs->fw.ip.invflags & IPT_INV_PROTO ?
- "!= " : "", name);
+ const char *pname = proto_to_name(cs->fw.ip.proto, 0);
+
+ if (!pname || !xlate_find_match(cs, pname)) {
+ xt_xlate_add(xl, "ip protocol");
+ if (cs->fw.ip.invflags & IPT_INV_PROTO)
+ xt_xlate_add(xl, " !=");
+ if (pname)
+ xt_xlate_add(xl, "%s", pname);
+ else
+ xt_xlate_add(xl, "%hu", cs->fw.ip.proto);
}
}
@@ -365,7 +266,7 @@ nft_ipv4_add_entry(struct nft_handle *h,
if (append) {
ret = nft_cmd_rule_append(h, chain, table,
- cs, NULL, verbose);
+ cs, verbose);
} else {
ret = nft_cmd_rule_insert(h, chain, table,
cs, rulenum, verbose);
@@ -438,20 +339,22 @@ nft_ipv4_replace_entry(struct nft_handle *h,
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,
.set_goto_flag = nft_ipv4_set_goto_flag,
.print_header = print_header,
.print_rule = nft_ipv4_print_rule,
.save_rule = nft_ipv4_save_rule,
.save_chain = nft_ipv46_save_chain,
+ .rule_parse = &nft_ruleparse_ops_ipv4,
.cmd_parse = {
.proto_parse = ipv4_proto_parse,
.post_parse = ipv4_post_parse,
+ .option_name = ip46t_option_name,
+ .option_invert = ip46t_option_invert,
+ .command_default = command_default,
+ .print_help = xtables_printhelp,
},
- .parse_target = nft_ipv46_parse_target,
.rule_to_cs = nft_rule_to_iptables_command_state,
- .clear_cs = nft_clear_iptables_command_state,
+ .clear_cs = xtables_clear_iptables_command_state,
.xlate = nft_ipv4_xlate,
.add_entry = nft_ipv4_add_entry,
.delete_entry = nft_ipv4_delete_entry,
diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c
index 9a29d18b..c371ba8c 100644
--- a/iptables/nft-ipv6.c
+++ b/iptables/nft-ipv6.c
@@ -25,28 +25,13 @@
#include "nft.h"
#include "nft-shared.h"
-static int nft_ipv6_add(struct nft_handle *h, struct nftnl_rule *r,
- struct iptables_command_state *cs)
+static int nft_ipv6_add(struct nft_handle *h, struct nft_rule_ctx *ctx,
+ struct nftnl_rule *r, struct iptables_command_state *cs)
{
struct xtables_rule_match *matchp;
uint32_t op;
int ret;
- if (cs->fw6.ipv6.iniface[0] != '\0') {
- op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_VIA_IN);
- add_iniface(h, r, cs->fw6.ipv6.iniface, op);
- }
-
- if (cs->fw6.ipv6.outiface[0] != '\0') {
- op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_VIA_OUT);
- add_outiface(h, r, cs->fw6.ipv6.outiface, op);
- }
-
- if (cs->fw6.ipv6.proto != 0) {
- op = nft_invflags2cmp(cs->fw6.ipv6.invflags, XT_INV_PROTO);
- add_l4proto(h, r, cs->fw6.ipv6.proto, op);
- }
-
if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src) ||
!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.smsk) ||
(cs->fw6.ipv6.invflags & IPT_INV_SRCIP)) {
@@ -56,6 +41,7 @@ static int nft_ipv6_add(struct nft_handle *h, struct nftnl_rule *r,
&cs->fw6.ipv6.src, &cs->fw6.ipv6.smsk,
sizeof(struct in6_addr), op);
}
+
if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dst) ||
!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dmsk) ||
(cs->fw6.ipv6.invflags & IPT_INV_DSTIP)) {
@@ -65,10 +51,26 @@ static int nft_ipv6_add(struct nft_handle *h, struct nftnl_rule *r,
&cs->fw6.ipv6.dst, &cs->fw6.ipv6.dmsk,
sizeof(struct in6_addr), op);
}
+
+ if (cs->fw6.ipv6.iniface[0] != '\0') {
+ op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_VIA_IN);
+ add_iface(h, r, cs->fw6.ipv6.iniface, NFT_META_IIFNAME, op);
+ }
+
+ if (cs->fw6.ipv6.outiface[0] != '\0') {
+ op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_VIA_OUT);
+ add_iface(h, r, cs->fw6.ipv6.outiface, NFT_META_OIFNAME, op);
+ }
+
+ if (cs->fw6.ipv6.proto != 0) {
+ op = nft_invflags2cmp(cs->fw6.ipv6.invflags, XT_INV_PROTO);
+ add_l4proto(h, r, cs->fw6.ipv6.proto, op);
+ }
+
add_compat(r, cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags & XT_INV_PROTO);
for (matchp = cs->matches; matchp; matchp = matchp->next) {
- ret = add_match(h, r, matchp->match->m);
+ ret = add_match(h, ctx, r, matchp->match->m);
if (ret < 0)
return ret;
}
@@ -104,77 +106,6 @@ static bool nft_ipv6_is_same(const struct iptables_command_state *a,
b->fw6.ipv6.outiface_mask);
}
-static void nft_ipv6_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
- struct iptables_command_state *cs)
-{
- switch (ctx->meta.key) {
- case NFT_META_L4PROTO:
- cs->fw6.ipv6.proto = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
- if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
- cs->fw6.ipv6.invflags |= XT_INV_PROTO;
- return;
- default:
- break;
- }
-
- parse_meta(ctx, 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 nftnl_expr *e,
- struct iptables_command_state *cs)
-{
- 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);
- ctx->flags &= ~NFT_XT_CTX_BITWISE;
- } else {
- memset(&cs->fw6.ipv6.smsk, 0xff,
- min(ctx->payload.len, sizeof(struct in6_addr)));
- }
-
- if (inv)
- cs->fw6.ipv6.invflags |= IP6T_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);
- ctx->flags &= ~NFT_XT_CTX_BITWISE;
- } else {
- memset(&cs->fw6.ipv6.dmsk, 0xff,
- min(ctx->payload.len, sizeof(struct in6_addr)));
- }
-
- if (inv)
- cs->fw6.ipv6.invflags |= IP6T_INV_DSTIP;
- break;
- case offsetof(struct ip6_hdr, ip6_nxt):
- get_cmp_data(e, &proto, sizeof(proto), &inv);
- cs->fw6.ipv6.proto = proto;
- if (inv)
- cs->fw6.ipv6.invflags |= IP6T_INV_PROTO;
- default:
- DEBUGP("unknown payload offset %d\n", ctx->payload.offset);
- break;
- }
-}
-
static void nft_ipv6_set_goto_flag(struct iptables_command_state *cs)
{
cs->fw6.ipv6.flags |= IP6T_F_GOTO;
@@ -205,7 +136,7 @@ static void nft_ipv6_print_rule(struct nft_handle *h, struct nftnl_rule *r,
if (!(format & FMT_NONEWLINE))
fputc('\n', stdout);
- nft_clear_iptables_command_state(&cs);
+ xtables_clear_iptables_command_state(&cs);
}
static void nft_ipv6_save_rule(const struct iptables_command_state *cs,
@@ -216,8 +147,7 @@ static void nft_ipv6_save_rule(const struct iptables_command_state *cs,
save_ipv6_addr('d', &cs->fw6.ipv6.dst, &cs->fw6.ipv6.dmsk,
cs->fw6.ipv6.invflags & IP6T_INV_DSTIP);
- save_rule_details(cs->fw6.ipv6.iniface, cs->fw6.ipv6.iniface_mask,
- cs->fw6.ipv6.outiface, cs->fw6.ipv6.outiface_mask,
+ save_rule_details(cs->fw6.ipv6.iniface, cs->fw6.ipv6.outiface,
cs->fw6.ipv6.proto, 0, cs->fw6.ipv6.invflags);
save_matches_and_target(cs, cs->fw6.ipv6.flags & IP6T_F_GOTO,
@@ -263,22 +193,17 @@ static int nft_ipv6_xlate(const struct iptables_command_state *cs,
cs->fw6.ipv6.invflags & IP6T_INV_VIA_OUT);
if (cs->fw6.ipv6.proto != 0) {
- const struct protoent *pent =
- getprotobynumber(cs->fw6.ipv6.proto);
- char protonum[sizeof("65535")];
- const char *name = protonum;
-
- snprintf(protonum, sizeof(protonum), "%u",
- cs->fw6.ipv6.proto);
-
- if (!pent || !xlate_find_match(cs, pent->p_name)) {
- if (pent)
- name = pent->p_name;
- xt_xlate_add(xl, "meta l4proto %s%s ",
- cs->fw6.ipv6.invflags & IP6T_INV_PROTO ?
- "!= " : "", name);
+ const char *pname = proto_to_name(cs->fw6.ipv6.proto, 0);
+
+ if (!pname || !xlate_find_match(cs, pname)) {
+ xt_xlate_add(xl, "meta l4proto");
+ if (cs->fw6.ipv6.invflags & IP6T_INV_PROTO)
+ xt_xlate_add(xl, " !=");
+ if (pname)
+ xt_xlate_add(xl, "%s", pname);
+ else
+ xt_xlate_add(xl, "%hu", cs->fw6.ipv6.proto);
}
-
}
xlate_ipv6_addr("ip6 saddr", &cs->fw6.ipv6.src, &cs->fw6.ipv6.smsk,
@@ -323,7 +248,7 @@ nft_ipv6_add_entry(struct nft_handle *h,
&args->d.mask.v6[j], sizeof(struct in6_addr));
if (append) {
ret = nft_cmd_rule_append(h, chain, table,
- cs, NULL, verbose);
+ cs, verbose);
} else {
ret = nft_cmd_rule_insert(h, chain, table,
cs, rulenum, verbose);
@@ -404,20 +329,22 @@ nft_ipv6_replace_entry(struct nft_handle *h,
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,
.set_goto_flag = nft_ipv6_set_goto_flag,
.print_header = print_header,
.print_rule = nft_ipv6_print_rule,
.save_rule = nft_ipv6_save_rule,
.save_chain = nft_ipv46_save_chain,
+ .rule_parse = &nft_ruleparse_ops_ipv6,
.cmd_parse = {
.proto_parse = ipv6_proto_parse,
.post_parse = ipv6_post_parse,
+ .option_name = ip46t_option_name,
+ .option_invert = ip46t_option_invert,
+ .command_default = command_default,
+ .print_help = xtables_printhelp,
},
- .parse_target = nft_ipv46_parse_target,
.rule_to_cs = nft_rule_to_iptables_command_state,
- .clear_cs = nft_clear_iptables_command_state,
+ .clear_cs = xtables_clear_iptables_command_state,
.xlate = nft_ipv6_xlate,
.add_entry = nft_ipv6_add_entry,
.delete_entry = nft_ipv6_delete_entry,
diff --git a/iptables/nft-ruleparse-arp.c b/iptables/nft-ruleparse-arp.c
new file mode 100644
index 00000000..b0671cb0
--- /dev/null
+++ b/iptables/nft-ruleparse-arp.c
@@ -0,0 +1,174 @@
+/*
+ * (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 <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/if_ether.h>
+
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+#include "nft-shared.h"
+#include "nft-ruleparse.h"
+#include "xshared.h"
+
+static void nft_arp_parse_meta(struct nft_xt_ctx *ctx,
+ const struct nft_xt_ctx_reg *reg,
+ struct nftnl_expr *e,
+ struct iptables_command_state *cs)
+{
+ struct arpt_entry *fw = &cs->arp;
+ uint8_t flags = 0;
+
+ if (parse_meta(ctx, e, reg->meta_dreg.key, fw->arp.iniface,
+ fw->arp.outiface, &flags) == 0) {
+ fw->arp.invflags |= flags;
+ return;
+ }
+
+ ctx->errmsg = "Unknown arp meta key";
+}
+
+static void parse_mask_ipv4(const struct nft_xt_ctx_reg *reg, struct in_addr *mask)
+{
+ mask->s_addr = reg->bitwise.mask[0];
+}
+
+static bool nft_arp_parse_devaddr(const struct nft_xt_ctx_reg *reg,
+ struct nftnl_expr *e,
+ struct arpt_devaddr_info *info)
+{
+ uint32_t hlen;
+ bool inv;
+
+ nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &hlen);
+
+ if (hlen != ETH_ALEN)
+ return false;
+
+ get_cmp_data(e, info->addr, ETH_ALEN, &inv);
+
+ if (reg->bitwise.set)
+ memcpy(info->mask, reg->bitwise.mask, ETH_ALEN);
+ else
+ memset(info->mask, 0xff,
+ min(reg->payload.len, ETH_ALEN));
+
+ return inv;
+}
+
+static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
+ const struct nft_xt_ctx_reg *reg,
+ struct nftnl_expr *e,
+ struct iptables_command_state *cs)
+{
+ struct arpt_entry *fw = &cs->arp;
+ struct in_addr addr;
+ uint16_t ar_hrd, ar_pro, ar_op;
+ uint8_t ar_hln, ar_pln;
+ bool inv;
+
+ switch (reg->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 |= IPT_INV_ARPHRD;
+ if (reg->bitwise.set)
+ fw->arp.arhrd_mask = reg->bitwise.mask[0];
+ 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 |= IPT_INV_PROTO;
+ if (reg->bitwise.set)
+ fw->arp.arpro_mask = reg->bitwise.mask[0];
+ 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 |= IPT_INV_ARPOP;
+ if (reg->bitwise.set)
+ fw->arp.arpop_mask = reg->bitwise.mask[0];
+ break;
+ case offsetof(struct arphdr, ar_hln):
+ get_cmp_data(e, &ar_hln, sizeof(ar_hln), &inv);
+ fw->arp.arhln = ar_hln;
+ fw->arp.arhln_mask = 0xff;
+ if (inv)
+ fw->arp.invflags |= IPT_INV_ARPHLN;
+ if (reg->bitwise.set)
+ fw->arp.arhln_mask = reg->bitwise.mask[0];
+ break;
+ case offsetof(struct arphdr, ar_pln):
+ get_cmp_data(e, &ar_pln, sizeof(ar_pln), &inv);
+ if (ar_pln != 4 || inv)
+ ctx->errmsg = "unexpected ARP protocol length match";
+ break;
+ default:
+ if (reg->payload.offset == sizeof(struct arphdr)) {
+ if (nft_arp_parse_devaddr(reg, e, &fw->arp.src_devaddr))
+ fw->arp.invflags |= IPT_INV_SRCDEVADDR;
+ } else if (reg->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 (reg->bitwise.set)
+ parse_mask_ipv4(reg, &fw->arp.smsk);
+ else
+ memset(&fw->arp.smsk, 0xff,
+ min(reg->payload.len,
+ sizeof(struct in_addr)));
+
+ if (inv)
+ fw->arp.invflags |= IPT_INV_SRCIP;
+ } else if (reg->payload.offset == sizeof(struct arphdr) +
+ fw->arp.arhln +
+ sizeof(struct in_addr)) {
+ if (nft_arp_parse_devaddr(reg, e, &fw->arp.tgt_devaddr))
+ fw->arp.invflags |= IPT_INV_TGTDEVADDR;
+ } else if (reg->payload.offset == sizeof(struct arphdr) +
+ fw->arp.arhln +
+ sizeof(struct in_addr) +
+ fw->arp.arhln) {
+ get_cmp_data(e, &addr, sizeof(addr), &inv);
+ fw->arp.tgt.s_addr = addr.s_addr;
+ if (reg->bitwise.set)
+ parse_mask_ipv4(reg, &fw->arp.tmsk);
+ else
+ memset(&fw->arp.tmsk, 0xff,
+ min(reg->payload.len,
+ sizeof(struct in_addr)));
+
+ if (inv)
+ fw->arp.invflags |= IPT_INV_DSTIP;
+ } else {
+ ctx->errmsg = "unknown payload offset";
+ }
+ break;
+ }
+}
+
+struct nft_ruleparse_ops nft_ruleparse_ops_arp = {
+ .meta = nft_arp_parse_meta,
+ .payload = nft_arp_parse_payload,
+};
diff --git a/iptables/nft-ruleparse-bridge.c b/iptables/nft-ruleparse-bridge.c
new file mode 100644
index 00000000..aee08b13
--- /dev/null
+++ b/iptables/nft-ruleparse-bridge.c
@@ -0,0 +1,422 @@
+/*
+ * (C) 2014 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.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <net/if.h>
+//#include <net/if_arp.h>
+#include <netinet/if_ether.h>
+
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/set.h>
+
+#include <xtables.h>
+
+#include "nft.h" /* just for nft_set_batch_lookup_byid? */
+#include "nft-bridge.h"
+#include "nft-cache.h"
+#include "nft-shared.h"
+#include "nft-ruleparse.h"
+#include "xshared.h"
+
+static void nft_bridge_parse_meta(struct nft_xt_ctx *ctx,
+ const struct nft_xt_ctx_reg *reg,
+ struct nftnl_expr *e,
+ struct iptables_command_state *cs)
+{
+ struct ebt_entry *fw = &cs->eb;
+ uint8_t invflags = 0;
+ char iifname[IFNAMSIZ] = {}, oifname[IFNAMSIZ] = {};
+
+ switch (reg->meta_dreg.key) {
+ case NFT_META_PROTOCOL:
+ return;
+ }
+
+ if (parse_meta(ctx, e, reg->meta_dreg.key,
+ iifname, oifname, &invflags) < 0) {
+ ctx->errmsg = "unknown meta key";
+ return;
+ }
+
+ switch (reg->meta_dreg.key) {
+ case NFT_META_BRI_IIFNAME:
+ if (invflags & IPT_INV_VIA_IN)
+ cs->eb.invflags |= EBT_ILOGICALIN;
+ snprintf(fw->logical_in, sizeof(fw->logical_in), "%s", iifname);
+ break;
+ case NFT_META_IIFNAME:
+ if (invflags & IPT_INV_VIA_IN)
+ cs->eb.invflags |= EBT_IIN;
+ snprintf(fw->in, sizeof(fw->in), "%s", iifname);
+ break;
+ case NFT_META_BRI_OIFNAME:
+ if (invflags & IPT_INV_VIA_OUT)
+ cs->eb.invflags |= EBT_ILOGICALOUT;
+ snprintf(fw->logical_out, sizeof(fw->logical_out), "%s", oifname);
+ break;
+ case NFT_META_OIFNAME:
+ if (invflags & IPT_INV_VIA_OUT)
+ cs->eb.invflags |= EBT_IOUT;
+ snprintf(fw->out, sizeof(fw->out), "%s", oifname);
+ break;
+ default:
+ ctx->errmsg = "unknown bridge meta key";
+ break;
+ }
+}
+
+static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx,
+ const struct nft_xt_ctx_reg *reg,
+ struct nftnl_expr *e,
+ struct iptables_command_state *cs)
+{
+ struct ebt_entry *fw = &cs->eb;
+ unsigned char addr[ETH_ALEN];
+ unsigned short int ethproto;
+ uint8_t op;
+ bool inv;
+ int i;
+
+ switch (reg->payload.offset) {
+ case offsetof(struct ethhdr, h_dest):
+ get_cmp_data(e, addr, sizeof(addr), &inv);
+ for (i = 0; i < ETH_ALEN; i++)
+ fw->destmac[i] = addr[i];
+ if (inv)
+ fw->invflags |= EBT_IDEST;
+
+ if (reg->bitwise.set)
+ memcpy(fw->destmsk, reg->bitwise.mask, ETH_ALEN);
+ else
+ memset(&fw->destmsk, 0xff,
+ min(reg->payload.len, ETH_ALEN));
+ fw->bitmask |= EBT_IDEST;
+ break;
+ case offsetof(struct ethhdr, h_source):
+ get_cmp_data(e, addr, sizeof(addr), &inv);
+ for (i = 0; i < ETH_ALEN; i++)
+ fw->sourcemac[i] = addr[i];
+ if (inv)
+ fw->invflags |= EBT_ISOURCE;
+ if (reg->bitwise.set)
+ memcpy(fw->sourcemsk, reg->bitwise.mask, ETH_ALEN);
+ else
+ memset(&fw->sourcemsk, 0xff,
+ min(reg->payload.len, ETH_ALEN));
+ fw->bitmask |= EBT_ISOURCE;
+ break;
+ case offsetof(struct ethhdr, h_proto):
+ __get_cmp_data(e, &ethproto, sizeof(ethproto), &op);
+ if (ethproto == htons(0x0600)) {
+ fw->bitmask |= EBT_802_3;
+ inv = (op == NFT_CMP_GTE);
+ } else {
+ fw->ethproto = ethproto;
+ inv = (op == NFT_CMP_NEQ);
+ }
+ if (inv)
+ fw->invflags |= EBT_IPROTO;
+ fw->bitmask &= ~EBT_NOPROTO;
+ break;
+ default:
+ DEBUGP("unknown payload offset %d\n", reg->payload.offset);
+ ctx->errmsg = "unknown payload offset";
+ break;
+ }
+}
+
+/* return 0 if saddr, 1 if daddr, -1 on error */
+static int
+lookup_check_ether_payload(uint32_t base, uint32_t offset, uint32_t len)
+{
+ if (base != 0 || len != ETH_ALEN)
+ return -1;
+
+ switch (offset) {
+ case offsetof(struct ether_header, ether_dhost):
+ return 1;
+ case offsetof(struct ether_header, ether_shost):
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+/* return 0 if saddr, 1 if daddr, -1 on error */
+static int
+lookup_check_iphdr_payload(uint32_t base, uint32_t offset, uint32_t len)
+{
+ if (base != 1 || len != 4)
+ return -1;
+
+ switch (offset) {
+ case offsetof(struct iphdr, daddr):
+ return 1;
+ case offsetof(struct iphdr, saddr):
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+/* Make sure previous payload expression(s) is/are consistent and extract if
+ * matching on source or destination address and if matching on MAC and IP or
+ * only MAC address. */
+static int lookup_analyze_payloads(struct nft_xt_ctx *ctx,
+ enum nft_registers sreg,
+ uint32_t key_len,
+ bool *dst, bool *ip)
+{
+ const struct nft_xt_ctx_reg *reg;
+ int val, val2 = -1;
+
+ reg = nft_xt_ctx_get_sreg(ctx, sreg);
+ if (!reg)
+ return -1;
+
+ if (reg->type != NFT_XT_REG_PAYLOAD) {
+ ctx->errmsg = "lookup reg is not payload type";
+ return -1;
+ }
+
+ switch (key_len) {
+ case 12: /* ether + ipv4addr */
+ val = lookup_check_ether_payload(reg->payload.base,
+ reg->payload.offset,
+ reg->payload.len);
+ if (val < 0) {
+ DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
+ reg->payload.base, reg->payload.offset,
+ reg->payload.len);
+ return -1;
+ }
+
+ sreg = nft_get_next_reg(sreg, ETH_ALEN);
+
+ reg = nft_xt_ctx_get_sreg(ctx, sreg);
+ if (!reg) {
+ ctx->errmsg = "next lookup register is invalid";
+ return -1;
+ }
+
+ if (reg->type != NFT_XT_REG_PAYLOAD) {
+ ctx->errmsg = "next lookup reg is not payload type";
+ return -1;
+ }
+
+ val2 = lookup_check_iphdr_payload(reg->payload.base,
+ reg->payload.offset,
+ reg->payload.len);
+ if (val2 < 0) {
+ DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
+ reg->payload.base, reg->payload.offset,
+ reg->payload.len);
+ return -1;
+ } else if (val != val2) {
+ DEBUGP("mismatching payload match offsets\n");
+ return -1;
+ }
+ break;
+ case 6: /* ether */
+ val = lookup_check_ether_payload(reg->payload.base,
+ reg->payload.offset,
+ reg->payload.len);
+ if (val < 0) {
+ DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
+ reg->payload.base, reg->payload.offset,
+ reg->payload.len);
+ return -1;
+ }
+ break;
+ default:
+ ctx->errmsg = "unsupported lookup key length";
+ return -1;
+ }
+
+ if (dst)
+ *dst = (val == 1);
+ if (ip)
+ *ip = (val2 != -1);
+ return 0;
+}
+
+static int set_elems_to_among_pairs(struct nft_among_pair *pairs,
+ const struct nftnl_set *s, int cnt)
+{
+ struct nftnl_set_elems_iter *iter = nftnl_set_elems_iter_create(s);
+ struct nftnl_set_elem *elem;
+ size_t tmpcnt = 0;
+ const void *data;
+ uint32_t datalen;
+ int ret = -1;
+
+ if (!iter) {
+ fprintf(stderr, "BUG: set elems iter allocation failed\n");
+ return ret;
+ }
+
+ while ((elem = nftnl_set_elems_iter_next(iter))) {
+ data = nftnl_set_elem_get(elem, NFTNL_SET_ELEM_KEY, &datalen);
+ if (!data) {
+ fprintf(stderr, "BUG: set elem without key\n");
+ goto err;
+ }
+ if (datalen > sizeof(*pairs)) {
+ fprintf(stderr, "BUG: overlong set elem\n");
+ goto err;
+ }
+ nft_among_insert_pair(pairs, &tmpcnt, data);
+ }
+ ret = 0;
+err:
+ nftnl_set_elems_iter_destroy(iter);
+ return ret;
+}
+
+static struct nftnl_set *set_from_lookup_expr(struct nft_xt_ctx *ctx,
+ const struct nftnl_expr *e)
+{
+ const char *set_name = nftnl_expr_get_str(e, NFTNL_EXPR_LOOKUP_SET);
+ uint32_t set_id = nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_SET_ID);
+ struct nftnl_set_list *slist;
+ struct nftnl_set *set;
+
+ slist = nft_set_list_get(ctx->h, ctx->table, set_name);
+ if (slist) {
+ set = nftnl_set_list_lookup_byname(slist, set_name);
+ if (set)
+ return set;
+
+ set = nft_set_batch_lookup_byid(ctx->h, set_id);
+ if (set)
+ return set;
+ }
+
+ return NULL;
+}
+
+static void nft_bridge_parse_lookup(struct nft_xt_ctx *ctx,
+ struct nftnl_expr *e)
+{
+ struct xtables_match *match = NULL;
+ struct nft_among_data *among_data;
+ bool is_dst, have_ip, inv;
+ struct ebt_match *ematch;
+ struct nftnl_set *s;
+ size_t poff, size;
+ uint32_t cnt;
+
+ s = set_from_lookup_expr(ctx, e);
+ if (!s)
+ xtables_error(OTHER_PROBLEM,
+ "BUG: lookup expression references unknown set");
+
+ if (lookup_analyze_payloads(ctx,
+ nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_SREG),
+ nftnl_set_get_u32(s, NFTNL_SET_KEY_LEN),
+ &is_dst, &have_ip))
+ return;
+
+ cnt = nftnl_set_get_u32(s, NFTNL_SET_DESC_SIZE);
+
+ for (ematch = ctx->cs->match_list; ematch; ematch = ematch->next) {
+ if (!ematch->ismatch || strcmp(ematch->u.match->name, "among"))
+ continue;
+
+ match = ematch->u.match;
+ among_data = (struct nft_among_data *)match->m->data;
+
+ size = cnt + among_data->src.cnt + among_data->dst.cnt;
+ size *= sizeof(struct nft_among_pair);
+
+ size += XT_ALIGN(sizeof(struct xt_entry_match)) +
+ sizeof(struct nft_among_data);
+
+ match->m = xtables_realloc(match->m, size);
+ break;
+ }
+ if (!match) {
+ match = xtables_find_match("among", XTF_TRY_LOAD,
+ &ctx->cs->matches);
+
+ size = cnt * sizeof(struct nft_among_pair);
+ size += XT_ALIGN(sizeof(struct xt_entry_match)) +
+ sizeof(struct nft_among_data);
+
+ match->m = xtables_calloc(1, size);
+ strcpy(match->m->u.user.name, match->name);
+ match->m->u.user.revision = match->revision;
+ xs_init_match(match);
+
+ if (ctx->h->ops->rule_parse->match != NULL)
+ ctx->h->ops->rule_parse->match(match, ctx->cs);
+ }
+ if (!match)
+ return;
+
+ match->m->u.match_size = size;
+
+ inv = !!(nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_FLAGS) &
+ NFT_LOOKUP_F_INV);
+
+ among_data = (struct nft_among_data *)match->m->data;
+ poff = nft_among_prepare_data(among_data, is_dst, cnt, inv, have_ip);
+ if (set_elems_to_among_pairs(among_data->pairs + poff, s, cnt))
+ xtables_error(OTHER_PROBLEM,
+ "ebtables among pair parsing failed");
+}
+
+static void parse_watcher(void *object, struct ebt_match **match_list,
+ bool ismatch)
+{
+ struct ebt_match *m = xtables_calloc(1, sizeof(struct ebt_match));
+
+ if (ismatch)
+ m->u.match = object;
+ else
+ m->u.watcher = object;
+
+ m->ismatch = ismatch;
+ if (*match_list == NULL)
+ *match_list = m;
+ else
+ (*match_list)->next = m;
+}
+
+static void nft_bridge_parse_match(struct xtables_match *m,
+ struct iptables_command_state *cs)
+{
+ parse_watcher(m, &cs->match_list, true);
+}
+
+static void nft_bridge_parse_target(struct xtables_target *t,
+ struct iptables_command_state *cs)
+{
+ /* harcoded names :-( */
+ if (strcmp(t->name, "log") == 0 ||
+ strcmp(t->name, "nflog") == 0) {
+ parse_watcher(t, &cs->match_list, false);
+ cs->jumpto = NULL;
+ cs->target = NULL;
+ return;
+ }
+}
+
+struct nft_ruleparse_ops nft_ruleparse_ops_bridge = {
+ .meta = nft_bridge_parse_meta,
+ .payload = nft_bridge_parse_payload,
+ .lookup = nft_bridge_parse_lookup,
+ .match = nft_bridge_parse_match,
+ .target = nft_bridge_parse_target,
+};
diff --git a/iptables/nft-ruleparse-ipv4.c b/iptables/nft-ruleparse-ipv4.c
new file mode 100644
index 00000000..fe65b33c
--- /dev/null
+++ b/iptables/nft-ruleparse-ipv4.c
@@ -0,0 +1,133 @@
+/*
+ * (C) 2012-2014 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 <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+#include "nft-shared.h"
+#include "nft-ruleparse.h"
+#include "xshared.h"
+
+static void nft_ipv4_parse_meta(struct nft_xt_ctx *ctx,
+ const struct nft_xt_ctx_reg *reg,
+ struct nftnl_expr *e,
+ struct iptables_command_state *cs)
+{
+ switch (reg->meta_dreg.key) {
+ case NFT_META_L4PROTO:
+ cs->fw.ip.proto = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
+ if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ cs->fw.ip.invflags |= XT_INV_PROTO;
+ return;
+ default:
+ break;
+ }
+
+ if (parse_meta(ctx, e, reg->meta_dreg.key, cs->fw.ip.iniface,
+ cs->fw.ip.outiface, &cs->fw.ip.invflags) == 0)
+ return;
+
+ ctx->errmsg = "unknown ipv4 meta key";
+}
+
+static void parse_mask_ipv4(const struct nft_xt_ctx_reg *sreg, struct in_addr *mask)
+{
+ mask->s_addr = sreg->bitwise.mask[0];
+}
+
+static bool get_frag(const struct nft_xt_ctx_reg *reg, struct nftnl_expr *e)
+{
+ uint8_t op;
+
+ /* we assume correct mask and xor */
+ if (!reg->bitwise.set)
+ return false;
+
+ /* we assume correct data */
+ op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
+ if (op == NFT_CMP_EQ)
+ return true;
+
+ return false;
+}
+
+static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx,
+ const struct nft_xt_ctx_reg *sreg,
+ struct nftnl_expr *e,
+ struct iptables_command_state *cs)
+{
+ struct in_addr addr;
+ uint8_t proto;
+ bool inv;
+
+ switch (sreg->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 (sreg->bitwise.set) {
+ parse_mask_ipv4(sreg, &cs->fw.ip.smsk);
+ } else {
+ memset(&cs->fw.ip.smsk, 0xff,
+ min(sreg->payload.len, sizeof(struct in_addr)));
+ }
+
+ 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 (sreg->bitwise.set)
+ parse_mask_ipv4(sreg, &cs->fw.ip.dmsk);
+ else
+ memset(&cs->fw.ip.dmsk, 0xff,
+ min(sreg->payload.len, sizeof(struct in_addr)));
+
+ 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;
+ inv = get_frag(sreg, e);
+ if (inv)
+ cs->fw.ip.invflags |= IPT_INV_FRAG;
+ break;
+ case offsetof(struct iphdr, ttl):
+ if (nft_parse_hl(ctx, e, cs) < 0)
+ ctx->errmsg = "invalid ttl field match";
+ break;
+ default:
+ DEBUGP("unknown payload offset %d\n", sreg->payload.offset);
+ ctx->errmsg = "unknown payload offset";
+ break;
+ }
+}
+
+struct nft_ruleparse_ops nft_ruleparse_ops_ipv4 = {
+ .meta = nft_ipv4_parse_meta,
+ .payload = nft_ipv4_parse_payload,
+};
diff --git a/iptables/nft-ruleparse-ipv6.c b/iptables/nft-ruleparse-ipv6.c
new file mode 100644
index 00000000..29b08580
--- /dev/null
+++ b/iptables/nft-ruleparse-ipv6.c
@@ -0,0 +1,110 @@
+/*
+ * (C) 2012-2014 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 <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip6.h>
+
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+#include "nft-shared.h"
+#include "nft-ruleparse.h"
+#include "xshared.h"
+
+static void nft_ipv6_parse_meta(struct nft_xt_ctx *ctx,
+ const struct nft_xt_ctx_reg *reg,
+ struct nftnl_expr *e,
+ struct iptables_command_state *cs)
+{
+ switch (reg->meta_dreg.key) {
+ case NFT_META_L4PROTO:
+ cs->fw6.ipv6.proto = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
+ if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ cs->fw6.ipv6.invflags |= XT_INV_PROTO;
+ return;
+ default:
+ break;
+ }
+
+ if (parse_meta(ctx, e, reg->meta_dreg.key, cs->fw6.ipv6.iniface,
+ cs->fw6.ipv6.outiface, &cs->fw6.ipv6.invflags) == 0)
+ return;
+
+ ctx->errmsg = "unknown ipv6 meta key";
+}
+
+static void parse_mask_ipv6(const struct nft_xt_ctx_reg *reg,
+ struct in6_addr *mask)
+{
+ memcpy(mask, reg->bitwise.mask, sizeof(struct in6_addr));
+}
+
+static void nft_ipv6_parse_payload(struct nft_xt_ctx *ctx,
+ const struct nft_xt_ctx_reg *reg,
+ struct nftnl_expr *e,
+ struct iptables_command_state *cs)
+{
+ struct in6_addr addr;
+ uint8_t proto;
+ bool inv;
+
+ switch (reg->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 (reg->bitwise.set)
+ parse_mask_ipv6(reg, &cs->fw6.ipv6.smsk);
+ else
+ memset(&cs->fw6.ipv6.smsk, 0xff,
+ min(reg->payload.len, sizeof(struct in6_addr)));
+
+ if (inv)
+ cs->fw6.ipv6.invflags |= IP6T_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 (reg->bitwise.set)
+ parse_mask_ipv6(reg, &cs->fw6.ipv6.dmsk);
+ else
+ memset(&cs->fw6.ipv6.dmsk, 0xff,
+ min(reg->payload.len, sizeof(struct in6_addr)));
+
+ if (inv)
+ cs->fw6.ipv6.invflags |= IP6T_INV_DSTIP;
+ break;
+ case offsetof(struct ip6_hdr, ip6_nxt):
+ get_cmp_data(e, &proto, sizeof(proto), &inv);
+ cs->fw6.ipv6.proto = proto;
+ if (inv)
+ cs->fw6.ipv6.invflags |= IP6T_INV_PROTO;
+ case offsetof(struct ip6_hdr, ip6_hlim):
+ if (nft_parse_hl(ctx, e, cs) < 0)
+ ctx->errmsg = "invalid ttl field match";
+ break;
+ default:
+ DEBUGP("unknown payload offset %d\n", reg->payload.offset);
+ ctx->errmsg = "unknown payload offset";
+ break;
+ }
+}
+
+struct nft_ruleparse_ops nft_ruleparse_ops_ipv6 = {
+ .meta = nft_ipv6_parse_meta,
+ .payload = nft_ipv6_parse_payload,
+};
diff --git a/iptables/nft-ruleparse.c b/iptables/nft-ruleparse.c
new file mode 100644
index 00000000..3b1cbe4f
--- /dev/null
+++ b/iptables/nft-ruleparse.c
@@ -0,0 +1,1177 @@
+/*
+ * (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 <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <linux/netfilter/nf_log.h>
+#include <linux/netfilter/xt_limit.h>
+#include <linux/netfilter/xt_mark.h>
+#include <linux/netfilter/xt_NFLOG.h>
+#include <linux/netfilter/xt_pkttype.h>
+
+#include <linux/netfilter_ipv6/ip6t_hl.h>
+
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+#include <xtables.h>
+
+#include "nft-ruleparse.h"
+#include "nft.h"
+
+static struct xtables_match *
+nft_find_match_in_cs(struct iptables_command_state *cs, const char *name)
+{
+ struct xtables_rule_match *rm;
+ struct ebt_match *ebm;
+
+ for (ebm = cs->match_list; ebm; ebm = ebm->next) {
+ if (ebm->ismatch &&
+ !strcmp(ebm->u.match->m->u.user.name, name))
+ return ebm->u.match;
+ }
+ for (rm = cs->matches; rm; rm = rm->next) {
+ if (!strcmp(rm->match->m->u.user.name, name))
+ return rm->match;
+ }
+ return NULL;
+}
+
+void *
+nft_create_match(struct nft_xt_ctx *ctx,
+ struct iptables_command_state *cs,
+ const char *name, bool reuse)
+{
+ struct xtables_match *match;
+ struct xt_entry_match *m;
+ unsigned int size;
+
+ if (reuse) {
+ match = nft_find_match_in_cs(cs, name);
+ if (match)
+ return match->m->data;
+ }
+
+ match = xtables_find_match(name, XTF_TRY_LOAD,
+ &cs->matches);
+ if (!match)
+ return NULL;
+
+ size = XT_ALIGN(sizeof(struct xt_entry_match)) + match->size;
+ m = xtables_calloc(1, size);
+ m->u.match_size = size;
+ m->u.user.revision = match->revision;
+
+ strcpy(m->u.user.name, match->name);
+ match->m = m;
+
+ xs_init_match(match);
+
+ if (ctx->h->ops->rule_parse->match)
+ ctx->h->ops->rule_parse->match(match, cs);
+
+ return match->m->data;
+}
+
+static void *
+__nft_create_target(struct nft_xt_ctx *ctx, const char *name, size_t tgsize)
+{
+ struct xtables_target *target;
+ size_t size;
+
+ target = xtables_find_target(name, XTF_TRY_LOAD);
+ if (!target)
+ return NULL;
+
+ size = XT_ALIGN(sizeof(*target->t)) + (tgsize ?: target->size);
+
+ target->t = xtables_calloc(1, size);
+ target->t->u.target_size = size;
+ target->t->u.user.revision = target->revision;
+ strcpy(target->t->u.user.name, name);
+
+ xs_init_target(target);
+
+ ctx->cs->jumpto = name;
+ ctx->cs->target = target;
+
+ if (ctx->h->ops->rule_parse->target)
+ ctx->h->ops->rule_parse->target(target, ctx->cs);
+
+ return target->t->data;
+}
+
+void *
+nft_create_target(struct nft_xt_ctx *ctx, const char *name)
+{
+ return __nft_create_target(ctx, name, 0);
+}
+
+static void nft_parse_counter(struct nftnl_expr *e, struct xt_counters *counters)
+{
+ counters->pcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_PACKETS);
+ counters->bcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_BYTES);
+}
+
+static void nft_parse_payload(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ enum nft_registers regnum = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_DREG);
+ struct nft_xt_ctx_reg *reg = nft_xt_ctx_get_dreg(ctx, regnum);
+
+ if (!reg)
+ return;
+
+ reg->type = NFT_XT_REG_PAYLOAD;
+ reg->payload.base = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_BASE);
+ reg->payload.offset = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET);
+ reg->payload.len = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_LEN);
+}
+
+static bool nft_parse_meta_set_common(struct nft_xt_ctx* ctx,
+ struct nft_xt_ctx_reg *sreg)
+{
+ if ((sreg->type != NFT_XT_REG_IMMEDIATE)) {
+ ctx->errmsg = "meta sreg is not an immediate";
+ return false;
+ }
+
+ return true;
+}
+
+static void nft_parse_meta_set(struct nft_xt_ctx *ctx,
+ struct nftnl_expr *e)
+{
+ struct nft_xt_ctx_reg *sreg;
+ enum nft_registers sregnum;
+
+ sregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_META_SREG);
+ sreg = nft_xt_ctx_get_sreg(ctx, sregnum);
+ if (!sreg)
+ return;
+
+ switch (nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY)) {
+ case NFT_META_NFTRACE:
+ if (!nft_parse_meta_set_common(ctx, sreg))
+ return;
+
+ if (sreg->immediate.data[0] == 0) {
+ ctx->errmsg = "meta sreg immediate is 0";
+ return;
+ }
+
+ if (!nft_create_target(ctx, "TRACE"))
+ ctx->errmsg = "target TRACE not found";
+ break;
+ case NFT_META_BRI_BROUTE:
+ if (!nft_parse_meta_set_common(ctx, sreg))
+ return;
+
+ ctx->cs->jumpto = "DROP";
+ break;
+ case NFT_META_MARK: {
+ struct xt_mark_tginfo2 *mt;
+
+ if (!nft_parse_meta_set_common(ctx, sreg))
+ return;
+
+ mt = nft_create_target(ctx, "MARK");
+ if (!mt) {
+ ctx->errmsg = "target MARK not found";
+ return;
+ }
+
+ mt->mark = sreg->immediate.data[0];
+ if (sreg->bitwise.set)
+ mt->mask = sreg->bitwise.mask[0];
+ else
+ mt->mask = ~0u;
+ break;
+ }
+ default:
+ ctx->errmsg = "meta sreg key not supported";
+ break;
+ }
+}
+
+static void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ struct nft_xt_ctx_reg *reg;
+
+ if (nftnl_expr_is_set(e, NFTNL_EXPR_META_SREG)) {
+ nft_parse_meta_set(ctx, e);
+ return;
+ }
+
+ reg = nft_xt_ctx_get_dreg(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG));
+ if (!reg)
+ return;
+
+ reg->meta_dreg.key = nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY);
+ reg->type = NFT_XT_REG_META_DREG;
+}
+
+static void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ enum nft_registers sregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_SREG);
+ enum nft_registers dregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_DREG);
+ struct nft_xt_ctx_reg *sreg = nft_xt_ctx_get_sreg(ctx, sregnum);
+ struct nft_xt_ctx_reg *dreg = sreg;
+ const void *data;
+ uint32_t len;
+
+ if (!sreg)
+ return;
+
+ if (sregnum != dregnum) {
+ dreg = nft_xt_ctx_get_sreg(ctx, dregnum); /* sreg, do NOT clear ... */
+ if (!dreg)
+ return;
+
+ *dreg = *sreg; /* .. and copy content instead */
+ }
+
+ data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_XOR, &len);
+
+ if (len > sizeof(dreg->bitwise.xor)) {
+ ctx->errmsg = "bitwise xor too large";
+ return;
+ }
+
+ memcpy(dreg->bitwise.xor, data, len);
+
+ data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_MASK, &len);
+
+ if (len > sizeof(dreg->bitwise.mask)) {
+ ctx->errmsg = "bitwise mask too large";
+ return;
+ }
+
+ memcpy(dreg->bitwise.mask, data, len);
+
+ dreg->bitwise.set = true;
+}
+
+static void nft_parse_icmp(struct nft_xt_ctx *ctx,
+ struct iptables_command_state *cs,
+ struct nft_xt_ctx_reg *sreg,
+ uint8_t op, const char *data, size_t dlen)
+{
+ struct ipt_icmp icmp = {
+ .type = UINT8_MAX,
+ .code = { 0, UINT8_MAX },
+ }, *icmpp;
+
+ if (dlen < 1)
+ goto out_err_len;
+
+ switch (sreg->payload.offset) {
+ case 0:
+ icmp.type = data[0];
+ if (dlen == 1)
+ break;
+ dlen--;
+ data++;
+ /* fall through */
+ case 1:
+ if (dlen > 1)
+ goto out_err_len;
+ icmp.code[0] = icmp.code[1] = data[0];
+ break;
+ default:
+ ctx->errmsg = "unexpected payload offset";
+ return;
+ }
+
+ switch (ctx->h->family) {
+ case NFPROTO_IPV4:
+ icmpp = nft_create_match(ctx, cs, "icmp", false);
+ break;
+ case NFPROTO_IPV6:
+ if (icmp.type == UINT8_MAX) {
+ ctx->errmsg = "icmp6 code with any type match not supported";
+ return;
+ }
+ icmpp = nft_create_match(ctx, cs, "icmp6", false);
+ break;
+ default:
+ ctx->errmsg = "unexpected family for icmp match";
+ return;
+ }
+
+ if (!icmpp) {
+ ctx->errmsg = "icmp match extension not found";
+ return;
+ }
+ memcpy(icmpp, &icmp, sizeof(icmp));
+ return;
+
+out_err_len:
+ ctx->errmsg = "unexpected RHS data length";
+}
+
+static void port_match_single_to_range(__u16 *ports, __u8 *invflags,
+ uint8_t op, int port, __u8 invflag)
+{
+ if (port < 0)
+ return;
+
+ switch (op) {
+ case NFT_CMP_NEQ:
+ *invflags |= invflag;
+ /* fallthrough */
+ case NFT_CMP_EQ:
+ ports[0] = port;
+ ports[1] = port;
+ break;
+ case NFT_CMP_LT:
+ ports[1] = max(port - 1, 1);
+ break;
+ case NFT_CMP_LTE:
+ ports[1] = port;
+ break;
+ case NFT_CMP_GT:
+ ports[0] = min(port + 1, UINT16_MAX);
+ break;
+ case NFT_CMP_GTE:
+ ports[0] = port;
+ break;
+ }
+}
+
+static void nft_parse_udp(struct nft_xt_ctx *ctx,
+ struct iptables_command_state *cs,
+ int sport, int dport,
+ uint8_t op)
+{
+ struct xt_udp *udp = nft_create_match(ctx, cs, "udp", true);
+
+ if (!udp) {
+ ctx->errmsg = "udp match extension not found";
+ return;
+ }
+
+ port_match_single_to_range(udp->spts, &udp->invflags,
+ op, sport, XT_UDP_INV_SRCPT);
+ port_match_single_to_range(udp->dpts, &udp->invflags,
+ op, dport, XT_UDP_INV_DSTPT);
+}
+
+static void nft_parse_tcp(struct nft_xt_ctx *ctx,
+ struct iptables_command_state *cs,
+ int sport, int dport,
+ uint8_t op)
+{
+ struct xt_tcp *tcp = nft_create_match(ctx, cs, "tcp", true);
+
+ if (!tcp) {
+ ctx->errmsg = "tcp match extension not found";
+ return;
+ }
+
+ port_match_single_to_range(tcp->spts, &tcp->invflags,
+ op, sport, XT_TCP_INV_SRCPT);
+ port_match_single_to_range(tcp->dpts, &tcp->invflags,
+ op, dport, XT_TCP_INV_DSTPT);
+}
+
+static void nft_parse_th_port(struct nft_xt_ctx *ctx,
+ struct iptables_command_state *cs,
+ uint8_t proto,
+ int sport, int dport, uint8_t op)
+{
+ switch (proto) {
+ case IPPROTO_UDP:
+ nft_parse_udp(ctx, cs, sport, dport, op);
+ break;
+ case IPPROTO_TCP:
+ nft_parse_tcp(ctx, cs, sport, dport, op);
+ break;
+ default:
+ ctx->errmsg = "unknown layer 4 protocol for TH match";
+ }
+}
+
+static void nft_parse_tcp_flags(struct nft_xt_ctx *ctx,
+ struct iptables_command_state *cs,
+ uint8_t op, uint8_t flags, uint8_t mask)
+{
+ struct xt_tcp *tcp = nft_create_match(ctx, cs, "tcp", true);
+
+ if (!tcp) {
+ ctx->errmsg = "tcp match extension not found";
+ return;
+ }
+
+ if (op == NFT_CMP_NEQ)
+ tcp->invflags |= XT_TCP_INV_FLAGS;
+ tcp->flg_cmp = flags;
+ tcp->flg_mask = mask;
+}
+
+static void nft_parse_transport(struct nft_xt_ctx *ctx,
+ struct nftnl_expr *e,
+ struct iptables_command_state *cs)
+{
+ struct nft_xt_ctx_reg *sreg;
+ enum nft_registers reg;
+ uint32_t sdport;
+ uint16_t port;
+ uint8_t proto, op;
+ unsigned int len;
+
+ switch (ctx->h->family) {
+ case NFPROTO_IPV4:
+ proto = ctx->cs->fw.ip.proto;
+ break;
+ case NFPROTO_IPV6:
+ proto = ctx->cs->fw6.ipv6.proto;
+ break;
+ default:
+ ctx->errmsg = "invalid family for TH match";
+ return;
+ }
+
+ nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
+ op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
+
+ reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG);
+ sreg = nft_xt_ctx_get_sreg(ctx, reg);
+ if (!sreg)
+ return;
+
+ if (sreg->type != NFT_XT_REG_PAYLOAD) {
+ ctx->errmsg = "sgreg not payload";
+ return;
+ }
+
+ switch (proto) {
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ break;
+ case IPPROTO_ICMP:
+ case IPPROTO_ICMPV6:
+ nft_parse_icmp(ctx, cs, sreg, op,
+ nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len),
+ len);
+ return;
+ default:
+ ctx->errmsg = "unsupported layer 4 protocol value";
+ return;
+ }
+
+ switch(sreg->payload.offset) {
+ case 0: /* th->sport */
+ switch (len) {
+ case 2: /* load sport only */
+ port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA));
+ nft_parse_th_port(ctx, cs, proto, port, -1, op);
+ return;
+ case 4: /* load both src and dst port */
+ sdport = ntohl(nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA));
+ nft_parse_th_port(ctx, cs, proto, sdport >> 16, sdport & 0xffff, op);
+ return;
+ }
+ break;
+ case 2: /* th->dport */
+ switch (len) {
+ case 2:
+ port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA));
+ nft_parse_th_port(ctx, cs, proto, -1, port, op);
+ return;
+ }
+ break;
+ case 13: /* th->flags */
+ if (len == 1 && proto == IPPROTO_TCP) {
+ uint8_t flags = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
+ uint8_t mask = ~0;
+
+ if (sreg->bitwise.set)
+ memcpy(&mask, &sreg->bitwise.mask, sizeof(mask));
+
+ nft_parse_tcp_flags(ctx, cs, op, flags, mask);
+ }
+ return;
+ }
+}
+
+static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ struct nft_xt_ctx_reg *sreg;
+ uint32_t reg;
+
+ reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG);
+
+ sreg = nft_xt_ctx_get_sreg(ctx, reg);
+ if (!sreg)
+ return;
+
+ switch (sreg->type) {
+ case NFT_XT_REG_UNDEF:
+ ctx->errmsg = "cmp sreg undef";
+ break;
+ case NFT_XT_REG_META_DREG:
+ ctx->h->ops->rule_parse->meta(ctx, sreg, e, ctx->cs);
+ break;
+ case NFT_XT_REG_PAYLOAD:
+ switch (sreg->payload.base) {
+ case NFT_PAYLOAD_LL_HEADER:
+ if (ctx->h->family == NFPROTO_BRIDGE)
+ ctx->h->ops->rule_parse->payload(ctx, sreg, e, ctx->cs);
+ break;
+ case NFT_PAYLOAD_NETWORK_HEADER:
+ ctx->h->ops->rule_parse->payload(ctx, sreg, e, ctx->cs);
+ break;
+ case NFT_PAYLOAD_TRANSPORT_HEADER:
+ nft_parse_transport(ctx, e, ctx->cs);
+ break;
+ }
+
+ break;
+ default:
+ ctx->errmsg = "cmp sreg has unknown type";
+ break;
+ }
+}
+
+static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ const char *chain = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN);
+ struct iptables_command_state *cs = ctx->cs;
+ int verdict;
+
+ if (nftnl_expr_is_set(e, NFTNL_EXPR_IMM_DATA)) {
+ struct nft_xt_ctx_reg *dreg;
+ const void *imm_data;
+ uint32_t len;
+
+ imm_data = nftnl_expr_get(e, NFTNL_EXPR_IMM_DATA, &len);
+ dreg = nft_xt_ctx_get_dreg(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_DREG));
+ if (!dreg)
+ return;
+
+ if (len > sizeof(dreg->immediate.data)) {
+ ctx->errmsg = "oversized immediate data";
+ return;
+ }
+
+ memcpy(dreg->immediate.data, imm_data, len);
+ dreg->immediate.len = len;
+ dreg->type = NFT_XT_REG_IMMEDIATE;
+
+ return;
+ }
+
+ verdict = nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_VERDICT);
+ /* Standard target? */
+ switch(verdict) {
+ case NF_ACCEPT:
+ if (cs->jumpto && strcmp(ctx->table, "broute") == 0)
+ break;
+ cs->jumpto = "ACCEPT";
+ break;
+ case NF_DROP:
+ cs->jumpto = "DROP";
+ break;
+ case NFT_RETURN:
+ cs->jumpto = "RETURN";
+ break;;
+ case NFT_GOTO:
+ if (ctx->h->ops->set_goto_flag)
+ ctx->h->ops->set_goto_flag(cs);
+ /* fall through */
+ case NFT_JUMP:
+ cs->jumpto = chain;
+ /* fall through */
+ default:
+ return;
+ }
+
+ if (!nft_create_target(ctx, cs->jumpto))
+ ctx->errmsg = "verdict extension not found";
+}
+
+static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ uint32_t mt_len;
+ const char *mt_name = nftnl_expr_get_str(e, NFTNL_EXPR_MT_NAME);
+ const void *mt_info = nftnl_expr_get(e, NFTNL_EXPR_MT_INFO, &mt_len);
+ struct xtables_match *match;
+ struct xtables_rule_match **matches;
+ struct xt_entry_match *m;
+
+ switch (ctx->h->family) {
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ case NFPROTO_BRIDGE:
+ matches = &ctx->cs->matches;
+ break;
+ default:
+ fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n",
+ ctx->h->family);
+ exit(EXIT_FAILURE);
+ }
+
+ match = xtables_find_match(mt_name, XTF_TRY_LOAD, matches);
+ if (match == NULL) {
+ ctx->errmsg = "match extension not found";
+ return;
+ }
+
+ m = xtables_calloc(1, sizeof(struct xt_entry_match) + mt_len);
+ memcpy(&m->data, mt_info, mt_len);
+ m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match));
+ m->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
+ strcpy(m->u.user.name, match->name);
+
+ match->m = m;
+
+ if (ctx->h->ops->rule_parse->match != NULL)
+ ctx->h->ops->rule_parse->match(match, ctx->cs);
+}
+
+static void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ uint32_t tg_len;
+ const char *targname = nftnl_expr_get_str(e, NFTNL_EXPR_TG_NAME);
+ const void *targinfo = nftnl_expr_get(e, NFTNL_EXPR_TG_INFO, &tg_len);
+ void *data;
+
+ data = __nft_create_target(ctx, targname, tg_len);
+ if (!data)
+ ctx->errmsg = "target extension not found";
+ else
+ memcpy(data, targinfo, tg_len);
+}
+
+static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ __u32 burst = nftnl_expr_get_u32(e, NFTNL_EXPR_LIMIT_BURST);
+ __u64 unit = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_UNIT);
+ __u64 rate = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_RATE);
+ struct xt_rateinfo *rinfo;
+
+ switch (ctx->h->family) {
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ case NFPROTO_BRIDGE:
+ break;
+ default:
+ fprintf(stderr, "BUG: nft_parse_limit() unknown family %d\n",
+ ctx->h->family);
+ exit(EXIT_FAILURE);
+ }
+
+ rinfo = nft_create_match(ctx, ctx->cs, "limit", false);
+ if (!rinfo) {
+ ctx->errmsg = "limit match extension not found";
+ return;
+ }
+
+ rinfo->avg = XT_LIMIT_SCALE * unit / rate;
+ rinfo->burst = burst;
+}
+
+static void nft_parse_lookup(struct nft_xt_ctx *ctx, struct nft_handle *h,
+ struct nftnl_expr *e)
+{
+ if (ctx->h->ops->rule_parse->lookup)
+ ctx->h->ops->rule_parse->lookup(ctx, e);
+}
+
+static void nft_parse_log(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ /*
+ * In order to handle the longer log-prefix supported by nft, instead of
+ * using struct xt_nflog_info, we use a struct with a compatible layout, but
+ * a larger buffer for the prefix.
+ */
+ struct xt_nflog_info_nft {
+ __u32 len;
+ __u16 group;
+ __u16 threshold;
+ __u16 flags;
+ __u16 pad;
+ char prefix[NF_LOG_PREFIXLEN];
+ } info = {
+ .group = nftnl_expr_get_u16(e, NFTNL_EXPR_LOG_GROUP),
+ .threshold = nftnl_expr_get_u16(e, NFTNL_EXPR_LOG_QTHRESHOLD),
+ };
+ void *data;
+
+ if (nftnl_expr_is_set(e, NFTNL_EXPR_LOG_SNAPLEN)) {
+ info.len = nftnl_expr_get_u32(e, NFTNL_EXPR_LOG_SNAPLEN);
+ info.flags = XT_NFLOG_F_COPY_LEN;
+ }
+ if (nftnl_expr_is_set(e, NFTNL_EXPR_LOG_PREFIX))
+ snprintf(info.prefix, sizeof(info.prefix), "%s",
+ nftnl_expr_get_str(e, NFTNL_EXPR_LOG_PREFIX));
+
+ data = __nft_create_target(ctx, "NFLOG",
+ XT_ALIGN(sizeof(struct xt_nflog_info_nft)));
+ if (!data)
+ ctx->errmsg = "NFLOG target extension not found";
+ else
+ memcpy(data, &info, sizeof(info));
+}
+
+static void nft_parse_udp_range(struct nft_xt_ctx *ctx,
+ struct iptables_command_state *cs,
+ int sport_from, int sport_to,
+ int dport_from, int dport_to,
+ uint8_t op)
+{
+ struct xt_udp *udp = nft_create_match(ctx, cs, "udp", true);
+
+ if (!udp) {
+ ctx->errmsg = "udp match extension not found";
+ return;
+ }
+
+ if (sport_from >= 0) {
+ switch (op) {
+ case NFT_RANGE_NEQ:
+ udp->invflags |= XT_UDP_INV_SRCPT;
+ /* fallthrough */
+ case NFT_RANGE_EQ:
+ udp->spts[0] = sport_from;
+ udp->spts[1] = sport_to;
+ break;
+ }
+ }
+
+ if (dport_to >= 0) {
+ switch (op) {
+ case NFT_CMP_NEQ:
+ udp->invflags |= XT_UDP_INV_DSTPT;
+ /* fallthrough */
+ case NFT_CMP_EQ:
+ udp->dpts[0] = dport_from;
+ udp->dpts[1] = dport_to;
+ break;
+ }
+ }
+}
+
+static void nft_parse_tcp_range(struct nft_xt_ctx *ctx,
+ struct iptables_command_state *cs,
+ int sport_from, int sport_to,
+ int dport_from, int dport_to,
+ uint8_t op)
+{
+ struct xt_tcp *tcp = nft_create_match(ctx, cs, "tcp", true);
+
+ if (!tcp) {
+ ctx->errmsg = "tcp match extension not found";
+ return;
+ }
+
+ if (sport_from >= 0) {
+ switch (op) {
+ case NFT_RANGE_NEQ:
+ tcp->invflags |= XT_TCP_INV_SRCPT;
+ /* fallthrough */
+ case NFT_RANGE_EQ:
+ tcp->spts[0] = sport_from;
+ tcp->spts[1] = sport_to;
+ break;
+ }
+ }
+
+ if (dport_to >= 0) {
+ switch (op) {
+ case NFT_CMP_NEQ:
+ tcp->invflags |= XT_TCP_INV_DSTPT;
+ /* fallthrough */
+ case NFT_CMP_EQ:
+ tcp->dpts[0] = dport_from;
+ tcp->dpts[1] = dport_to;
+ break;
+ }
+ }
+}
+
+static void nft_parse_th_port_range(struct nft_xt_ctx *ctx,
+ struct iptables_command_state *cs,
+ uint8_t proto,
+ int sport_from, int sport_to,
+ int dport_from, int dport_to, uint8_t op)
+{
+ switch (proto) {
+ case IPPROTO_UDP:
+ nft_parse_udp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op);
+ break;
+ case IPPROTO_TCP:
+ nft_parse_tcp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op);
+ break;
+ }
+}
+
+static void nft_parse_transport_range(struct nft_xt_ctx *ctx,
+ const struct nft_xt_ctx_reg *sreg,
+ struct nftnl_expr *e,
+ struct iptables_command_state *cs)
+{
+ unsigned int len_from, len_to;
+ uint8_t proto, op;
+ uint16_t from, to;
+
+ switch (ctx->h->family) {
+ case NFPROTO_IPV4:
+ proto = ctx->cs->fw.ip.proto;
+ break;
+ case NFPROTO_IPV6:
+ proto = ctx->cs->fw6.ipv6.proto;
+ break;
+ default:
+ proto = 0;
+ break;
+ }
+
+ nftnl_expr_get(e, NFTNL_EXPR_RANGE_FROM_DATA, &len_from);
+ nftnl_expr_get(e, NFTNL_EXPR_RANGE_FROM_DATA, &len_to);
+ if (len_to != len_from || len_to != 2)
+ return;
+
+ op = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_OP);
+
+ from = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_FROM_DATA));
+ to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA));
+
+ switch (sreg->payload.offset) {
+ case 0:
+ nft_parse_th_port_range(ctx, cs, proto, from, to, -1, -1, op);
+ return;
+ case 2:
+ to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA));
+ nft_parse_th_port_range(ctx, cs, proto, -1, -1, from, to, op);
+ return;
+ }
+}
+
+static void nft_parse_range(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ struct nft_xt_ctx_reg *sreg;
+ uint32_t reg;
+
+ reg = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_SREG);
+ sreg = nft_xt_ctx_get_sreg(ctx, reg);
+
+ switch (sreg->type) {
+ case NFT_XT_REG_UNDEF:
+ ctx->errmsg = "range sreg undef";
+ break;
+ case NFT_XT_REG_PAYLOAD:
+ switch (sreg->payload.base) {
+ case NFT_PAYLOAD_TRANSPORT_HEADER:
+ nft_parse_transport_range(ctx, sreg, e, ctx->cs);
+ break;
+ default:
+ ctx->errmsg = "range with unknown payload base";
+ break;
+ }
+ break;
+ default:
+ ctx->errmsg = "range sreg type unsupported";
+ break;
+ }
+}
+
+bool nft_rule_to_iptables_command_state(struct nft_handle *h,
+ const struct nftnl_rule *r,
+ struct iptables_command_state *cs)
+{
+ struct nftnl_expr_iter *iter;
+ struct nftnl_expr *expr;
+ struct nft_xt_ctx ctx = {
+ .cs = cs,
+ .h = h,
+ .table = nftnl_rule_get_str(r, NFTNL_RULE_TABLE),
+ };
+ bool ret = true;
+
+ iter = nftnl_expr_iter_create(r);
+ if (iter == NULL)
+ return false;
+
+ ctx.iter = iter;
+ expr = nftnl_expr_iter_next(iter);
+ while (expr != NULL) {
+ const char *name =
+ nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
+
+ if (strcmp(name, "counter") == 0)
+ nft_parse_counter(expr, &ctx.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);
+ else if (strcmp(name, "limit") == 0)
+ nft_parse_limit(&ctx, expr);
+ else if (strcmp(name, "lookup") == 0)
+ nft_parse_lookup(&ctx, h, expr);
+ else if (strcmp(name, "log") == 0)
+ nft_parse_log(&ctx, expr);
+ else if (strcmp(name, "range") == 0)
+ nft_parse_range(&ctx, expr);
+
+ if (ctx.errmsg) {
+ fprintf(stderr, "Error: %s\n", ctx.errmsg);
+ ctx.errmsg = NULL;
+ ret = false;
+ }
+
+ expr = nftnl_expr_iter_next(iter);
+ }
+
+ nftnl_expr_iter_destroy(iter);
+
+ if (nftnl_rule_is_set(r, NFTNL_RULE_USERDATA)) {
+ const void *data;
+ uint32_t len, size;
+ const char *comment;
+
+ data = nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &len);
+ comment = get_comment(data, len);
+ if (comment) {
+ struct xtables_match *match;
+ struct xt_entry_match *m;
+
+ match = xtables_find_match("comment", XTF_TRY_LOAD,
+ &cs->matches);
+ if (match == NULL)
+ return false;
+
+ size = XT_ALIGN(sizeof(struct xt_entry_match))
+ + match->size;
+ m = xtables_calloc(1, size);
+
+ strncpy((char *)m->data, comment, match->size - 1);
+ m->u.match_size = size;
+ m->u.user.revision = 0;
+ strcpy(m->u.user.name, match->name);
+
+ match->m = m;
+ }
+ }
+
+ if (!cs->jumpto)
+ cs->jumpto = "";
+
+ if (!ret)
+ xtables_error(VERSION_PROBLEM, "Parsing nftables rule failed");
+ return ret;
+}
+
+static void parse_ifname(const char *name, unsigned int len, char *dst)
+{
+ if (len == 0)
+ return;
+
+ memcpy(dst, name, len);
+ if (name[len - 1] == '\0')
+ return;
+
+ if (len >= IFNAMSIZ)
+ return;
+
+ /* wildcard */
+ dst[len++] = '+';
+ if (len >= IFNAMSIZ)
+ return;
+ dst[len++] = 0;
+}
+
+static void parse_invalid_iface(char *iface, uint8_t *invflags, uint8_t invbit)
+{
+ if (*invflags & invbit || strcmp(iface, "INVAL/D"))
+ return;
+
+ /* nft's poor "! -o +" excuse */
+ *invflags |= invbit;
+ iface[0] = '+';
+ iface[1] = '\0';
+}
+
+static uint32_t get_meta_mask(struct nft_xt_ctx *ctx, enum nft_registers sreg)
+{
+ struct nft_xt_ctx_reg *reg = nft_xt_ctx_get_sreg(ctx, sreg);
+
+ if (reg->bitwise.set)
+ return reg->bitwise.mask[0];
+
+ return ~0u;
+}
+
+static int parse_meta_mark(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ struct xt_mark_mtinfo1 *mark;
+ uint32_t value;
+
+ mark = nft_create_match(ctx, ctx->cs, "mark", false);
+ if (!mark)
+ return -1;
+
+ if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ mark->invert = 1;
+
+ value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
+ mark->mark = value;
+ mark->mask = get_meta_mask(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG));
+
+ return 0;
+}
+
+static int parse_meta_pkttype(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ struct xt_pkttype_info *pkttype;
+ uint8_t value;
+
+ pkttype = nft_create_match(ctx, ctx->cs, "pkttype", false);
+ if (!pkttype)
+ return -1;
+
+ if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ pkttype->invert = 1;
+
+ value = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
+ pkttype->pkttype = value;
+
+ return 0;
+}
+
+int parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, uint8_t key,
+ char *iniface, char *outiface, uint8_t *invflags)
+{
+ uint32_t value;
+ const void *ifname;
+ uint32_t len;
+
+ switch(key) {
+ case NFT_META_IIF:
+ value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
+ if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ *invflags |= IPT_INV_VIA_IN;
+
+ if_indextoname(value, iniface);
+ break;
+ case NFT_META_OIF:
+ value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
+ if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ *invflags |= IPT_INV_VIA_OUT;
+
+ if_indextoname(value, outiface);
+ break;
+ case NFT_META_BRI_IIFNAME:
+ case NFT_META_IIFNAME:
+ ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
+ if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ *invflags |= IPT_INV_VIA_IN;
+
+ parse_ifname(ifname, len, iniface);
+ parse_invalid_iface(iniface, invflags, IPT_INV_VIA_IN);
+ break;
+ case NFT_META_BRI_OIFNAME:
+ case NFT_META_OIFNAME:
+ ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
+ if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
+ *invflags |= IPT_INV_VIA_OUT;
+
+ parse_ifname(ifname, len, outiface);
+ parse_invalid_iface(outiface, invflags, IPT_INV_VIA_OUT);
+ break;
+ case NFT_META_MARK:
+ parse_meta_mark(ctx, e);
+ break;
+ case NFT_META_PKTTYPE:
+ parse_meta_pkttype(ctx, e);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+int nft_parse_hl(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
+ struct iptables_command_state *cs)
+{
+ struct ip6t_hl_info *info;
+ uint8_t hl, mode;
+ int op;
+
+ hl = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
+ op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
+
+ switch (op) {
+ case NFT_CMP_NEQ:
+ mode = IP6T_HL_NE;
+ break;
+ case NFT_CMP_EQ:
+ mode = IP6T_HL_EQ;
+ break;
+ case NFT_CMP_LT:
+ mode = IP6T_HL_LT;
+ break;
+ case NFT_CMP_GT:
+ mode = IP6T_HL_GT;
+ break;
+ case NFT_CMP_LTE:
+ mode = IP6T_HL_LT;
+ if (hl == 255)
+ return -1;
+ hl++;
+ break;
+ case NFT_CMP_GTE:
+ mode = IP6T_HL_GT;
+ if (hl == 0)
+ return -1;
+ hl--;
+ break;
+ default:
+ return -1;
+ }
+
+ /* ipt_ttl_info and ip6t_hl_info have same layout,
+ * IPT_TTL_x and IP6T_HL_x are aliases as well, so
+ * just use HL for both ipv4 and ipv6.
+ */
+ switch (ctx->h->family) {
+ case NFPROTO_IPV4:
+ info = nft_create_match(ctx, ctx->cs, "ttl", false);
+ break;
+ case NFPROTO_IPV6:
+ info = nft_create_match(ctx, ctx->cs, "hl", false);
+ break;
+ default:
+ return -1;
+ }
+
+ if (!info)
+ return -1;
+
+ info->hop_limit = hl;
+ info->mode = mode;
+
+ return 0;
+}
diff --git a/iptables/nft-ruleparse.h b/iptables/nft-ruleparse.h
new file mode 100644
index 00000000..62c9160d
--- /dev/null
+++ b/iptables/nft-ruleparse.h
@@ -0,0 +1,136 @@
+#ifndef _NFT_RULEPARSE_H_
+#define _NFT_RULEPARSE_H_
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/expr.h>
+
+#include "xshared.h"
+
+enum nft_ctx_reg_type {
+ NFT_XT_REG_UNDEF,
+ NFT_XT_REG_PAYLOAD,
+ NFT_XT_REG_IMMEDIATE,
+ NFT_XT_REG_META_DREG,
+};
+
+struct nft_xt_ctx_reg {
+ enum nft_ctx_reg_type type:8;
+
+ union {
+ struct {
+ uint32_t base;
+ uint32_t offset;
+ uint32_t len;
+ } payload;
+ struct {
+ uint32_t data[4];
+ uint8_t len;
+ } immediate;
+ struct {
+ uint32_t key;
+ } meta_dreg;
+ struct {
+ uint32_t key;
+ } meta_sreg;
+ };
+
+ struct {
+ uint32_t mask[4];
+ uint32_t xor[4];
+ bool set;
+ } bitwise;
+};
+
+struct nft_xt_ctx {
+ struct iptables_command_state *cs;
+ struct nftnl_expr_iter *iter;
+ struct nft_handle *h;
+ uint32_t flags;
+ const char *table;
+
+ struct nft_xt_ctx_reg regs[1 + 16];
+
+ const char *errmsg;
+};
+
+static inline struct nft_xt_ctx_reg *nft_xt_ctx_get_sreg(struct nft_xt_ctx *ctx, enum nft_registers reg)
+{
+ switch (reg) {
+ case NFT_REG_VERDICT:
+ return &ctx->regs[0];
+ case NFT_REG_1:
+ return &ctx->regs[1];
+ case NFT_REG_2:
+ return &ctx->regs[5];
+ case NFT_REG_3:
+ return &ctx->regs[9];
+ case NFT_REG_4:
+ return &ctx->regs[13];
+ case NFT_REG32_00...NFT_REG32_15:
+ return &ctx->regs[reg - NFT_REG32_00];
+ default:
+ ctx->errmsg = "Unknown register requested";
+ break;
+ }
+
+ return NULL;
+}
+
+static inline void nft_xt_reg_clear(struct nft_xt_ctx_reg *r)
+{
+ r->type = 0;
+ r->bitwise.set = false;
+}
+
+static inline struct nft_xt_ctx_reg *nft_xt_ctx_get_dreg(struct nft_xt_ctx *ctx, enum nft_registers reg)
+{
+ struct nft_xt_ctx_reg *r = nft_xt_ctx_get_sreg(ctx, reg);
+
+ if (r)
+ nft_xt_reg_clear(r);
+
+ return r;
+}
+
+struct nft_ruleparse_ops {
+ void (*meta)(struct nft_xt_ctx *ctx,
+ const struct nft_xt_ctx_reg *sreg,
+ struct nftnl_expr *e,
+ struct iptables_command_state *cs);
+ void (*payload)(struct nft_xt_ctx *ctx,
+ const struct nft_xt_ctx_reg *sreg,
+ struct nftnl_expr *e,
+ struct iptables_command_state *cs);
+ void (*lookup)(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
+ void (*match)(struct xtables_match *m,
+ struct iptables_command_state *cs);
+ void (*target)(struct xtables_target *t,
+ struct iptables_command_state *cs);
+};
+
+extern struct nft_ruleparse_ops nft_ruleparse_ops_arp;
+extern struct nft_ruleparse_ops nft_ruleparse_ops_bridge;
+extern struct nft_ruleparse_ops nft_ruleparse_ops_ipv4;
+extern struct nft_ruleparse_ops nft_ruleparse_ops_ipv6;
+
+void *nft_create_match(struct nft_xt_ctx *ctx,
+ struct iptables_command_state *cs,
+ const char *name, bool reuse);
+void *nft_create_target(struct nft_xt_ctx *ctx, const char *name);
+
+
+bool nft_rule_to_iptables_command_state(struct nft_handle *h,
+ const struct nftnl_rule *r,
+ struct iptables_command_state *cs);
+
+#define min(x, y) ((x) < (y) ? (x) : (y))
+#define max(x, y) ((x) > (y) ? (x) : (y))
+
+int parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, uint8_t key,
+ char *iniface, char *outiface, uint8_t *invflags);
+
+int nft_parse_hl(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
+ struct iptables_command_state *cs);
+
+#endif /* _NFT_RULEPARSE_H_ */
diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
index 74e19cca..6775578b 100644
--- a/iptables/nft-shared.c
+++ b/iptables/nft-shared.c
@@ -10,6 +10,7 @@
* This code has been sponsored by Sophos Astaro <http://www.sophos.com>
*/
+#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
@@ -20,12 +21,6 @@
#include <xtables.h>
-#include <linux/netfilter/nf_log.h>
-#include <linux/netfilter/xt_comment.h>
-#include <linux/netfilter/xt_limit.h>
-#include <linux/netfilter/xt_NFLOG.h>
-#include <linux/netfilter/xt_mark.h>
-
#include <libmnl/libmnl.h>
#include <libnftnl/rule.h>
#include <libnftnl/expr.h>
@@ -152,38 +147,29 @@ void add_cmp_u32(struct nftnl_rule *r, uint32_t val, uint32_t op, uint8_t sreg)
add_cmp_ptr(r, op, &val, sizeof(val), sreg);
}
-void add_iniface(struct nft_handle *h, struct nftnl_rule *r,
- char *iface, uint32_t op)
+void add_iface(struct nft_handle *h, struct nftnl_rule *r,
+ char *iface, uint32_t key, uint32_t op)
{
- int iface_len;
+ int iface_len = strlen(iface);
uint8_t reg;
- iface_len = strlen(iface);
- add_meta(h, r, NFT_META_IIFNAME, &reg);
if (iface[iface_len - 1] == '+') {
- if (iface_len > 1)
- add_cmp_ptr(r, op, iface, iface_len - 1, reg);
+ if (iface_len > 1) {
+ iface_len -= 1;
+ } else if (op != NFT_CMP_EQ) {
+ op = NFT_CMP_EQ;
+ iface = "INVAL/D";
+ iface_len = strlen(iface) + 1;
+ } else {
+ return; /* -o + */
+ }
} else {
- add_cmp_ptr(r, op, iface, iface_len + 1, reg);
+ iface_len += 1;
}
-}
-
-void add_outiface(struct nft_handle *h, struct nftnl_rule *r,
- char *iface, uint32_t op)
-{
- int iface_len;
- uint8_t reg;
- iface_len = strlen(iface);
-
- add_meta(h, r, NFT_META_OIFNAME, &reg);
- if (iface[iface_len - 1] == '+') {
- if (iface_len > 1)
- add_cmp_ptr(r, op, iface, iface_len - 1, reg);
- } else {
- add_cmp_ptr(r, op, iface, iface_len + 1, reg);
- }
+ add_meta(h, r, key, &reg);
+ add_cmp_ptr(r, op, iface, iface_len, reg);
}
void add_addr(struct nft_handle *h, struct nftnl_rule *r,
@@ -267,978 +253,20 @@ bool is_same_interfaces(const char *a_iniface, const char *a_outiface,
return true;
}
-static void parse_ifname(const char *name, unsigned int len, char *dst, unsigned char *mask)
-{
- if (len == 0)
- return;
-
- memcpy(dst, name, len);
- if (name[len - 1] == '\0') {
- if (mask)
- memset(mask, 0xff, len);
- return;
- }
-
- if (len >= IFNAMSIZ)
- return;
-
- /* wildcard */
- dst[len++] = '+';
- if (len >= IFNAMSIZ)
- return;
- dst[len++] = 0;
- if (mask)
- memset(mask, 0xff, len - 2);
-}
-
-static struct xtables_match *
-nft_create_match(struct nft_xt_ctx *ctx,
- struct iptables_command_state *cs,
- const char *name);
-
-static int parse_meta_mark(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
- struct xt_mark_mtinfo1 *mark;
- struct xtables_match *match;
- uint32_t value;
-
- match = nft_create_match(ctx, ctx->cs, "mark");
- if (!match)
- return -1;
-
- mark = (void*)match->m->data;
-
- if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
- mark->invert = 1;
-
- value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
- mark->mark = value;
- if (ctx->flags & NFT_XT_CTX_BITWISE) {
- memcpy(&mark->mask, &ctx->bitwise.mask, sizeof(mark->mask));
- ctx->flags &= ~NFT_XT_CTX_BITWISE;
- } else {
- mark->mask = 0xffffffff;
- }
-
- return 0;
-}
-
-int parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, uint8_t key,
- char *iniface, unsigned char *iniface_mask,
- char *outiface, unsigned char *outiface_mask, uint8_t *invflags)
+void __get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, uint8_t *op)
{
- uint32_t value;
- const void *ifname;
uint32_t len;
- switch(key) {
- case NFT_META_IIF:
- value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
- if (nftnl_expr_get_u32(e, NFTNL_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 = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
- if (nftnl_expr_get_u32(e, NFTNL_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_BRI_IIFNAME:
- case NFT_META_IIFNAME:
- ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
- if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
- *invflags |= IPT_INV_VIA_IN;
-
- parse_ifname(ifname, len, iniface, iniface_mask);
- break;
- case NFT_META_BRI_OIFNAME:
- case NFT_META_OIFNAME:
- ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
- if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
- *invflags |= IPT_INV_VIA_OUT;
-
- parse_ifname(ifname, len, outiface, outiface_mask);
- break;
- case NFT_META_MARK:
- parse_meta_mark(ctx, e);
- break;
- default:
- return -1;
- }
-
- return 0;
-}
-
-static void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
- uint32_t tg_len;
- const char *targname = nftnl_expr_get_str(e, NFTNL_EXPR_TG_NAME);
- const void *targinfo = nftnl_expr_get(e, NFTNL_EXPR_TG_INFO, &tg_len);
- struct xtables_target *target;
- struct xt_entry_target *t;
- size_t size;
-
- target = xtables_find_target(targname, XTF_TRY_LOAD);
- if (target == NULL)
- return;
-
- size = XT_ALIGN(sizeof(struct xt_entry_target)) + tg_len;
-
- t = xtables_calloc(1, size);
- memcpy(&t->data, targinfo, tg_len);
- t->u.target_size = size;
- t->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
- strcpy(t->u.user.name, target->name);
-
- target->t = t;
-
- ctx->h->ops->parse_target(target, ctx->cs);
-}
-
-static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
- uint32_t mt_len;
- const char *mt_name = nftnl_expr_get_str(e, NFTNL_EXPR_MT_NAME);
- const void *mt_info = nftnl_expr_get(e, NFTNL_EXPR_MT_INFO, &mt_len);
- struct xtables_match *match;
- struct xtables_rule_match **matches;
- struct xt_entry_match *m;
-
- switch (ctx->h->family) {
- case NFPROTO_IPV4:
- case NFPROTO_IPV6:
- case NFPROTO_BRIDGE:
- matches = &ctx->cs->matches;
- break;
- default:
- fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n",
- ctx->h->family);
- exit(EXIT_FAILURE);
- }
-
- match = xtables_find_match(mt_name, XTF_TRY_LOAD, matches);
- if (match == NULL)
- return;
-
- m = xtables_calloc(1, sizeof(struct xt_entry_match) + mt_len);
- memcpy(&m->data, mt_info, mt_len);
- m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match));
- m->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
- strcpy(m->u.user.name, match->name);
-
- match->m = m;
-
- if (ctx->h->ops->parse_match != NULL)
- ctx->h->ops->parse_match(match, ctx->cs);
+ memcpy(data, nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len), dlen);
+ *op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
}
void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv)
{
- uint32_t len;
uint8_t op;
- memcpy(data, nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len), dlen);
- op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
- if (op == NFT_CMP_NEQ)
- *inv = true;
- else
- *inv = false;
-}
-
-static void nft_meta_set_to_target(struct nft_xt_ctx *ctx)
-{
- struct xtables_target *target;
- struct xt_entry_target *t;
- unsigned int size;
- const char *targname;
-
- switch (ctx->meta.key) {
- case NFT_META_NFTRACE:
- if (ctx->immediate.data[0] == 0)
- return;
- targname = "TRACE";
- break;
- default:
- return;
- }
-
- target = xtables_find_target(targname, XTF_TRY_LOAD);
- if (target == NULL)
- return;
-
- size = XT_ALIGN(sizeof(struct xt_entry_target)) + target->size;
-
- t = xtables_calloc(1, size);
- t->u.target_size = size;
- t->u.user.revision = target->revision;
- strcpy(t->u.user.name, targname);
-
- target->t = t;
-
- ctx->h->ops->parse_target(target, ctx->cs);
-}
-
-static void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
- ctx->meta.key = nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY);
-
- if (nftnl_expr_is_set(e, NFTNL_EXPR_META_SREG) &&
- (ctx->flags & NFT_XT_CTX_IMMEDIATE) &&
- nftnl_expr_get_u32(e, NFTNL_EXPR_META_SREG) == ctx->immediate.reg) {
- ctx->flags &= ~NFT_XT_CTX_IMMEDIATE;
- nft_meta_set_to_target(ctx);
- return;
- }
-
- ctx->reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
- ctx->flags |= NFT_XT_CTX_META;
-}
-
-static void nft_parse_payload(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
- if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
- memcpy(&ctx->prev_payload, &ctx->payload,
- sizeof(ctx->prev_payload));
- ctx->flags |= NFT_XT_CTX_PREV_PAYLOAD;
- }
-
- ctx->reg = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_DREG);
- ctx->payload.base = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_BASE);
- ctx->payload.offset = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET);
- ctx->payload.len = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_LEN);
- ctx->flags |= NFT_XT_CTX_PAYLOAD;
-}
-
-static void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
- uint32_t reg, len;
- const void *data;
-
- reg = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_SREG);
- if (ctx->reg && reg != ctx->reg)
- return;
-
- reg = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_DREG);
- ctx->reg = reg;
- data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_XOR, &len);
- memcpy(ctx->bitwise.xor, data, len);
- data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_MASK, &len);
- memcpy(ctx->bitwise.mask, data, len);
- ctx->flags |= NFT_XT_CTX_BITWISE;
-}
-
-static struct xtables_match *
-nft_create_match(struct nft_xt_ctx *ctx,
- struct iptables_command_state *cs,
- const char *name)
-{
- struct xtables_match *match;
- struct xt_entry_match *m;
- unsigned int size;
-
- match = xtables_find_match(name, XTF_TRY_LOAD,
- &cs->matches);
- if (!match)
- return NULL;
-
- size = XT_ALIGN(sizeof(struct xt_entry_match)) + match->size;
- m = xtables_calloc(1, size);
- m->u.match_size = size;
- m->u.user.revision = match->revision;
-
- strcpy(m->u.user.name, match->name);
- match->m = m;
-
- xs_init_match(match);
-
- return match;
-}
-
-static struct xt_udp *nft_udp_match(struct nft_xt_ctx *ctx,
- struct iptables_command_state *cs)
-{
- struct xt_udp *udp = ctx->tcpudp.udp;
- struct xtables_match *match;
-
- if (!udp) {
- match = nft_create_match(ctx, cs, "udp");
- if (!match)
- return NULL;
-
- udp = (void*)match->m->data;
- ctx->tcpudp.udp = udp;
- }
-
- return udp;
-}
-
-static struct xt_tcp *nft_tcp_match(struct nft_xt_ctx *ctx,
- struct iptables_command_state *cs)
-{
- struct xt_tcp *tcp = ctx->tcpudp.tcp;
- struct xtables_match *match;
-
- if (!tcp) {
- match = nft_create_match(ctx, cs, "tcp");
- if (!match)
- return NULL;
-
- tcp = (void*)match->m->data;
- ctx->tcpudp.tcp = tcp;
- }
-
- return tcp;
-}
-
-static void nft_parse_udp_range(struct nft_xt_ctx *ctx,
- struct iptables_command_state *cs,
- int sport_from, int sport_to,
- int dport_from, int dport_to,
- uint8_t op)
-{
- struct xt_udp *udp = nft_udp_match(ctx, cs);
-
- if (!udp)
- return;
-
- if (sport_from >= 0) {
- switch (op) {
- case NFT_RANGE_NEQ:
- udp->invflags |= XT_UDP_INV_SRCPT;
- /* fallthrough */
- case NFT_RANGE_EQ:
- udp->spts[0] = sport_from;
- udp->spts[1] = sport_to;
- break;
- }
- }
-
- if (dport_to >= 0) {
- switch (op) {
- case NFT_CMP_NEQ:
- udp->invflags |= XT_UDP_INV_DSTPT;
- /* fallthrough */
- case NFT_CMP_EQ:
- udp->dpts[0] = dport_from;
- udp->dpts[1] = dport_to;
- break;
- }
- }
-}
-
-static void nft_parse_tcp_range(struct nft_xt_ctx *ctx,
- struct iptables_command_state *cs,
- int sport_from, int sport_to,
- int dport_from, int dport_to,
- uint8_t op)
-{
- struct xt_tcp *tcp = nft_tcp_match(ctx, cs);
-
- if (!tcp)
- return;
-
- if (sport_from >= 0) {
- switch (op) {
- case NFT_RANGE_NEQ:
- tcp->invflags |= XT_TCP_INV_SRCPT;
- /* fallthrough */
- case NFT_RANGE_EQ:
- tcp->spts[0] = sport_from;
- tcp->spts[1] = sport_to;
- break;
- }
- }
-
- if (dport_to >= 0) {
- switch (op) {
- case NFT_CMP_NEQ:
- tcp->invflags |= XT_TCP_INV_DSTPT;
- /* fallthrough */
- case NFT_CMP_EQ:
- tcp->dpts[0] = dport_from;
- tcp->dpts[1] = dport_to;
- break;
- }
- }
-}
-
-static void nft_parse_udp(struct nft_xt_ctx *ctx,
- struct iptables_command_state *cs,
- int sport, int dport,
- uint8_t op)
-{
- struct xt_udp *udp = nft_udp_match(ctx, cs);
-
- if (!udp)
- return;
-
- if (sport >= 0) {
- switch (op) {
- case NFT_CMP_NEQ:
- udp->invflags |= XT_UDP_INV_SRCPT;
- /* fallthrough */
- case NFT_CMP_EQ:
- udp->spts[0] = sport;
- udp->spts[1] = sport;
- break;
- case NFT_CMP_LT:
- udp->spts[1] = sport > 1 ? sport - 1 : 1;
- break;
- case NFT_CMP_LTE:
- udp->spts[1] = sport;
- break;
- case NFT_CMP_GT:
- udp->spts[0] = sport < 0xffff ? sport + 1 : 0xffff;
- break;
- case NFT_CMP_GTE:
- udp->spts[0] = sport;
- break;
- }
- }
- if (dport >= 0) {
- switch (op) {
- case NFT_CMP_NEQ:
- udp->invflags |= XT_UDP_INV_DSTPT;
- /* fallthrough */
- case NFT_CMP_EQ:
- udp->dpts[0] = dport;
- udp->dpts[1] = dport;
- break;
- case NFT_CMP_LT:
- udp->dpts[1] = dport > 1 ? dport - 1 : 1;
- break;
- case NFT_CMP_LTE:
- udp->dpts[1] = dport;
- break;
- case NFT_CMP_GT:
- udp->dpts[0] = dport < 0xffff ? dport + 1 : 0xffff;
- break;
- case NFT_CMP_GTE:
- udp->dpts[0] = dport;
- break;
- }
- }
-}
-
-static void nft_parse_tcp(struct nft_xt_ctx *ctx,
- struct iptables_command_state *cs,
- int sport, int dport,
- uint8_t op)
-{
- struct xt_tcp *tcp = nft_tcp_match(ctx, cs);
-
- if (!tcp)
- return;
-
- if (sport >= 0) {
- switch (op) {
- case NFT_CMP_NEQ:
- tcp->invflags |= XT_TCP_INV_SRCPT;
- /* fallthrough */
- case NFT_CMP_EQ:
- tcp->spts[0] = sport;
- tcp->spts[1] = sport;
- break;
- case NFT_CMP_LT:
- tcp->spts[1] = sport > 1 ? sport - 1 : 1;
- break;
- case NFT_CMP_LTE:
- tcp->spts[1] = sport;
- break;
- case NFT_CMP_GT:
- tcp->spts[0] = sport < 0xffff ? sport + 1 : 0xffff;
- break;
- case NFT_CMP_GTE:
- tcp->spts[0] = sport;
- break;
- }
- }
-
- if (dport >= 0) {
- switch (op) {
- case NFT_CMP_NEQ:
- tcp->invflags |= XT_TCP_INV_DSTPT;
- /* fallthrough */
- case NFT_CMP_EQ:
- tcp->dpts[0] = dport;
- tcp->dpts[1] = dport;
- break;
- case NFT_CMP_LT:
- tcp->dpts[1] = dport > 1 ? dport - 1 : 1;
- break;
- case NFT_CMP_LTE:
- tcp->dpts[1] = dport;
- break;
- case NFT_CMP_GT:
- tcp->dpts[0] = dport < 0xffff ? dport + 1 : 0xffff;
- break;
- case NFT_CMP_GTE:
- tcp->dpts[0] = dport;
- break;
- }
- }
-}
-
-static void nft_parse_th_port(struct nft_xt_ctx *ctx,
- struct iptables_command_state *cs,
- uint8_t proto,
- int sport, int dport, uint8_t op)
-{
- switch (proto) {
- case IPPROTO_UDP:
- nft_parse_udp(ctx, cs, sport, dport, op);
- break;
- case IPPROTO_TCP:
- nft_parse_tcp(ctx, cs, sport, dport, op);
- break;
- }
-}
-
-static void nft_parse_th_port_range(struct nft_xt_ctx *ctx,
- struct iptables_command_state *cs,
- uint8_t proto,
- int sport_from, int sport_to,
- int dport_from, int dport_to, uint8_t op)
-{
- switch (proto) {
- case IPPROTO_UDP:
- nft_parse_udp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op);
- break;
- case IPPROTO_TCP:
- nft_parse_tcp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op);
- break;
- }
-}
-
-static void nft_parse_tcp_flags(struct nft_xt_ctx *ctx,
- struct iptables_command_state *cs,
- uint8_t op, uint8_t flags, uint8_t mask)
-{
- struct xt_tcp *tcp = nft_tcp_match(ctx, cs);
-
- if (!tcp)
- return;
-
- if (op == NFT_CMP_NEQ)
- tcp->invflags |= XT_TCP_INV_FLAGS;
- tcp->flg_cmp = flags;
- tcp->flg_mask = mask;
-}
-
-static void nft_parse_transport(struct nft_xt_ctx *ctx,
- struct nftnl_expr *e,
- struct iptables_command_state *cs)
-{
- uint32_t sdport;
- uint16_t port;
- uint8_t proto, op;
- unsigned int len;
-
- switch (ctx->h->family) {
- case NFPROTO_IPV4:
- proto = ctx->cs->fw.ip.proto;
- break;
- case NFPROTO_IPV6:
- proto = ctx->cs->fw6.ipv6.proto;
- break;
- default:
- proto = 0;
- break;
- }
-
- nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
- op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
-
- switch(ctx->payload.offset) {
- case 0: /* th->sport */
- switch (len) {
- case 2: /* load sport only */
- port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA));
- nft_parse_th_port(ctx, cs, proto, port, -1, op);
- return;
- case 4: /* load both src and dst port */
- sdport = ntohl(nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA));
- nft_parse_th_port(ctx, cs, proto, sdport >> 16, sdport & 0xffff, op);
- return;
- }
- break;
- case 2: /* th->dport */
- switch (len) {
- case 2:
- port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA));
- nft_parse_th_port(ctx, cs, proto, -1, port, op);
- return;
- }
- break;
- case 13: /* th->flags */
- if (len == 1 && proto == IPPROTO_TCP) {
- uint8_t flags = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
- uint8_t mask = ~0;
-
- if (ctx->flags & NFT_XT_CTX_BITWISE) {
- memcpy(&mask, &ctx->bitwise.mask, sizeof(mask));
- ctx->flags &= ~NFT_XT_CTX_BITWISE;
- }
- nft_parse_tcp_flags(ctx, cs, op, flags, mask);
- }
- return;
- }
-}
-
-static void nft_parse_transport_range(struct nft_xt_ctx *ctx,
- struct nftnl_expr *e,
- struct iptables_command_state *cs)
-{
- unsigned int len_from, len_to;
- uint8_t proto, op;
- uint16_t from, to;
-
- switch (ctx->h->family) {
- case NFPROTO_IPV4:
- proto = ctx->cs->fw.ip.proto;
- break;
- case NFPROTO_IPV6:
- proto = ctx->cs->fw6.ipv6.proto;
- break;
- default:
- proto = 0;
- break;
- }
-
- nftnl_expr_get(e, NFTNL_EXPR_RANGE_FROM_DATA, &len_from);
- nftnl_expr_get(e, NFTNL_EXPR_RANGE_FROM_DATA, &len_to);
- if (len_to != len_from || len_to != 2)
- return;
-
- op = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_OP);
-
- from = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_FROM_DATA));
- to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA));
-
- switch(ctx->payload.offset) {
- case 0:
- nft_parse_th_port_range(ctx, cs, proto, from, to, -1, -1, op);
- return;
- case 2:
- to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA));
- nft_parse_th_port_range(ctx, cs, proto, -1, -1, from, to, op);
- return;
- }
-}
-
-static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
- uint32_t reg;
-
- reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG);
- if (ctx->reg && reg != ctx->reg)
- return;
-
- if (ctx->flags & NFT_XT_CTX_META) {
- ctx->h->ops->parse_meta(ctx, e, ctx->cs);
- ctx->flags &= ~NFT_XT_CTX_META;
- }
- /* bitwise context is interpreted from payload */
- if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
- switch (ctx->payload.base) {
- case NFT_PAYLOAD_LL_HEADER:
- if (ctx->h->family == NFPROTO_BRIDGE)
- ctx->h->ops->parse_payload(ctx, e, ctx->cs);
- break;
- case NFT_PAYLOAD_NETWORK_HEADER:
- ctx->h->ops->parse_payload(ctx, e, ctx->cs);
- break;
- case NFT_PAYLOAD_TRANSPORT_HEADER:
- nft_parse_transport(ctx, e, ctx->cs);
- break;
- }
- }
-}
-
-static void nft_parse_counter(struct nftnl_expr *e, struct xt_counters *counters)
-{
- counters->pcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_PACKETS);
- counters->bcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_BYTES);
-}
-
-static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
- const char *chain = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN);
- struct iptables_command_state *cs = ctx->cs;
- struct xt_entry_target *t;
- uint32_t size;
- int verdict;
-
- if (nftnl_expr_is_set(e, NFTNL_EXPR_IMM_DATA)) {
- const void *imm_data;
- uint32_t len;
-
- imm_data = nftnl_expr_get_data(e, NFTNL_EXPR_IMM_DATA, &len);
-
- if (len > sizeof(ctx->immediate.data))
- return;
-
- memcpy(ctx->immediate.data, imm_data, len);
- ctx->immediate.len = len;
- ctx->immediate.reg = nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_DREG);
- ctx->flags |= NFT_XT_CTX_IMMEDIATE;
- return;
- }
-
- verdict = nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_VERDICT);
- /* Standard target? */
- switch(verdict) {
- case NF_ACCEPT:
- cs->jumpto = "ACCEPT";
- break;
- case NF_DROP:
- cs->jumpto = "DROP";
- break;
- case NFT_RETURN:
- cs->jumpto = "RETURN";
- break;;
- case NFT_GOTO:
- if (ctx->h->ops->set_goto_flag)
- ctx->h->ops->set_goto_flag(cs);
- /* fall through */
- case NFT_JUMP:
- cs->jumpto = chain;
- /* fall through */
- default:
- return;
- }
-
- cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD);
- if (!cs->target)
- return;
-
- size = XT_ALIGN(sizeof(struct xt_entry_target)) + cs->target->size;
- t = xtables_calloc(1, size);
- t->u.target_size = size;
- t->u.user.revision = cs->target->revision;
- strcpy(t->u.user.name, cs->jumpto);
- cs->target->t = t;
-}
-
-static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
- __u32 burst = nftnl_expr_get_u32(e, NFTNL_EXPR_LIMIT_BURST);
- __u64 unit = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_UNIT);
- __u64 rate = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_RATE);
- struct xtables_rule_match **matches;
- struct xtables_match *match;
- struct xt_rateinfo *rinfo;
- size_t size;
-
- switch (ctx->h->family) {
- case NFPROTO_IPV4:
- case NFPROTO_IPV6:
- case NFPROTO_BRIDGE:
- matches = &ctx->cs->matches;
- break;
- default:
- fprintf(stderr, "BUG: nft_parse_limit() unknown family %d\n",
- ctx->h->family);
- exit(EXIT_FAILURE);
- }
-
- match = xtables_find_match("limit", XTF_TRY_LOAD, matches);
- if (match == NULL)
- return;
-
- size = XT_ALIGN(sizeof(struct xt_entry_match)) + match->size;
- match->m = xtables_calloc(1, size);
- match->m->u.match_size = size;
- strcpy(match->m->u.user.name, match->name);
- match->m->u.user.revision = match->revision;
- xs_init_match(match);
-
- rinfo = (void *)match->m->data;
- rinfo->avg = XT_LIMIT_SCALE * unit / rate;
- rinfo->burst = burst;
-
- if (ctx->h->ops->parse_match != NULL)
- ctx->h->ops->parse_match(match, ctx->cs);
-}
-
-static void nft_parse_log(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
- struct xtables_target *target;
- struct xt_entry_target *t;
- size_t target_size;
- /*
- * In order to handle the longer log-prefix supported by nft, instead of
- * using struct xt_nflog_info, we use a struct with a compatible layout, but
- * a larger buffer for the prefix.
- */
- struct xt_nflog_info_nft {
- __u32 len;
- __u16 group;
- __u16 threshold;
- __u16 flags;
- __u16 pad;
- char prefix[NF_LOG_PREFIXLEN];
- } info = {
- .group = nftnl_expr_get_u16(e, NFTNL_EXPR_LOG_GROUP),
- .threshold = nftnl_expr_get_u16(e, NFTNL_EXPR_LOG_QTHRESHOLD),
- };
- if (nftnl_expr_is_set(e, NFTNL_EXPR_LOG_SNAPLEN)) {
- info.len = nftnl_expr_get_u32(e, NFTNL_EXPR_LOG_SNAPLEN);
- info.flags = XT_NFLOG_F_COPY_LEN;
- }
- if (nftnl_expr_is_set(e, NFTNL_EXPR_LOG_PREFIX))
- snprintf(info.prefix, sizeof(info.prefix), "%s",
- nftnl_expr_get_str(e, NFTNL_EXPR_LOG_PREFIX));
-
- target = xtables_find_target("NFLOG", XTF_TRY_LOAD);
- if (target == NULL)
- return;
-
- target_size = XT_ALIGN(sizeof(struct xt_entry_target)) +
- XT_ALIGN(sizeof(struct xt_nflog_info_nft));
-
- t = xtables_calloc(1, target_size);
- t->u.target_size = target_size;
- strcpy(t->u.user.name, target->name);
- t->u.user.revision = target->revision;
-
- target->t = t;
-
- memcpy(&target->t->data, &info, sizeof(info));
-
- ctx->h->ops->parse_target(target, ctx->cs);
-}
-
-static void nft_parse_lookup(struct nft_xt_ctx *ctx, struct nft_handle *h,
- struct nftnl_expr *e)
-{
- if (ctx->h->ops->parse_lookup)
- ctx->h->ops->parse_lookup(ctx, e);
-}
-
-static void nft_parse_range(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
-{
- uint32_t reg;
-
- reg = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_SREG);
- if (reg != ctx->reg)
- return;
-
- if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
- switch (ctx->payload.base) {
- case NFT_PAYLOAD_TRANSPORT_HEADER:
- nft_parse_transport_range(ctx, e, ctx->cs);
- break;
- default:
- break;
- }
- }
-}
-
-void nft_rule_to_iptables_command_state(struct nft_handle *h,
- const struct nftnl_rule *r,
- struct iptables_command_state *cs)
-{
- struct nftnl_expr_iter *iter;
- struct nftnl_expr *expr;
- struct nft_xt_ctx ctx = {
- .cs = cs,
- .h = h,
- .table = nftnl_rule_get_str(r, NFTNL_RULE_TABLE),
- };
-
- iter = nftnl_expr_iter_create(r);
- if (iter == NULL)
- return;
-
- ctx.iter = iter;
- expr = nftnl_expr_iter_next(iter);
- while (expr != NULL) {
- const char *name =
- nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
-
- if (strcmp(name, "counter") == 0)
- nft_parse_counter(expr, &ctx.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);
- else if (strcmp(name, "limit") == 0)
- nft_parse_limit(&ctx, expr);
- else if (strcmp(name, "lookup") == 0)
- nft_parse_lookup(&ctx, h, expr);
- else if (strcmp(name, "log") == 0)
- nft_parse_log(&ctx, expr);
- else if (strcmp(name, "range") == 0)
- nft_parse_range(&ctx, expr);
-
- expr = nftnl_expr_iter_next(iter);
- }
-
- nftnl_expr_iter_destroy(iter);
-
- if (nftnl_rule_is_set(r, NFTNL_RULE_USERDATA)) {
- const void *data;
- uint32_t len, size;
- const char *comment;
-
- data = nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &len);
- comment = get_comment(data, len);
- if (comment) {
- struct xtables_match *match;
- struct xt_entry_match *m;
-
- match = xtables_find_match("comment", XTF_TRY_LOAD,
- &cs->matches);
- if (match == NULL)
- return;
-
- size = XT_ALIGN(sizeof(struct xt_entry_match))
- + match->size;
- m = xtables_calloc(1, size);
-
- strncpy((char *)m->data, comment, match->size - 1);
- m->u.match_size = size;
- m->u.user.revision = 0;
- strcpy(m->u.user.name, match->name);
-
- match->m = m;
- }
- }
-
- if (!cs->jumpto)
- cs->jumpto = "";
-}
-
-void nft_clear_iptables_command_state(struct iptables_command_state *cs)
-{
- xtables_rule_matches_free(&cs->matches);
- if (cs->target) {
- free(cs->target->t);
- cs->target->t = NULL;
-
- if (cs->target == cs->target->next) {
- free(cs->target);
- cs->target = NULL;
- }
- }
+ __get_cmp_data(e, data, dlen, &op);
+ *inv = (op == NFT_CMP_NEQ);
}
void nft_ipv46_save_chain(const struct nftnl_chain *c, const char *policy)
@@ -1338,6 +366,7 @@ bool compare_matches(struct xtables_rule_match *mt1,
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;
+ size_t cmplen = mp1->match->userspacesize;
if (strcmp(m1->u.user.name, m2->u.user.name) != 0) {
DEBUGP("mismatching match name\n");
@@ -1349,9 +378,13 @@ bool compare_matches(struct xtables_rule_match *mt1,
return false;
}
- if (memcmp(m1->data, m2->data,
- mp1->match->userspacesize) != 0) {
+ if (!strcmp(m1->u.user.name, "among"))
+ cmplen = m1->u.match_size - sizeof(*m1);
+
+ if (memcmp(m1->data, m2->data, cmplen) != 0) {
DEBUGP("mismatch match data\n");
+ DEBUG_HEXDUMP("m1->data", m1->data, cmplen);
+ DEBUG_HEXDUMP("m2->data", m2->data, cmplen);
return false;
}
}
@@ -1384,13 +417,6 @@ bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2)
return true;
}
-void nft_ipv46_parse_target(struct xtables_target *t,
- struct iptables_command_state *cs)
-{
- cs->target = t;
- cs->jumpto = t->name;
-}
-
void nft_check_xt_legacy(int family, bool is_ipt_save)
{
static const char tables6[] = "/proc/net/ip6_tables_names";
@@ -1424,3 +450,18 @@ void nft_check_xt_legacy(int family, bool is_ipt_save)
prefix, prefix, is_ipt_save ? "-save" : "");
fclose(fp);
}
+
+enum nft_registers nft_get_next_reg(enum nft_registers reg, size_t size)
+{
+ /* convert size to NETLINK_ALIGN-sized chunks */
+ size = (size + NETLINK_ALIGN - 1) / NETLINK_ALIGN;
+
+ /* map 16byte reg to 4byte one */
+ if (reg < __NFT_REG_MAX)
+ reg = NFT_REG32_00 + (reg - 1) * NFT_REG_SIZE / NFT_REG32_SIZE;
+
+ reg += size;
+ assert(reg <= NFT_REG32_15);
+
+ return reg;
+}
diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h
index b0404904..51d1e460 100644
--- a/iptables/nft-shared.h
+++ b/iptables/nft-shared.h
@@ -11,6 +11,7 @@
#include <linux/netfilter/nf_tables.h>
#include "xshared.h"
+#include "nft-ruleparse.h"
#ifdef DEBUG
#define DEBUG_DEL
@@ -34,61 +35,18 @@
| FMT_NUMERIC | FMT_NOTABLE)
#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
+struct nft_rule_ctx;
struct xtables_args;
struct nft_handle;
struct xt_xlate;
-enum {
- NFT_XT_CTX_PAYLOAD = (1 << 0),
- NFT_XT_CTX_META = (1 << 1),
- NFT_XT_CTX_BITWISE = (1 << 2),
- NFT_XT_CTX_IMMEDIATE = (1 << 3),
- NFT_XT_CTX_PREV_PAYLOAD = (1 << 4),
- NFT_XT_CTX_RANGE = (1 << 5),
-};
-
-struct nft_xt_ctx {
- struct iptables_command_state *cs;
- struct nftnl_expr_iter *iter;
- struct nft_handle *h;
- uint32_t flags;
- const char *table;
- union {
- struct xt_tcp *tcp;
- struct xt_udp *udp;
- } tcpudp;
-
- uint32_t reg;
- struct {
- uint32_t base;
- uint32_t offset;
- uint32_t len;
- } payload, prev_payload;
- struct {
- uint32_t key;
- } meta;
- struct {
- uint32_t data[4];
- uint32_t len, reg;
- } immediate;
- struct {
- uint32_t mask[4];
- uint32_t xor[4];
- } bitwise;
-};
-
struct nft_family_ops {
- int (*add)(struct nft_handle *h, struct nftnl_rule *r,
- struct iptables_command_state *cs);
+ int (*add)(struct nft_handle *h, struct nft_rule_ctx *ctx,
+ struct nftnl_rule *r, struct iptables_command_state *cs);
bool (*is_same)(const struct iptables_command_state *cs_a,
const struct iptables_command_state *cs_b);
void (*print_payload)(struct nftnl_expr *e,
struct nftnl_expr_iter *iter);
- void (*parse_meta)(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
- struct iptables_command_state *cs);
- void (*parse_payload)(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
- struct iptables_command_state *cs);
- void (*parse_lookup)(struct nft_xt_ctx *ctx, struct nftnl_expr *e);
void (*set_goto_flag)(struct iptables_command_state *cs);
void (*print_table_header)(const char *tablename);
@@ -101,13 +59,10 @@ struct nft_family_ops {
void (*save_rule)(const struct iptables_command_state *cs,
unsigned int format);
void (*save_chain)(const struct nftnl_chain *c, const char *policy);
+ struct nft_ruleparse_ops *rule_parse;
struct xt_cmd_parse_ops cmd_parse;
- void (*parse_match)(struct xtables_match *m,
- struct iptables_command_state *cs);
- void (*parse_target)(struct xtables_target *t,
- struct iptables_command_state *cs);
void (*init_cs)(struct iptables_command_state *cs);
- void (*rule_to_cs)(struct nft_handle *h, const struct nftnl_rule *r,
+ bool (*rule_to_cs)(struct nft_handle *h, const struct nftnl_rule *r,
struct iptables_command_state *cs);
void (*clear_cs)(struct iptables_command_state *cs);
int (*xlate)(const struct iptables_command_state *cs,
@@ -140,8 +95,8 @@ void add_cmp_ptr(struct nftnl_rule *r, uint32_t op, void *data, size_t len, uint
void add_cmp_u8(struct nftnl_rule *r, uint8_t val, uint32_t op, uint8_t sreg);
void add_cmp_u16(struct nftnl_rule *r, uint16_t val, uint32_t op, uint8_t sreg);
void add_cmp_u32(struct nftnl_rule *r, uint32_t val, uint32_t op, uint8_t sreg);
-void add_iniface(struct nft_handle *h, struct nftnl_rule *r, char *iface, uint32_t op);
-void add_outiface(struct nft_handle *h, struct nftnl_rule *r, char *iface, uint32_t op);
+void add_iface(struct nft_handle *h, struct nftnl_rule *r,
+ char *iface, uint32_t key, uint32_t op);
void add_addr(struct nft_handle *h, struct nftnl_rule *r, enum nft_payload_bases base, int offset,
void *data, void *mask, size_t len, uint32_t op);
void add_proto(struct nft_handle *h, struct nftnl_rule *r, int offset, size_t len,
@@ -156,14 +111,8 @@ bool is_same_interfaces(const char *a_iniface, const char *a_outiface,
unsigned const char *b_iniface_mask,
unsigned const char *b_outiface_mask);
-int parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, uint8_t key,
- char *iniface, unsigned char *iniface_mask, char *outiface,
- unsigned char *outiface_mask, uint8_t *invflags);
+void __get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, uint8_t *op);
void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv);
-void nft_rule_to_iptables_command_state(struct nft_handle *h,
- const struct nftnl_rule *r,
- struct iptables_command_state *cs);
-void nft_clear_iptables_command_state(struct iptables_command_state *cs);
void print_matches_and_target(struct iptables_command_state *cs,
unsigned int format);
void nft_ipv46_save_chain(const struct nftnl_chain *c, const char *policy);
@@ -173,9 +122,6 @@ void save_matches_and_target(const struct iptables_command_state *cs,
struct nft_family_ops *nft_family_ops_lookup(int family);
-void nft_ipv46_parse_target(struct xtables_target *t,
- struct iptables_command_state *cs);
-
bool compare_matches(struct xtables_rule_match *mt1, struct xtables_rule_match *mt2);
bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2);
@@ -212,7 +158,9 @@ void xtables_restore_parse(struct nft_handle *h,
void nft_check_xt_legacy(int family, bool is_ipt_save);
-#define min(x, y) ((x) < (y) ? (x) : (y))
-#define max(x, y) ((x) > (y) ? (x) : (y))
+/* simplified nftables:include/netlink.h, netlink_padded_len() */
+#define NETLINK_ALIGN 4
+
+enum nft_registers nft_get_next_reg(enum nft_registers reg, size_t size);
#endif
diff --git a/iptables/nft.c b/iptables/nft.c
index ee003511..884cc77e 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -109,7 +109,9 @@ static struct nftnl_batch *mnl_batch_init(void)
static void mnl_nft_batch_continue(struct nftnl_batch *batch)
{
- assert(nftnl_batch_update(batch) >= 0);
+ int ret = nftnl_batch_update(batch);
+
+ assert(ret >= 0);
}
static uint32_t mnl_batch_begin(struct nftnl_batch *batch, uint32_t genid, uint32_t seqnum)
@@ -335,6 +337,7 @@ static int mnl_append_error(const struct nft_handle *h,
case NFT_COMPAT_RULE_REPLACE:
case NFT_COMPAT_RULE_DELETE:
case NFT_COMPAT_RULE_FLUSH:
+ case NFT_COMPAT_RULE_CHANGE_COUNTERS:
snprintf(tcr, sizeof(tcr), "rule in chain %s",
nftnl_rule_get_str(o->rule, NFTNL_RULE_CHAIN));
#if 0
@@ -434,7 +437,7 @@ static void batch_chain_flush(struct nft_handle *h,
}
}
-const struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
+static const struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
[NFT_TABLE_RAW] = {
.name = "raw",
.type = NFT_TABLE_RAW,
@@ -571,7 +574,7 @@ const struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
#include <linux/netfilter_arp.h>
-const struct builtin_table xtables_arp[NFT_TABLE_MAX] = {
+static const struct builtin_table xtables_arp[NFT_TABLE_MAX] = {
[NFT_TABLE_FILTER] = {
.name = "filter",
.type = NFT_TABLE_FILTER,
@@ -594,7 +597,7 @@ const struct builtin_table xtables_arp[NFT_TABLE_MAX] = {
#include <linux/netfilter_bridge.h>
-const struct builtin_table xtables_bridge[NFT_TABLE_MAX] = {
+static const struct builtin_table xtables_bridge[NFT_TABLE_MAX] = {
[NFT_TABLE_FILTER] = {
.name = "filter",
.type = NFT_TABLE_FILTER,
@@ -643,6 +646,19 @@ const struct builtin_table xtables_bridge[NFT_TABLE_MAX] = {
},
},
},
+ [NFT_TABLE_BROUTE] = {
+ .name = "broute",
+ .type = NFT_TABLE_BROUTE,
+ .chains = {
+ {
+ .name = "BROUTING",
+ .type = "filter",
+ .prio = NF_BR_PRI_FIRST,
+ .hook = NF_BR_PRE_ROUTING,
+ },
+ },
+ },
+
};
static int nft_table_builtin_add(struct nft_handle *h,
@@ -686,6 +702,9 @@ nft_chain_builtin_alloc(int family, const char *tname,
nftnl_chain_set_str(c, NFTNL_CHAIN_TYPE, chain->type);
+ nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, 0);
+ nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, 0);
+
return c;
}
@@ -946,6 +965,7 @@ static struct nftnl_chain *nft_chain_new(struct nft_handle *h,
int policy,
const struct xt_counters *counters)
{
+ static const struct xt_counters zero = {};
struct nftnl_chain *c;
const struct builtin_table *_t;
const struct builtin_chain *_c;
@@ -970,12 +990,10 @@ static struct nftnl_chain *nft_chain_new(struct nft_handle *h,
return NULL;
}
- if (counters) {
- nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES,
- counters->bcnt);
- nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS,
- counters->pcnt);
- }
+ if (!counters)
+ counters = &zero;
+ nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, counters->bcnt);
+ nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, counters->pcnt);
return c;
}
@@ -1133,9 +1151,6 @@ gen_lookup(uint32_t sreg, const char *set_name, uint32_t set_id, uint32_t flags)
return e;
}
-/* simplified nftables:include/netlink.h, netlink_padded_len() */
-#define NETLINK_ALIGN 4
-
/* from nftables:include/datatype.h, TYPE_BITS */
#define CONCAT_TYPE_BITS 6
@@ -1167,7 +1182,7 @@ static int __add_nft_among(struct nft_handle *h, const char *table,
type = type << CONCAT_TYPE_BITS | NFT_DATATYPE_IPADDR;
len += sizeof(struct in_addr) + NETLINK_ALIGN - 1;
len &= ~(NETLINK_ALIGN - 1);
- flags = NFT_SET_INTERVAL;
+ flags = NFT_SET_INTERVAL | NFT_SET_CONCAT;
}
s = add_anon_set(h, table, flags, type, len, cnt);
@@ -1208,8 +1223,9 @@ static int __add_nft_among(struct nft_handle *h, const char *table,
nftnl_rule_add_expr(r, e);
if (ip) {
+ reg = nft_get_next_reg(reg, ETH_ALEN);
e = __gen_payload(NFT_PAYLOAD_NETWORK_HEADER, ip_addr_off[dst],
- sizeof(struct in_addr), NFT_REG32_02);
+ sizeof(struct in_addr), reg);
if (!e)
return -ENOMEM;
nftnl_rule_add_expr(r, e);
@@ -1291,14 +1307,12 @@ static int add_nft_tcpudp(struct nft_handle *h,struct nftnl_rule *r,
uint8_t reg;
int ret;
- if (src[0] && src[0] == src[1] &&
+ if (!invert_src &&
+ src[0] && src[0] == src[1] &&
dst[0] && dst[0] == dst[1] &&
invert_src == invert_dst) {
uint32_t combined = dst[0] | (src[0] << 16);
- if (invert_src)
- op = NFT_CMP_NEQ;
-
expr = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 0, 4, &reg);
if (!expr)
return -ENOMEM;
@@ -1308,7 +1322,7 @@ static int add_nft_tcpudp(struct nft_handle *h,struct nftnl_rule *r,
return 0;
}
- if (src[0] || src[1] < 0xffff) {
+ if (src[0] || src[1] < UINT16_MAX || invert_src) {
expr = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 0, 2, &reg);
if (!expr)
return -ENOMEM;
@@ -1319,7 +1333,7 @@ static int add_nft_tcpudp(struct nft_handle *h,struct nftnl_rule *r,
return ret;
}
- if (dst[0] || dst[1] < 0xffff) {
+ if (dst[0] || dst[1] < UINT16_MAX || invert_dst) {
expr = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 2, 2, &reg);
if (!expr)
return -ENOMEM;
@@ -1362,6 +1376,9 @@ static int add_nft_udp(struct nft_handle *h, struct nftnl_rule *r,
return ret;
}
+ if (nftnl_rule_get_u32(r, NFTNL_RULE_COMPAT_PROTO) != IPPROTO_UDP)
+ xtables_error(PARAMETER_PROBLEM, "UDP match requires '-p udp'");
+
return add_nft_tcpudp(h, r, udp->spts, udp->invflags & XT_UDP_INV_SRCPT,
udp->dpts, udp->invflags & XT_UDP_INV_DSTPT);
}
@@ -1412,6 +1429,9 @@ static int add_nft_tcp(struct nft_handle *h, struct nftnl_rule *r,
return ret;
}
+ if (nftnl_rule_get_u32(r, NFTNL_RULE_COMPAT_PROTO) != IPPROTO_TCP)
+ xtables_error(PARAMETER_PROBLEM, "TCP match requires '-p tcp'");
+
if (tcp->flg_mask) {
int ret = add_nft_tcpflags(h, r, tcp->flg_cmp, tcp->flg_mask,
tcp->invflags & XT_TCP_INV_FLAGS);
@@ -1445,22 +1465,30 @@ static int add_nft_mark(struct nft_handle *h, struct nftnl_rule *r,
return 0;
}
-int add_match(struct nft_handle *h,
+int add_match(struct nft_handle *h, struct nft_rule_ctx *ctx,
struct nftnl_rule *r, struct xt_entry_match *m)
{
struct nftnl_expr *expr;
int ret;
- if (!strcmp(m->u.user.name, "limit"))
- return add_nft_limit(r, m);
- else if (!strcmp(m->u.user.name, "among"))
- return add_nft_among(h, r, m);
- else if (!strcmp(m->u.user.name, "udp"))
- return add_nft_udp(h, r, m);
- else if (!strcmp(m->u.user.name, "tcp"))
- return add_nft_tcp(h, r, m);
- else if (!strcmp(m->u.user.name, "mark"))
- return add_nft_mark(h, r, m);
+ switch (ctx->command) {
+ case NFT_COMPAT_RULE_APPEND:
+ case NFT_COMPAT_RULE_INSERT:
+ case NFT_COMPAT_RULE_REPLACE:
+ if (!strcmp(m->u.user.name, "limit"))
+ return add_nft_limit(r, m);
+ else if (!strcmp(m->u.user.name, "among"))
+ return add_nft_among(h, r, m);
+ else if (!strcmp(m->u.user.name, "udp"))
+ return add_nft_udp(h, r, m);
+ else if (!strcmp(m->u.user.name, "tcp"))
+ return add_nft_tcp(h, r, m);
+ else if (!strcmp(m->u.user.name, "mark"))
+ return add_nft_mark(h, r, m);
+ break;
+ default:
+ break;
+ }
expr = nftnl_expr_alloc("match");
if (expr == NULL)
@@ -1688,7 +1716,8 @@ void add_compat(struct nftnl_rule *r, uint32_t proto, bool inv)
}
struct nftnl_rule *
-nft_rule_new(struct nft_handle *h, const char *chain, const char *table,
+nft_rule_new(struct nft_handle *h, struct nft_rule_ctx *ctx,
+ const char *chain, const char *table,
struct iptables_command_state *cs)
{
struct nftnl_rule *r;
@@ -1701,7 +1730,7 @@ nft_rule_new(struct nft_handle *h, const char *chain, const char *table,
nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);
nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
- if (h->ops->add(h, r, cs) < 0)
+ if (h->ops->add(h, ctx, r, cs) < 0)
goto err;
return r;
@@ -1750,15 +1779,16 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
return 1;
}
-void
+bool
nft_rule_print_save(struct nft_handle *h, const struct nftnl_rule *r,
enum nft_rule_print type, unsigned int format)
{
const char *chain = nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
struct iptables_command_state cs = {};
struct nft_family_ops *ops = h->ops;
+ bool ret;
- ops->rule_to_cs(h, r, &cs);
+ ret = ops->rule_to_cs(h, r, &cs);
if (!(format & (FMT_NOCOUNTS | FMT_C_COUNTS)))
printf("[%llu:%llu] ", (unsigned long long)cs.counters.pcnt,
@@ -1779,6 +1809,8 @@ nft_rule_print_save(struct nft_handle *h, const struct nftnl_rule *r,
if (ops->clear_cs)
ops->clear_cs(&cs);
+
+ return ret;
}
static bool nft_rule_is_policy_rule(struct nftnl_rule *r)
@@ -1889,6 +1921,7 @@ int nft_chain_save(struct nft_chain *nc, void *data)
struct nft_rule_save_data {
struct nft_handle *h;
unsigned int format;
+ unsigned int errors;
};
static int nft_rule_save_cb(struct nft_chain *c, void *data)
@@ -1903,7 +1936,11 @@ static int nft_rule_save_cb(struct nft_chain *c, void *data)
r = nftnl_rule_iter_next(iter);
while (r != NULL) {
- nft_rule_print_save(d->h, r, NFT_RULE_APPEND, d->format);
+ bool ret = nft_rule_print_save(d->h, r, NFT_RULE_APPEND, d->format);
+
+ if (!ret)
+ d->errors++;
+
r = nftnl_rule_iter_next(iter);
}
@@ -1921,6 +1958,9 @@ int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format)
ret = nft_chain_foreach(h, table, nft_rule_save_cb, &d);
+ if (ret == 0 && d.errors)
+ xtables_error(VERSION_PROBLEM, "Cannot decode all rules provided by kernel");
+
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
@@ -2328,7 +2368,8 @@ static int __nft_rule_del(struct nft_handle *h, struct nftnl_rule *r)
nftnl_rule_list_del(r);
- if (!nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE))
+ if (!nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE) &&
+ !nftnl_rule_get_u32(r, NFTNL_RULE_ID))
nftnl_rule_set_u32(r, NFTNL_RULE_ID, ++h->rule_id);
obj = batch_rule_add(h, NFT_COMPAT_RULE_DELETE, r);
@@ -2343,15 +2384,18 @@ static bool nft_rule_cmp(struct nft_handle *h, struct nftnl_rule *r,
struct nftnl_rule *rule)
{
struct iptables_command_state _cs = {}, this = {}, *cs = &_cs;
- bool ret = false;
+ bool ret = false, ret_this, ret_that;
- h->ops->rule_to_cs(h, r, &this);
- h->ops->rule_to_cs(h, rule, cs);
+ ret_this = h->ops->rule_to_cs(h, r, &this);
+ ret_that = h->ops->rule_to_cs(h, rule, cs);
DEBUGP("comparing with... ");
#ifdef DEBUG_DEL
nft_rule_print_save(h, r, NFT_RULE_APPEND, 0);
#endif
+ if (!ret_this || !ret_that)
+ DEBUGP("Cannot convert rules: %d %d\n", ret_this, ret_that);
+
if (!h->ops->is_same(cs, &this))
goto out;
@@ -2596,6 +2640,58 @@ int nft_rule_replace(struct nft_handle *h, const char *chain,
return ret;
}
+static int nft_rule_change_counters(struct nft_handle *h, const char *table,
+ const char *chain, struct nftnl_rule *rule,
+ int rulenum, struct xt_counters *counters,
+ uint8_t counter_op, bool verbose)
+{
+ struct iptables_command_state cs = {};
+ struct nftnl_rule *r, *new_rule;
+ struct nft_rule_ctx ctx = {
+ .command = NFT_COMPAT_RULE_APPEND,
+ };
+ struct nft_chain *c;
+
+ nft_fn = nft_rule_change_counters;
+
+ c = nft_chain_find(h, table, chain);
+ if (!c) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ r = nft_rule_find(h, c, rule, rulenum);
+ if (!r) {
+ errno = E2BIG;
+ return 0;
+ }
+
+ DEBUGP("changing counters of rule with handle=%llu\n",
+ (unsigned long long)
+ nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE));
+
+ h->ops->rule_to_cs(h, r, &cs);
+
+ if (counter_op & CTR_OP_INC_PKTS)
+ cs.counters.pcnt += counters->pcnt;
+ else if (counter_op & CTR_OP_DEC_PKTS)
+ cs.counters.pcnt -= counters->pcnt;
+ else
+ cs.counters.pcnt = counters->pcnt;
+
+ if (counter_op & CTR_OP_INC_BYTES)
+ cs.counters.bcnt += counters->bcnt;
+ else if (counter_op & CTR_OP_DEC_BYTES)
+ cs.counters.bcnt -= counters->bcnt;
+ else
+ cs.counters.bcnt = counters->bcnt;
+
+ new_rule = nft_rule_new(h, &ctx, chain, table, &cs);
+ h->ops->clear_cs(&cs);
+
+ return nft_rule_append(h, chain, table, new_rule, r, verbose);
+}
+
static int
__nft_rule_list(struct nft_handle *h, struct nftnl_chain *c,
int rulenum, unsigned int format,
@@ -2714,8 +2810,10 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
if (chain) {
c = nft_chain_find(h, table, chain);
- if (!c)
+ if (!c) {
+ errno = ENOENT;
return 0;
+ }
if (rulenum)
d.save_fmt = true; /* skip header printing */
@@ -2822,8 +2920,10 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
if (chain) {
c = nft_chain_find(h, table, chain);
- if (!c)
+ if (!c) {
+ errno = ENOENT;
return 0;
+ }
if (!rulenum)
nft_rule_list_chain_save(c, &counters);
@@ -2846,14 +2946,19 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
{
struct iptables_command_state cs = {};
struct nftnl_rule *r, *new_rule;
+ struct nft_rule_ctx ctx = {
+ .command = NFT_COMPAT_RULE_APPEND,
+ };
struct nft_chain *c;
int ret = 0;
nft_fn = nft_rule_delete;
c = nft_chain_find(h, table, chain);
- if (!c)
+ if (!c) {
+ errno = ENOENT;
return 0;
+ }
r = nft_rule_find(h, c, NULL, rulenum);
if (r == NULL) {
@@ -2862,10 +2967,11 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
goto error;
}
- nft_rule_to_iptables_command_state(h, r, &cs);
-
+ h->ops->rule_to_cs(h, r, &cs);
cs.counters.pcnt = cs.counters.bcnt = 0;
- new_rule = nft_rule_new(h, chain, table, &cs);
+ new_rule = nft_rule_new(h, &ctx, chain, table, &cs);
+ h->ops->clear_cs(&cs);
+
if (!new_rule)
return 1;
@@ -2893,8 +2999,8 @@ static void nft_compat_table_batch_add(struct nft_handle *h, uint16_t type,
{
struct nlmsghdr *nlh;
- nlh = nftnl_table_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
- type, h->family, flags, seq);
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
+ type, h->family, flags, seq);
nftnl_table_nlmsg_build_payload(nlh, table);
nft_table_print_debug(h, table, nlh);
}
@@ -2930,6 +3036,12 @@ static void nft_compat_setelem_batch_add(struct nft_handle *h, uint16_t type,
break;
}
nftnl_set_elems_iter_destroy(iter);
+
+ if (h->verbose > 1) {
+ fprintf(stdout, "set ");
+ nftnl_set_fprintf(stdout, set, 0, 0);
+ fprintf(stdout, "\n");
+ }
}
static void nft_compat_chain_batch_add(struct nft_handle *h, uint16_t type,
@@ -2938,8 +3050,8 @@ static void nft_compat_chain_batch_add(struct nft_handle *h, uint16_t type,
{
struct nlmsghdr *nlh;
- nlh = nftnl_chain_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
- type, h->family, flags, seq);
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
+ type, h->family, flags, seq);
nftnl_chain_nlmsg_build_payload(nlh, chain);
nft_chain_print_debug(h, chain, nlh);
}
@@ -2950,8 +3062,8 @@ static void nft_compat_rule_batch_add(struct nft_handle *h, uint16_t type,
{
struct nlmsghdr *nlh;
- nlh = nftnl_rule_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
- type, h->family, flags, seq);
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
+ type, h->family, flags, seq);
nftnl_rule_nlmsg_build_payload(nlh, rule);
nft_rule_print_debug(h, rule, nlh);
}
@@ -2976,6 +3088,7 @@ static void batch_obj_del(struct nft_handle *h, struct obj_update *o)
case NFT_COMPAT_RULE_APPEND:
case NFT_COMPAT_RULE_INSERT:
case NFT_COMPAT_RULE_REPLACE:
+ case NFT_COMPAT_RULE_CHANGE_COUNTERS:
break;
case NFT_COMPAT_RULE_DELETE:
case NFT_COMPAT_RULE_FLUSH:
@@ -3063,6 +3176,7 @@ static void nft_refresh_transaction(struct nft_handle *h)
case NFT_COMPAT_RULE_APPEND:
case NFT_COMPAT_RULE_INSERT:
case NFT_COMPAT_RULE_REPLACE:
+ case NFT_COMPAT_RULE_CHANGE_COUNTERS:
case NFT_COMPAT_RULE_DELETE:
case NFT_COMPAT_SET_ADD:
case NFT_COMPAT_RULE_LIST:
@@ -3153,6 +3267,7 @@ retry:
n->rule);
break;
case NFT_COMPAT_RULE_REPLACE:
+ case NFT_COMPAT_RULE_CHANGE_COUNTERS:
nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
NLM_F_CREATE | NLM_F_REPLACE,
n->seq, n->rule);
@@ -3176,6 +3291,7 @@ retry:
case NFT_COMPAT_RULE_ZERO:
case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
assert(0);
+ return 0;
}
mnl_nft_batch_continue(h->batch);
@@ -3241,6 +3357,9 @@ static int ebt_add_policy_rule(struct nftnl_chain *c, void *data)
.eb.bitmask = EBT_NOPROTO,
};
struct nftnl_udata_buf *udata;
+ struct nft_rule_ctx ctx = {
+ .command = NFT_COMPAT_RULE_APPEND,
+ };
struct nft_handle *h = data;
struct nftnl_rule *r;
const char *pname;
@@ -3268,7 +3387,7 @@ static int ebt_add_policy_rule(struct nftnl_chain *c, void *data)
command_jump(&cs, pname);
- r = nft_rule_new(h, nftnl_chain_get_str(c, NFTNL_CHAIN_NAME),
+ r = nft_rule_new(h, &ctx, nftnl_chain_get_str(c, NFTNL_CHAIN_NAME),
nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE), &cs);
ebt_cs_clean(&cs);
@@ -3360,6 +3479,8 @@ static int nft_prepare(struct nft_handle *h)
nft_cache_build(h);
list_for_each_entry_safe(cmd, next, &h->cmd_list, head) {
+ h->error.lineno = cmd->error.lineno;
+
switch (cmd->command) {
case NFT_COMPAT_TABLE_FLUSH:
ret = nft_table_flush(h, cmd->table);
@@ -3448,6 +3569,15 @@ static int nft_prepare(struct nft_handle *h)
case NFT_COMPAT_TABLE_ADD:
case NFT_COMPAT_CHAIN_ADD:
assert(0);
+ return 0;
+ case NFT_COMPAT_RULE_CHANGE_COUNTERS:
+ ret = nft_rule_change_counters(h, cmd->table,
+ cmd->chain,
+ cmd->obj.rule,
+ cmd->rulenum,
+ &cmd->counters,
+ cmd->counter_op,
+ cmd->verbose);
break;
}
@@ -3618,6 +3748,27 @@ const char *nft_strerror(int err)
return strerror(err);
}
+static int l4proto_expr_get_dreg(struct nftnl_expr *e, uint32_t *dregp)
+{
+ const char *name = nftnl_expr_get_str(e, NFTNL_EXPR_NAME);
+ uint32_t poff = offsetof(struct iphdr, protocol);
+ uint32_t pbase = NFT_PAYLOAD_NETWORK_HEADER;
+
+ if (!strcmp(name, "payload") &&
+ nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_BASE) == pbase &&
+ nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET) == poff &&
+ nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_LEN) == sizeof(uint8_t)) {
+ *dregp = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_DREG);
+ return 0;
+ }
+ if (!strcmp(name, "meta") &&
+ nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY) == NFT_META_L4PROTO) {
+ *dregp = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
+ return 0;
+ }
+ return -1;
+}
+
static int recover_rule_compat(struct nftnl_rule *r)
{
struct nftnl_expr_iter *iter;
@@ -3634,12 +3785,10 @@ next_expr:
if (!e)
goto out;
- if (strcmp("meta", nftnl_expr_get_str(e, NFTNL_EXPR_NAME)) ||
- nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY) != NFT_META_L4PROTO)
+ /* may be 'ip protocol' or 'meta l4proto' with identical RHS */
+ if (l4proto_expr_get_dreg(e, &reg) < 0)
goto next_expr;
- reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
-
e = nftnl_expr_iter_next(iter);
if (!e)
goto out;
@@ -3825,7 +3974,7 @@ bool nft_is_table_compatible(struct nft_handle *h,
if (chain) {
struct nft_chain *c = nft_chain_find(h, table, chain);
- return c && !nft_is_chain_compatible(c, h);
+ return !c || !nft_is_chain_compatible(c, h);
}
return !nft_chain_foreach(h, table, nft_is_chain_compatible, h);
@@ -3857,6 +4006,6 @@ void nft_assert_table_compatible(struct nft_handle *h,
chain = "";
}
xtables_error(OTHER_PROBLEM,
- "%s%s%stable `%s' is incompatible, use 'nft' tool.\n",
+ "%s%s%stable `%s' is incompatible, use 'nft' tool.",
pfx, chain, sfx, table);
}
diff --git a/iptables/nft.h b/iptables/nft.h
index 68b0910c..57533b65 100644
--- a/iptables/nft.h
+++ b/iptables/nft.h
@@ -14,8 +14,9 @@ enum nft_table_type {
NFT_TABLE_RAW,
NFT_TABLE_FILTER,
NFT_TABLE_NAT,
+ NFT_TABLE_BROUTE,
};
-#define NFT_TABLE_MAX (NFT_TABLE_NAT + 1)
+#define NFT_TABLE_MAX (NFT_TABLE_BROUTE + 1)
struct builtin_chain {
const char *name;
@@ -71,6 +72,7 @@ enum obj_update_type {
NFT_COMPAT_RULE_SAVE,
NFT_COMPAT_RULE_ZERO,
NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE,
+ NFT_COMPAT_RULE_CHANGE_COUNTERS,
};
struct cache_chain {
@@ -117,10 +119,6 @@ struct nft_handle {
} error;
};
-extern const struct builtin_table xtables_ipv4[NFT_TABLE_MAX];
-extern const struct builtin_table xtables_arp[NFT_TABLE_MAX];
-extern const struct builtin_table xtables_bridge[NFT_TABLE_MAX];
-
int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
int (*cb)(const struct nlmsghdr *nlh, void *data),
void *data);
@@ -171,9 +169,11 @@ struct nftnl_set *nft_set_batch_lookup_byid(struct nft_handle *h,
/*
* Operations with rule-set.
*/
-struct nftnl_rule;
+struct nft_rule_ctx {
+ int command;
+};
-struct nftnl_rule *nft_rule_new(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cs);
+struct nftnl_rule *nft_rule_new(struct nft_handle *h, struct nft_rule_ctx *rule, const char *chain, const char *table, struct iptables_command_state *cs);
int nft_rule_append(struct nft_handle *h, const char *chain, const char *table, struct nftnl_rule *r, struct nftnl_rule *ref, bool verbose);
int nft_rule_insert(struct nft_handle *h, const char *chain, const char *table, struct nftnl_rule *r, int rulenum, bool verbose);
int nft_rule_check(struct nft_handle *h, const char *chain, const char *table, struct nftnl_rule *r, bool verbose);
@@ -191,7 +191,8 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain, const char *
*/
int add_counters(struct nftnl_rule *r, uint64_t packets, uint64_t bytes);
int add_verdict(struct nftnl_rule *r, int verdict);
-int add_match(struct nft_handle *h, struct nftnl_rule *r, struct xt_entry_match *m);
+int add_match(struct nft_handle *h, struct nft_rule_ctx *ctx,
+ struct nftnl_rule *r, struct xt_entry_match *m);
int add_target(struct nftnl_rule *r, struct xt_entry_target *t);
int add_jumpto(struct nftnl_rule *r, const char *name, int verdict);
int add_action(struct nftnl_rule *r, struct iptables_command_state *cs, bool goto_set);
@@ -203,7 +204,7 @@ enum nft_rule_print {
NFT_RULE_DEL,
};
-void nft_rule_print_save(struct nft_handle *h, const struct nftnl_rule *r,
+bool nft_rule_print_save(struct nft_handle *h, const struct nftnl_rule *r,
enum nft_rule_print type, unsigned int format);
uint32_t nft_invflags2cmp(uint32_t invflags, uint32_t flag);
@@ -233,7 +234,6 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table, bo
/* For xtables-eb.c */
int nft_init_eb(struct nft_handle *h, const char *pname);
void nft_fini_eb(struct nft_handle *h);
-int ebt_get_current_chain(const char *chain);
int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table, bool restore);
/*
diff --git a/iptables/tests/shell/run-tests.sh b/iptables/tests/shell/run-tests.sh
index 7878760f..11256905 100755
--- a/iptables/tests/shell/run-tests.sh
+++ b/iptables/tests/shell/run-tests.sh
@@ -21,7 +21,6 @@ EOF
msg_error() {
echo "E: $1 ..." >&2
- exit 1
}
msg_warn() {
@@ -34,10 +33,12 @@ msg_info() {
if [ "$(id -u)" != "0" ] ; then
msg_error "this requires root!"
+ exit 77
fi
if [ ! -d "$TESTDIR" ] ; then
msg_error "missing testdir $TESTDIR"
+ exit 99
fi
# support matching repeated pattern in SINGLE check below
@@ -76,6 +77,7 @@ while [ -n "$1" ]; do
;;
*)
msg_error "unknown parameter '$1'"
+ exit 99
;;
esac
done
@@ -122,7 +124,8 @@ EOF
if [ "$VALGRIND" == "y" ]; then
tmpd=$(mktemp -d)
msg_info "writing valgrind logs to $tmpd"
- chmod a+rx $tmpd
+ # let nobody write logs, too (././testcases/iptables/0008-unprivileged_0)
+ chmod 777 $tmpd
printscript "$XTABLES_NFT_MULTI" "$tmpd" >${tmpd}/xtables-nft-multi
printscript "$XTABLES_LEGACY_MULTI" "$tmpd" >${tmpd}/xtables-legacy-multi
trap "rm ${tmpd}/xtables-*-multi" EXIT
diff --git a/iptables/tests/shell/testcases/chain/0003rename_0 b/iptables/tests/shell/testcases/chain/0003rename_0
new file mode 100755
index 00000000..4cb2745b
--- /dev/null
+++ b/iptables/tests/shell/testcases/chain/0003rename_0
@@ -0,0 +1,40 @@
+#!/bin/bash -x
+
+die() {
+ echo "E: $@"
+ exit 1
+}
+
+cmds="iptables ip6tables"
+[[ $XT_MULTI == *xtables-nft-multi ]] && cmds+=" arptables ebtables"
+
+declare -A invnames
+invnames["existing"]="c2"
+invnames["spaced"]="foo bar"
+invnames["dashed"]="-foo"
+invnames["negated"]="!foo"
+# XXX: ebtables-nft accepts 255 chars
+#invnames["overlong"]="thisisquitealongnameforachain"
+invnames["standard target"]="ACCEPT"
+invnames["extension target"]="DNAT"
+
+for cmd in $cmds; do
+ $XT_MULTI $cmd -N c1 || die "$cmd: can't add chain c1"
+ $XT_MULTI $cmd -N c2 || die "$cmd: can't add chain c2"
+ for key in "${!invnames[@]}"; do
+ val="${invnames[$key]}"
+ if [[ $key == "extension target" ]]; then
+ if [[ $cmd == "arptables" ]]; then
+ val="mangle"
+ elif [[ $cmd == "ebtables" ]]; then
+ val="dnat"
+ fi
+ fi
+ $XT_MULTI $cmd -N "$val" && \
+ die "$cmd: added chain with $key name"
+ $XT_MULTI $cmd -E c1 "$val" && \
+ die "$cmd: renamed to $key name"
+ done
+done
+
+exit 0
diff --git a/iptables/tests/shell/testcases/chain/0003rename_1 b/iptables/tests/shell/testcases/chain/0003rename_1
deleted file mode 100755
index 975c8e19..00000000
--- a/iptables/tests/shell/testcases/chain/0003rename_1
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-
-$XT_MULTI iptables -N c1 || exit 0
-$XT_MULTI iptables -N c2 || exit 0
-$XT_MULTI iptables -E c1 c2 || exit 1
-
-$XT_MULTI ip6tables -N c1 || exit 0
-$XT_MULTI ip6tables -N c2 || exit 0
-$XT_MULTI ip6tables -E c1 c2 || exit 1
-
-echo "E: Renamed with existing chain" >&2
-exit 0
diff --git a/iptables/tests/shell/testcases/chain/0006rename-segfault_0 b/iptables/tests/shell/testcases/chain/0006rename-segfault_0
new file mode 100755
index 00000000..c10a8006
--- /dev/null
+++ b/iptables/tests/shell/testcases/chain/0006rename-segfault_0
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Cover for a bug in libiptc:
+# - the chain 'node-98-tmp' is the last in the list sorted by name
+# - there are 81 chains in total, so three chain index buckets
+# - the last index bucket contains only the 'node-98-tmp' chain
+# => rename temporarily removes it from the bucket, leaving a NULL bucket
+# behind which is dereferenced later when inserting the chain again with new
+# name again
+
+(
+ echo "*filter"
+ for chain in node-1 node-10 node-101 node-102 node-104 node-107 node-11 node-12 node-13 node-14 node-15 node-16 node-17 node-18 node-19 node-2 node-20 node-21 node-22 node-23 node-25 node-26 node-27 node-28 node-29 node-3 node-30 node-31 node-32 node-33 node-34 node-36 node-37 node-39 node-4 node-40 node-41 node-42 node-43 node-44 node-45 node-46 node-47 node-48 node-49 node-5 node-50 node-51 node-53 node-54 node-55 node-56 node-57 node-58 node-59 node-6 node-60 node-61 node-62 node-63 node-64 node-65 node-66 node-68 node-69 node-7 node-70 node-71 node-74 node-75 node-76 node-8 node-80 node-81 node-86 node-89 node-9 node-92 node-93 node-95 node-98-tmp; do
+ echo ":$chain - [0:0]"
+ done
+ echo "COMMIT"
+) | $XT_MULTI iptables-restore
+$XT_MULTI iptables -E node-98-tmp node-98
+exit $?
diff --git a/iptables/tests/shell/testcases/chain/0007counters_0 b/iptables/tests/shell/testcases/chain/0007counters_0
new file mode 100755
index 00000000..0b21a926
--- /dev/null
+++ b/iptables/tests/shell/testcases/chain/0007counters_0
@@ -0,0 +1,78 @@
+#!/bin/bash -e
+
+SETUP="*filter
+:FORWARD ACCEPT [13:37]
+-A FORWARD -c 1 2 -j ACCEPT
+-A FORWARD -c 3 4 -j ACCEPT
+COMMIT"
+
+
+### -Z with index shall zero a single chain only
+
+EXPECT="-P FORWARD ACCEPT -c 13 37
+-A FORWARD -c 0 0 -j ACCEPT
+-A FORWARD -c 3 4 -j ACCEPT"
+
+$XT_MULTI iptables-restore --counters <<< "$SETUP"
+$XT_MULTI iptables -Z FORWARD 1
+diff -u <(echo "$EXPECT") <($XT_MULTI iptables -vS FORWARD)
+
+
+### -Z without index shall zero the chain and all rules
+
+EXPECT="-P FORWARD ACCEPT -c 0 0
+-A FORWARD -c 0 0 -j ACCEPT
+-A FORWARD -c 0 0 -j ACCEPT"
+
+$XT_MULTI iptables -Z FORWARD
+diff -u <(echo "$EXPECT") <($XT_MULTI iptables -vS FORWARD)
+
+
+### prepare for live test
+
+# iptables-nft will create output chain on demand, so make sure it exists
+$XT_MULTI iptables -A OUTPUT -d 127.2.3.4 -j ACCEPT
+
+# test runs in its own netns, lo is there but down by default
+ip link set lo up
+
+
+### pings (and pongs) hit OUTPUT policy, its counters must increase
+
+get_pkt_counter() { # (CHAIN)
+ $XT_MULTI iptables -vS $1 | awk '/^-P '$1'/{print $5; exit}'
+}
+
+counter_inc_test() {
+ pkt_pre=$(get_pkt_counter OUTPUT)
+ ping -q -i 0.2 -c 3 127.0.0.1
+ pkt_post=$(get_pkt_counter OUTPUT)
+ [[ $pkt_post -gt $pkt_pre ]]
+}
+
+counter_inc_test
+
+# iptables-nft-restore needed --counters to create chains with them
+if [[ $XT_MULTI == *xtables-nft-multi ]]; then
+ $XT_MULTI iptables -F OUTPUT
+ $XT_MULTI iptables -X OUTPUT
+ $XT_MULTI iptables-restore <<EOF
+*filter
+:OUTPUT ACCEPT [0:0]
+COMMIT
+EOF
+ counter_inc_test
+fi
+
+### unrelated restore must not touch changing counters in kernel
+
+# With legacy iptables, this works without --noflush even. With iptables-nft,
+# ruleset is flushed though. Not sure which behaviour is actually correct. :)
+pkt_pre=$pkt_post
+$XT_MULTI iptables-restore --noflush <<EOF
+*filter$(ping -i 0.2 -c 3 127.0.0.1 >/dev/null 2>&1)
+COMMIT
+EOF
+nft list ruleset
+pkt_post=$(get_pkt_counter OUTPUT)
+[[ $pkt_post -eq $((pkt_pre + 6 )) ]]
diff --git a/iptables/tests/shell/testcases/chain/0008rename-segfault2_0 b/iptables/tests/shell/testcases/chain/0008rename-segfault2_0
new file mode 100755
index 00000000..bc473d25
--- /dev/null
+++ b/iptables/tests/shell/testcases/chain/0008rename-segfault2_0
@@ -0,0 +1,32 @@
+#!/bin/bash
+#
+# Another funny rename bug in libiptc:
+# If there is a chain index bucket with only a single chain in it and it is not
+# the last one and that chain is renamed, a chain index rebuild is triggered.
+# Since TC_RENAME_CHAIN missed to temporarily decrement num_chains value, an
+# extra index is allocated and remains NULL. The following insert of renamed
+# chain then segfaults.
+
+(
+ echo "*filter"
+ # first bucket
+ for ((i = 0; i < 40; i++)); do
+ echo ":chain-a-$i - [0:0]"
+ done
+ # second bucket
+ for ((i = 0; i < 40; i++)); do
+ echo ":chain-b-$i - [0:0]"
+ done
+ # third bucket, just make sure it exists
+ echo ":chain-c-0 - [0:0]"
+ echo "COMMIT"
+) | $XT_MULTI iptables-restore
+
+# rename all chains of the middle bucket
+(
+ echo "*filter"
+ for ((i = 0; i < 40; i++)); do
+ echo "-E chain-b-$i chain-d-$i"
+ done
+ echo "COMMIT"
+) | $XT_MULTI iptables-restore --noflush
diff --git a/iptables/tests/shell/testcases/ebtables/0001-ebtables-basic_0 b/iptables/tests/shell/testcases/ebtables/0001-ebtables-basic_0
index 6f11bd12..bae0de7d 100755
--- a/iptables/tests/shell/testcases/ebtables/0001-ebtables-basic_0
+++ b/iptables/tests/shell/testcases/ebtables/0001-ebtables-basic_0
@@ -15,13 +15,13 @@ get_entries_count() { # (chain)
set -x
-for t in filter nat;do
+for t in filter nat broute; do
$XT_MULTI ebtables -t $t -L || exit 1
$XT_MULTI ebtables -t $t -X || exit 1
$XT_MULTI ebtables -t $t -F || exit 1
done
-for t in broute foobar ;do
+for t in foobar; do
$XT_MULTI ebtables -t $t -L &&
$XT_MULTI ebtables -t $t -X &&
$XT_MULTI ebtables -t $t -F
diff --git a/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0 b/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0
index ccdef19c..b4f9728b 100755
--- a/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0
+++ b/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0
@@ -13,8 +13,8 @@ $XT_MULTI ebtables -A INPUT -p IPv4 -i lo -j ACCEPT
$XT_MULTI ebtables -P FORWARD DROP
$XT_MULTI ebtables -A OUTPUT -s ff:ff:ff:ff:ff:ff/ff:ff:ff:ff:ff:ff -j DROP
$XT_MULTI ebtables -N foo
-$XT_MULTI ebtables -A foo --802_3-sap 0x23 -j ACCEPT
-$XT_MULTI ebtables -A foo --802_3-sap 0xaa --802_3-type 0x1337 -j ACCEPT
+$XT_MULTI ebtables -A foo -p length --802_3-sap 0x23 -j ACCEPT
+$XT_MULTI ebtables -A foo -p length --802_3-sap 0xaa --802_3-type 0x1337 -j ACCEPT
#$XT_MULTI ebtables -A foo --among-dst fe:ed:ba:be:00:01,fe:ed:ba:be:00:02,fe:ed:ba:be:00:03 -j ACCEPT
$XT_MULTI ebtables -A foo -p ARP --arp-gratuitous -j ACCEPT
$XT_MULTI ebtables -A foo -p ARP --arp-opcode Request -j ACCEPT
@@ -38,13 +38,13 @@ $XT_MULTI ebtables -A foo -p IPv6 --ip6-proto tcp -j ACCEPT
$XT_MULTI ebtables -A foo --limit 100 --limit-burst 42 -j ACCEPT
$XT_MULTI ebtables -A foo --log
-$XT_MULTI ebtables -A foo --mark-set 0x23 --mark-target ACCEPT
+$XT_MULTI ebtables -A foo -j mark --mark-set 0x23 --mark-target ACCEPT
$XT_MULTI ebtables -A foo --nflog
$XT_MULTI ebtables -A foo --pkttype-type multicast -j ACCEPT
$XT_MULTI ebtables -A foo --stp-type config -j ACCEPT
#$XT_MULTI ebtables -A foo --vlan-id 42 -j ACCEPT
-$XT_MULTI ebtables -A foo --802_3-sap 0x23 --limit 100 -j ACCEPT
+$XT_MULTI ebtables -A foo -p length --802_3-sap 0x23 --limit 100 -j ACCEPT
$XT_MULTI ebtables -A foo --pkttype-type multicast --log
$XT_MULTI ebtables -A foo --pkttype-type multicast --limit 100 -j ACCEPT
@@ -53,7 +53,7 @@ $XT_MULTI ebtables -A FORWARD -j foo
$XT_MULTI ebtables -N bar
$XT_MULTI ebtables -P bar RETURN
-$XT_MULTI ebtables -t nat -A PREROUTING --redirect-target ACCEPT
+$XT_MULTI ebtables -t nat -A PREROUTING -j redirect --redirect-target ACCEPT
#$XT_MULTI ebtables -t nat -A PREROUTING --to-src fe:ed:ba:be:00:01
$XT_MULTI ebtables -t nat -A OUTPUT -j ACCEPT
@@ -75,8 +75,8 @@ DUMP='*filter
-A INPUT -p IPv4 -i lo -j ACCEPT
-A FORWARD -j foo
-A OUTPUT -s Broadcast -j DROP
--A foo --802_3-sap 0x23 -j ACCEPT
--A foo --802_3-sap 0xaa --802_3-type 0x1337 -j ACCEPT
+-A foo -p Length --802_3-sap 0x23 -j ACCEPT
+-A foo -p Length --802_3-sap 0xaa --802_3-type 0x1337 -j ACCEPT
-A foo -p ARP --arp-gratuitous -j ACCEPT
-A foo -p ARP --arp-op Request -j ACCEPT
-A foo -p ARP --arp-ip-src 10.0.0.1 -j ACCEPT
@@ -91,13 +91,13 @@ DUMP='*filter
-A foo -p IPv6 --ip6-dst feed:babe::/64 -j ACCEPT
-A foo -p IPv6 --ip6-proto tcp -j ACCEPT
-A foo --limit 100/sec --limit-burst 42 -j ACCEPT
--A foo --log-level notice --log-prefix "" -j CONTINUE
+-A foo --log-level notice -j CONTINUE
-A foo -j mark --mark-set 0x23 --mark-target ACCEPT
-A foo --nflog-group 1 -j CONTINUE
-A foo --pkttype-type multicast -j ACCEPT
-A foo --stp-type config -j ACCEPT
--A foo --802_3-sap 0x23 --limit 100/sec --limit-burst 5 -j ACCEPT
--A foo --pkttype-type multicast --log-level notice --log-prefix "" -j CONTINUE
+-A foo -p Length --802_3-sap 0x23 --limit 100/sec --limit-burst 5 -j ACCEPT
+-A foo --pkttype-type multicast --log-level notice -j CONTINUE
-A foo --pkttype-type multicast --limit 100/sec --limit-burst 5 -j ACCEPT
*nat
:PREROUTING ACCEPT
diff --git a/iptables/tests/shell/testcases/ebtables/0003-ebtables-restore-defaults_0 b/iptables/tests/shell/testcases/ebtables/0003-ebtables-restore-defaults_0
index 63891c1b..7554ef85 100755
--- a/iptables/tests/shell/testcases/ebtables/0003-ebtables-restore-defaults_0
+++ b/iptables/tests/shell/testcases/ebtables/0003-ebtables-restore-defaults_0
@@ -24,7 +24,7 @@ EXPECT='*filter
-A FORWARD --limit 100/sec --limit-burst 42 -j ACCEPT
-A FORWARD --limit 1000/sec --limit-burst 5 -j ACCEPT
-A FORWARD --log-level notice --log-prefix "foobar" -j CONTINUE
--A FORWARD --log-level notice --log-prefix "" -j CONTINUE'
+-A FORWARD --log-level notice -j CONTINUE'
$XT_MULTI ebtables --init-table
$XT_MULTI ebtables-restore <<<$DUMP
diff --git a/iptables/tests/shell/testcases/ebtables/0006-flush_0 b/iptables/tests/shell/testcases/ebtables/0006-flush_0
new file mode 100755
index 00000000..5d714529
--- /dev/null
+++ b/iptables/tests/shell/testcases/ebtables/0006-flush_0
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+set -e
+
+# there is no legacy backend to test
+[[ $XT_MULTI == *xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
+
+RULESET='*filter
+:INPUT ACCEPT
+:FORWARD ACCEPT
+:OUTPUT ACCEPT
+-A FORWARD --among-dst fe:ed:ba:be:13:37=10.0.0.1 -j ACCEPT
+-A OUTPUT --among-src c0:ff:ee:90:0:0=192.168.0.1 -j DROP
+*nat
+:PREROUTING ACCEPT
+:OUTPUT ACCEPT
+:POSTROUTING ACCEPT
+-A OUTPUT --among-src c0:ff:ee:90:90:90=192.168.0.1 -j DROP'
+
+$XT_MULTI ebtables-restore <<<$RULESET
+diff -u <(echo -e "$RULESET") <($XT_MULTI ebtables-save | grep -v '^#')
+
+RULESET='*filter
+:INPUT ACCEPT
+:FORWARD ACCEPT
+:OUTPUT ACCEPT
+-A FORWARD --among-dst fe:ed:ba:be:13:37=10.0.0.1 -j ACCEPT
+-A OUTPUT --among-src c0:ff:ee:90:0:0=192.168.0.1 -j DROP
+*nat
+:PREROUTING ACCEPT
+:OUTPUT ACCEPT
+:POSTROUTING ACCEPT'
+
+$XT_MULTI ebtables -t nat -F
+diff -u <(echo -e "$RULESET") <($XT_MULTI ebtables-save | grep -v '^#')
+
+RULESET='*filter
+:INPUT ACCEPT
+:FORWARD ACCEPT
+:OUTPUT ACCEPT
+*nat
+:PREROUTING ACCEPT
+:OUTPUT ACCEPT
+:POSTROUTING ACCEPT'
+
+$XT_MULTI ebtables -t filter -F
+diff -u <(echo -e "$RULESET") <($XT_MULTI ebtables-save | grep -v '^#')
diff --git a/iptables/tests/shell/testcases/ebtables/0008-ebtables-among_0 b/iptables/tests/shell/testcases/ebtables/0008-ebtables-among_0
new file mode 100755
index 00000000..962b1e03
--- /dev/null
+++ b/iptables/tests/shell/testcases/ebtables/0008-ebtables-among_0
@@ -0,0 +1,106 @@
+#!/bin/sh
+
+case "$XT_MULTI" in
+*xtables-nft-multi)
+ ;;
+*)
+ echo "skip $XT_MULTI"
+ exit 0
+ ;;
+esac
+
+sfx=$(mktemp -u "XXXXXXXX")
+nsa="nsa-$sfx"
+nsb="nsb-$sfx"
+nsc="nsc-$sfx"
+
+cleanup()
+{
+ ip netns del "$nsa"
+ ip netns del "$nsb"
+ ip netns del "$nsc"
+}
+
+trap cleanup EXIT
+
+assert_fail()
+{
+ if [ $1 -eq 0 ]; then
+ echo "FAILED: $2"
+ exit 1
+ fi
+}
+
+assert_pass()
+{
+ if [ $1 -ne 0 ]; then
+ echo "FAILED: $2"
+ exit 2
+ fi
+}
+
+ip netns add "$nsa"
+ip netns add "$nsb"
+ip netns add "$nsc"
+
+ip link add name c_b netns "$nsc" type veth peer name b_c netns "$nsb"
+ip link add name s_b netns "$nsa" type veth peer name b_s netns "$nsb"
+ip netns exec "$nsb" ip link add name br0 type bridge
+
+ip -net "$nsb" link set b_c up
+ip netns exec "$nsb" ip link set b_s up
+ip netns exec "$nsb" ip addr add 10.167.11.254/24 dev br0
+ip netns exec "$nsb" ip link set br0 up
+ip netns exec "$nsb" ip link set b_c master br0
+ip netns exec "$nsb" ip link set b_s master br0
+ip netns exec "$nsc" ip addr add 10.167.11.2/24 dev c_b
+ip netns exec "$nsc" ip link set c_b up
+ip -net "$nsa" addr add 10.167.11.1/24 dev s_b
+ip -net "$nsa" link set s_b up
+
+ip netns exec "$nsc" ping -q 10.167.11.1 -c1 >/dev/null || exit 1
+
+bf_bridge_mac1=`ip netns exec "$nsb" cat /sys/class/net/b_s/address`
+bf_bridge_mac0=`ip netns exec "$nsb" cat /sys/class/net/b_c/address`
+bf_client_mac1=`ip netns exec "$nsc" cat /sys/class/net/c_b/address`
+bf_server_mac1=`ip netns exec "$nsa" cat /sys/class/net/s_b/address`
+
+bf_server_ip1="10.167.11.1"
+bf_bridge_ip0="10.167.11.254"
+bf_client_ip1="10.167.11.2"
+pktsize=64
+
+# --among-src [mac,IP]
+among="$bf_bridge_mac0=$bf_bridge_ip0,$bf_client_mac1=$bf_client_ip1"
+ip netns exec "$nsb" $XT_MULTI ebtables -F
+ip netns exec "$nsb" $XT_MULTI ebtables -A FORWARD \
+ -p ip --ip-dst $bf_server_ip1 --among-src "$among" -j DROP > /dev/null
+ip netns exec "$nsc" ping -q $bf_server_ip1 -c 1 -s $pktsize -W 1 >/dev/null
+assert_fail $? "--among-src [match]"
+
+# ip netns exec "$nsb" $XT_MULTI ebtables -L --Ln --Lc
+
+among="$bf_bridge_mac0=$bf_bridge_ip0,$bf_client_mac1=$bf_client_ip1"
+ip netns exec "$nsb" $XT_MULTI ebtables -F
+ip netns exec "$nsb" $XT_MULTI ebtables -A FORWARD \
+ -p ip --ip-dst $bf_server_ip1 ! --among-src "$among" -j DROP > /dev/null
+ip netns exec "$nsc" ping $bf_server_ip1 -c 1 -s $pktsize -W 1 >/dev/null
+assert_pass $? "--among-src [not match]"
+
+# --among-dst [mac,IP]
+among="$bf_client_mac1=$bf_client_ip1,$bf_server_mac1=$bf_server_ip1"
+ip netns exec "$nsb" $XT_MULTI ebtables -F
+ip netns exec "$nsb" $XT_MULTI ebtables -A FORWARD \
+ -p ip --ip-src $bf_client_ip1 --among-dst "$among" -j DROP > /dev/null
+ip netns exec "$nsc" ping -q $bf_server_ip1 -c 1 -s $pktsize -W 1 > /dev/null
+assert_fail $? "--among-dst [match]"
+
+# ! --among-dst [mac,IP]
+among="$bf_client_mac1=$bf_client_ip1,$bf_server_mac1=$bf_server_ip1"
+ip netns exec "$nsb" $XT_MULTI ebtables -F
+ip netns exec "$nsb" $XT_MULTI ebtables -A FORWARD \
+ -p ip --ip-src $bf_client_ip1 ! --among-dst "$among" -j DROP > /dev/null
+ip netns exec "$nsc" ping -q $bf_server_ip1 -c 1 -s $pktsize -W 1 > /dev/null
+assert_pass $? "--among-dst [not match]"
+
+exit 0
diff --git a/iptables/tests/shell/testcases/ebtables/0009-broute-bug_0 b/iptables/tests/shell/testcases/ebtables/0009-broute-bug_0
new file mode 100755
index 00000000..0def0ac5
--- /dev/null
+++ b/iptables/tests/shell/testcases/ebtables/0009-broute-bug_0
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Missing BROUTING-awareness in ebt_get_current_chain() caused an odd caching bug when restoring:
+# - with --noflush
+# - a second table after the broute one
+# - A policy command but no chain line for BROUTING chain
+
+set -e
+
+case "$XT_MULTI" in
+*xtables-nft-multi)
+ ;;
+*)
+ echo "skip $XT_MULTI"
+ exit 0
+ ;;
+esac
+
+$XT_MULTI ebtables-restore --noflush <<EOF
+*broute
+-P BROUTING ACCEPT
+*nat
+-P PREROUTING ACCEPT
+COMMIT
+EOF
diff --git a/iptables/tests/shell/testcases/ebtables/0010-change-counters_0 b/iptables/tests/shell/testcases/ebtables/0010-change-counters_0
new file mode 100755
index 00000000..4f783819
--- /dev/null
+++ b/iptables/tests/shell/testcases/ebtables/0010-change-counters_0
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+case "$XT_MULTI" in
+*xtables-nft-multi)
+ ;;
+*)
+ echo "skip $XT_MULTI"
+ exit 0
+ ;;
+esac
+
+set -e
+set -x
+
+check_rule() { # (pcnt, bcnt)
+ $XT_MULTI ebtables -L FORWARD --Lc --Ln | \
+ grep -q "^1. -o eth0 -j CONTINUE , pcnt = $1 -- bcnt = $2$"
+}
+
+$XT_MULTI ebtables -A FORWARD -o eth0 -c 10 20
+check_rule 10 20
+
+$XT_MULTI ebtables -C FORWARD 1 100 200
+check_rule 100 200
+
+$XT_MULTI ebtables -C FORWARD 101 201 -o eth0
+check_rule 101 201
+
+$XT_MULTI ebtables -C FORWARD 1 +10 -20
+check_rule 111 181
+
+$XT_MULTI ebtables -C FORWARD -10 +20 -o eth0
+check_rule 101 201
+
+$XT_MULTI ebtables -A FORWARD -o eth1 -c 111 211
+$XT_MULTI ebtables -A FORWARD -o eth2 -c 121 221
+
+$XT_MULTI ebtables -C FORWARD 2:3 +100 -200
+
+EXPECT='1. -o eth0 -j CONTINUE , pcnt = 101 -- bcnt = 201
+2. -o eth1 -j CONTINUE , pcnt = 211 -- bcnt = 11
+3. -o eth2 -j CONTINUE , pcnt = 221 -- bcnt = 21'
+diff -u <(echo "$EXPECT") \
+ <($XT_MULTI ebtables -L FORWARD --Lc --Ln | grep -- '-o eth')
+
diff --git a/iptables/tests/shell/testcases/ip6tables/0002-verbose-output_0 b/iptables/tests/shell/testcases/ip6tables/0002-verbose-output_0
index 2a1518d6..45fab830 100755
--- a/iptables/tests/shell/testcases/ip6tables/0002-verbose-output_0
+++ b/iptables/tests/shell/testcases/ip6tables/0002-verbose-output_0
@@ -12,7 +12,7 @@ VOUT2='ACCEPT all opt -- in eth2 out eth3 feed:babe::4 -> feed:babe::5'
RULE3='-p icmpv6 -m icmp6 --icmpv6-type no-route'
VOUT3=' ipv6-icmp opt -- in * out * ::/0 -> ::/0 ipv6-icmptype 1 code 0'
RULE4='-m dst --dst-len 42 -m rt --rt-type 23'
-VOUT4=' all opt -- in * out * ::/0 -> ::/0 dst length:42 rt type:23'
+VOUT4=' all opt -- in * out * ::/0 -> ::/0 dst length:42 rt type:23'
RULE5='-m frag --fragid 1337 -j LOG'
VOUT5='LOG all opt -- in * out * ::/0 -> ::/0 frag id:1337 LOG flags 0 level 4'
@@ -33,11 +33,11 @@ EXPECT='Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
- 0 0 ACCEPT 0 -- eth2 eth3 feed:babe::1 feed:babe::2
- 0 0 ACCEPT 0 -- eth2 eth3 feed:babe::4 feed:babe::5
- 0 0 58 -- * * ::/0 ::/0 ipv6-icmptype 1 code 0
- 0 0 0 -- * * ::/0 ::/0 dst length:42 rt type:23
- 0 0 LOG 0 -- * * ::/0 ::/0 frag id:1337 LOG flags 0 level 4
+ 0 0 ACCEPT all -- eth2 eth3 feed:babe::1 feed:babe::2
+ 0 0 ACCEPT all -- eth2 eth3 feed:babe::4 feed:babe::5
+ 0 0 ipv6-icmp -- * * ::/0 ::/0 ipv6-icmptype 1 code 0
+ 0 0 all -- * * ::/0 ::/0 dst length:42 rt type:23
+ 0 0 LOG all -- * * ::/0 ::/0 frag id:1337 LOG flags 0 level 4
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination'
diff --git a/iptables/tests/shell/testcases/ip6tables/0003-list-rules_0 b/iptables/tests/shell/testcases/ip6tables/0003-list-rules_0
index c98bdd6e..09e39927 100755
--- a/iptables/tests/shell/testcases/ip6tables/0003-list-rules_0
+++ b/iptables/tests/shell/testcases/ip6tables/0003-list-rules_0
@@ -3,7 +3,7 @@
set -e
$XT_MULTI ip6tables -N foo
-$XT_MULTI ip6tables -A FORWARD -i eth23 -o eth42 -j ACCEPT
+$XT_MULTI ip6tables -A FORWARD -i eth23 -o eth42 -j ACCEPT -c 23 42
$XT_MULTI ip6tables -A FORWARD -i eth42 -o eth23 -g foo
$XT_MULTI ip6tables -t nat -A OUTPUT -o eth123 -m mark --mark 0x42 -j ACCEPT
@@ -20,7 +20,7 @@ EXPECT='-P INPUT ACCEPT -c 0 0
-P FORWARD ACCEPT -c 0 0
-P OUTPUT ACCEPT -c 0 0
-N foo
--A FORWARD -i eth23 -o eth42 -c 0 0 -j ACCEPT
+-A FORWARD -i eth23 -o eth42 -c 23 42 -j ACCEPT
-A FORWARD -i eth42 -o eth23 -c 0 0 -g foo'
diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI ip6tables -v -S)
@@ -32,7 +32,7 @@ EXPECT='-P FORWARD ACCEPT
diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI ip6tables -S FORWARD)
EXPECT='-P FORWARD ACCEPT -c 0 0
--A FORWARD -i eth23 -o eth42 -c 0 0 -j ACCEPT
+-A FORWARD -i eth23 -o eth42 -c 23 42 -j ACCEPT
-A FORWARD -i eth42 -o eth23 -c 0 0 -g foo'
diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI ip6tables -v -S FORWARD)
diff --git a/iptables/tests/shell/testcases/ip6tables/0005-rule-check_0 b/iptables/tests/shell/testcases/ip6tables/0005-rule-check_0
new file mode 100755
index 00000000..cc8215bf
--- /dev/null
+++ b/iptables/tests/shell/testcases/ip6tables/0005-rule-check_0
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Test the fix in commit 78850e7dba64a ("ip6tables: Fix checking existence of
+# rule"). Happens with legacy ip6tables only, but testing ip6tables-nft doesn't
+# hurt.
+#
+# Code taken from https://bugzilla.netfilter.org/show_bug.cgi?id=1667
+# Thanks to Jonathan Caicedo <jonathan@jcaicedo.com> for providing it.
+
+RULE='-p tcp --dport 81 -j DNAT --to-destination [::1]:81'
+
+$XT_MULTI ip6tables -t nat -N testchain || exit 1
+$XT_MULTI ip6tables -t nat -A testchain $RULE || exit 1
+$XT_MULTI ip6tables -t nat -C testchain $RULE || exit 1
+
+$XT_MULTI ip6tables -t nat -C testchain ${RULE//81/82} 2>/dev/null && exit 1
+exit 0
diff --git a/iptables/tests/shell/testcases/ipt-restore/0003-restore-ordering_0 b/iptables/tests/shell/testcases/ipt-restore/0003-restore-ordering_0
index 3f1d229e..5482b7ea 100755
--- a/iptables/tests/shell/testcases/ipt-restore/0003-restore-ordering_0
+++ b/iptables/tests/shell/testcases/ipt-restore/0003-restore-ordering_0
@@ -123,3 +123,19 @@ EXPECT='-A FORWARD -m comment --comment "rule 1" -j ACCEPT
-A FORWARD -m comment --comment "rule 3" -j ACCEPT'
diff -u -Z <(echo -e "$EXPECT") <(ipt_show)
+
+# test adding, referencing and deleting the same rule in a batch
+
+$XT_MULTI iptables-restore <<EOF
+*filter
+-A FORWARD -m comment --comment "first rule" -j ACCEPT
+-A FORWARD -m comment --comment "referenced rule" -j ACCEPT
+-I FORWARD 2 -m comment --comment "referencing rule" -j ACCEPT
+-D FORWARD -m comment --comment "referenced rule" -j ACCEPT
+COMMIT
+EOF
+
+EXPECT='-A FORWARD -m comment --comment "first rule" -j ACCEPT
+-A FORWARD -m comment --comment "referencing rule" -j ACCEPT'
+
+diff -u -Z <(echo -e "$EXPECT") <(ipt_show)
diff --git a/iptables/tests/shell/testcases/ipt-restore/0008-restore-counters_0 b/iptables/tests/shell/testcases/ipt-restore/0008-restore-counters_0
index 5ac70682..854768c9 100755
--- a/iptables/tests/shell/testcases/ipt-restore/0008-restore-counters_0
+++ b/iptables/tests/shell/testcases/ipt-restore/0008-restore-counters_0
@@ -20,3 +20,10 @@ EXPECT=":foo - [0:0]
$XT_MULTI iptables-restore --counters <<< "$DUMP"
diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI iptables-save --counters | grep foo)
+
+# if present, counters must be in proper format
+! $XT_MULTI iptables-restore <<EOF
+*filter
+:FORWARD ACCEPT bar
+COMMIT
+EOF
diff --git a/iptables/tests/shell/testcases/ipt-restore/0011-noflush-empty-line_0 b/iptables/tests/shell/testcases/ipt-restore/0011-noflush-empty-line_0
index 1a3af46f..bea1a690 100755
--- a/iptables/tests/shell/testcases/ipt-restore/0011-noflush-empty-line_0
+++ b/iptables/tests/shell/testcases/ipt-restore/0011-noflush-empty-line_0
@@ -12,5 +12,5 @@ EOF
EXPECT='Chain FORWARD (policy ACCEPT)
target prot opt source destination
-ACCEPT 0 -- 0.0.0.0/0 0.0.0.0/0 '
+ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 '
diff -u <(echo "$EXPECT") <($XT_MULTI iptables -n -L FORWARD)
diff --git a/iptables/tests/shell/testcases/ipt-save/0001load-dumps_0 b/iptables/tests/shell/testcases/ipt-save/0001load-dumps_0
index 4e0be51c..48f5f7b4 100755
--- a/iptables/tests/shell/testcases/ipt-save/0001load-dumps_0
+++ b/iptables/tests/shell/testcases/ipt-save/0001load-dumps_0
@@ -39,6 +39,7 @@ do_simple()
$XT_MULTI ${iptables}-restore < "$dumpfile"
$XT_MULTI ${iptables}-save | grep -v "^#" > "$tmpfile"
+ sed -i -e 's/-p 47 /-p gre /' "$tmpfile"
do_diff $dumpfile "$tmpfile"
if [ $? -ne 0 ]; then
# cp "$tmpfile" "$dumpfile.got"
diff --git a/iptables/tests/shell/testcases/iptables/0002-verbose-output_0 b/iptables/tests/shell/testcases/iptables/0002-verbose-output_0
index 15c72af3..5d2af4c8 100755
--- a/iptables/tests/shell/testcases/iptables/0002-verbose-output_0
+++ b/iptables/tests/shell/testcases/iptables/0002-verbose-output_0
@@ -21,8 +21,8 @@ EXPECT='Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
- 0 0 ACCEPT 0 -- eth2 eth3 10.0.0.1 10.0.0.2
- 0 0 ACCEPT 0 -- eth2 eth3 10.0.0.4 10.0.0.5
+ 0 0 ACCEPT all -- eth2 eth3 10.0.0.1 10.0.0.2
+ 0 0 ACCEPT all -- eth2 eth3 10.0.0.4 10.0.0.5
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination'
diff --git a/iptables/tests/shell/testcases/iptables/0003-list-rules_0 b/iptables/tests/shell/testcases/iptables/0003-list-rules_0
index d335d442..d07bd151 100755
--- a/iptables/tests/shell/testcases/iptables/0003-list-rules_0
+++ b/iptables/tests/shell/testcases/iptables/0003-list-rules_0
@@ -3,7 +3,7 @@
set -e
$XT_MULTI iptables -N foo
-$XT_MULTI iptables -A FORWARD -i eth23 -o eth42 -j ACCEPT
+$XT_MULTI iptables -A FORWARD -i eth23 -o eth42 -j ACCEPT -c 23 42
$XT_MULTI iptables -A FORWARD -i eth42 -o eth23 -g foo
$XT_MULTI iptables -t nat -A OUTPUT -o eth123 -m mark --mark 0x42 -j ACCEPT
@@ -20,7 +20,7 @@ EXPECT='-P INPUT ACCEPT -c 0 0
-P FORWARD ACCEPT -c 0 0
-P OUTPUT ACCEPT -c 0 0
-N foo
--A FORWARD -i eth23 -o eth42 -c 0 0 -j ACCEPT
+-A FORWARD -i eth23 -o eth42 -c 23 42 -j ACCEPT
-A FORWARD -i eth42 -o eth23 -c 0 0 -g foo'
diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI iptables -v -S)
@@ -32,7 +32,7 @@ EXPECT='-P FORWARD ACCEPT
diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI iptables -S FORWARD)
EXPECT='-P FORWARD ACCEPT -c 0 0
--A FORWARD -i eth23 -o eth42 -c 0 0 -j ACCEPT
+-A FORWARD -i eth23 -o eth42 -c 23 42 -j ACCEPT
-A FORWARD -i eth42 -o eth23 -c 0 0 -g foo'
diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI iptables -v -S FORWARD)
diff --git a/iptables/tests/shell/testcases/iptables/0004-return-codes_0 b/iptables/tests/shell/testcases/iptables/0004-return-codes_0
index 33c5f1f3..234f3040 100755
--- a/iptables/tests/shell/testcases/iptables/0004-return-codes_0
+++ b/iptables/tests/shell/testcases/iptables/0004-return-codes_0
@@ -58,6 +58,7 @@ cmd 1 "$ENOENT" -Z bar
cmd 0 -E foo bar
cmd 1 "$EEXIST_F" -E foo bar
cmd 1 "$ENOENT" -E foo bar2
+cmd 1 "$ENOENT" -L foo
cmd 0 -N foo2
cmd 1 "$EEXIST_F" -E foo2 bar
diff --git a/iptables/tests/shell/testcases/iptables/0009-unknown-arg_0 b/iptables/tests/shell/testcases/iptables/0009-unknown-arg_0
new file mode 100755
index 00000000..ac6e7439
--- /dev/null
+++ b/iptables/tests/shell/testcases/iptables/0009-unknown-arg_0
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+rc=0
+
+check() {
+ local cmd="$1"
+ local msg="$2"
+
+ $XT_MULTI $cmd 2>&1 | grep -q "$msg" || {
+ echo "cmd: $XT_MULTI $1"
+ echo "exp: $msg"
+ echo "res: $($XT_MULTI $cmd 2>&1)"
+ rc=1
+ }
+}
+
+cmds="iptables ip6tables"
+[[ $XT_MULTI == *xtables-nft-multi ]] && {
+ cmds+=" ebtables"
+ cmds+=" iptables-translate"
+ cmds+=" ip6tables-translate"
+ cmds+=" ebtables-translate"
+}
+
+for cmd in $cmds; do
+ check "${cmd} --foo" 'unknown option "--foo"'
+ check "${cmd} -A" 'option "-A" requires an argument'
+ check "${cmd} -aL" 'unknown option "-a"'
+done
+
+exit $rc
diff --git a/iptables/tests/shell/testcases/iptables/0010-wait_0 b/iptables/tests/shell/testcases/iptables/0010-wait_0
new file mode 100755
index 00000000..4481f966
--- /dev/null
+++ b/iptables/tests/shell/testcases/iptables/0010-wait_0
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+case "$XT_MULTI" in
+*xtables-legacy-multi)
+ ;;
+*)
+ echo skip $XT_MULTI
+ exit 0
+ ;;
+esac
+
+coproc RESTORE { $XT_MULTI iptables-restore; }
+echo "*filter" >&${RESTORE[1]}
+
+
+$XT_MULTI iptables -A FORWARD -j ACCEPT &
+ipt_pid=$!
+
+waitpid -t 1 $ipt_pid
+[[ $? -eq 3 ]] && {
+ echo "process waits when it should not"
+ exit 1
+}
+wait $ipt_pid
+[[ $? -eq 0 ]] && {
+ echo "process exited 0 despite busy lock"
+ exit 1
+}
+
+t0=$(date +%s)
+$XT_MULTI iptables -w 3 -A FORWARD -j ACCEPT
+t1=$(date +%s)
+[[ $((t1 - t0)) -ge 3 ]] || {
+ echo "wait time not expired"
+ exit 1
+}
+
+$XT_MULTI iptables -w -A FORWARD -j ACCEPT &
+ipt_pid=$!
+
+waitpid -t 3 $ipt_pid
+[[ $? -eq 3 ]] || {
+ echo "no indefinite wait"
+ exit 1
+}
+kill $ipt_pid
+waitpid -t 3 $ipt_pid
+[[ $? -eq 3 ]] && {
+ echo "killed waiting iptables call did not exit in time"
+ exit 1
+}
+
+kill $RESTORE_PID
+wait
+exit 0
diff --git a/iptables/tests/shell/testcases/nft-only/0007-mid-restore-flush_0 b/iptables/tests/shell/testcases/nft-only/0007-mid-restore-flush_0
index 43880ffb..981f007f 100755
--- a/iptables/tests/shell/testcases/nft-only/0007-mid-restore-flush_0
+++ b/iptables/tests/shell/testcases/nft-only/0007-mid-restore-flush_0
@@ -13,11 +13,11 @@ COMMIT
:foo [0:0]
EOF
-$XT_MULTI iptables-save | grep -q ':foo'
+sleep 1
+$XT_MULTI iptables-save | grep -q ':foo' || exit 1
nft flush ruleset
echo "COMMIT" >&"${COPROC[1]}"
-sleep 1
-
-[[ -n $COPROC_PID ]] && kill $COPROC_PID
-wait
+# close the pipe to make iptables-restore exit if it didn't error out yet
+eval "exec ${COPROC[1]}>&-"
+wait $COPROC_PID
diff --git a/iptables/tests/shell/testcases/nft-only/0009-needless-bitwise_0 b/iptables/tests/shell/testcases/nft-only/0009-needless-bitwise_0
index 41588a10..34802cc2 100755
--- a/iptables/tests/shell/testcases/nft-only/0009-needless-bitwise_0
+++ b/iptables/tests/shell/testcases/nft-only/0009-needless-bitwise_0
@@ -340,7 +340,7 @@ bridge filter OUTPUT 10 9
# - lines with bytecode (starting with ' [')
# - empty lines (so printed diff is not a complete mess)
filter() {
- awk '/^( \[|$)/{print}'
+ awk '/^table /{exit} /^( \[|$)/{print}'
}
diff -u -Z <(filter <<< "$EXPECT") <(nft --debug=netlink list ruleset | filter)
diff --git a/iptables/tests/shell/testcases/nft-only/0010-iptables-nft-save.txt b/iptables/tests/shell/testcases/nft-only/0010-iptables-nft-save.txt
new file mode 100644
index 00000000..5ee4c231
--- /dev/null
+++ b/iptables/tests/shell/testcases/nft-only/0010-iptables-nft-save.txt
@@ -0,0 +1,26 @@
+*filter
+:INPUT ACCEPT [0:0]
+:FORWARD ACCEPT [0:0]
+:OUTPUT ACCEPT [0:0]
+-A INPUT -s 1.2.3.4/32 -p tcp -m tcp --dport 23 -j ACCEPT
+-A INPUT -s 1.2.3.0/24 -d 0.0.0.0/32 -p udp -m udp --dport 67:69 -j DROP
+-A INPUT -s 1.0.0.0/8 -d 0.0.0.0/32 -p tcp -m tcp --sport 1024:65535 --dport 443 --tcp-flags SYN,ACK SYN -j ACCEPT
+-A INPUT -p tcp -m tcp --dport 443 ! --tcp-flags SYN NONE -m comment --comment "checks if SYN bit is set"
+-A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m comment --comment "same as iptables --syn"
+-A INPUT -p tcp -m tcp --tcp-flags SYN SYN
+-A INPUT -p tcp -m tcp ! --tcp-flags SYN,ACK SYN,ACK
+-A INPUT -d 0.0.0.0/1 -m ttl --ttl-eq 1 -j DROP
+-A INPUT -d 0.0.0.0/2 -m ttl --ttl-gt 2 -j ACCEPT
+-A INPUT -d 0.0.0.0/3 -m ttl --ttl-lt 254 -j ACCEPT
+-A INPUT -d 0.0.0.0/4 -m ttl ! --ttl-eq 255 -j DROP
+-A INPUT -d 8.0.0.0/5 -p icmp -m icmp --icmp-type 1 -j ACCEPT
+-A INPUT -d 8.0.0.0/6 -p icmp -m icmp --icmp-type 2/3 -j ACCEPT
+-A INPUT -d 10.0.0.0/7 -p icmp -m icmp --icmp-type 8 -j ACCEPT
+-A INPUT -m pkttype --pkt-type broadcast -j ACCEPT
+-A INPUT -m pkttype ! --pkt-type unicast -j DROP
+-A INPUT -p tcp
+-A INPUT -d 0.0.0.0/1 -p udp
+-A FORWARD -m limit --limit 10/day
+-A FORWARD -p udp -m udp --dport 42
+-A FORWARD -i lo -o lo+ -j NFLOG --nflog-prefix "should use NFLOG" --nflog-group 1 --nflog-size 123 --nflog-threshold 42
+COMMIT
diff --git a/iptables/tests/shell/testcases/nft-only/0010-native-delinearize_0 b/iptables/tests/shell/testcases/nft-only/0010-native-delinearize_0
new file mode 100755
index 00000000..7859e76c
--- /dev/null
+++ b/iptables/tests/shell/testcases/nft-only/0010-native-delinearize_0
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+[[ $XT_MULTI == *xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
+nft -v >/dev/null || exit 0
+
+set -e
+
+unshare -n bash -c "nft -f $(dirname $0)/0010-nft-native.txt;
+ diff -u -Z $(dirname $0)/0010-iptables-nft-save.txt <($XT_MULTI iptables-save | grep -v '^#')"
diff --git a/iptables/tests/shell/testcases/nft-only/0010-nft-native.txt b/iptables/tests/shell/testcases/nft-only/0010-nft-native.txt
new file mode 100644
index 00000000..d37ce873
--- /dev/null
+++ b/iptables/tests/shell/testcases/nft-only/0010-nft-native.txt
@@ -0,0 +1,41 @@
+table ip filter {
+ chain INPUT {
+ type filter hook input priority filter; policy accept;
+
+ ip saddr 1.2.3.4 tcp dport 23 accept
+ ip saddr 1.2.3.0/24 ip daddr 0.0.0.0 udp dport 67-69 drop
+
+ ip saddr 1.0.0.0/8 ip daddr 0.0.0.0 tcp sport 1024-65535 tcp dport 443 tcp flags syn / syn,ack accept
+ tcp dport 443 tcp flags syn comment "checks if SYN bit is set"
+ tcp flags syn / syn,rst,ack,fin comment "same as iptables --syn"
+ tcp flags & syn == syn
+ tcp flags & (syn | ack) != (syn | ack )
+
+ ip daddr 0.0.0.0/1 ip ttl 1 drop
+ ip daddr 0.0.0.0/2 ip ttl > 2 accept
+ ip daddr 0.0.0.0/3 ip ttl < 254 accept
+ ip daddr 0.0.0.0/4 ip ttl != 255 drop
+
+ ip daddr 8.0.0.0/5 icmp type 1 accept
+ ip daddr 8.0.0.0/6 icmp type 2 icmp code port-unreachable accept
+ ip daddr 10.0.0.0/7 icmp type echo-request accept
+
+ meta pkttype broadcast accept
+ meta pkttype != host drop
+
+ ip saddr 0.0.0.0/0 ip protocol tcp
+ ip daddr 0.0.0.0/1 ip protocol udp
+ }
+
+ chain FORWARD {
+ type filter hook forward priority filter;
+ limit rate 10/day counter
+ udp dport 42 counter
+
+ # FIXME: can't dissect plain syslog
+ # meta iif "lo" log prefix "just doing a log" level alert flags tcp sequence,options
+
+ # iif, not iifname, and wildcard
+ meta iif "lo" oifname "lo*" log group 1 prefix "should use NFLOG" queue-threshold 42 snaplen 123
+ }
+}
diff --git a/iptables/tests/shell/testcases/nft-only/0011-zero-needs-compat_0 b/iptables/tests/shell/testcases/nft-only/0011-zero-needs-compat_0
new file mode 100755
index 00000000..e276a953
--- /dev/null
+++ b/iptables/tests/shell/testcases/nft-only/0011-zero-needs-compat_0
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+[[ $XT_MULTI == *xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
+
+set -e
+
+rule="-p tcp -m tcp --dport 27374 -c 23 42 -j TPROXY --on-port 50080"
+for cmd in iptables ip6tables; do
+ $XT_MULTI $cmd -t mangle -A PREROUTING $rule
+ $XT_MULTI $cmd -t mangle -Z
+ $XT_MULTI $cmd -t mangle -v -S | grep -q -- "${rule/23 42/0 0}"
+done
diff --git a/iptables/xshared.c b/iptables/xshared.c
index 69515789..b998dd75 100644
--- a/iptables/xshared.c
+++ b/iptables/xshared.c
@@ -62,7 +62,7 @@ static void print_extension_helps(const struct xtables_target *t,
}
}
-static const char *
+const char *
proto_to_name(uint16_t proto, int nolookup)
{
unsigned int i;
@@ -111,26 +111,19 @@ find_proto(const char *pname, enum xtables_tryload tryload,
* [think of ip6tables-restore!]
* - the protocol extension can be successively loaded
*/
-static bool should_load_proto(struct iptables_command_state *cs)
-{
- if (cs->protocol == NULL)
- return false;
- if (find_proto(cs->protocol, XTF_DONT_LOAD,
- cs->options & OPT_NUMERIC, NULL) == NULL)
- return true;
- return !cs->proto_used;
-}
-
static struct xtables_match *load_proto(struct iptables_command_state *cs)
{
- if (!should_load_proto(cs))
+ if (cs->protocol == NULL)
return NULL;
+ if (cs->proto_used)
+ return NULL;
+ cs->proto_used = true;
return find_proto(cs->protocol, XTF_TRY_LOAD,
cs->options & OPT_NUMERIC, &cs->matches);
}
-static int command_default(struct iptables_command_state *cs,
- struct xtables_globals *gl, bool invert)
+int command_default(struct iptables_command_state *cs,
+ struct xtables_globals *gl, bool invert)
{
struct xtables_rule_match *matchp;
struct xtables_match *m;
@@ -157,13 +150,10 @@ static int command_default(struct iptables_command_state *cs,
return 0;
}
- /* Try loading protocol */
m = load_proto(cs);
if (m != NULL) {
size_t size;
- cs->proto_used = 1;
-
size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
m->m = xtables_calloc(1, size);
@@ -192,9 +182,12 @@ static int command_default(struct iptables_command_state *cs,
if (cs->c == ':')
xtables_error(PARAMETER_PROBLEM, "option \"%s\" "
"requires an argument", cs->argv[optind-1]);
- if (cs->c == '?')
- xtables_error(PARAMETER_PROBLEM, "unknown option "
- "\"%s\"", cs->argv[optind-1]);
+ if (cs->c == '?') {
+ char optoptstr[3] = {'-', optopt, '\0'};
+
+ xtables_error(PARAMETER_PROBLEM, "unknown option \"%s\"",
+ optopt ? optoptstr : cs->argv[optind - 1]);
+ }
xtables_error(PARAMETER_PROBLEM, "Unknown arg \"%s\"", optarg);
}
@@ -277,7 +270,7 @@ static int xtables_lock(int wait)
return XT_LOCK_FAILED;
}
- if (wait != -1) {
+ if (wait > 0) {
sigact_alarm.sa_handler = alarm_ignore;
sigact_alarm.sa_flags = SA_RESETHAND;
sigemptyset(&sigact_alarm.sa_mask);
@@ -285,7 +278,7 @@ static int xtables_lock(int wait)
alarm(wait);
}
- if (flock(fd, LOCK_EX) == 0)
+ if (flock(fd, LOCK_EX | (wait ? 0 : LOCK_NB)) == 0)
return fd;
if (errno == EINTR) {
@@ -400,15 +393,15 @@ bool tokenize_rule_counters(char **bufferp, char **pcntp, char **bcntp, int line
ptr = strchr(buffer, ']');
if (!ptr)
- xtables_error(PARAMETER_PROBLEM, "Bad line %u: need ]\n", line);
+ xtables_error(PARAMETER_PROBLEM, "Bad line %u: need ]", line);
pcnt = strtok(buffer+1, ":");
if (!pcnt)
- xtables_error(PARAMETER_PROBLEM, "Bad line %u: need :\n", line);
+ xtables_error(PARAMETER_PROBLEM, "Bad line %u: need :", line);
bcnt = strtok(NULL, "]");
if (!bcnt)
- xtables_error(PARAMETER_PROBLEM, "Bad line %u: need ]\n", line);
+ xtables_error(PARAMETER_PROBLEM, "Bad line %u: need ]", line);
*pcntp = pcnt;
*bcntp = bcnt;
@@ -433,10 +426,10 @@ void add_argv(struct argv_store *store, const char *what, int quoted)
if (store->argc + 1 >= MAX_ARGC)
xtables_error(PARAMETER_PROBLEM,
- "Parser cannot handle more arguments\n");
+ "Parser cannot handle more arguments");
if (!what)
xtables_error(PARAMETER_PROBLEM,
- "Trying to store NULL argument\n");
+ "Trying to store NULL argument");
store->argv[store->argc] = xtables_strdup(what);
store->argvattr[store->argc] = quoted;
@@ -764,29 +757,12 @@ void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags,
printf(FMT("%-6s ", "out %s "), iface);
}
-/* This assumes that mask is contiguous, and byte-bounded. */
-void save_iface(char letter, const char *iface,
- const unsigned char *mask, int invert)
+void save_iface(char letter, const char *iface, int invert)
{
- unsigned int i;
-
- if (mask[0] == 0)
+ if (!strlen(iface) || !strcmp(iface, "+"))
return;
- printf("%s -%c ", invert ? " !" : "", letter);
-
- for (i = 0; i < IFNAMSIZ; i++) {
- if (mask[i] != 0) {
- if (iface[i] != '\0')
- printf("%c", iface[i]);
- } else {
- /* we can access iface[i-1] here, because
- * a few lines above we make sure that mask[0] != 0 */
- if (iface[i-1] != '\0')
- printf("+");
- break;
- }
- }
+ printf("%s -%c %s", invert ? " !" : "", letter, iface);
}
static void command_match(struct iptables_command_state *cs, bool invert)
@@ -822,6 +798,9 @@ static void command_match(struct iptables_command_state *cs, bool invert)
else if (m->extra_opts != NULL)
opts = xtables_merge_options(xt_params->orig_opts, opts,
m->extra_opts, &m->option_offset);
+ else
+ return;
+
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
xt_params->opts = opts;
@@ -880,10 +859,13 @@ void command_jump(struct iptables_command_state *cs, const char *jumpto)
opts = xtables_options_xfrm(xt_params->orig_opts, opts,
cs->target->x6_options,
&cs->target->option_offset);
- else
+ else if (cs->target->extra_opts != NULL)
opts = xtables_merge_options(xt_params->orig_opts, opts,
cs->target->extra_opts,
&cs->target->option_offset);
+ else
+ return;
+
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
xt_params->opts = opts;
@@ -900,8 +882,7 @@ static char cmd2char(int option)
;
if (i >= ARRAY_SIZE(cmdflags))
xtables_error(OTHER_PROBLEM,
- "cmd2char(): Invalid command number %u.\n",
- 1 << i);
+ "cmd2char(): Invalid command number %u.", 1 << i);
return cmdflags[i];
}
@@ -911,8 +892,8 @@ static void add_command(unsigned int *cmd, const int newcmd,
if (invert)
xtables_error(PARAMETER_PROBLEM, "unexpected '!' flag");
if (*cmd & (~othercmds))
- xtables_error(PARAMETER_PROBLEM, "Cannot use -%c with -%c\n",
- cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
+ xtables_error(PARAMETER_PROBLEM, "Cannot use -%c with -%c",
+ cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
*cmd |= newcmd;
}
@@ -928,124 +909,137 @@ static int parse_rulenumber(const char *rule)
return rulenum;
}
-#define NUMBER_OF_OPT ARRAY_SIZE(optflags)
-static const char optflags[]
-= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '0', 'c', 'f', 2, 3, 'l', 4, 5, 6 };
+static void parse_rule_range(struct xt_cmd_parse *p, const char *argv)
+{
+ char *colon = strchr(argv, ':'), *buffer;
-/* Table of legal combinations of commands and options. If any of the
- * given commands make an option legal, that option is legal (applies to
- * CMD_LIST and CMD_ZERO only).
- * Key:
- * + compulsory
- * x illegal
- * optional
- */
-static const char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
-/* Well, it's better than "Re: Linux vs FreeBSD" */
-{
- /* -n -s -d -p -j -v -x -i -o --line -c -f 2 3 l 4 5 6 */
-/*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' ',' ',' ',' ',' ',' ',' '},
-/*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' ',' ',' ',' ',' ',' ',' '},
-/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
-/*REPLACE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' ',' ',' ',' ',' ',' ',' '},
-/*APPEND*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' ',' ',' ',' ',' ',' ',' '},
-/*LIST*/ {' ','x','x','x','x',' ',' ','x','x',' ','x','x','x','x','x','x','x','x'},
-/*FLUSH*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
-/*ZERO*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
-/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
-/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
-/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x',' ','x','x','x','x','x','x','x'},
-/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
-/*LIST_RULES*/{'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
-/*ZERO_NUM*/ {'x','x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x','x','x'},
-/*CHECK*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' ',' ',' ',' ',' ',' ',' '},
+ if (colon) {
+ if (!p->rule_ranges)
+ xtables_error(PARAMETER_PROBLEM,
+ "Rule ranges are not supported");
+
+ *colon = '\0';
+ if (*(colon + 1) == '\0')
+ p->rulenum_end = -1; /* Until the last rule */
+ else {
+ p->rulenum_end = strtol(colon + 1, &buffer, 10);
+ if (*buffer != '\0' || p->rulenum_end == 0)
+ xtables_error(PARAMETER_PROBLEM,
+ "Invalid rule range end`%s'",
+ colon + 1);
+ }
+ }
+ if (colon == argv)
+ p->rulenum = 1; /* Beginning with the first rule */
+ else {
+ p->rulenum = strtol(argv, &buffer, 10);
+ if (*buffer != '\0' || p->rulenum == 0)
+ xtables_error(PARAMETER_PROBLEM,
+ "Invalid rule number `%s'", argv);
+ }
+ if (!colon)
+ p->rulenum_end = p->rulenum;
+}
+
+/* list the commands an option is allowed with */
+#define CMD_IDRAC CMD_INSERT | CMD_DELETE | CMD_REPLACE | \
+ CMD_APPEND | CMD_CHECK | CMD_CHANGE_COUNTERS
+static const unsigned int options_v_commands[NUMBER_OF_OPT] = {
+/*OPT_NUMERIC*/ CMD_LIST,
+/*OPT_SOURCE*/ CMD_IDRAC,
+/*OPT_DESTINATION*/ CMD_IDRAC,
+/*OPT_PROTOCOL*/ CMD_IDRAC,
+/*OPT_JUMP*/ CMD_IDRAC,
+/*OPT_VERBOSE*/ UINT_MAX,
+/*OPT_EXPANDED*/ CMD_LIST,
+/*OPT_VIANAMEIN*/ CMD_IDRAC,
+/*OPT_VIANAMEOUT*/ CMD_IDRAC,
+/*OPT_LINENUMBERS*/ CMD_LIST,
+/*OPT_COUNTERS*/ CMD_INSERT | CMD_REPLACE | CMD_APPEND | CMD_SET_POLICY,
+/*OPT_FRAGMENT*/ CMD_IDRAC,
+/*OPT_S_MAC*/ CMD_IDRAC,
+/*OPT_D_MAC*/ CMD_IDRAC,
+/*OPT_H_LENGTH*/ CMD_IDRAC,
+/*OPT_OPCODE*/ CMD_IDRAC,
+/*OPT_H_TYPE*/ CMD_IDRAC,
+/*OPT_P_TYPE*/ CMD_IDRAC,
+/*OPT_LOGICALIN*/ CMD_IDRAC,
+/*OPT_LOGICALOUT*/ CMD_IDRAC,
+/*OPT_LIST_C*/ CMD_LIST,
+/*OPT_LIST_X*/ CMD_LIST,
+/*OPT_LIST_MAC2*/ CMD_LIST,
};
+#undef CMD_IDRAC
-static void generic_opt_check(int command, int options)
+static void generic_opt_check(struct xt_cmd_parse_ops *ops,
+ int command, int options)
{
- int i, j, legal = 0;
+ int i, optval;
/* Check that commands are valid with options. Complicated by the
* fact that if an option is legal with *any* command given, it is
* legal overall (ie. -z and -l).
*/
- for (i = 0; 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)
+ for (i = 0, optval = 1; i < NUMBER_OF_OPT; optval = (1 << ++i)) {
+ if ((options & optval) &&
+ (options_v_commands[i] & command) != command)
xtables_error(PARAMETER_PROBLEM,
- "Illegal option `-%c' with this command\n",
- optflags[i]);
+ "Illegal option `%s' with this command",
+ ops->option_name(optval));
}
}
-static char opt2char(int option)
+const char *ip46t_option_name(int option)
{
- const char *ptr;
-
- for (ptr = optflags; option > 1; option >>= 1, ptr++)
- ;
+ switch (option) {
+ case OPT_NUMERIC: return "--numeric";
+ case OPT_SOURCE: return "--source";
+ case OPT_DESTINATION: return "--destination";
+ case OPT_PROTOCOL: return "--protocol";
+ case OPT_JUMP: return "--jump";
+ case OPT_VERBOSE: return "--verbose";
+ case OPT_EXPANDED: return "--exact";
+ case OPT_VIANAMEIN: return "--in-interface";
+ case OPT_VIANAMEOUT: return "--out-interface";
+ case OPT_LINENUMBERS: return "--line-numbers";
+ case OPT_COUNTERS: return "--set-counters";
+ case OPT_FRAGMENT: return "--fragments";
+ default: return "unknown option";
+ }
+}
- return *ptr;
-}
-
-static const int inverse_for_options[NUMBER_OF_OPT] =
-{
-/* -n */ 0,
-/* -s */ IPT_INV_SRCIP,
-/* -d */ IPT_INV_DSTIP,
-/* -p */ XT_INV_PROTO,
-/* -j */ 0,
-/* -v */ 0,
-/* -x */ 0,
-/* -i */ IPT_INV_VIA_IN,
-/* -o */ IPT_INV_VIA_OUT,
-/*--line*/ 0,
-/* -c */ 0,
-/* -f */ IPT_INV_FRAG,
-/* 2 */ IPT_INV_SRCDEVADDR,
-/* 3 */ IPT_INV_TGTDEVADDR,
-/* -l */ IPT_INV_ARPHLN,
-/* 4 */ IPT_INV_ARPOP,
-/* 5 */ IPT_INV_ARPHRD,
-/* 6 */ IPT_INV_PROTO,
-};
+int ip46t_option_invert(int option)
+{
+ switch (option) {
+ case OPT_SOURCE: return IPT_INV_SRCIP;
+ case OPT_DESTINATION: return IPT_INV_DSTIP;
+ case OPT_PROTOCOL: return XT_INV_PROTO;
+ case OPT_VIANAMEIN: return IPT_INV_VIA_IN;
+ case OPT_VIANAMEOUT: return IPT_INV_VIA_OUT;
+ case OPT_FRAGMENT: return IPT_INV_FRAG;
+ default: return -1;
+ }
+}
static void
-set_option(unsigned int *options, unsigned int option, uint16_t *invflg,
- bool invert)
+set_option(struct xt_cmd_parse_ops *ops,
+ unsigned int *options, unsigned int option,
+ uint16_t *invflg, bool invert)
{
if (*options & option)
- xtables_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
- opt2char(option));
+ xtables_error(PARAMETER_PROBLEM,
+ "multiple %s options not allowed",
+ ops->option_name(option));
*options |= option;
if (invert) {
- unsigned int i;
- for (i = 0; 1 << i != option; i++);
+ int invopt = ops->option_invert(option);
- if (!inverse_for_options[i])
+ if (invopt < 0)
xtables_error(PARAMETER_PROBLEM,
- "cannot have ! before -%c",
- opt2char(option));
- *invflg |= inverse_for_options[i];
+ "cannot have ! before %s",
+ ops->option_name(option));
+ *invflg |= invopt;
}
}
@@ -1060,12 +1054,12 @@ void assert_valid_chain_name(const char *chainname)
if (*chainname == '-' || *chainname == '!')
xtables_error(PARAMETER_PROBLEM,
- "chain name not allowed to start with `%c'\n",
+ "chain name not allowed to start with `%c'",
*chainname);
if (xtables_find_target(chainname, XTF_TRY_LOAD))
xtables_error(PARAMETER_PROBLEM,
- "chain name may not clash with target name\n");
+ "chain name may not clash with target name");
for (ptr = chainname; *ptr; ptr++)
if (isspace(*ptr))
@@ -1092,27 +1086,24 @@ void print_rule_details(unsigned int linenum, const struct xt_counters *ctrs,
fputc(invflags & XT_INV_PROTO ? '!' : ' ', stdout);
- if (((format & (FMT_NUMERIC | FMT_NOTABLE)) == FMT_NUMERIC) || !pname)
- printf(FMT("%-4hu ", "%hu "), proto);
- else
+ if (pname)
printf(FMT("%-4s ", "%s "), pname);
+ else
+ printf(FMT("%-4hu ", "%hu "), proto);
}
-void save_rule_details(const char *iniface, unsigned const char *iniface_mask,
- const char *outiface, unsigned const char *outiface_mask,
+void save_rule_details(const char *iniface, const char *outiface,
uint16_t proto, int frag, uint8_t invflags)
{
if (iniface != NULL) {
- save_iface('i', iniface, iniface_mask,
- invflags & IPT_INV_VIA_IN);
+ save_iface('i', iniface, invflags & IPT_INV_VIA_IN);
}
if (outiface != NULL) {
- save_iface('o', outiface, outiface_mask,
- invflags & IPT_INV_VIA_OUT);
+ save_iface('o', outiface, invflags & IPT_INV_VIA_OUT);
}
if (proto > 0) {
- const char *pname = proto_to_name(proto, 0);
+ const char *pname = proto_to_name(proto, true);
if (invflags & XT_INV_PROTO)
printf(" !");
@@ -1160,9 +1151,9 @@ int print_match_save(const struct xt_entry_match *e, const void *ip)
return 0;
}
-static void
-xtables_printhelp(const struct xtables_rule_match *matches)
+void xtables_printhelp(struct iptables_command_state *cs)
{
+ const struct xtables_rule_match *matches = cs->matches;
const char *prog_name = xt_params->program_name;
const char *prog_vers = xt_params->program_version;
@@ -1317,10 +1308,11 @@ static void check_empty_interface(struct xtables_args *args, const char *arg)
}
static void check_inverse(struct xtables_args *args, const char option[],
- bool *invert, int *optidx, int argc)
+ bool *invert, int argc, char **argv)
{
switch (args->family) {
case NFPROTO_ARP:
+ case NFPROTO_BRIDGE:
break;
default:
return;
@@ -1336,12 +1328,11 @@ static void check_inverse(struct xtables_args *args, const char option[],
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 `!'");
- }
+ optind++;
+ if (optind > argc)
+ xtables_error(PARAMETER_PROBLEM, "no argument following `!'");
+
+ optarg = argv[optind - 1];
}
static const char *optstring_lookup(int family)
@@ -1358,10 +1349,122 @@ static const char *optstring_lookup(int family)
return "";
}
+void xtables_clear_iptables_command_state(struct iptables_command_state *cs)
+{
+ xtables_rule_matches_free(&cs->matches);
+ if (cs->target) {
+ free(cs->target->t);
+ cs->target->t = NULL;
+
+ free(cs->target->udata);
+ cs->target->udata = NULL;
+
+ if (cs->target == cs->target->next) {
+ free(cs->target);
+ cs->target = NULL;
+ }
+ }
+}
+
+void iface_to_mask(const char *iface, unsigned char *mask)
+{
+ unsigned int len = strlen(iface);
+
+ memset(mask, 0, IFNAMSIZ);
+
+ if (!len) {
+ return;
+ } else if (iface[len - 1] == '+') {
+ memset(mask, 0xff, len - 1);
+ /* Don't remove `+' here! -HW */
+ } else {
+ /* Include nul-terminator in match */
+ memset(mask, 0xff, len + 1);
+ }
+}
+
+static void parse_interface(const char *arg, char *iface)
+{
+ unsigned int len = strlen(arg);
+
+ memset(iface, 0, IFNAMSIZ);
+
+ if (!len)
+ return;
+ if (len >= IFNAMSIZ)
+ xtables_error(PARAMETER_PROBLEM,
+ "interface name `%s' must be shorter than %d characters",
+ arg, IFNAMSIZ);
+
+ if (strchr(arg, '/') || strchr(arg, ' '))
+ fprintf(stderr,
+ "Warning: weird character in interface `%s' ('/' and ' ' are not allowed by the kernel).\n",
+ arg);
+
+ strcpy(iface, arg);
+}
+
+static bool
+parse_signed_counter(char *argv, unsigned long long *val, uint8_t *ctr_op,
+ uint8_t flag_inc, uint8_t flag_dec)
+{
+ char *endptr, *p = argv;
+
+ switch (*p) {
+ case '+':
+ *ctr_op |= flag_inc;
+ p++;
+ break;
+ case '-':
+ *ctr_op |= flag_dec;
+ p++;
+ break;
+ }
+ *val = strtoull(p, &endptr, 10);
+ return *endptr == '\0';
+}
+
+static void parse_change_counters_rule(int argc, char **argv,
+ struct xt_cmd_parse *p,
+ struct xtables_args *args)
+{
+ if (optind + 1 >= argc ||
+ (argv[optind][0] == '-' && !isdigit(argv[optind][1])) ||
+ (argv[optind + 1][0] == '-' && !isdigit(argv[optind + 1][1])))
+ xtables_error(PARAMETER_PROBLEM,
+ "The command -C needs at least 2 arguments");
+ if (optind + 2 < argc &&
+ (argv[optind + 2][0] != '-' || isdigit(argv[optind + 2][1]))) {
+ if (optind + 3 != argc)
+ xtables_error(PARAMETER_PROBLEM,
+ "No extra options allowed with -C start_nr[:end_nr] pcnt bcnt");
+ parse_rule_range(p, argv[optind++]);
+ }
+
+ if (!parse_signed_counter(argv[optind++], &args->pcnt_cnt,
+ &args->counter_op,
+ CTR_OP_INC_PKTS, CTR_OP_DEC_PKTS) ||
+ !parse_signed_counter(argv[optind++], &args->bcnt_cnt,
+ &args->counter_op,
+ CTR_OP_INC_BYTES, CTR_OP_DEC_BYTES))
+ xtables_error(PARAMETER_PROBLEM,
+ "Packet counter '%s' invalid", argv[optind - 1]);
+}
+
+static void option_test_and_reject(struct xt_cmd_parse *p,
+ struct iptables_command_state *cs,
+ unsigned int option)
+{
+ if (cs->options & option)
+ xtables_error(PARAMETER_PROBLEM, "Can't use %s with %s",
+ p->ops->option_name(option), p->chain);
+}
+
void do_parse(int argc, char *argv[],
struct xt_cmd_parse *p, struct iptables_command_state *cs,
struct xtables_args *args)
{
+ bool family_is_bridge = args->family == NFPROTO_BRIDGE;
struct xtables_match *m;
struct xtables_rule_match *matchp;
bool wait_interval_set = false;
@@ -1387,10 +1490,10 @@ void do_parse(int argc, char *argv[],
demand-load a protocol. */
opterr = 0;
- xt_params->opts = xt_params->orig_opts;
while ((cs->c = getopt_long(argc, argv,
optstring_lookup(afinfo->family),
- xt_params->opts, NULL)) != -1) {
+ xt_params->opts ?: xt_params->orig_opts,
+ NULL)) != -1) {
switch (cs->c) {
/*
* Command selection
@@ -1401,6 +1504,15 @@ void do_parse(int argc, char *argv[],
break;
case 'C':
+ if (family_is_bridge) {
+ add_command(&p->command, CMD_CHANGE_COUNTERS,
+ CMD_NONE, invert);
+ p->chain = optarg;
+ parse_change_counters_rule(argc, argv, p, args);
+ break;
+ }
+ /* fall through */
+ case 14: /* ebtables --check */
add_command(&p->command, CMD_CHECK, CMD_NONE, invert);
p->chain = optarg;
break;
@@ -1409,7 +1521,7 @@ void do_parse(int argc, char *argv[],
add_command(&p->command, CMD_DELETE, CMD_NONE, invert);
p->chain = optarg;
if (xs_has_arg(argc, argv)) {
- p->rulenum = parse_rulenumber(argv[optind++]);
+ parse_rule_range(p, argv[optind++]);
p->command = CMD_DELETE_NUM;
}
break;
@@ -1504,52 +1616,51 @@ void do_parse(int argc, char *argv[],
"-%c requires old-chain-name and "
"new-chain-name",
cmd2char(CMD_RENAME_CHAIN));
+ assert_valid_chain_name(p->newname);
break;
case 'P':
- add_command(&p->command, CMD_SET_POLICY, CMD_NONE,
+ add_command(&p->command, CMD_SET_POLICY,
+ family_is_bridge ? CMD_NEW_CHAIN : CMD_NONE,
invert);
- p->chain = optarg;
- if (xs_has_arg(argc, argv))
+ if (p->command & CMD_NEW_CHAIN) {
+ p->policy = optarg;
+ } else if (xs_has_arg(argc, argv)) {
+ p->chain = optarg;
p->policy = argv[optind++];
- else
+ } else {
xtables_error(PARAMETER_PROBLEM,
"-%c requires a chain and a policy",
cmd2char(CMD_SET_POLICY));
+ }
break;
case 'h':
- if (!optarg)
- optarg = argv[optind];
-
/* iptables -p icmp -h */
if (!cs->matches && cs->protocol)
xtables_find_match(cs->protocol,
XTF_TRY_LOAD, &cs->matches);
- xtables_printhelp(cs->matches);
+ p->ops->print_help(cs);
+ xtables_clear_iptables_command_state(cs);
+ xtables_free_opts(1);
+ xtables_fini();
exit(0);
/*
* Option selection
*/
case 'p':
- check_inverse(args, optarg, &invert, &optind, argc);
- set_option(&cs->options, OPT_PROTOCOL,
+ check_inverse(args, optarg, &invert, argc, argv);
+ set_option(p->ops, &cs->options, OPT_PROTOCOL,
&args->invflags, invert);
/* Canonicalize into lower case */
- for (cs->protocol = argv[optind - 1];
+ for (cs->protocol = optarg;
*cs->protocol; cs->protocol++)
*cs->protocol = tolower(*cs->protocol);
- cs->protocol = argv[optind - 1];
- 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");
+ cs->protocol = optarg;
/* This needs to happen here to parse extensions */
if (p->ops->proto_parse)
@@ -1557,96 +1668,132 @@ void do_parse(int argc, char *argv[],
break;
case 's':
- check_inverse(args, optarg, &invert, &optind, argc);
- set_option(&cs->options, OPT_SOURCE,
+ check_inverse(args, optarg, &invert, argc, argv);
+ set_option(p->ops, &cs->options, OPT_SOURCE,
&args->invflags, invert);
- args->shostnetworkmask = argv[optind - 1];
+ args->shostnetworkmask = optarg;
break;
case 'd':
- check_inverse(args, optarg, &invert, &optind, argc);
- set_option(&cs->options, OPT_DESTINATION,
+ check_inverse(args, optarg, &invert, argc, argv);
+ set_option(p->ops, &cs->options, OPT_DESTINATION,
&args->invflags, invert);
- args->dhostnetworkmask = argv[optind - 1];
+ args->dhostnetworkmask = optarg;
break;
#ifdef IPT_F_GOTO
case 'g':
- set_option(&cs->options, OPT_JUMP, &args->invflags,
- invert);
+ set_option(p->ops, &cs->options, OPT_JUMP,
+ &args->invflags, invert);
args->goto_set = true;
cs->jumpto = xt_parse_target(optarg);
break;
#endif
case 2:/* src-mac */
- check_inverse(args, optarg, &invert, &optind, argc);
- set_option(&cs->options, OPT_S_MAC, &args->invflags,
- invert);
- args->src_mac = argv[optind - 1];
+ check_inverse(args, optarg, &invert, argc, argv);
+ set_option(p->ops, &cs->options, OPT_S_MAC,
+ &args->invflags, invert);
+ args->src_mac = optarg;
break;
case 3:/* dst-mac */
- check_inverse(args, optarg, &invert, &optind, argc);
- set_option(&cs->options, OPT_D_MAC, &args->invflags,
- invert);
- args->dst_mac = argv[optind - 1];
+ check_inverse(args, optarg, &invert, argc, argv);
+ set_option(p->ops, &cs->options, OPT_D_MAC,
+ &args->invflags, invert);
+ args->dst_mac = optarg;
break;
case 'l':/* hardware length */
- check_inverse(args, optarg, &invert, &optind, argc);
- set_option(&cs->options, OPT_H_LENGTH, &args->invflags,
- invert);
- args->arp_hlen = argv[optind - 1];
+ check_inverse(args, optarg, &invert, argc, argv);
+ set_option(p->ops, &cs->options, OPT_H_LENGTH,
+ &args->invflags, invert);
+ args->arp_hlen = optarg;
break;
case 8: /* was never supported, not even in arptables-legacy */
xtables_error(PARAMETER_PROBLEM, "not supported");
case 4:/* opcode */
- check_inverse(args, optarg, &invert, &optind, argc);
- set_option(&cs->options, OPT_OPCODE, &args->invflags,
- invert);
- args->arp_opcode = argv[optind - 1];
+ check_inverse(args, optarg, &invert, argc, argv);
+ set_option(p->ops, &cs->options, OPT_OPCODE,
+ &args->invflags, invert);
+ args->arp_opcode = optarg;
break;
case 5:/* h-type */
- check_inverse(args, optarg, &invert, &optind, argc);
- set_option(&cs->options, OPT_H_TYPE, &args->invflags,
- invert);
- args->arp_htype = argv[optind - 1];
+ check_inverse(args, optarg, &invert, argc, argv);
+ set_option(p->ops, &cs->options, OPT_H_TYPE,
+ &args->invflags, invert);
+ args->arp_htype = optarg;
break;
case 6:/* proto-type */
- check_inverse(args, optarg, &invert, &optind, argc);
- set_option(&cs->options, OPT_P_TYPE, &args->invflags,
- invert);
- args->arp_ptype = argv[optind - 1];
+ check_inverse(args, optarg, &invert, argc, argv);
+ set_option(p->ops, &cs->options, OPT_P_TYPE,
+ &args->invflags, invert);
+ args->arp_ptype = optarg;
+ break;
+
+ case 11: /* ebtables --init-table */
+ if (p->restore)
+ xtables_error(PARAMETER_PROBLEM,
+ "--init-table is not supported in daemon mode");
+ add_command(&p->command, CMD_INIT_TABLE, CMD_NONE, invert);
+ break;
+
+ case 12 : /* ebtables --Lmac2 */
+ set_option(p->ops, &cs->options, OPT_LIST_MAC2,
+ &args->invflags, invert);
+ break;
+
+ case 13 : /* ebtables --concurrent */
+ break;
+
+ case 15 : /* ebtables --logical-in */
+ check_inverse(args, optarg, &invert, argc, argv);
+ set_option(p->ops, &cs->options, OPT_LOGICALIN,
+ &args->invflags, invert);
+ parse_interface(optarg, args->bri_iniface);
+ break;
+
+ case 16 : /* ebtables --logical-out */
+ check_inverse(args, optarg, &invert, argc, argv);
+ set_option(p->ops, &cs->options, OPT_LOGICALOUT,
+ &args->invflags, invert);
+ parse_interface(optarg, args->bri_outiface);
+ break;
+
+ case 17 : /* ebtables --Lc */
+ set_option(p->ops, &cs->options, OPT_LIST_C,
+ &args->invflags, invert);
+ break;
+
+ case 19 : /* ebtables --Lx */
+ set_option(p->ops, &cs->options, OPT_LIST_X,
+ &args->invflags, invert);
break;
case 'j':
- set_option(&cs->options, OPT_JUMP, &args->invflags,
- invert);
- command_jump(cs, argv[optind - 1]);
+ set_option(p->ops, &cs->options, OPT_JUMP,
+ &args->invflags, invert);
+ if (strcmp(optarg, "CONTINUE"))
+ command_jump(cs, optarg);
break;
case 'i':
check_empty_interface(args, optarg);
- check_inverse(args, optarg, &invert, &optind, argc);
- set_option(&cs->options, OPT_VIANAMEIN,
+ check_inverse(args, optarg, &invert, argc, argv);
+ set_option(p->ops, &cs->options, OPT_VIANAMEIN,
&args->invflags, invert);
- xtables_parse_interface(argv[optind - 1],
- args->iniface,
- args->iniface_mask);
+ parse_interface(optarg, args->iniface);
break;
case 'o':
check_empty_interface(args, optarg);
- check_inverse(args, optarg, &invert, &optind, argc);
- set_option(&cs->options, OPT_VIANAMEOUT,
+ check_inverse(args, optarg, &invert, argc, argv);
+ set_option(p->ops, &cs->options, OPT_VIANAMEOUT,
&args->invflags, invert);
- xtables_parse_interface(argv[optind - 1],
- args->outiface,
- args->outiface_mask);
+ parse_interface(optarg, args->outiface);
break;
case 'f':
@@ -1655,14 +1802,14 @@ void do_parse(int argc, char *argv[],
"`-f' is not supported in IPv6, "
"use -m frag instead");
}
- set_option(&cs->options, OPT_FRAGMENT, &args->invflags,
- invert);
+ set_option(p->ops, &cs->options, OPT_FRAGMENT,
+ &args->invflags, invert);
args->flags |= IPT_F_FRAG;
break;
case 'v':
if (!p->verbose)
- set_option(&cs->options, OPT_VERBOSE,
+ set_option(p->ops, &cs->options, OPT_VERBOSE,
&args->invflags, invert);
p->verbose++;
break;
@@ -1672,8 +1819,8 @@ void do_parse(int argc, char *argv[],
break;
case 'n':
- set_option(&cs->options, OPT_NUMERIC, &args->invflags,
- invert);
+ set_option(p->ops, &cs->options, OPT_NUMERIC,
+ &args->invflags, invert);
break;
case 't':
@@ -1689,8 +1836,8 @@ void do_parse(int argc, char *argv[],
break;
case 'x':
- set_option(&cs->options, OPT_EXPANDED, &args->invflags,
- invert);
+ set_option(p->ops, &cs->options, OPT_EXPANDED,
+ &args->invflags, invert);
break;
case 'V':
@@ -1725,7 +1872,8 @@ void do_parse(int argc, char *argv[],
break;
case '0':
- set_option(&cs->options, OPT_LINENUMBERS,
+ case 18 : /* ebtables --Ln */
+ set_option(p->ops, &cs->options, OPT_LINENUMBERS,
&args->invflags, invert);
break;
@@ -1734,8 +1882,8 @@ void do_parse(int argc, char *argv[],
break;
case 'c':
- set_option(&cs->options, OPT_COUNTERS, &args->invflags,
- invert);
+ set_option(p->ops, &cs->options, OPT_COUNTERS,
+ &args->invflags, invert);
args->pcnt = optarg;
args->bcnt = strchr(args->pcnt + 1, ',');
if (args->bcnt)
@@ -1744,18 +1892,18 @@ void do_parse(int argc, char *argv[],
args->bcnt = argv[optind++];
if (!args->bcnt)
xtables_error(PARAMETER_PROBLEM,
- "-%c requires packet and byte counter",
- opt2char(OPT_COUNTERS));
+ "%s requires packet and byte counter",
+ p->ops->option_name(OPT_COUNTERS));
if (sscanf(args->pcnt, "%llu", &args->pcnt_cnt) != 1)
xtables_error(PARAMETER_PROBLEM,
- "-%c packet counter not numeric",
- opt2char(OPT_COUNTERS));
+ "%s packet counter not numeric",
+ p->ops->option_name(OPT_COUNTERS));
if (sscanf(args->bcnt, "%llu", &args->bcnt_cnt) != 1)
xtables_error(PARAMETER_PROBLEM,
- "-%c byte counter not numeric",
- opt2char(OPT_COUNTERS));
+ "%s byte counter not numeric",
+ p->ops->option_name(OPT_COUNTERS));
break;
case '4':
@@ -1790,7 +1938,8 @@ void do_parse(int argc, char *argv[],
exit_tryhelp(2, p->line);
default:
- if (command_default(cs, xt_params, invert))
+ check_inverse(args, optarg, &invert, argc, argv);
+ if (p->ops->command_default(cs, xt_params, invert))
/* cf. ip6tables.c */
continue;
break;
@@ -1798,7 +1947,8 @@ void do_parse(int argc, char *argv[],
invert = false;
}
- if (strcmp(p->table, "nat") == 0 &&
+ if (!family_is_bridge &&
+ strcmp(p->table, "nat") == 0 &&
((p->policy != NULL && strcmp(p->policy, "DROP") == 0) ||
(cs->jumpto != NULL && strcmp(cs->jumpto, "DROP") == 0)))
xtables_error(PARAMETER_PROBLEM,
@@ -1828,12 +1978,7 @@ void do_parse(int argc, char *argv[],
if (p->ops->post_parse)
p->ops->post_parse(p->command, cs, args);
- if (p->command == CMD_REPLACE &&
- (args->s.naddrs != 1 || args->d.naddrs != 1))
- xtables_error(PARAMETER_PROBLEM, "Replacement rule does not "
- "specify a unique address");
-
- generic_opt_check(p->command, cs->options);
+ generic_opt_check(p->ops, p->command, cs->options);
if (p->chain != NULL && strlen(p->chain) >= XT_EXTENSION_MAXNAMELEN)
xtables_error(PARAMETER_PROBLEM,
@@ -1842,28 +1987,24 @@ void do_parse(int argc, char *argv[],
if (p->command == CMD_APPEND ||
p->command == CMD_DELETE ||
- p->command == CMD_DELETE_NUM ||
p->command == CMD_CHECK ||
p->command == CMD_INSERT ||
- p->command == CMD_REPLACE) {
+ p->command == CMD_REPLACE ||
+ p->command == CMD_CHANGE_COUNTERS) {
if (strcmp(p->chain, "PREROUTING") == 0
|| strcmp(p->chain, "INPUT") == 0) {
/* -o not valid with incoming packets. */
- if (cs->options & OPT_VIANAMEOUT)
- xtables_error(PARAMETER_PROBLEM,
- "Can't use -%c with %s\n",
- opt2char(OPT_VIANAMEOUT),
- p->chain);
+ option_test_and_reject(p, cs, OPT_VIANAMEOUT);
+ /* same with --logical-out */
+ option_test_and_reject(p, cs, OPT_LOGICALOUT);
}
if (strcmp(p->chain, "POSTROUTING") == 0
|| strcmp(p->chain, "OUTPUT") == 0) {
/* -i not valid with outgoing packets */
- if (cs->options & OPT_VIANAMEIN)
- xtables_error(PARAMETER_PROBLEM,
- "Can't use -%c with %s\n",
- opt2char(OPT_VIANAMEIN),
- p->chain);
+ option_test_and_reject(p, cs, OPT_VIANAMEIN);
+ /* same with --logical-in */
+ option_test_and_reject(p, cs, OPT_LOGICALIN);
}
}
}
@@ -1871,7 +2012,13 @@ void do_parse(int argc, char *argv[],
void ipv4_proto_parse(struct iptables_command_state *cs,
struct xtables_args *args)
{
- cs->fw.ip.proto = args->proto;
+ cs->fw.ip.proto = xtables_parse_protocol(cs->protocol);
+
+ if (cs->fw.ip.proto == 0 &&
+ (args->invflags & XT_INV_PROTO))
+ xtables_error(PARAMETER_PROBLEM,
+ "rule would never match protocol");
+
cs->fw.ip.invflags = args->invflags;
}
@@ -1887,7 +2034,13 @@ static int is_exthdr(uint16_t proto)
void ipv6_proto_parse(struct iptables_command_state *cs,
struct xtables_args *args)
{
- cs->fw6.ipv6.proto = args->proto;
+ cs->fw6.ipv6.proto = xtables_parse_protocol(cs->protocol);
+
+ if (cs->fw6.ipv6.proto == 0 &&
+ (args->invflags & XT_INV_PROTO))
+ xtables_error(PARAMETER_PROBLEM,
+ "rule would never match protocol");
+
cs->fw6.ipv6.invflags = args->invflags;
/* this is needed for ip6tables-legacy only */
@@ -1912,12 +2065,7 @@ void ipv4_post_parse(int command, struct iptables_command_state *cs,
cs->fw.ip.invflags = args->invflags;
memcpy(cs->fw.ip.iniface, args->iniface, IFNAMSIZ);
- memcpy(cs->fw.ip.iniface_mask,
- args->iniface_mask, IFNAMSIZ*sizeof(unsigned char));
-
memcpy(cs->fw.ip.outiface, args->outiface, IFNAMSIZ);
- memcpy(cs->fw.ip.outiface_mask,
- args->outiface_mask, IFNAMSIZ*sizeof(unsigned char));
if (args->goto_set)
cs->fw.ip.flags |= IPT_F_GOTO;
@@ -1972,6 +2120,9 @@ void ipv6_post_parse(int command, struct iptables_command_state *cs,
if (args->goto_set)
cs->fw6.ipv6.flags |= IP6T_F_GOTO;
+ /* nft-variants use cs->counters, legacy uses cs->fw6.counters */
+ cs->counters.pcnt = args->pcnt_cnt;
+ cs->counters.bcnt = args->bcnt_cnt;
cs->fw6.counters.pcnt = args->pcnt_cnt;
cs->fw6.counters.bcnt = args->bcnt_cnt;
@@ -2000,3 +2151,45 @@ void ipv6_post_parse(int command, struct iptables_command_state *cs,
"! not allowed with multiple"
" source or destination IP addresses");
}
+
+unsigned char *
+make_delete_mask(const struct xtables_rule_match *matches,
+ const struct xtables_target *target,
+ size_t entry_size)
+{
+ /* Establish mask for comparison */
+ unsigned int size = entry_size;
+ const struct xtables_rule_match *matchp;
+ unsigned char *mask, *mptr;
+
+ for (matchp = matches; matchp; matchp = matchp->next)
+ size += XT_ALIGN(sizeof(struct xt_entry_match)) + matchp->match->size;
+
+ mask = xtables_calloc(1, size
+ + XT_ALIGN(sizeof(struct xt_entry_target))
+ + target->size);
+
+ memset(mask, 0xFF, entry_size);
+ mptr = mask + entry_size;
+
+ for (matchp = matches; matchp; matchp = matchp->next) {
+ memset(mptr, 0xFF,
+ XT_ALIGN(sizeof(struct xt_entry_match))
+ + matchp->match->userspacesize);
+ mptr += XT_ALIGN(sizeof(struct xt_entry_match)) + matchp->match->size;
+ }
+
+ memset(mptr, 0xFF,
+ XT_ALIGN(sizeof(struct xt_entry_target))
+ + target->userspacesize);
+
+ return mask;
+}
+
+void xtables_clear_args(struct xtables_args *args)
+{
+ free(args->s.addr.ptr);
+ free(args->s.mask.ptr);
+ free(args->d.addr.ptr);
+ free(args->d.mask.ptr);
+}
diff --git a/iptables/xshared.h b/iptables/xshared.h
index 1a019a7c..26c492eb 100644
--- a/iptables/xshared.h
+++ b/iptables/xshared.h
@@ -12,8 +12,15 @@
#ifdef DEBUG
#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
+#define DEBUG_HEXDUMP(pfx, data, len) \
+ for (int __i = 0; __i < (len); __i++) { \
+ if (__i % 16 == 0) \
+ printf("%s%s: ", __i ? "\n" : "", (pfx)); \
+ printf("%02x ", ((const unsigned char *)data)[__i]); \
+ } printf("\n")
#else
#define DEBUGP(x, args...)
+#define DEBUG_HEXDUMP(pfx, data, len)
#endif
enum {
@@ -37,7 +44,14 @@ enum {
OPT_OPCODE = 1 << 15,
OPT_H_TYPE = 1 << 16,
OPT_P_TYPE = 1 << 17,
+ /* below are for ebtables only */
+ OPT_LOGICALIN = 1 << 18,
+ OPT_LOGICALOUT = 1 << 19,
+ OPT_LIST_C = 1 << 20,
+ OPT_LIST_X = 1 << 21,
+ OPT_LIST_MAC2 = 1 << 22,
};
+#define NUMBER_OF_OPT 24
enum {
CMD_NONE = 0,
@@ -56,19 +70,23 @@ enum {
CMD_LIST_RULES = 1 << 12,
CMD_ZERO_NUM = 1 << 13,
CMD_CHECK = 1 << 14,
+ CMD_CHANGE_COUNTERS = 1 << 15, /* ebtables only */
+ CMD_INIT_TABLE = 1 << 16, /* ebtables only */
};
-#define NUMBER_OF_CMD 16
+#define NUMBER_OF_CMD 18
struct xtables_globals;
struct xtables_rule_match;
struct xtables_target;
-#define OPTSTRING_COMMON "-:A:C:D:E:F::I:L::M:N:P:VX::Z::" "c:d:i:j:o:p:s:t:"
-#define IPT_OPTSTRING OPTSTRING_COMMON "R:S::W::" "46bfg:h::m:nvw::x"
-#define ARPT_OPTSTRING OPTSTRING_COMMON "R:S::" "h::l:nvx" /* "m:" */
-#define EBT_OPTSTRING OPTSTRING_COMMON "hv"
+#define OPTSTRING_COMMON "-:A:C:D:E:F::I:L::M:N:P:R:S::VX::Z::" "c:d:i:j:o:p:s:t:v"
+#define IPT_OPTSTRING OPTSTRING_COMMON "W::" "46bfg:h::m:nw::x"
+#define ARPT_OPTSTRING OPTSTRING_COMMON "h::l:nx" /* "m:" */
+#define EBT_OPTSTRING OPTSTRING_COMMON "h"
-/* define invflags which won't collide with IPT ones */
+/* define invflags which won't collide with IPT ones.
+ * arptables-nft does NOT use the legacy ARPT_INV_* defines.
+ */
#define IPT_INV_SRCDEVADDR 0x0080
#define IPT_INV_TGTDEVADDR 0x0100
#define IPT_INV_ARPHLN 0x0200
@@ -121,10 +139,13 @@ struct iptables_command_state {
char *protocol;
int proto_used;
const char *jumpto;
+ int argc;
char **argv;
bool restore;
};
+void xtables_clear_iptables_command_state(struct iptables_command_state *cs);
+
typedef int (*mainfunc_t)(int, char **);
struct subcommand {
@@ -195,8 +216,7 @@ void save_ipv6_addr(char letter, const struct in6_addr *addr,
void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags,
unsigned int format);
-void save_iface(char letter, const char *iface,
- const unsigned char *mask, int invert);
+void save_iface(char letter, const char *iface, int invert);
void print_fragment(unsigned int flags, unsigned int invflags,
unsigned int format, bool fake);
@@ -208,8 +228,7 @@ void assert_valid_chain_name(const char *chainname);
void print_rule_details(unsigned int linenum, const struct xt_counters *ctrs,
const char *targname, uint8_t proto, uint8_t flags,
uint8_t invflags, unsigned int format);
-void save_rule_details(const char *iniface, unsigned const char *iniface_mask,
- const char *outiface, unsigned const char *outiface_mask,
+void save_rule_details(const char *iniface, const char *outiface,
uint16_t proto, int frag, uint8_t invflags);
int print_match_save(const struct xt_entry_match *e, const void *ip);
@@ -232,13 +251,20 @@ struct addr_mask {
} mask;
};
+enum {
+ CTR_OP_INC_PKTS = 1 << 0,
+ CTR_OP_DEC_PKTS = 1 << 1,
+ CTR_OP_INC_BYTES = 1 << 2,
+ CTR_OP_DEC_BYTES = 1 << 3,
+};
+
struct xtables_args {
int family;
- uint16_t proto;
uint8_t flags;
uint16_t invflags;
char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
+ char bri_iniface[IFNAMSIZ], bri_outiface[IFNAMSIZ];
bool goto_set;
const char *shostnetworkmask, *dhostnetworkmask;
const char *pcnt, *bcnt;
@@ -247,6 +273,7 @@ struct xtables_args {
const char *arp_hlen, *arp_opcode;
const char *arp_htype, *arp_ptype;
unsigned long long pcnt_cnt, bcnt_cnt;
+ uint8_t counter_op;
int wait;
};
@@ -256,11 +283,17 @@ struct xt_cmd_parse_ops {
void (*post_parse)(int command,
struct iptables_command_state *cs,
struct xtables_args *args);
+ const char *(*option_name)(int option);
+ int (*option_invert)(int option);
+ int (*command_default)(struct iptables_command_state *cs,
+ struct xtables_globals *gl, bool invert);
+ void (*print_help)(struct iptables_command_state *cs);
};
struct xt_cmd_parse {
unsigned int command;
unsigned int rulenum;
+ unsigned int rulenum_end;
char *table;
const char *chain;
const char *newname;
@@ -268,10 +301,16 @@ struct xt_cmd_parse {
bool restore;
int line;
int verbose;
- bool xlate;
+ bool rule_ranges;
struct xt_cmd_parse_ops *ops;
};
+void xtables_printhelp(struct iptables_command_state *cs);
+const char *ip46t_option_name(int option);
+int ip46t_option_invert(int option);
+int command_default(struct iptables_command_state *cs,
+ struct xtables_globals *gl, bool invert);
+
void do_parse(int argc, char *argv[],
struct xt_cmd_parse *p, struct iptables_command_state *cs,
struct xtables_args *args);
@@ -288,4 +327,14 @@ void ipv6_post_parse(int command, struct iptables_command_state *cs,
extern char *arp_opcodes[];
#define ARP_NUMOPCODES 9
+unsigned char *make_delete_mask(const struct xtables_rule_match *matches,
+ const struct xtables_target *target,
+ size_t entry_size);
+
+void iface_to_mask(const char *ifname, unsigned char *mask);
+
+void xtables_clear_args(struct xtables_args *args);
+
+const char *proto_to_name(uint16_t proto, int nolookup);
+
#endif /* IPTABLES_XSHARED_H */
diff --git a/iptables/xtables-eb-translate.c b/iptables/xtables-eb-translate.c
index 86177024..fbeff74f 100644
--- a/iptables/xtables-eb-translate.c
+++ b/iptables/xtables-eb-translate.c
@@ -21,77 +21,10 @@
#include "nft-bridge.h"
#include "nft.h"
#include "nft-shared.h"
-/*
- * From include/ebtables_u.h
- */
-#define EXEC_STYLE_PRG 0
-#define EXEC_STYLE_DAEMON 1
-#define ebt_check_option2(flags, mask) EBT_CHECK_OPTION(flags, mask)
-
-extern int ebt_invert;
-
-static int ebt_check_inverse2(const char option[], int argc, char **argv)
-{
- if (!option)
- return ebt_invert;
- if (strcmp(option, "!") == 0) {
- if (ebt_invert == 1)
- xtables_error(PARAMETER_PROBLEM,
- "Double use of '!' not allowed");
- if (optind >= argc)
- optarg = NULL;
- else
- optarg = argv[optind];
- optind++;
- ebt_invert = 1;
- return 1;
- }
- return ebt_invert;
-}
-
-/*
- * Glue code to use libxtables
- */
-static int parse_rule_number(const char *rule)
-{
- unsigned int rule_nr;
-
- if (!xtables_strtoui(rule, NULL, &rule_nr, 1, INT_MAX))
- xtables_error(PARAMETER_PROBLEM,
- "Invalid rule number `%s'", rule);
-
- return rule_nr;
-}
-
-/*
- * The original ebtables parser
- */
-
-/* Checks whether a command has already been specified */
-#define OPT_COMMANDS (flags & OPT_COMMAND || flags & OPT_ZERO)
-
-#define OPT_COMMAND 0x01
-#define OPT_TABLE 0x02
-#define OPT_IN 0x04
-#define OPT_OUT 0x08
-#define OPT_JUMP 0x10
-#define OPT_PROTOCOL 0x20
-#define OPT_SOURCE 0x40
-#define OPT_DEST 0x80
-#define OPT_ZERO 0x100
-#define OPT_LOGICALIN 0x200
-#define OPT_LOGICALOUT 0x400
-#define OPT_COUNT 0x1000 /* This value is also defined in libebtc.c */
-
-/* Default command line options. Do not mess around with the already
- * assigned numbers unless you know what you are doing */
-extern struct option ebt_original_options[];
-#define opts ebtables_globals.opts
#define prog_name ebtables_globals.program_name
-#define prog_vers ebtables_globals.program_version
-static void print_help(void)
+static void print_help(struct iptables_command_state *cs)
{
fprintf(stderr, "%s: Translate ebtables command to nft syntax\n"
"no side effects occur, the translated command is written "
@@ -101,46 +34,6 @@ static void print_help(void)
exit(0);
}
-static int parse_rule_range(const char *argv, int *rule_nr, int *rule_nr_end)
-{
- char *colon = strchr(argv, ':'), *buffer;
-
- if (colon) {
- *colon = '\0';
- if (*(colon + 1) == '\0')
- *rule_nr_end = -1; /* Until the last rule */
- else {
- *rule_nr_end = strtol(colon + 1, &buffer, 10);
- if (*buffer != '\0' || *rule_nr_end == 0)
- return -1;
- }
- }
- if (colon == argv)
- *rule_nr = 1; /* Beginning with the first rule */
- else {
- *rule_nr = strtol(argv, &buffer, 10);
- if (*buffer != '\0' || *rule_nr == 0)
- return -1;
- }
- if (!colon)
- *rule_nr_end = *rule_nr;
- return 0;
-}
-
-static void ebtables_parse_interface(const char *arg, char *vianame)
-{
- unsigned char mask[IFNAMSIZ];
- char *c;
-
- xtables_parse_interface(arg, vianame, mask);
-
- if ((c = strchr(vianame, '+'))) {
- if (*(c + 1) != '\0')
- xtables_error(PARAMETER_PROBLEM,
- "Spurious characters after '+' wildcard");
- }
-}
-
static void print_ebt_cmd(int argc, char *argv[])
{
int i;
@@ -156,383 +49,106 @@ static int nft_rule_eb_xlate_add(struct nft_handle *h, const struct xt_cmd_parse
const struct iptables_command_state *cs, bool append)
{
struct xt_xlate *xl = xt_xlate_alloc(10240);
+ const char *tick = cs->restore ? "" : "'";
int ret;
- if (append) {
- xt_xlate_add(xl, "add rule bridge %s %s ", p->table, p->chain);
- } else {
- xt_xlate_add(xl, "insert rule bridge %s %s ", p->table, p->chain);
- }
+ xt_xlate_add(xl, "%s%s rule bridge %s %s ", tick,
+ append ? "add" : "insert", p->table, p->chain);
ret = h->ops->xlate(cs, xl);
if (ret)
- printf("%s\n", xt_xlate_get(xl));
+ printf("%s%s\n", xt_xlate_get(xl), tick);
+ else
+ printf("%s ", tick);
xt_xlate_free(xl);
return ret;
}
-/* We use exec_style instead of #ifdef's because ebtables.so is a shared object. */
static int do_commandeb_xlate(struct nft_handle *h, int argc, char *argv[], char **table)
{
- char *buffer;
- int c, i;
- int rule_nr = 0;
- int rule_nr_end = 0;
- int ret = 0;
- unsigned int flags = 0;
struct iptables_command_state cs = {
.argv = argv,
+ .jumpto = "",
.eb.bitmask = EBT_NOPROTO,
};
- char command = 'h';
- const char *chain = NULL;
- int exec_style = EXEC_STYLE_PRG;
- int selected_chain = -1;
- struct xtables_rule_match *xtrm_i;
- struct ebt_match *match;
struct xt_cmd_parse p = {
.table = *table,
+ .rule_ranges = true,
+ .ops = &h->ops->cmd_parse,
};
+ struct xtables_args args = {
+ .family = h->family,
+ };
+ int ret = 0;
- /* prevent getopt to spoil our error reporting */
- opterr = false;
-
- printf("nft ");
- /* Getopt saves the day */
- while ((c = getopt_long(argc, argv,
- "-A:D:I:N:E:X::L::Z::F::P:Vhi:o:j:c:p:s:d:t:M:", opts, NULL)) != -1) {
- cs.c = c;
- switch (c) {
- case 'A': /* Add a rule */
- case 'D': /* Delete a rule */
- case 'P': /* Define policy */
- case 'I': /* Insert a rule */
- case 'N': /* Make a user defined chain */
- case 'E': /* Rename chain */
- case 'X': /* Delete chain */
- /* We allow -N chainname -P policy */
- /* XXX: Not in ebtables-compat */
- if (command == 'N' && c == 'P') {
- command = c;
- optind--; /* No table specified */
- break;
- }
- if (OPT_COMMANDS)
- xtables_error(PARAMETER_PROBLEM,
- "Multiple commands are not allowed");
- command = c;
- chain = optarg;
- selected_chain = ebt_get_current_chain(chain);
- p.chain = chain;
- flags |= OPT_COMMAND;
-
- if (c == 'N') {
- printf("add chain bridge %s %s\n", p.table, p.chain);
- ret = 1;
- break;
- } else if (c == 'X') {
- printf("delete chain bridge %s %s\n", p.table, p.chain);
- ret = 1;
- break;
- }
-
- if (c == 'E') {
- break;
- } else if (c == 'D' && optind < argc && (argv[optind][0] != '-' || (argv[optind][1] >= '0' && argv[optind][1] <= '9'))) {
- if (optind != argc - 1)
- xtables_error(PARAMETER_PROBLEM,
- "No extra options allowed with -D start_nr[:end_nr]");
- if (parse_rule_range(argv[optind], &rule_nr, &rule_nr_end))
- xtables_error(PARAMETER_PROBLEM,
- "Problem with the specified rule number(s) '%s'", argv[optind]);
- optind++;
- } else if (c == 'I') {
- if (optind >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')))
- rule_nr = 1;
- else {
- rule_nr = parse_rule_number(argv[optind]);
- optind++;
- }
- p.rulenum = rule_nr;
- } else if (c == 'P') {
- break;
- }
- break;
- case 'L': /* List */
- printf("list table bridge %s\n", p.table);
- ret = 1;
- break;
- case 'F': /* Flush */
- if (p.chain) {
- printf("flush chain bridge %s %s\n", p.table, p.chain);
- } else {
- printf("flush table bridge %s\n", p.table);
- }
- ret = 1;
- break;
- case 'Z': /* Zero counters */
- if (c == 'Z') {
- if ((flags & OPT_ZERO) || (flags & OPT_COMMAND && command != 'L'))
-print_zero:
- xtables_error(PARAMETER_PROBLEM,
- "Command -Z only allowed together with command -L");
- flags |= OPT_ZERO;
- } else {
- if (flags & OPT_COMMAND)
- xtables_error(PARAMETER_PROBLEM,
- "Multiple commands are not allowed");
- command = c;
- flags |= OPT_COMMAND;
- if (flags & OPT_ZERO && c != 'L')
- goto print_zero;
- }
- break;
- case 'V': /* Version */
- if (OPT_COMMANDS)
- xtables_error(PARAMETER_PROBLEM,
- "Multiple commands are not allowed");
- if (exec_style == EXEC_STYLE_DAEMON)
- xtables_error(PARAMETER_PROBLEM,
- "%s %s\n", prog_name, prog_vers);
- printf("%s %s\n", prog_name, prog_vers);
- exit(0);
- case 'h':
- if (OPT_COMMANDS)
- xtables_error(PARAMETER_PROBLEM,
- "Multiple commands are not allowed");
- print_help();
- break;
- case 't': /* Table */
- if (OPT_COMMANDS)
- xtables_error(PARAMETER_PROBLEM,
- "Please put the -t option first");
- ebt_check_option2(&flags, OPT_TABLE);
- if (strlen(optarg) > EBT_TABLE_MAXNAMELEN - 1)
- xtables_error(PARAMETER_PROBLEM,
- "Table name length cannot exceed %d characters",
- EBT_TABLE_MAXNAMELEN - 1);
- *table = optarg;
- p.table = optarg;
- break;
- case 'i': /* Input interface */
- case 2 : /* Logical input interface */
- case 'o': /* Output interface */
- case 3 : /* Logical output interface */
- case 'j': /* Target */
- case 'p': /* Net family protocol */
- case 's': /* Source mac */
- case 'd': /* Destination mac */
- case 'c': /* Set counters */
- if (!OPT_COMMANDS)
- xtables_error(PARAMETER_PROBLEM,
- "No command specified");
- if (command != 'A' && command != 'D' && command != 'I')
- xtables_error(PARAMETER_PROBLEM,
- "Command and option do not match");
- if (c == 'i') {
- ebt_check_option2(&flags, OPT_IN);
- if (selected_chain > 2 && selected_chain < NF_BR_BROUTING)
- xtables_error(PARAMETER_PROBLEM,
- "Use -i only in INPUT, FORWARD, PREROUTING and BROUTING chains");
- if (ebt_check_inverse2(optarg, argc, argv))
- cs.eb.invflags |= EBT_IIN;
-
- ebtables_parse_interface(optarg, cs.eb.in);
- break;
- } else if (c == 2) {
- ebt_check_option2(&flags, OPT_LOGICALIN);
- if (selected_chain > 2 && selected_chain < NF_BR_BROUTING)
- xtables_error(PARAMETER_PROBLEM,
- "Use --logical-in only in INPUT, FORWARD, PREROUTING and BROUTING chains");
- if (ebt_check_inverse2(optarg, argc, argv))
- cs.eb.invflags |= EBT_ILOGICALIN;
-
- ebtables_parse_interface(optarg, cs.eb.logical_in);
- break;
- } else if (c == 'o') {
- ebt_check_option2(&flags, OPT_OUT);
- if (selected_chain < 2 || selected_chain == NF_BR_BROUTING)
- xtables_error(PARAMETER_PROBLEM,
- "Use -o only in OUTPUT, FORWARD and POSTROUTING chains");
- if (ebt_check_inverse2(optarg, argc, argv))
- cs.eb.invflags |= EBT_IOUT;
-
- ebtables_parse_interface(optarg, cs.eb.out);
- break;
- } else if (c == 3) {
- ebt_check_option2(&flags, OPT_LOGICALOUT);
- if (selected_chain < 2 || selected_chain == NF_BR_BROUTING)
- xtables_error(PARAMETER_PROBLEM,
- "Use --logical-out only in OUTPUT, FORWARD and POSTROUTING chains");
- if (ebt_check_inverse2(optarg, argc, argv))
- cs.eb.invflags |= EBT_ILOGICALOUT;
-
- ebtables_parse_interface(optarg, cs.eb.logical_out);
- break;
- } else if (c == 'j') {
- ebt_check_option2(&flags, OPT_JUMP);
- command_jump(&cs, optarg);
- break;
- } else if (c == 's') {
- ebt_check_option2(&flags, OPT_SOURCE);
- if (ebt_check_inverse2(optarg, argc, argv))
- cs.eb.invflags |= EBT_ISOURCE;
-
- if (xtables_parse_mac_and_mask(optarg,
- cs.eb.sourcemac,
- cs.eb.sourcemsk))
- xtables_error(PARAMETER_PROBLEM, "Problem with specified source mac '%s'", optarg);
- cs.eb.bitmask |= EBT_SOURCEMAC;
- break;
- } else if (c == 'd') {
- ebt_check_option2(&flags, OPT_DEST);
- if (ebt_check_inverse2(optarg, argc, argv))
- cs.eb.invflags |= EBT_IDEST;
-
- if (xtables_parse_mac_and_mask(optarg,
- cs.eb.destmac,
- cs.eb.destmsk))
- xtables_error(PARAMETER_PROBLEM, "Problem with specified destination mac '%s'", optarg);
- cs.eb.bitmask |= EBT_DESTMAC;
- break;
- } else if (c == 'c') {
- ebt_check_option2(&flags, OPT_COUNT);
- if (ebt_check_inverse2(optarg, argc, argv))
- xtables_error(PARAMETER_PROBLEM,
- "Unexpected '!' after -c");
- if (optind >= argc || optarg[0] == '-' || argv[optind][0] == '-')
- xtables_error(PARAMETER_PROBLEM,
- "Option -c needs 2 arguments");
-
- cs.counters.pcnt = strtoull(optarg, &buffer, 10);
- if (*buffer != '\0')
- xtables_error(PARAMETER_PROBLEM,
- "Packet counter '%s' invalid",
- optarg);
- cs.counters.bcnt = strtoull(argv[optind], &buffer, 10);
- if (*buffer != '\0')
- xtables_error(PARAMETER_PROBLEM,
- "Packet counter '%s' invalid",
- argv[optind]);
- optind++;
- break;
- }
- ebt_check_option2(&flags, OPT_PROTOCOL);
- if (ebt_check_inverse2(optarg, argc, argv))
- cs.eb.invflags |= EBT_IPROTO;
-
- cs.eb.bitmask &= ~((unsigned int)EBT_NOPROTO);
- i = strtol(optarg, &buffer, 16);
- if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
- xtables_error(PARAMETER_PROBLEM,
- "Problem with the specified protocol");
- if (*buffer != '\0') {
- struct xt_ethertypeent *ent;
-
- if (!strcasecmp(optarg, "LENGTH")) {
- cs.eb.bitmask |= EBT_802_3;
- break;
- }
- ent = xtables_getethertypebyname(optarg);
- if (!ent)
- xtables_error(PARAMETER_PROBLEM,
- "Problem with the specified Ethernet protocol '%s', perhaps "XT_PATH_ETHERTYPES " is missing", optarg);
- cs.eb.ethproto = ent->e_ethertype;
- } else
- cs.eb.ethproto = i;
-
- if (cs.eb.ethproto < 0x0600)
- xtables_error(PARAMETER_PROBLEM,
- "Sorry, protocols have values above or equal to 0x0600");
- break;
- case 4 : /* Lc */
- ebt_check_option2(&flags, LIST_C);
- if (command != 'L')
- xtables_error(PARAMETER_PROBLEM,
- "Use --Lc with -L");
- flags |= LIST_C;
- break;
- case 5 : /* Ln */
- ebt_check_option2(&flags, LIST_N);
- if (command != 'L')
- xtables_error(PARAMETER_PROBLEM,
- "Use --Ln with -L");
- if (flags & LIST_X)
- xtables_error(PARAMETER_PROBLEM,
- "--Lx is not compatible with --Ln");
- flags |= LIST_N;
- break;
- case 6 : /* Lx */
- ebt_check_option2(&flags, LIST_X);
- if (command != 'L')
- xtables_error(PARAMETER_PROBLEM,
- "Use --Lx with -L");
- if (flags & LIST_N)
- xtables_error(PARAMETER_PROBLEM,
- "--Lx is not compatible with --Ln");
- flags |= LIST_X;
- break;
- case 12 : /* Lmac2 */
- ebt_check_option2(&flags, LIST_MAC2);
- if (command != 'L')
- xtables_error(PARAMETER_PROBLEM,
- "Use --Lmac2 with -L");
- flags |= LIST_MAC2;
- break;
- case 1 :
- if (!strcmp(optarg, "!"))
- ebt_check_inverse2(optarg, argc, argv);
- else
- xtables_error(PARAMETER_PROBLEM,
- "Bad argument : '%s'", optarg);
- /* ebt_ebt_check_inverse2() did optind++ */
- optind--;
- continue;
- default:
- ebt_check_inverse2(optarg, argc, argv);
+ p.ops->print_help = print_help;
- if (ebt_command_default(&cs))
- xtables_error(PARAMETER_PROBLEM,
- "Unknown argument: '%s'",
- argv[optind - 1]);
+ do_parse(argc, argv, &p, &cs, &args);
- if (command != 'A' && command != 'I' &&
- command != 'D')
- xtables_error(PARAMETER_PROBLEM,
- "Extensions only for -A, -I, -D");
- }
- ebt_invert = 0;
- }
+ h->verbose = p.verbose;
/* Do the final checks */
- if (command == 'A' || command == 'I' || command == 'D') {
- for (xtrm_i = cs.matches; xtrm_i; xtrm_i = xtrm_i->next)
- xtables_option_mfcall(xtrm_i->match);
+ if (!nft_table_builtin_find(h, p.table))
+ xtables_error(VERSION_PROBLEM,
+ "table '%s' does not exist", p.table);
- for (match = cs.match_list; match; match = match->next) {
- if (match->ismatch)
- continue;
-
- xtables_option_tfcall(match->u.watcher);
+ printf("nft ");
+ switch (p.command) {
+ case CMD_FLUSH:
+ if (p.chain) {
+ printf("flush chain bridge %s %s\n", p.table, p.chain);
+ } else {
+ printf("flush table bridge %s\n", p.table);
}
-
- if (cs.target != NULL)
- xtables_option_tfcall(cs.target);
- }
-
- cs.eb.ethproto = htons(cs.eb.ethproto);
-
- if (command == 'P') {
- return 0;
- } else if (command == 'A') {
+ ret = 1;
+ break;
+ case CMD_APPEND:
ret = nft_rule_eb_xlate_add(h, &p, &cs, true);
if (!ret)
print_ebt_cmd(argc, argv);
- } else if (command == 'I') {
+ break;
+ case CMD_INSERT:
ret = nft_rule_eb_xlate_add(h, &p, &cs, false);
if (!ret)
print_ebt_cmd(argc, argv);
+ break;
+ case CMD_LIST:
+ printf("list table bridge %s\n", p.table);
+ ret = 1;
+ break;
+ case CMD_NEW_CHAIN:
+ printf("add chain bridge %s %s\n", p.table, p.chain);
+ ret = 1;
+ break;
+ case CMD_DELETE_CHAIN:
+ printf("delete chain bridge %s %s\n", p.table, p.chain);
+ ret = 1;
+ break;
+ case CMD_INIT_TABLE:
+ printf("flush table bridge %s\n", p.table);
+ ret = 1;
+ break;
+ case CMD_DELETE:
+ case CMD_DELETE_NUM:
+ case CMD_CHECK:
+ case CMD_REPLACE:
+ case CMD_ZERO:
+ case CMD_ZERO_NUM:
+ case CMD_LIST|CMD_ZERO:
+ case CMD_LIST|CMD_ZERO_NUM:
+ case CMD_LIST_RULES:
+ case CMD_LIST_RULES|CMD_ZERO:
+ case CMD_LIST_RULES|CMD_ZERO_NUM:
+ case CMD_NEW_CHAIN|CMD_SET_POLICY:
+ case CMD_SET_POLICY:
+ case CMD_RENAME_CHAIN:
+ case CMD_CHANGE_COUNTERS:
+ break;
+ default:
+ /* We should never reach this... */
+ printf("Unsupported command?\n");
+ exit(1);
}
ebt_cs_clean(&cs);
diff --git a/iptables/xtables-eb.c b/iptables/xtables-eb.c
index b986fd9e..51c699de 100644
--- a/iptables/xtables-eb.c
+++ b/iptables/xtables-eb.c
@@ -42,70 +42,6 @@
#include "nft.h"
#include "nft-bridge.h"
-/*
- * From include/ebtables_u.h
- */
-#define ebt_check_option2(flags, mask) EBT_CHECK_OPTION(flags, mask)
-
-/*
- * From useful_functions.c
- */
-
-/* 0: default
- * 1: the inverse '!' of the option has already been specified */
-int ebt_invert = 0;
-
-static int ebt_check_inverse2(const char option[], int argc, char **argv)
-{
- if (!option)
- return ebt_invert;
- if (strcmp(option, "!") == 0) {
- if (ebt_invert == 1)
- xtables_error(PARAMETER_PROBLEM,
- "Double use of '!' not allowed");
- if (optind >= argc)
- optarg = NULL;
- else
- optarg = argv[optind];
- optind++;
- ebt_invert = 1;
- return 1;
- }
- return ebt_invert;
-}
-
-/*
- * Glue code to use libxtables
- */
-static int parse_rule_number(const char *rule)
-{
- unsigned int rule_nr;
-
- if (!xtables_strtoui(rule, NULL, &rule_nr, 1, INT_MAX))
- xtables_error(PARAMETER_PROBLEM,
- "Invalid rule number `%s'", rule);
-
- return rule_nr;
-}
-
-static int
-append_entry(struct nft_handle *h,
- const char *chain,
- const char *table,
- struct iptables_command_state *cs,
- int rule_nr,
- bool verbose, bool append)
-{
- int ret = 1;
-
- if (append)
- ret = nft_cmd_rule_append(h, chain, table, cs, NULL, verbose);
- else
- ret = nft_cmd_rule_insert(h, chain, table, cs, rule_nr, verbose);
-
- return ret;
-}
-
static int
delete_entry(struct nft_handle *h,
const char *chain,
@@ -130,48 +66,28 @@ delete_entry(struct nft_handle *h,
return ret;
}
-int ebt_get_current_chain(const char *chain)
+static int
+change_entry_counters(struct nft_handle *h,
+ const char *chain, const char *table,
+ struct iptables_command_state *cs,
+ int rule_nr, int rule_nr_end, uint8_t counter_op,
+ bool verbose)
{
- if (!chain)
- return -1;
-
- if (strcmp(chain, "PREROUTING") == 0)
- return NF_BR_PRE_ROUTING;
- else if (strcmp(chain, "INPUT") == 0)
- return NF_BR_LOCAL_IN;
- else if (strcmp(chain, "FORWARD") == 0)
- return NF_BR_FORWARD;
- else if (strcmp(chain, "OUTPUT") == 0)
- return NF_BR_LOCAL_OUT;
- else if (strcmp(chain, "POSTROUTING") == 0)
- return NF_BR_POST_ROUTING;
-
- /* placeholder for user defined chain */
- return NF_BR_NUMHOOKS;
-}
+ int ret = 1;
-/*
- * The original ebtables parser
- */
+ if (rule_nr == -1)
+ return nft_cmd_rule_change_counters(h, chain, table, cs,
+ rule_nr, counter_op,
+ verbose);
+ do {
+ ret = nft_cmd_rule_change_counters(h, chain, table, cs,
+ rule_nr, counter_op,
+ verbose);
+ rule_nr++;
+ } while (rule_nr < rule_nr_end);
-/* Checks whether a command has already been specified */
-#define OPT_COMMANDS (flags & OPT_COMMAND || flags & OPT_ZERO)
-
-#define OPT_COMMAND 0x01
-#define OPT_TABLE 0x02
-#define OPT_IN 0x04
-#define OPT_OUT 0x08
-#define OPT_JUMP 0x10
-#define OPT_PROTOCOL 0x20
-#define OPT_SOURCE 0x40
-#define OPT_DEST 0x80
-#define OPT_ZERO 0x100
-#define OPT_LOGICALIN 0x200
-#define OPT_LOGICALOUT 0x400
-#define OPT_KERNELDATA 0x800 /* This value is also defined in ebtablesd.c */
-#define OPT_COUNT 0x1000 /* This value is also defined in libebtc.c */
-#define OPT_CNT_INCR 0x2000 /* This value is also defined in libebtc.c */
-#define OPT_CNT_DECR 0x4000 /* This value is also defined in libebtc.c */
+ return ret;
+}
/* Default command line options. Do not mess around with the already
* assigned numbers unless you know what you are doing */
@@ -181,17 +97,17 @@ struct option ebt_original_options[] =
{ "insert" , required_argument, 0, 'I' },
{ "delete" , required_argument, 0, 'D' },
{ "list" , optional_argument, 0, 'L' },
- { "Lc" , no_argument , 0, 4 },
- { "Ln" , no_argument , 0, 5 },
- { "Lx" , no_argument , 0, 6 },
+ { "Lc" , no_argument , 0, 17 },
+ { "Ln" , no_argument , 0, 18 },
+ { "Lx" , no_argument , 0, 19 },
{ "Lmac2" , no_argument , 0, 12 },
{ "zero" , optional_argument, 0, 'Z' },
{ "flush" , optional_argument, 0, 'F' },
{ "policy" , required_argument, 0, 'P' },
{ "in-interface" , required_argument, 0, 'i' },
{ "in-if" , required_argument, 0, 'i' },
- { "logical-in" , required_argument, 0, 2 },
- { "logical-out" , required_argument, 0, 3 },
+ { "logical-in" , required_argument, 0, 15 },
+ { "logical-out" , required_argument, 0, 16 },
{ "out-interface" , required_argument, 0, 'o' },
{ "out-if" , required_argument, 0, 'o' },
{ "version" , no_argument , 0, 'V' },
@@ -214,6 +130,7 @@ struct option ebt_original_options[] =
{ "delete-chain" , optional_argument, 0, 'X' },
{ "init-table" , no_argument , 0, 11 },
{ "concurrent" , no_argument , 0, 13 },
+ { "check" , required_argument, 0, 14 },
{ 0 }
};
@@ -224,14 +141,9 @@ struct xtables_globals ebtables_globals = {
.compat_rev = nft_compatible_revision,
};
-#define opts ebtables_globals.opts
#define prog_name ebtables_globals.program_name
#define prog_vers ebtables_globals.program_version
-/*
- * From libebtc.c
- */
-
/* Prints all registered extensions */
static void ebt_list_extensions(const struct xtables_target *t,
const struct xtables_rule_match *m)
@@ -257,39 +169,38 @@ static void ebt_list_extensions(const struct xtables_target *t,
}*/
}
-#define OPTION_OFFSET 256
-static struct option *merge_options(struct option *oldopts,
- const struct option *newopts,
- unsigned int *options_offset)
+void nft_bridge_print_help(struct iptables_command_state *cs)
{
- unsigned int num_old, num_new, i;
- struct option *merge;
-
- if (!newopts || !oldopts || !options_offset)
- return oldopts;
- for (num_old = 0; oldopts[num_old].name; num_old++);
- for (num_new = 0; newopts[num_new].name; num_new++);
-
- ebtables_globals.option_offset += OPTION_OFFSET;
- *options_offset = ebtables_globals.option_offset;
-
- merge = xtables_malloc(sizeof(struct option) * (num_new + num_old + 1));
- memcpy(merge, oldopts, num_old * sizeof(struct option));
- for (i = 0; i < num_new; i++) {
- merge[num_old + i] = newopts[i];
- merge[num_old + i].val += *options_offset;
- }
- memset(merge + num_old + num_new, 0, sizeof(struct option));
- /* Only free dynamically allocated stuff */
- if (oldopts != ebt_original_options)
- free(oldopts);
+ const struct xtables_rule_match *m = cs->matches;
+ struct xtables_target *t = cs->target;
- return merge;
-}
+ while (optind < cs->argc) {
+ /*struct ebt_u_match *m;
+ struct ebt_u_watcher *w;*/
+
+ if (!strcasecmp("list_extensions", cs->argv[optind])) {
+ ebt_list_extensions(xtables_targets, cs->matches);
+ exit(0);
+ }
+ /*if ((m = ebt_find_match(cs->argv[optind])))
+ ebt_add_match(new_entry, m);
+ else if ((w = ebt_find_watcher(cs->argv[optind])))
+ ebt_add_watcher(new_entry, w);
+ else {*/
+ if (!(t = xtables_find_target(cs->argv[optind],
+ XTF_TRY_LOAD)))
+ xtables_error(PARAMETER_PROBLEM,
+ "Extension '%s' not found",
+ cs->argv[optind]);
+ if (cs->options & OPT_JUMP)
+ xtables_error(PARAMETER_PROBLEM,
+ "Sorry, you can only see help for one target extension at a time");
+ cs->options |= OPT_JUMP;
+ cs->target = t;
+ //}
+ optind++;
+ }
-static void print_help(const struct xtables_target *t,
- const struct xtables_rule_match *m, const char *table)
-{
printf("%s %s\n", prog_name, prog_vers);
printf(
"Usage:\n"
@@ -314,13 +225,13 @@ static void print_help(const struct xtables_target *t,
"--rename-chain -E old new : rename a chain\n"
"--delete-chain -X [chain] : delete a user defined chain\n"
"Options:\n"
-"--proto -p [!] proto : protocol hexadecimal, by name or LENGTH\n"
-"--src -s [!] address[/mask]: source mac address\n"
-"--dst -d [!] address[/mask]: destination mac address\n"
-"--in-if -i [!] name[+] : network input interface name\n"
-"--out-if -o [!] name[+] : network output interface name\n"
-"--logical-in [!] name[+] : logical bridge input interface name\n"
-"--logical-out [!] name[+] : logical bridge output interface name\n"
+"[!] --proto -p proto : protocol hexadecimal, by name or LENGTH\n"
+"[!] --src -s address[/mask]: source mac address\n"
+"[!] --dst -d address[/mask]: destination mac address\n"
+"[!] --in-if -i name[+] : network input interface name\n"
+"[!] --out-if -o name[+] : network output interface name\n"
+"[!] --logical-in name[+] : logical bridge input interface name\n"
+"[!] --logical-out name[+] : logical bridge output interface name\n"
"--set-counters -c chain\n"
" pcnt bcnt : set the counters of the to be added rule\n"
"--modprobe -M program : try to insert modules using this program\n"
@@ -338,9 +249,6 @@ static void print_help(const struct xtables_target *t,
printf("\n");
t->help();
}
-
-// if (table->help)
-// table->help(ebt_hooknames);
}
/* Execute command L */
@@ -369,99 +277,10 @@ static int list_rules(struct nft_handle *h, const char *chain, const char *table
return nft_cmd_rule_list(h, chain, table, rule_nr, format);
}
-static int parse_rule_range(const char *argv, int *rule_nr, int *rule_nr_end)
-{
- char *colon = strchr(argv, ':'), *buffer;
-
- if (colon) {
- *colon = '\0';
- if (*(colon + 1) == '\0')
- *rule_nr_end = -1; /* Until the last rule */
- else {
- *rule_nr_end = strtol(colon + 1, &buffer, 10);
- if (*buffer != '\0' || *rule_nr_end == 0)
- return -1;
- }
- }
- if (colon == argv)
- *rule_nr = 1; /* Beginning with the first rule */
- else {
- *rule_nr = strtol(argv, &buffer, 10);
- if (*buffer != '\0' || *rule_nr == 0)
- return -1;
- }
- if (!colon)
- *rule_nr_end = *rule_nr;
- return 0;
-}
-
-/* Incrementing or decrementing rules in daemon mode is not supported as the
- * involved code overload is not worth it (too annoying to take the increased
- * counters in the kernel into account). */
-static int parse_change_counters_rule(int argc, char **argv, int *rule_nr, int *rule_nr_end, struct iptables_command_state *cs)
-{
- char *buffer;
- int ret = 0;
-
- if (optind + 1 >= argc || argv[optind][0] == '-' || argv[optind + 1][0] == '-')
- xtables_error(PARAMETER_PROBLEM,
- "The command -C needs at least 2 arguments");
- if (optind + 2 < argc && (argv[optind + 2][0] != '-' || (argv[optind + 2][1] >= '0' && argv[optind + 2][1] <= '9'))) {
- if (optind + 3 != argc)
- xtables_error(PARAMETER_PROBLEM,
- "No extra options allowed with -C start_nr[:end_nr] pcnt bcnt");
- if (parse_rule_range(argv[optind], rule_nr, rule_nr_end))
- xtables_error(PARAMETER_PROBLEM,
- "Something is wrong with the rule number specification '%s'", argv[optind]);
- optind++;
- }
-
- if (argv[optind][0] == '+') {
- ret += 1;
- cs->counters.pcnt = strtoull(argv[optind] + 1, &buffer, 10);
- } else if (argv[optind][0] == '-') {
- ret += 2;
- cs->counters.pcnt = strtoull(argv[optind] + 1, &buffer, 10);
- } else
- cs->counters.pcnt = strtoull(argv[optind], &buffer, 10);
-
- if (*buffer != '\0')
- goto invalid;
- optind++;
- if (argv[optind][0] == '+') {
- ret += 3;
- cs->counters.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
- } else if (argv[optind][0] == '-') {
- ret += 6;
- cs->counters.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
- } else
- cs->counters.bcnt = strtoull(argv[optind], &buffer, 10);
-
- if (*buffer != '\0')
- goto invalid;
- optind++;
- return ret;
-invalid:
- xtables_error(PARAMETER_PROBLEM,"Packet counter '%s' invalid", argv[optind]);
-}
-
-static void ebtables_parse_interface(const char *arg, char *vianame)
-{
- unsigned char mask[IFNAMSIZ];
- char *c;
-
- xtables_parse_interface(arg, vianame, mask);
-
- if ((c = strchr(vianame, '+'))) {
- if (*(c + 1) != '\0')
- xtables_error(PARAMETER_PROBLEM,
- "Spurious characters after '+' wildcard");
- }
-}
-
/* This code is very similar to iptables/xtables.c:command_match() */
static void ebt_load_match(const char *name)
{
+ struct option *opts = xt_params->opts;
struct xtables_match *m;
size_t size;
@@ -478,19 +297,29 @@ static void ebt_load_match(const char *name)
m->m->u.user.revision = m->revision;
xs_init_match(m);
- opts = merge_options(opts, m->extra_opts, &m->option_offset);
+ if (m->x6_options != NULL)
+ opts = xtables_options_xfrm(xt_params->orig_opts, opts,
+ m->x6_options, &m->option_offset);
+ else if (m->extra_opts != NULL)
+ opts = xtables_merge_options(xt_params->orig_opts, opts,
+ m->extra_opts, &m->option_offset);
+ else
+ return;
+
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "Can't alloc memory");
+ xt_params->opts = opts;
}
-static void __ebt_load_watcher(const char *name, const char *typename)
+static void ebt_load_watcher(const char *name)
{
+ struct option *opts = xt_params->opts;
struct xtables_target *watcher;
size_t size;
watcher = xtables_find_target(name, XTF_TRY_LOAD);
if (!watcher) {
- fprintf(stderr, "Unable to load %s %s\n", name, typename);
+ fprintf(stderr, "Unable to load %s watcher\n", name);
return;
}
@@ -505,25 +334,24 @@ static void __ebt_load_watcher(const char *name, const char *typename)
xs_init_target(watcher);
- opts = merge_options(opts, watcher->extra_opts,
- &watcher->option_offset);
+ if (watcher->x6_options != NULL)
+ opts = xtables_options_xfrm(xt_params->orig_opts, opts,
+ watcher->x6_options,
+ &watcher->option_offset);
+ else if (watcher->extra_opts != NULL)
+ opts = xtables_merge_options(xt_params->orig_opts, opts,
+ watcher->extra_opts,
+ &watcher->option_offset);
+ else
+ return;
+
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "Can't alloc memory");
+ xt_params->opts = opts;
}
-static void ebt_load_watcher(const char *name)
+static void ebt_load_match_extensions(void)
{
- return __ebt_load_watcher(name, "watcher");
-}
-
-static void ebt_load_target(const char *name)
-{
- return __ebt_load_watcher(name, "target");
-}
-
-void ebt_load_match_extensions(void)
-{
- opts = ebt_original_options;
ebt_load_match("802_3");
ebt_load_match("arp");
ebt_load_match("ip");
@@ -537,13 +365,6 @@ void ebt_load_match_extensions(void)
ebt_load_watcher("log");
ebt_load_watcher("nflog");
-
- ebt_load_target("mark");
- ebt_load_target("dnat");
- ebt_load_target("snat");
- ebt_load_target("arpreply");
- ebt_load_target("redirect");
- ebt_load_target("standard");
}
void ebt_add_match(struct xtables_match *m,
@@ -606,56 +427,84 @@ void ebt_add_watcher(struct xtables_target *watcher,
*matchp = newnode;
}
-int ebt_command_default(struct iptables_command_state *cs)
+int ebt_command_default(struct iptables_command_state *cs,
+ struct xtables_globals *unused, bool ebt_invert)
{
struct xtables_target *t = cs->target;
struct xtables_match *m;
struct ebt_match *matchp;
/* Is it a target option? */
- if (t && t->parse) {
- if (t->parse(cs->c - t->option_offset, cs->argv,
- ebt_invert, &t->tflags, NULL, &t->t))
- return 0;
+ if (cs->target != NULL &&
+ (cs->target->parse != NULL || cs->target->x6_parse != NULL) &&
+ cs->c >= cs->target->option_offset &&
+ cs->c < cs->target->option_offset + XT_OPTION_OFFSET_SCALE) {
+ xtables_option_tpcall(cs->c, cs->argv, ebt_invert,
+ cs->target, &cs->eb);
+ return 0;
}
/* check previously added matches/watchers to this rule first */
for (matchp = cs->match_list; matchp; matchp = matchp->next) {
if (matchp->ismatch) {
m = matchp->u.match;
- if (m->parse &&
- m->parse(cs->c - m->option_offset, cs->argv,
- ebt_invert, &m->mflags, NULL, &m->m))
- return 0;
+ if (!m->parse && !m->x6_parse)
+ continue;
+ if (cs->c < m->option_offset ||
+ cs->c >= m->option_offset + XT_OPTION_OFFSET_SCALE)
+ continue;
+ xtables_option_mpcall(cs->c, cs->argv, ebt_invert,
+ m, &cs->eb);
+ return 0;
} else {
t = matchp->u.watcher;
- if (t->parse &&
- t->parse(cs->c - t->option_offset, cs->argv,
- ebt_invert, &t->tflags, NULL, &t->t))
- return 0;
+ if (!t->parse && !t->x6_parse)
+ continue;
+ if (cs->c < t->option_offset ||
+ cs->c >= t->option_offset + XT_OPTION_OFFSET_SCALE)
+ continue;
+ xtables_option_tpcall(cs->c, cs->argv, ebt_invert,
+ t, &cs->eb);
+ return 0;
}
}
/* Is it a match_option? */
for (m = xtables_matches; m; m = m->next) {
- if (m->parse &&
- m->parse(cs->c - m->option_offset, cs->argv,
- ebt_invert, &m->mflags, NULL, &m->m)) {
- ebt_add_match(m, cs);
- return 0;
- }
+ if (!m->parse && !m->x6_parse)
+ continue;
+ if (cs->c < m->option_offset ||
+ cs->c >= m->option_offset + XT_OPTION_OFFSET_SCALE)
+ continue;
+ xtables_option_mpcall(cs->c, cs->argv, ebt_invert, m, &cs->eb);
+ ebt_add_match(m, cs);
+ return 0;
}
/* Is it a watcher option? */
for (t = xtables_targets; t; t = t->next) {
- if (t->parse &&
- t->parse(cs->c - t->option_offset, cs->argv,
- ebt_invert, &t->tflags, NULL, &t->t)) {
- ebt_add_watcher(t, cs);
- return 0;
- }
+ if (!(t->ext_flags & XTABLES_EXT_WATCHER))
+ continue;
+
+ if (!t->parse && !t->x6_parse)
+ continue;
+ if (cs->c < t->option_offset ||
+ cs->c >= t->option_offset + XT_OPTION_OFFSET_SCALE)
+ continue;
+ xtables_option_tpcall(cs->c, cs->argv, ebt_invert, t, &cs->eb);
+ ebt_add_watcher(t, cs);
+ return 0;
}
- return 1;
+ if (cs->c == ':')
+ xtables_error(PARAMETER_PROBLEM, "option \"%s\" "
+ "requires an argument", cs->argv[optind - 1]);
+ if (cs->c == '?') {
+ char optoptstr[3] = {'-', optopt, '\0'};
+
+ xtables_error(PARAMETER_PROBLEM, "unknown option \"%s\"",
+ optopt ? optoptstr : cs->argv[optind - 1]);
+ }
+ xtables_error(PARAMETER_PROBLEM, "Unknown arg \"%s\"", optarg);
}
int nft_init_eb(struct nft_handle *h, const char *pname)
@@ -695,7 +544,7 @@ void nft_fini_eb(struct nft_handle *h)
free(target->t);
}
- free(opts);
+ free(xt_params->opts);
nft_fini(h);
xtables_fini();
@@ -704,482 +553,133 @@ void nft_fini_eb(struct nft_handle *h)
int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table,
bool restore)
{
- char *buffer;
- int c, i;
- int chcounter = 0; /* Needed for -C */
- int rule_nr = 0;
- int rule_nr_end = 0;
- int ret = 0;
- unsigned int flags = 0;
- struct xtables_target *t;
struct iptables_command_state cs = {
+ .argc = argc,
.argv = argv,
.jumpto = "",
.eb.bitmask = EBT_NOPROTO,
};
- char command = 'h';
- const char *chain = NULL;
- const char *policy = NULL;
- int selected_chain = -1;
- struct xtables_rule_match *xtrm_i;
- struct ebt_match *match;
- bool table_set = false;
-
- /* avoid cumulating verbosity with ebtables-restore */
- h->verbose = 0;
-
- /* prevent getopt to spoil our error reporting */
- optind = 0;
- opterr = false;
-
- /* Getopt saves the day */
- while ((c = getopt_long(argc, argv, EBT_OPTSTRING,
- opts, NULL)) != -1) {
- cs.c = c;
- switch (c) {
-
- case 'A': /* Add a rule */
- case 'D': /* Delete a rule */
- case 'C': /* Change counters */
- case 'P': /* Define policy */
- case 'I': /* Insert a rule */
- case 'N': /* Make a user defined chain */
- case 'E': /* Rename chain */
- case 'X': /* Delete chain */
- /* We allow -N chainname -P policy */
- if (command == 'N' && c == 'P') {
- command = c;
- optind--; /* No table specified */
- goto handle_P;
- }
- if (OPT_COMMANDS)
- xtables_error(PARAMETER_PROBLEM,
- "Multiple commands are not allowed");
-
- command = c;
- if (optarg && (optarg[0] == '-' || !strcmp(optarg, "!")))
- xtables_error(PARAMETER_PROBLEM, "No chain name specified");
- chain = optarg;
- selected_chain = ebt_get_current_chain(chain);
- flags |= OPT_COMMAND;
-
- if (c == 'N') {
- ret = nft_cmd_chain_user_add(h, chain, *table);
- break;
- } else if (c == 'X') {
- /* X arg is optional, optarg is NULL */
- if (!chain && optind < argc && argv[optind][0] != '-') {
- chain = argv[optind];
- optind++;
- }
- ret = nft_cmd_chain_del(h, chain, *table, 0);
- break;
- }
-
- if (c == 'E') {
- if (optind >= argc)
- xtables_error(PARAMETER_PROBLEM, "No new chain name specified");
- else if (optind < argc - 1)
- xtables_error(PARAMETER_PROBLEM, "No extra options allowed with -E");
- else if (strlen(argv[optind]) >= NFT_CHAIN_MAXNAMELEN)
- xtables_error(PARAMETER_PROBLEM, "Chain name length can't exceed %d"" characters", NFT_CHAIN_MAXNAMELEN - 1);
- else if (strchr(argv[optind], ' ') != NULL)
- xtables_error(PARAMETER_PROBLEM, "Use of ' ' not allowed in chain names");
-
- errno = 0;
- ret = nft_cmd_chain_user_rename(h, chain, *table,
- argv[optind]);
- if (ret != 0 && errno == ENOENT)
- xtables_error(PARAMETER_PROBLEM, "Chain '%s' doesn't exists", chain);
-
- optind++;
- break;
- } else if (c == 'D' && optind < argc && (argv[optind][0] != '-' || (argv[optind][1] >= '0' && argv[optind][1] <= '9'))) {
- if (optind != argc - 1)
- xtables_error(PARAMETER_PROBLEM,
- "No extra options allowed with -D start_nr[:end_nr]");
- if (parse_rule_range(argv[optind], &rule_nr, &rule_nr_end))
- xtables_error(PARAMETER_PROBLEM,
- "Problem with the specified rule number(s) '%s'", argv[optind]);
- optind++;
- } else if (c == 'C') {
- if ((chcounter = parse_change_counters_rule(argc, argv, &rule_nr, &rule_nr_end, &cs)) == -1)
- return -1;
- } else if (c == 'I') {
- if (optind >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')))
- rule_nr = 1;
- else {
- rule_nr = parse_rule_number(argv[optind]);
- optind++;
- }
- } else if (c == 'P') {
-handle_P:
- if (optind >= argc)
- xtables_error(PARAMETER_PROBLEM,
- "No policy specified");
- for (i = 0; i < NUM_STANDARD_TARGETS; i++)
- if (!strcmp(argv[optind], nft_ebt_standard_target(i))) {
- policy = argv[optind];
- if (-i-1 == EBT_CONTINUE)
- xtables_error(PARAMETER_PROBLEM,
- "Wrong policy '%s'",
- argv[optind]);
- break;
- }
- if (i == NUM_STANDARD_TARGETS)
- xtables_error(PARAMETER_PROBLEM,
- "Unknown policy '%s'", argv[optind]);
- optind++;
- }
- break;
- case 'L': /* List */
- case 'F': /* Flush */
- case 'Z': /* Zero counters */
- if (c == 'Z') {
- if ((flags & OPT_ZERO) || (flags & OPT_COMMAND && command != 'L'))
-print_zero:
- xtables_error(PARAMETER_PROBLEM,
- "Command -Z only allowed together with command -L");
- flags |= OPT_ZERO;
- } else {
- if (flags & OPT_COMMAND)
- xtables_error(PARAMETER_PROBLEM,
- "Multiple commands are not allowed");
- command = c;
- flags |= OPT_COMMAND;
- if (flags & OPT_ZERO && c != 'L')
- goto print_zero;
- }
-
- if (optind < argc && argv[optind][0] != '-') {
- chain = argv[optind];
- optind++;
- }
- break;
- case 'v': /* verbose */
- flags |= OPT_VERBOSE;
- h->verbose++;
- break;
- case 'V': /* Version */
- if (OPT_COMMANDS)
- xtables_error(PARAMETER_PROBLEM,
- "Multiple commands are not allowed");
- printf("%s %s\n", prog_name, prog_vers);
- exit(0);
- case 'h': /* Help */
- if (OPT_COMMANDS)
- xtables_error(PARAMETER_PROBLEM,
- "Multiple commands are not allowed");
- command = 'h';
-
- /* All other arguments should be extension names */
- while (optind < argc) {
- /*struct ebt_u_match *m;
- struct ebt_u_watcher *w;*/
-
- if (!strcasecmp("list_extensions", argv[optind])) {
- ebt_list_extensions(xtables_targets, cs.matches);
- exit(0);
- }
- /*if ((m = ebt_find_match(argv[optind])))
- ebt_add_match(new_entry, m);
- else if ((w = ebt_find_watcher(argv[optind])))
- ebt_add_watcher(new_entry, w);
- else {*/
- if (!(t = xtables_find_target(argv[optind], XTF_TRY_LOAD)))
- xtables_error(PARAMETER_PROBLEM,"Extension '%s' not found", argv[optind]);
- if (flags & OPT_JUMP)
- xtables_error(PARAMETER_PROBLEM,"Sorry, you can only see help for one target extension at a time");
- flags |= OPT_JUMP;
- cs.target = t;
- //}
- optind++;
- }
- break;
- case 't': /* Table */
- ebt_check_option2(&flags, OPT_TABLE);
- if (restore && table_set)
- xtables_error(PARAMETER_PROBLEM,
- "The -t option cannot be used in %s.\n",
- xt_params->program_name);
- if (!nft_table_builtin_find(h, optarg))
- xtables_error(VERSION_PROBLEM,
- "table '%s' does not exist",
- optarg);
- *table = optarg;
- table_set = true;
- break;
- case 'i': /* Input interface */
- case 2 : /* Logical input interface */
- case 'o': /* Output interface */
- case 3 : /* Logical output interface */
- case 'j': /* Target */
- case 'p': /* Net family protocol */
- case 's': /* Source mac */
- case 'd': /* Destination mac */
- case 'c': /* Set counters */
- if (!OPT_COMMANDS)
- xtables_error(PARAMETER_PROBLEM,
- "No command specified");
- if (command != 'A' && command != 'D' && command != 'I' && command != 'C')
- xtables_error(PARAMETER_PROBLEM,
- "Command and option do not match");
- if (c == 'i') {
- ebt_check_option2(&flags, OPT_IN);
- if (selected_chain > 2 && selected_chain < NF_BR_BROUTING)
- xtables_error(PARAMETER_PROBLEM,
- "Use -i only in INPUT, FORWARD, PREROUTING and BROUTING chains");
- if (ebt_check_inverse2(optarg, argc, argv))
- cs.eb.invflags |= EBT_IIN;
-
- ebtables_parse_interface(optarg, cs.eb.in);
- break;
- } else if (c == 2) {
- ebt_check_option2(&flags, OPT_LOGICALIN);
- if (selected_chain > 2 && selected_chain < NF_BR_BROUTING)
- xtables_error(PARAMETER_PROBLEM,
- "Use --logical-in only in INPUT, FORWARD, PREROUTING and BROUTING chains");
- if (ebt_check_inverse2(optarg, argc, argv))
- cs.eb.invflags |= EBT_ILOGICALIN;
-
- ebtables_parse_interface(optarg, cs.eb.logical_in);
- break;
- } else if (c == 'o') {
- ebt_check_option2(&flags, OPT_OUT);
- if (selected_chain < 2 || selected_chain == NF_BR_BROUTING)
- xtables_error(PARAMETER_PROBLEM,
- "Use -o only in OUTPUT, FORWARD and POSTROUTING chains");
- if (ebt_check_inverse2(optarg, argc, argv))
- cs.eb.invflags |= EBT_IOUT;
-
- ebtables_parse_interface(optarg, cs.eb.out);
- break;
- } else if (c == 3) {
- ebt_check_option2(&flags, OPT_LOGICALOUT);
- if (selected_chain < 2 || selected_chain == NF_BR_BROUTING)
- xtables_error(PARAMETER_PROBLEM,
- "Use --logical-out only in OUTPUT, FORWARD and POSTROUTING chains");
- if (ebt_check_inverse2(optarg, argc, argv))
- cs.eb.invflags |= EBT_ILOGICALOUT;
-
- ebtables_parse_interface(optarg, cs.eb.logical_out);
- break;
- } else if (c == 'j') {
- ebt_check_option2(&flags, OPT_JUMP);
- if (strcmp(optarg, "CONTINUE") != 0) {
- command_jump(&cs, optarg);
- }
- break;
- } else if (c == 's') {
- ebt_check_option2(&flags, OPT_SOURCE);
- if (ebt_check_inverse2(optarg, argc, argv))
- cs.eb.invflags |= EBT_ISOURCE;
-
- if (xtables_parse_mac_and_mask(optarg,
- cs.eb.sourcemac,
- cs.eb.sourcemsk))
- xtables_error(PARAMETER_PROBLEM, "Problem with specified source mac '%s'", optarg);
- cs.eb.bitmask |= EBT_SOURCEMAC;
- break;
- } else if (c == 'd') {
- ebt_check_option2(&flags, OPT_DEST);
- if (ebt_check_inverse2(optarg, argc, argv))
- cs.eb.invflags |= EBT_IDEST;
-
- if (xtables_parse_mac_and_mask(optarg,
- cs.eb.destmac,
- cs.eb.destmsk))
- xtables_error(PARAMETER_PROBLEM, "Problem with specified destination mac '%s'", optarg);
- cs.eb.bitmask |= EBT_DESTMAC;
- break;
- } else if (c == 'c') {
- ebt_check_option2(&flags, OPT_COUNT);
- if (ebt_check_inverse2(optarg, argc, argv))
- xtables_error(PARAMETER_PROBLEM,
- "Unexpected '!' after -c");
- if (optind >= argc || optarg[0] == '-' || argv[optind][0] == '-')
- xtables_error(PARAMETER_PROBLEM,
- "Option -c needs 2 arguments");
-
- cs.counters.pcnt = strtoull(optarg, &buffer, 10);
- if (*buffer != '\0')
- xtables_error(PARAMETER_PROBLEM,
- "Packet counter '%s' invalid",
- optarg);
- cs.counters.bcnt = strtoull(argv[optind], &buffer, 10);
- if (*buffer != '\0')
- xtables_error(PARAMETER_PROBLEM,
- "Packet counter '%s' invalid",
- argv[optind]);
- optind++;
- break;
- }
- ebt_check_option2(&flags, OPT_PROTOCOL);
- if (ebt_check_inverse2(optarg, argc, argv))
- cs.eb.invflags |= EBT_IPROTO;
-
- cs.eb.bitmask &= ~((unsigned int)EBT_NOPROTO);
- i = strtol(optarg, &buffer, 16);
- if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
- xtables_error(PARAMETER_PROBLEM,
- "Problem with the specified protocol");
- if (*buffer != '\0') {
- struct xt_ethertypeent *ent;
-
- if (!strcasecmp(optarg, "LENGTH")) {
- cs.eb.bitmask |= EBT_802_3;
- break;
- }
- ent = xtables_getethertypebyname(optarg);
- if (!ent)
- xtables_error(PARAMETER_PROBLEM,
- "Problem with the specified Ethernet protocol '%s', perhaps "XT_PATH_ETHERTYPES " is missing", optarg);
- cs.eb.ethproto = ent->e_ethertype;
- } else
- cs.eb.ethproto = i;
-
- if (cs.eb.ethproto < 0x0600)
- xtables_error(PARAMETER_PROBLEM,
- "Sorry, protocols have values above or equal to 0x0600");
- break;
- case 4 : /* Lc */
- ebt_check_option2(&flags, LIST_C);
- if (command != 'L')
- xtables_error(PARAMETER_PROBLEM,
- "Use --Lc with -L");
- flags |= LIST_C;
- break;
- case 5 : /* Ln */
- ebt_check_option2(&flags, LIST_N);
- if (command != 'L')
- xtables_error(PARAMETER_PROBLEM,
- "Use --Ln with -L");
- if (flags & LIST_X)
- xtables_error(PARAMETER_PROBLEM,
- "--Lx is not compatible with --Ln");
- flags |= LIST_N;
- break;
- case 6 : /* Lx */
- ebt_check_option2(&flags, LIST_X);
- if (command != 'L')
- xtables_error(PARAMETER_PROBLEM,
- "Use --Lx with -L");
- if (flags & LIST_N)
- xtables_error(PARAMETER_PROBLEM,
- "--Lx is not compatible with --Ln");
- flags |= LIST_X;
- break;
- case 12 : /* Lmac2 */
- ebt_check_option2(&flags, LIST_MAC2);
- if (command != 'L')
- xtables_error(PARAMETER_PROBLEM,
- "Use --Lmac2 with -L");
- flags |= LIST_MAC2;
- break;
- case 11: /* init-table */
- if (restore)
- xtables_error(PARAMETER_PROBLEM,
- "--init-table is not supported in daemon mode");
- nft_cmd_table_flush(h, *table, false);
- return 1;
- case 13 :
- break;
- case 1 :
- if (!strcmp(optarg, "!"))
- ebt_check_inverse2(optarg, argc, argv);
- else
- xtables_error(PARAMETER_PROBLEM,
- "Bad argument : '%s'", optarg);
- /* ebt_ebt_check_inverse2() did optind++ */
- optind--;
- continue;
- default:
- ebt_check_inverse2(optarg, argc, argv);
-
- if (ebt_command_default(&cs))
- xtables_error(PARAMETER_PROBLEM,
- "Unknown argument: '%s'",
- argv[optind]);
-
- if (command != 'A' && command != 'I' &&
- command != 'D' && command != 'C')
- xtables_error(PARAMETER_PROBLEM,
- "Extensions only for -A, -I, -D and -C");
- }
- ebt_invert = 0;
- }
-
- /* Just in case we didn't catch an error */
- /*if (ebt_errormsg[0] != '\0')
- return -1;
-
- if (!(table = ebt_find_table(replace->name)))
- ebt_print_error2("Bad table name");*/
+ const struct builtin_table *t;
+ struct xtables_args args = {
+ .family = h->family,
+ };
+ struct xt_cmd_parse p = {
+ .table = *table,
+ .restore = restore,
+ .line = line,
+ .rule_ranges = true,
+ .ops = &h->ops->cmd_parse,
+ };
+ int ret = 0;
- if (command == 'h' && !(flags & OPT_ZERO)) {
- print_help(cs.target, cs.matches, *table);
- ret = 1;
- }
+ do_parse(argc, argv, &p, &cs, &args);
- /* Do the final checks */
- if (command == 'A' || command == 'I' ||
- command == 'D' || command == 'C') {
- for (xtrm_i = cs.matches; xtrm_i; xtrm_i = xtrm_i->next)
- xtables_option_mfcall(xtrm_i->match);
+ h->verbose = p.verbose;
- for (match = cs.match_list; match; match = match->next) {
- if (match->ismatch)
- continue;
+ t = nft_table_builtin_find(h, p.table);
+ if (!t)
+ xtables_error(VERSION_PROBLEM,
+ "table '%s' does not exist", p.table);
- xtables_option_tfcall(match->u.watcher);
+ switch (p.command) {
+ case CMD_NEW_CHAIN:
+ case CMD_NEW_CHAIN | CMD_SET_POLICY:
+ ret = nft_cmd_chain_user_add(h, p.chain, p.table);
+ if (!ret || !(p.command & CMD_SET_POLICY))
+ break;
+ /* fall through */
+ case CMD_SET_POLICY:
+ if (!nft_chain_builtin_find(t, p.chain)) {
+ ret = ebt_cmd_user_chain_policy(h, p.table, p.chain,
+ p.policy);
+ break;
}
-
- if (cs.target != NULL)
- xtables_option_tfcall(cs.target);
- }
- /* So, the extensions can work with the host endian.
- * The kernel does not have to do this of course */
- cs.eb.ethproto = htons(cs.eb.ethproto);
-
- if (command == 'P') {
- if (selected_chain >= NF_BR_NUMHOOKS) {
- ret = ebt_cmd_user_chain_policy(h, *table, chain, policy);
- } else {
- if (strcmp(policy, "RETURN") == 0) {
- xtables_error(PARAMETER_PROBLEM,
- "Policy RETURN only allowed for user defined chains");
- }
- ret = nft_cmd_chain_set(h, *table, chain, policy, NULL);
- if (ret < 0)
- xtables_error(PARAMETER_PROBLEM, "Wrong policy");
+ if (strcmp(p.policy, "RETURN") == 0) {
+ xtables_error(PARAMETER_PROBLEM,
+ "Policy RETURN only allowed for user defined chains");
}
- } else if (command == 'L') {
- ret = list_rules(h, chain, *table, rule_nr,
- flags & OPT_VERBOSE,
- 0,
- /*flags&OPT_EXPANDED*/0,
- flags&LIST_N,
- flags&LIST_C);
+ ret = nft_cmd_chain_set(h, p.table, p.chain, p.policy, NULL);
+ if (ret < 0)
+ xtables_error(PARAMETER_PROBLEM, "Wrong policy");
+ break;
+ case CMD_LIST:
+ case CMD_LIST | CMD_ZERO:
+ case CMD_LIST | CMD_ZERO_NUM:
+ case CMD_LIST_RULES:
+ case CMD_LIST_RULES | CMD_ZERO:
+ case CMD_LIST_RULES | CMD_ZERO_NUM:
+ if (p.command & CMD_LIST)
+ ret = list_rules(h, p.chain, p.table, p.rulenum,
+ cs.options & OPT_VERBOSE,
+ 0,
+ /*cs.options&OPT_EXPANDED*/0,
+ cs.options&OPT_LINENUMBERS,
+ cs.options&OPT_LIST_C);
+ else if (p.command & CMD_LIST_RULES)
+ ret = nft_cmd_rule_list_save(h, p.chain, p.table,
+ p.rulenum - 1,
+ cs.options & OPT_VERBOSE);
+ if (ret && (p.command & CMD_ZERO))
+ ret = nft_cmd_chain_zero_counters(h, p.chain, p.table,
+ cs.options & OPT_VERBOSE);
+ if (ret && (p.command & CMD_ZERO_NUM))
+ ret = nft_cmd_rule_zero_counters(h, p.chain, p.table,
+ p.rulenum - 1);
+ break;
+ case CMD_ZERO:
+ ret = nft_cmd_chain_zero_counters(h, p.chain, p.table,
+ cs.options & OPT_VERBOSE);
+ break;
+ case CMD_ZERO_NUM:
+ ret = nft_cmd_rule_zero_counters(h, p.chain, p.table,
+ p.rulenum - 1);
+ break;
+ case CMD_FLUSH:
+ ret = nft_cmd_rule_flush(h, p.chain, p.table,
+ cs.options & OPT_VERBOSE);
+ break;
+ case CMD_APPEND:
+ ret = nft_cmd_rule_append(h, p.chain, p.table, &cs,
+ cs.options & OPT_VERBOSE);
+ break;
+ case CMD_INSERT:
+ ret = nft_cmd_rule_insert(h, p.chain, p.table, &cs,
+ p.rulenum - 1,
+ cs.options & OPT_VERBOSE);
+ break;
+ case CMD_DELETE:
+ case CMD_DELETE_NUM:
+ ret = delete_entry(h, p.chain, p.table, &cs, p.rulenum - 1,
+ p.rulenum_end, cs.options & OPT_VERBOSE);
+ break;
+ case CMD_DELETE_CHAIN:
+ ret = nft_cmd_chain_del(h, p.chain, p.table, 0);
+ break;
+ case CMD_RENAME_CHAIN:
+ ret = nft_cmd_chain_user_rename(h, p.chain, p.table, p.newname);
+ break;
+ case CMD_INIT_TABLE:
+ ret = nft_cmd_table_flush(h, p.table, false);
+ break;
+ case CMD_CHECK:
+ ret = nft_cmd_rule_check(h, p.chain, p.table,
+ &cs, cs.options & OPT_VERBOSE);
+ break;
+ case CMD_CHANGE_COUNTERS:
+ ret = change_entry_counters(h, p.chain, p.table, &cs,
+ p.rulenum - 1, p.rulenum_end,
+ args.counter_op,
+ cs.options & OPT_VERBOSE);
+ break;
+ case CMD_REPLACE:
+ ret = nft_cmd_rule_replace(h, p.chain, p.table, &cs,
+ p.rulenum - 1,
+ cs.options & OPT_VERBOSE);
+ break;
+ default:
+ /* We should never reach this... */
+ exit_tryhelp(2, line);
}
- if (flags & OPT_ZERO) {
- ret = nft_cmd_chain_zero_counters(h, chain, *table,
- flags & OPT_VERBOSE);
- } else if (command == 'F') {
- ret = nft_cmd_rule_flush(h, chain, *table, flags & OPT_VERBOSE);
- } else if (command == 'A') {
- ret = append_entry(h, chain, *table, &cs, 0,
- flags & OPT_VERBOSE, true);
- } else if (command == 'I') {
- ret = append_entry(h, chain, *table, &cs, rule_nr - 1,
- flags & OPT_VERBOSE, false);
- } else if (command == 'D') {
- ret = delete_entry(h, chain, *table, &cs, rule_nr - 1,
- rule_nr_end, flags & OPT_VERBOSE);
- } /*else if (replace->command == 'C') {
- ebt_change_counters(replace, new_entry, rule_nr, rule_nr_end, &(new_entry->cnt_surplus), chcounter);
- if (ebt_errormsg[0] != '\0')
- return -1;
- }*/
ebt_cs_clean(&cs);
return ret;
diff --git a/iptables/xtables-monitor.8.in b/iptables/xtables-monitor.8.in
index a7f22c0d..ed2c5fb4 100644
--- a/iptables/xtables-monitor.8.in
+++ b/iptables/xtables-monitor.8.in
@@ -43,7 +43,7 @@ Restrict output to IPv6.
.PP
The first line shows a packet entering rule set evaluation.
The protocol number is shown (AF_INET in this case), then a packet
-identifier number that allows to correlate messages coming from rule set evaluation of
+identifier number that allows one to correlate messages coming from rule set evaluation of
this packet. After this, the rule that was matched by the packet is shown.
This is the TRACE rule that turns on tracing events for this packet.
diff --git a/iptables/xtables-monitor.c b/iptables/xtables-monitor.c
index a1eba2f4..cf2729d8 100644
--- a/iptables/xtables-monitor.c
+++ b/iptables/xtables-monitor.c
@@ -227,7 +227,7 @@ static void trace_print_rule(const struct nftnl_trace *nlt, struct cb_arg *args)
exit(EXIT_FAILURE);
}
- nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family, 0, 0);
+ nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family, 0, 0);
nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);
nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
diff --git a/iptables/xtables-multi.h b/iptables/xtables-multi.h
index 94c24d5a..760d3e4f 100644
--- a/iptables/xtables-multi.h
+++ b/iptables/xtables-multi.h
@@ -9,6 +9,7 @@ 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_xlate_main(int, char **);
extern int xtables_ip4_xlate_main(int, char **);
extern int xtables_ip6_xlate_main(int, char **);
extern int xtables_eb_xlate_main(int, char **);
@@ -20,7 +21,6 @@ extern int xtables_arp_save_main(int, char **);
extern int xtables_eb_main(int, char **);
extern int xtables_eb_restore_main(int, char **);
extern int xtables_eb_save_main(int, char **);
-extern int xtables_config_main(int, char **);
extern int xtables_monitor_main(int, char **);
extern struct xtables_globals arptables_globals;
diff --git a/iptables/xtables-nft-multi.c b/iptables/xtables-nft-multi.c
index e2b7c641..48265d8e 100644
--- a/iptables/xtables-nft-multi.c
+++ b/iptables/xtables-nft-multi.c
@@ -30,6 +30,7 @@ static const struct subcommand multi_subcommands[] = {
{"ip6tables-translate", xtables_ip6_xlate_main},
{"iptables-restore-translate", xtables_ip4_xlate_restore_main},
{"ip6tables-restore-translate", xtables_ip6_xlate_restore_main},
+ {"arptables-translate", xtables_arp_xlate_main},
{"arptables", xtables_arp_main},
{"arptables-nft", xtables_arp_main},
{"arptables-restore", xtables_arp_restore_main},
diff --git a/iptables/xtables-nft.8 b/iptables/xtables-nft.8
index 702bf954..ae54476c 100644
--- a/iptables/xtables-nft.8
+++ b/iptables/xtables-nft.8
@@ -105,15 +105,15 @@ One basic example is creating the skeleton ruleset in nf_tables from the
xtables-nft tools, in a fresh machine:
.nf
- root@machine:~# iptables\-nft \-L
+ root@machine:\(ti# iptables\-nft \-L
[...]
- root@machine:~# ip6tables\-nft \-L
+ root@machine:\(ti# ip6tables\-nft \-L
[...]
- root@machine:~# arptables\-nft \-L
+ root@machine:\(ti# arptables\-nft \-L
[...]
- root@machine:~# ebtables\-nft \-L
+ root@machine:\(ti# ebtables\-nft \-L
[...]
- root@machine:~# nft list ruleset
+ root@machine:\(ti# nft list ruleset
table ip filter {
chain INPUT {
type filter hook input priority 0; policy accept;
@@ -175,12 +175,12 @@ To migrate your complete filter ruleset, in the case of \fBiptables(8)\fP,
you would use:
.nf
- root@machine:~# iptables\-legacy\-save > myruleset # reads from x_tables
- root@machine:~# iptables\-nft\-restore myruleset # writes to nf_tables
+ root@machine:\(ti# iptables\-legacy\-save > myruleset # reads from x_tables
+ root@machine:\(ti# iptables\-nft\-restore myruleset # writes to nf_tables
.fi
or
.nf
- root@machine:~# iptables\-legacy\-save | iptables-translate-restore | less
+ root@machine:\(ti# iptables\-legacy\-save | iptables\-translate\-restore | less
.fi
to see how rules would look like in the nft
diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
index 1363f96a..23cd3498 100644
--- a/iptables/xtables-restore.c
+++ b/iptables/xtables-restore.c
@@ -115,14 +115,14 @@ static void xtables_restore_parse_line(struct nft_handle *h,
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);
+ "%s: line %u table name invalid",
+ xt_params->program_name, line);
state->curtable = nft_table_builtin_find(h, table);
if (!state->curtable)
xtables_error(PARAMETER_PROBLEM,
- "%s: line %u table name '%s' invalid\n",
- xt_params->program_name, line, table);
+ "%s: line %u table name '%s' invalid",
+ xt_params->program_name, line, table);
if (p->tablename && (strcmp(p->tablename, table) != 0))
return;
@@ -152,8 +152,8 @@ static void xtables_restore_parse_line(struct nft_handle *h,
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);
+ "%s: line %u chain name invalid",
+ xt_params->program_name, line);
xtables_announce_chain(chain);
assert_valid_chain_name(chain);
@@ -162,25 +162,23 @@ static void xtables_restore_parse_line(struct nft_handle *h,
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);
+ "%s: line %u policy invalid",
+ xt_params->program_name, line);
if (nft_chain_builtin_find(state->curtable, chain)) {
- if (counters) {
- char *ctrs;
- ctrs = strtok(NULL, " \t\n");
+ char *ctrs = strtok(NULL, " \t\n");
- if (!ctrs || !parse_counters(ctrs, &count))
- xtables_error(PARAMETER_PROBLEM,
- "invalid policy counters for chain '%s'\n",
- chain);
-
- }
+ if ((!ctrs && counters) ||
+ (ctrs && !parse_counters(ctrs, &count)))
+ xtables_error(PARAMETER_PROBLEM,
+ "invalid policy counters for chain '%s'",
+ chain);
if (cb->chain_set &&
cb->chain_set(h, state->curtable->name,
- chain, policy, &count) < 0) {
+ chain, policy,
+ counters ? &count : NULL) < 0) {
xtables_error(OTHER_PROBLEM,
- "Can't set policy `%s' on `%s' line %u: %s\n",
+ "Can't set policy `%s' on `%s' line %u: %s",
policy, chain, line,
strerror(errno));
}
@@ -189,13 +187,13 @@ static void xtables_restore_parse_line(struct nft_handle *h,
} else if (cb->chain_restore(h, chain, state->curtable->name) < 0 &&
errno != EEXIST) {
xtables_error(PARAMETER_PROBLEM,
- "cannot create chain '%s' (%s)\n",
+ "cannot create chain '%s' (%s)",
chain, strerror(errno));
} else if (h->family == NFPROTO_BRIDGE &&
!ebt_cmd_user_chain_policy(h, state->curtable->name,
chain, policy)) {
xtables_error(OTHER_PROBLEM,
- "Can't set policy `%s' on `%s' line %u: %s\n",
+ "Can't set policy `%s' on `%s' line %u: %s",
policy, chain, line,
strerror(errno));
}
@@ -249,8 +247,11 @@ static void xtables_restore_parse_line(struct nft_handle *h,
(strcmp(p->tablename, state->curtable->name) != 0))
return;
if (!ret) {
- fprintf(stderr, "%s: line %u failed\n",
- xt_params->program_name, line);
+ fprintf(stderr, "%s: line %u failed",
+ xt_params->program_name, h->error.lineno);
+ if (errno)
+ fprintf(stderr, ": %s.", nft_strerror(errno));
+ fprintf(stderr, "\n");
exit(1);
}
}
diff --git a/iptables/xtables-translate.8 b/iptables/xtables-translate.8
index 3dc72760..6fbbd617 100644
--- a/iptables/xtables-translate.8
+++ b/iptables/xtables-translate.8
@@ -28,24 +28,34 @@
iptables-translate \(em translation tool to migrate from iptables to nftables
.P
ip6tables-translate \(em translation tool to migrate from ip6tables to nftables
+.P
+ebtables-translate \(em translation tool to migrate from ebtables to nftables
+.P
+arptables-translate \(em translation tool to migrate from arptables to nftables
.SH DESCRIPTION
There is a set of tools to help the system administrator translate a given
-ruleset from \fBiptables(8)\fP and \fBip6tables(8)\fP to \fBnftables(8)\fP.
+ruleset from \fBiptables(8)\fP, \fBip6tables(8)\fP, \fBebtables(8)\fP and
+\fBarptables(8)\fP to \fBnftables(8)\fP.
The available commands are:
.IP \[bu] 2
-iptables-translate
+iptables\-translate
.IP \[bu]
-iptables-restore-translate
+iptables\-restore\-translate
.IP \[bu] 2
-ip6tables-translate
+ip6tables\-translate
.IP \[bu]
-ip6tables-restore-translate
+ip6tables\-restore\-translate
+.IP \[bu] 2
+ebtables\-translate
+.IP \[bu] 2
+arptables\-translate
.SH USAGE
-They take as input the original \fBiptables(8)\fP/\fBip6tables(8)\fP syntax and
-output the native \fBnftables(8)\fP syntax.
+They take as input the original
+\fBiptables(8)\fP/\fBip6tables(8)\fP/\fBebtables(8)\fP/\fBarptables(8)\fP
+syntax and output the native \fBnftables(8)\fP syntax.
The \fBiptables-restore-translate\fP tool reads a ruleset in the syntax
produced by \fBiptables-save(8)\fP. Likewise, the
@@ -63,38 +73,38 @@ Basic operation examples.
Single command translation:
.nf
-root@machine:~# iptables-translate -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
+root@machine:\(ti# iptables\-translate \-A INPUT \-p tcp \-\-dport 22 \-m conntrack \-\-ctstate NEW \-j ACCEPT
nft add rule ip filter INPUT tcp dport 22 ct state new counter accept
-root@machine:~# ip6tables-translate -A FORWARD -i eth0 -o eth3 -p udp -m multiport --dports 111,222 -j ACCEPT
+root@machine:\(ti# ip6tables\-translate \-A FORWARD \-i eth0 \-o eth3 \-p udp \-m multiport \-\-dports 111,222 \-j ACCEPT
nft add rule ip6 filter FORWARD iifname eth0 oifname eth3 meta l4proto udp udp dport { 111,222} counter accept
.fi
Whole ruleset translation:
.nf
-root@machine:~# iptables-save > save.txt
-root@machine:~# cat save.txt
-# Generated by iptables-save v1.6.0 on Sat Dec 24 14:26:40 2016
+root@machine:\(ti# iptables\-save > save.txt
+root@machine:\(ti# cat save.txt
+# Generated by iptables\-save v1.6.0 on Sat Dec 24 14:26:40 2016
*filter
:INPUT ACCEPT [5166:1752111]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [5058:628693]
--A FORWARD -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
+\-A FORWARD \-p tcp \-m tcp \-\-dport 22 \-m conntrack \-\-ctstate NEW \-j ACCEPT
COMMIT
# Completed on Sat Dec 24 14:26:40 2016
-root@machine:~# iptables-restore-translate -f save.txt
-# Translated by iptables-restore-translate v1.6.0 on Sat Dec 24 14:26:59 2016
+root@machine:\(ti# iptables\-restore\-translate \-f save.txt
+# Translated by iptables\-restore\-translate v1.6.0 on Sat Dec 24 14:26:59 2016
add table ip filter
add chain ip filter INPUT { type filter hook input priority 0; }
add chain ip filter FORWARD { type filter hook forward priority 0; }
add chain ip filter OUTPUT { type filter hook output priority 0; }
add rule ip filter FORWARD tcp dport 22 ct state new counter accept
-root@machine:~# iptables-restore-translate -f save.txt > ruleset.nft
-root@machine:~# nft -f ruleset.nft
-root@machine:~# nft list ruleset
+root@machine:\(ti# iptables\-restore\-translate \-f save.txt > ruleset.nft
+root@machine:\(ti# nft \-f ruleset.nft
+root@machine:\(ti# nft list ruleset
table ip filter {
chain INPUT {
type filter hook input priority 0; policy accept;
@@ -117,8 +127,7 @@ Some (few) extensions may be not supported (or fully-supported) for whatever
reason (for example, they were considered obsolete, or we didn't have the time
to work on them).
-There are no translations available for \fBebtables(8)\fP and
-\fBarptables(8)\fP.
+There is no translation available for \fBarptables(8)\fP.
To get up-to-date information about this, please head to
\fBhttps://wiki.nftables.org/\fP.
diff --git a/iptables/xtables-translate.c b/iptables/xtables-translate.c
index d1e87f16..8ebe523c 100644
--- a/iptables/xtables-translate.c
+++ b/iptables/xtables-translate.c
@@ -41,7 +41,9 @@ void xlate_ifname(struct xt_xlate *xl, const char *nftmeta, const char *ifname,
for (i = 0, j = 0; i < ifaclen + 1; i++, j++) {
switch (ifname[i]) {
case '*':
- iface[j++] = '\\';
+ /* asterisk is non-special mid-string */
+ if (i == ifaclen - 1)
+ iface[j++] = '\\';
/* fall through */
default:
iface[j] = ifname[i];
@@ -83,12 +85,10 @@ int xlate_action(const struct iptables_command_state *cs, bool goto_set,
else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
xt_xlate_add(xl, " return");
else if (cs->target->xlate) {
- xt_xlate_add(xl, " ");
struct xt_xlate_tg_params params = {
.ip = (const void *)&cs->fw,
.target = cs->target->t,
.numeric = numeric,
- .escape_quotes = !cs->restore,
};
ret = cs->target->xlate(xl, &params);
}
@@ -115,17 +115,12 @@ int xlate_matches(const struct iptables_command_state *cs, struct xt_xlate *xl)
.ip = (const void *)&cs->fw,
.match = matchp->match->m,
.numeric = numeric,
- .escape_quotes = !cs->restore,
};
if (!matchp->match->xlate)
return 0;
ret = matchp->match->xlate(xl, &params);
-
- if (strcmp(matchp->match->name, "comment") != 0)
- xt_xlate_add(xl, " ");
-
if (!ret)
break;
}
@@ -145,6 +140,7 @@ bool xlate_find_match(const struct iptables_command_state *cs, const char *p_nam
}
const char *family2str[] = {
+ [NFPROTO_ARP] = "arp",
[NFPROTO_IPV4] = "ip",
[NFPROTO_IPV6] = "ip6",
};
@@ -155,6 +151,7 @@ static int nft_rule_xlate_add(struct nft_handle *h,
bool append)
{
struct xt_xlate *xl = xt_xlate_alloc(10240);
+ const char *tick = cs->restore ? "" : "'";
const char *set;
int ret;
@@ -165,21 +162,22 @@ static int nft_rule_xlate_add(struct nft_handle *h,
set = xt_xlate_set_get(xl);
if (set[0]) {
- printf("add set %s %s %s\n", family2str[h->family], p->table,
- xt_xlate_set_get(xl));
+ printf("%sadd set %s %s %s%s\n",
+ tick, family2str[h->family], p->table,
+ xt_xlate_set_get(xl), tick);
if (!cs->restore && p->command != CMD_NONE)
printf("nft ");
}
- if (append) {
- printf("add rule %s %s %s ",
- family2str[h->family], p->table, p->chain);
- } else {
- printf("insert rule %s %s %s ",
- family2str[h->family], p->table, p->chain);
- }
- printf("%s\n", xt_xlate_rule_get(xl));
+ printf("%s%s rule %s %s %s ",
+ tick,
+ append ? "add" : "insert",
+ family2str[h->family], p->table, p->chain);
+ if (!append && p->rulenum > 1)
+ printf("index %d ", p->rulenum);
+
+ printf("%s%s\n", xt_xlate_rule_get(xl), tick);
err_out:
xt_xlate_free(xl);
@@ -199,6 +197,15 @@ static int xlate(struct nft_handle *h, struct xt_cmd_parse *p,
for (i = 0; i < args->s.naddrs; i++) {
switch (h->family) {
+ case NFPROTO_ARP:
+ cs->arp.arp.src.s_addr = args->s.addr.v4[i].s_addr;
+ cs->arp.arp.smsk.s_addr = args->s.mask.v4[i].s_addr;
+ for (j = 0; j < args->d.naddrs; j++) {
+ cs->arp.arp.tgt.s_addr = args->d.addr.v4[j].s_addr;
+ cs->arp.arp.tmsk.s_addr = args->d.mask.v4[j].s_addr;
+ ret = cb(h, p, cs, append);
+ }
+ break;
case AF_INET:
cs->fw.ip.src.s_addr = args->s.addr.v4[i].s_addr;
cs->fw.ip.smsk.s_addr = args->s.mask.v4[i].s_addr;
@@ -252,7 +259,6 @@ static int do_command_xlate(struct nft_handle *h, int argc, char *argv[],
.table = *table,
.restore = restore,
.line = line,
- .xlate = true,
.ops = &h->ops->cmd_parse,
};
struct iptables_command_state cs = {
@@ -341,19 +347,9 @@ static int do_command_xlate(struct nft_handle *h, int argc, char *argv[],
exit(1);
}
- nft_clear_iptables_command_state(&cs);
-
- 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);
- }
+ h->ops->clear_cs(&cs);
+
+ xtables_clear_args(&args);
xtables_free_opts(1);
return ret;
@@ -478,7 +474,24 @@ static int xtables_xlate_main_common(struct nft_handle *h,
xtables_globals.program_name = progname;
xtables_globals.compat_rev = dummy_compat_rev;
- ret = xtables_init_all(&xtables_globals, family);
+
+ switch (family) {
+ case NFPROTO_IPV4:
+ ret = xtables_init_all(&xtables_globals, family);
+ break;
+ case NFPROTO_IPV6:
+ ret = xtables_init_all(&xtables_globals, family);
+ break;
+ case NFPROTO_ARP:
+ arptables_globals.program_name = progname;
+ arptables_globals.compat_rev = dummy_compat_rev;
+ ret = xtables_init_all(&arptables_globals, family);
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
if (ret < 0) {
fprintf(stderr, "%s/%s Failed to initialize xtables\n",
xtables_globals.program_name,
@@ -593,6 +606,12 @@ static int xtables_restore_xlate_main(int family, const char *progname,
exit(0);
}
+int xtables_arp_xlate_main(int argc, char *argv[])
+{
+ return xtables_xlate_main(NFPROTO_ARP, "arptables-translate",
+ argc, argv);
+}
+
int xtables_ip4_xlate_main(int argc, char *argv[])
{
return xtables_xlate_main(NFPROTO_IPV4, "iptables-translate",
diff --git a/iptables/xtables.c b/iptables/xtables.c
index 70924176..5d73481c 100644
--- a/iptables/xtables.c
+++ b/iptables/xtables.c
@@ -262,12 +262,9 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
*table = p.table;
- nft_clear_iptables_command_state(&cs);
+ h->ops->clear_cs(&cs);
- free(args.s.addr.ptr);
- free(args.s.mask.ptr);
- free(args.d.addr.ptr);
- free(args.d.mask.ptr);
+ xtables_clear_args(&args);
xtables_free_opts(1);
return ret;