aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaciej Żenczykowski <maze@google.com>2023-10-23 11:10:01 -0700
committerMaciej Żenczykowski <maze@google.com>2023-10-23 11:10:41 -0700
commit6ca2997edebc1642a7c5849b1e6d9e7996941321 (patch)
tree23cc33747464a209477297192bf8ab73b10b53c9
parent9122b27635055a26c8a1d336bcf7382945dc576f (diff)
parent8ae55c2a331e932c0aeef8c6c138bf60deb9fd42 (diff)
downloadiptables-6ca2997edebc1642a7c5849b1e6d9e7996941321.tar.gz
Merge tag 'v1.8.10' of https://git.netfilter.org/iptables
iptables 1.8.10 release * tag 'v1.8.10' of https://git.netfilter.org/iptables: configure: Bump version for 1.8.10 release build: Bump dependency on libnftnl include: linux: Update kernel.h nft: Fix for useless meta expressions in rule tests: shell: Fix for ineffective 0007-mid-restore-flush_0 extensions: Fix checking of conntrack --ctproto 0 Revert --compat option related commits doc: fix example of xt_cpu tests: Test compat mode Add --compat option to *tables-nft and *-nft-restore commands nft: Introduce and use bool nft_handle::compat nft: Pass nft_handle to add_{target,action}() Use SOCK_CLOEXEC/O_CLOEXEC where available tests: shell: Test chain policy counter behaviour Revert "libiptc: fix wrong maptype of base chain counters on restore" nft: Create builtin chains with counters enabled tests: iptables-test: Fix command segfault reports nft-ruleparse: parse meta mark set as MARK target nft-ruleparse: Introduce nft_create_target() extensions: libip6t_icmp: Add names for mld-listener types nft: move processing logic out of asserts man: iptables-save.8: Start paragraphs in upper-case man: iptables-save.8: Fix --modprobe description man: iptables-save.8: Clarify 'available tables' man: Trivial: Missing space after comma man: iptables-restore.8: Start paragraphs in upper-case man: iptables-restore.8: Put 'file' in italics in synopsis man: iptables-restore.8: Drop -W option from synopsis man: iptables-restore.8: Consistently document -w option man: iptables-restore.8: Fix --modprobe description man: iptables.8: Trivial font fixes man: Use HTTPS for links to netfilter.org man: iptables.8: Clarify --goto description man: iptables.8: Fix intra page reference man: iptables.8: Trivial spelling fixes man: iptables.8: Extend exit code description tests: libipt_icmp.t: Enable tests with numeric output extensions: libipt_icmp: Fix confusion between 255/255 and any iptables-apply: Eliminate shellcheck warnings iptables-restore: Drop dead code tests: shell: Fix and extend chain rename test ebtables: Improve invalid chain name detection *tables: Reject invalid chain names when renaming *tables-restore: Enforce correct counters syntax if present nft: Include sets in debug output nft: Do not pass nft_rule_ctx to add_nft_among() nft: More verbose extension comparison debugging nft: Special casing for among match in compare_matches() tests: shell: Sanitize nft-only/0009-needless-bitwise_0 nft-bridge: pass context structure to ops->add() to improve anonymous set support iptables: Fix handling of non-existent chains iptables: Fix setting of ipv6 counters xshared: dissolve should_load_proto nft: use payload matching for layer 4 protocol man: string: document BM false negatives nft: check for source and destination address in first place nft: ruleparse: Create family-specific source files nft: Extract rule parsing callbacks from nft_family_ops nft: Introduce nft-ruleparse.{c,h} xshared: Fix parsing of option arguments in same word arptables: Don't omit standard matches if inverted arptables: Fix parsing of inverted 'arp operation' match nft-shared: Drop unused include utils: nfbpf_compile: Replace pcap_compile_nopcap() tests: shell: Test for false-positive rule check ebtables-nft: add broute table emulation include: update nf_tables uapi header build: use pkg-config for libpcap ip6tables: Fix checking existence of rule iptables-test.py: make explicit use of python3 iptables-nft: remove unused function argument iptables-nft: make builtin tables static xtables-eb: fix crash when opts isn't reallocated nft-restore: Fix for deletion of new, referenced rule include: Add missing linux/netfilter/xt_LOG.h xt_sctp: add the missing chunk types in sctp_help xtables-translate: Support insert with index ebtables: ip and ip6 matches depend on protocol match extensions: libebt_ip: Translation has to match on ether type extensions: libebt_ip: Do not use 'ip dscp' for translation extensions: libebt_redirect: Fix for wrong syntax in translation extensions: libebt_redirect: Fix target translation tests: xlate: Print file names even if specified tests: xlate: Properly split input in replay mode nft-shared: Simplify using nft_create_match() nft-shared: Use nft_create_match() in one more spot nft-shared: Lookup matches in iptables_command_state tests: CLUSTERIP: Drop test file tests: xlate: Support testing multiple individual files ebtables-translate: Print flush command after parsing is finished ebtables-translate: Ignore '-j CONTINUE' ebtables-translate: Use OPT_* from xshared.h ebtables-translate: Drop exec_style ebtables: Refuse unselected targets' options Proper fix for "unknown argument" error message etc: Drop xtables.conf Generated via: git fetch git://git.netfilter.org/iptables v1.8.10 git merge --log=999 FETCH_HEAD Test: with follow up Signed-off-by: Maciej Żenczykowski <maze@google.com> Change-Id: Ia4ce7e3670706ee0905228cfd147fa6499ca08bb
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac13
-rw-r--r--etc/xtables.conf74
-rw-r--r--extensions/libarpt_standard.t2
-rw-r--r--extensions/libebt_dnat.txlate12
-rw-r--r--extensions/libebt_ip.c25
-rw-r--r--extensions/libebt_ip.txlate8
-rw-r--r--extensions/libebt_log.c1
-rw-r--r--extensions/libebt_mark.txlate16
-rw-r--r--extensions/libebt_nflog.c1
-rw-r--r--extensions/libebt_redirect.c4
-rw-r--r--extensions/libebt_redirect.txlate8
-rw-r--r--extensions/libebt_snat.txlate8
-rw-r--r--extensions/libebt_standard.t4
-rw-r--r--extensions/libip6t_icmp6.t4
-rw-r--r--extensions/libip6t_standard.t3
-rw-r--r--extensions/libipt_CLUSTERIP.t4
-rw-r--r--extensions/libipt_icmp.c3
-rw-r--r--extensions/libipt_icmp.t11
-rw-r--r--extensions/libxt_conntrack.c17
-rw-r--r--extensions/libxt_conntrack.t2
-rw-r--r--extensions/libxt_cpu.man4
-rw-r--r--extensions/libxt_icmp.h7
-rw-r--r--extensions/libxt_nfacct.man2
-rw-r--r--extensions/libxt_sctp.c2
-rw-r--r--extensions/libxt_set.h8
-rw-r--r--extensions/libxt_standard.t5
-rw-r--r--extensions/libxt_string.man15
-rw-r--r--include/linux/const.h36
-rw-r--r--include/linux/kernel.h29
-rw-r--r--include/linux/netfilter/nf_tables.h59
-rw-r--r--include/linux/sysinfo.h25
-rw-r--r--include/xtables.h1
-rwxr-xr-xiptables-test.py11
-rw-r--r--iptables/Makefile.am3
-rw-r--r--iptables/ebtables-nft.842
-rw-r--r--iptables/ip6tables.c2
-rwxr-xr-xiptables/iptables-apply16
-rw-r--r--iptables/iptables-restore.8.in25
-rw-r--r--iptables/iptables-restore.c22
-rw-r--r--iptables/iptables-save.8.in18
-rw-r--r--iptables/iptables.8.in35
-rw-r--r--iptables/nft-arp.c155
-rw-r--r--iptables/nft-bridge.c498
-rw-r--r--iptables/nft-cache.c26
-rw-r--r--iptables/nft-cache.h2
-rw-r--r--iptables/nft-cmd.c8
-rw-r--r--iptables/nft-cmd.h2
-rw-r--r--iptables/nft-ipv4.c147
-rw-r--r--iptables/nft-ipv6.c123
-rw-r--r--iptables/nft-ruleparse-arp.c167
-rw-r--r--iptables/nft-ruleparse-bridge.c421
-rw-r--r--iptables/nft-ruleparse-ipv4.c134
-rw-r--r--iptables/nft-ruleparse-ipv6.c111
-rw-r--r--iptables/nft-ruleparse.c1194
-rw-r--r--iptables/nft-ruleparse.h137
-rw-r--r--iptables/nft-shared.c1252
-rw-r--r--iptables/nft-shared.h125
-rw-r--r--iptables/nft.c96
-rw-r--r--iptables/nft.h16
-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/0007counters_078
-rwxr-xr-xiptables/tests/shell/testcases/ebtables/0001-ebtables-basic_04
-rwxr-xr-xiptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_04
-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/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/nft-only/0007-mid-restore-flush_010
-rwxr-xr-xiptables/tests/shell/testcases/nft-only/0009-needless-bitwise_02
-rw-r--r--iptables/xshared.c92
-rw-r--r--iptables/xshared.h7
-rw-r--r--iptables/xtables-eb-translate.c61
-rw-r--r--iptables/xtables-eb.c84
-rw-r--r--iptables/xtables-multi.h1
-rw-r--r--iptables/xtables-restore.c18
-rw-r--r--iptables/xtables-translate.c2
-rw-r--r--libiptc/libiptc.c10
-rw-r--r--libxtables/xtables.c15
-rw-r--r--utils/Makefile.am6
-rw-r--r--utils/nfbpf_compile.c10
-rwxr-xr-xxlate-test.py25
86 files changed, 3133 insertions, 2635 deletions
diff --git a/Makefile.am b/Makefile.am
index 451c3cb2..299ab46d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -20,7 +20,7 @@ EXTRA_DIST = autogen.sh iptables-test.py xlate-test.py
if ENABLE_NFTABLES
confdir = $(sysconfdir)
-dist_conf_DATA = etc/ethertypes etc/xtables.conf
+dist_conf_DATA = etc/ethertypes
endif
.PHONY: tarball
diff --git a/configure.ac b/configure.ac
index bc2ed47b..d99fa3b9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
-AC_INIT([iptables], [1.8.9])
+AC_INIT([iptables], [1.8.10])
# See libtool.info "Libtool's versioning system"
libxtables_vcurrent=19
@@ -113,14 +113,15 @@ AM_CONDITIONAL([ENABLE_SYNCONF], [test "$enable_nfsynproxy" = "yes"])
AM_CONDITIONAL([ENABLE_NFTABLES], [test "$enable_nftables" = "yes"])
AM_CONDITIONAL([ENABLE_CONNLABEL], [test "$enable_connlabel" = "yes"])
-if test "x$enable_bpfc" = "xyes" || test "x$enable_nfsynproxy" = "xyes"; then
- AC_CHECK_LIB(pcap, pcap_compile,, AC_MSG_ERROR(missing libpcap library required by bpf compiler or nfsynproxy tool))
-fi
-
PKG_CHECK_MODULES([libnfnetlink], [libnfnetlink >= 1.0],
[nfnetlink=1], [nfnetlink=0])
AM_CONDITIONAL([HAVE_LIBNFNETLINK], [test "$nfnetlink" = 1])
+if test "x$enable_bpfc" = "xyes" || test "x$enable_nfsynproxy" = "xyes"; then
+ PKG_CHECK_MODULES([libpcap], [libpcap], [], [
+ AC_MSG_ERROR(missing libpcap library required by bpf compiler or nfsynproxy tool)])
+fi
+
if test "x$enable_nftables" = "xyes"; then
PKG_CHECK_MODULES([libmnl], [libmnl >= 1.0], [mnl=1], [mnl=0])
@@ -133,7 +134,7 @@ if test "x$enable_nftables" = "xyes"; then
exit 1
fi
- PKG_CHECK_MODULES([libnftnl], [libnftnl >= 1.1.6], [nftables=1], [nftables=0])
+ PKG_CHECK_MODULES([libnftnl], [libnftnl >= 1.2.6], [nftables=1], [nftables=0])
if test "$nftables" = 0;
then
diff --git a/etc/xtables.conf b/etc/xtables.conf
deleted file mode 100644
index 3c54ced0..00000000
--- a/etc/xtables.conf
+++ /dev/null
@@ -1,74 +0,0 @@
-family ipv4 {
- table raw {
- chain PREROUTING hook NF_INET_PRE_ROUTING prio -300
- chain OUTPUT hook NF_INET_LOCAL_OUT prio -300
- }
-
- table mangle {
- chain PREROUTING hook NF_INET_PRE_ROUTING prio -150
- chain INPUT hook NF_INET_LOCAL_IN prio -150
- chain FORWARD hook NF_INET_FORWARD prio -150
- chain OUTPUT hook NF_INET_LOCAL_OUT prio -150
- chain POSTROUTING hook NF_INET_POST_ROUTING prio -150
- }
-
- table filter {
- chain INPUT hook NF_INET_LOCAL_IN prio 0
- chain FORWARD hook NF_INET_FORWARD prio 0
- chain OUTPUT hook NF_INET_LOCAL_OUT prio 0
- }
-
- table nat {
- chain PREROUTING hook NF_INET_PRE_ROUTING prio -100
- chain INPUT hook NF_INET_LOCAL_IN prio 100
- chain OUTPUT hook NF_INET_LOCAL_OUT prio -100
- chain POSTROUTING hook NF_INET_POST_ROUTING prio 100
- }
-
- table security {
- chain INPUT hook NF_INET_LOCAL_IN prio 50
- chain FORWARD hook NF_INET_FORWARD prio 50
- chain OUTPUT hook NF_INET_LOCAL_OUT prio 50
- }
-}
-
-family ipv6 {
- table raw {
- chain PREROUTING hook NF_INET_PRE_ROUTING prio -300
- chain OUTPUT hook NF_INET_LOCAL_OUT prio -300
- }
-
- table mangle {
- chain PREROUTING hook NF_INET_PRE_ROUTING prio -150
- chain INPUT hook NF_INET_LOCAL_IN prio -150
- chain FORWARD hook NF_INET_FORWARD prio -150
- chain OUTPUT hook NF_INET_LOCAL_OUT prio -150
- chain POSTROUTING hook NF_INET_POST_ROUTING prio -150
- }
-
- table filter {
- chain INPUT hook NF_INET_LOCAL_IN prio 0
- chain FORWARD hook NF_INET_FORWARD prio 0
- chain OUTPUT hook NF_INET_LOCAL_OUT prio 0
- }
-
- table nat {
- chain PREROUTING hook NF_INET_PRE_ROUTING prio -100
- chain INPUT hook NF_INET_LOCAL_IN prio 100
- chain OUTPUT hook NF_INET_LOCAL_OUT prio -100
- chain POSTROUTING hook NF_INET_POST_ROUTING prio 100
- }
-
- table security {
- chain INPUT hook NF_INET_LOCAL_IN prio 50
- chain FORWARD hook NF_INET_FORWARD prio 50
- chain OUTPUT hook NF_INET_LOCAL_OUT prio 50
- }
-}
-
-family arp {
- table filter {
- chain INPUT hook NF_ARP_IN prio 0
- chain OUTPUT hook NF_ARP_OUT prio 0
- }
-}
diff --git a/extensions/libarpt_standard.t b/extensions/libarpt_standard.t
index e84a00b7..007fa2b8 100644
--- a/extensions/libarpt_standard.t
+++ b/extensions/libarpt_standard.t
@@ -12,3 +12,5 @@
-i lo --destination-mac 11:22:33:44:55:66;-i lo --dst-mac 11:22:33:44:55:66;OK
--source-mac Unicast;--src-mac 00:00:00:00:00:00/01:00:00:00:00:00;OK
! --src-mac Multicast;! --src-mac 01:00:00:00:00:00/01:00:00:00:00:00;OK
+--src-mac=01:02:03:04:05:06 --dst-mac=07:08:09:0A:0B:0C --h-length=6 --opcode=Request --h-type=Ethernet --proto-type=ipv4;--src-mac 01:02:03:04:05:06 --dst-mac 07:08:09:0a:0b:0c --opcode 1 --proto-type 0x800;OK
+--src-mac ! 01:02:03:04:05:06 --dst-mac ! 07:08:09:0A:0B:0C --h-length ! 6 --opcode ! Request --h-type ! Ethernet --proto-type ! ipv4;! --src-mac 01:02:03:04:05:06 ! --dst-mac 07:08:09:0a:0b:0c ! --h-length 6 ! --opcode 1 ! --h-type 1 ! --proto-type 0x800;OK
diff --git a/extensions/libebt_dnat.txlate b/extensions/libebt_dnat.txlate
index 9f305c76..531a22aa 100644
--- a/extensions/libebt_dnat.txlate
+++ b/extensions/libebt_dnat.txlate
@@ -1,8 +1,8 @@
-ebtables-translate -t nat -A PREROUTING -i someport --to-dst de:ad:00:be:ee:ff
-nft 'add rule bridge nat PREROUTING iifname "someport" ether daddr set de:ad:0:be:ee:ff accept counter'
+ebtables-translate -t nat -A PREROUTING -i someport -j dnat --to-dst de:ad:00:be:ee:ff
+nft 'add rule bridge nat PREROUTING iifname "someport" counter ether daddr set de:ad:0:be:ee:ff accept'
-ebtables-translate -t nat -A PREROUTING -i someport --to-dst de:ad:00:be:ee:ff --dnat-target ACCEPT
-nft 'add rule bridge nat PREROUTING iifname "someport" ether daddr set de:ad:0:be:ee:ff accept counter'
+ebtables-translate -t nat -A PREROUTING -i someport -j dnat --to-dst de:ad:00:be:ee:ff --dnat-target ACCEPT
+nft 'add rule bridge nat PREROUTING iifname "someport" counter ether daddr set de:ad:0:be:ee:ff accept'
-ebtables-translate -t nat -A PREROUTING -i someport --to-dst de:ad:00:be:ee:ff --dnat-target CONTINUE
-nft 'add rule bridge nat PREROUTING iifname "someport" ether daddr set de:ad:0:be:ee:ff continue counter'
+ebtables-translate -t nat -A PREROUTING -i someport -j dnat --to-dst de:ad:00:be:ee:ff --dnat-target CONTINUE
+nft 'add rule bridge nat PREROUTING iifname "someport" counter ether daddr set de:ad:0:be:ee:ff continue'
diff --git a/extensions/libebt_ip.c b/extensions/libebt_ip.c
index fd87dae7..68f34bff 100644
--- a/extensions/libebt_ip.c
+++ b/extensions/libebt_ip.c
@@ -432,6 +432,24 @@ static void brip_xlate_nh(struct xt_xlate *xl,
xtables_ipmask_to_numeric(maskp));
}
+static bool may_skip_ether_type_dep(uint8_t flags)
+{
+ /* these convert to "ip (s|d)addr" matches */
+ if (flags & (EBT_IP_SOURCE | EBT_IP_DEST))
+ return true;
+
+ /* icmp match triggers implicit ether type dependency in nft */
+ if (flags & EBT_IP_ICMP)
+ return true;
+
+ /* allow if "ip protocol" match is created by brip_xlate() */
+ if (flags & EBT_IP_PROTO &&
+ !(flags & (EBT_IP_SPORT | EBT_IP_DPORT | EBT_IP_ICMP)))
+ return true;
+
+ return false;
+}
+
static int brip_xlate(struct xt_xlate *xl,
const struct xt_xlate_mt_params *params)
{
@@ -441,11 +459,14 @@ static int brip_xlate(struct xt_xlate *xl,
brip_xlate_nh(xl, info, EBT_IP_SOURCE);
brip_xlate_nh(xl, info, EBT_IP_DEST);
+ if (!may_skip_ether_type_dep(info->bitmask))
+ xt_xlate_add(xl, "ether type ip ");
+
if (info->bitmask & EBT_IP_TOS) {
- xt_xlate_add(xl, "ip dscp ");
+ xt_xlate_add(xl, "@nh,8,8 ");
if (info->invflags & EBT_IP_TOS)
xt_xlate_add(xl, "!= ");
- xt_xlate_add(xl, "0x%02x ", info->tos & 0x3f); /* remove ECN bits */
+ xt_xlate_add(xl, "0x%02x ", info->tos);
}
if (info->bitmask & EBT_IP_PROTO) {
struct protoent *pe;
diff --git a/extensions/libebt_ip.txlate b/extensions/libebt_ip.txlate
index 75c1db24..44ce9276 100644
--- a/extensions/libebt_ip.txlate
+++ b/extensions/libebt_ip.txlate
@@ -4,14 +4,14 @@ nft 'add rule bridge filter FORWARD ip saddr != 192.168.0.0/24 counter accept'
ebtables-translate -I FORWARD -p ip --ip-dst 10.0.0.1
nft 'insert rule bridge filter FORWARD ip daddr 10.0.0.1 counter'
-ebtables-translate -I OUTPUT 3 -p ip -o eth0 --ip-tos 0xff
-nft 'insert rule bridge filter OUTPUT oifname "eth0" ip dscp 0x3f counter'
+ebtables-translate -I OUTPUT -p ip -o eth0 --ip-tos 0xff
+nft 'insert rule bridge filter OUTPUT oifname "eth0" ether type ip @nh,8,8 0xff counter'
ebtables-translate -A FORWARD -p ip --ip-proto tcp --ip-dport 22
-nft 'add rule bridge filter FORWARD tcp dport 22 counter'
+nft 'add rule bridge filter FORWARD ether type ip tcp dport 22 counter'
ebtables-translate -A FORWARD -p ip --ip-proto udp --ip-sport 1024:65535
-nft 'add rule bridge filter FORWARD udp sport 1024-65535 counter'
+nft 'add rule bridge filter FORWARD ether type ip udp sport 1024-65535 counter'
ebtables-translate -A FORWARD -p ip --ip-proto 253
nft 'add rule bridge filter FORWARD ip protocol 253 counter'
diff --git a/extensions/libebt_log.c b/extensions/libebt_log.c
index 04506219..9f8d1589 100644
--- a/extensions/libebt_log.c
+++ b/extensions/libebt_log.c
@@ -197,6 +197,7 @@ static int brlog_xlate(struct xt_xlate *xl,
static struct xtables_target brlog_target = {
.name = "log",
.revision = 0,
+ .ext_flags = XTABLES_EXT_WATCHER,
.version = XTABLES_VERSION,
.family = NFPROTO_BRIDGE,
.size = XT_ALIGN(sizeof(struct ebt_log_info)),
diff --git a/extensions/libebt_mark.txlate b/extensions/libebt_mark.txlate
index d006e8ac..4ace1a1f 100644
--- a/extensions/libebt_mark.txlate
+++ b/extensions/libebt_mark.txlate
@@ -1,11 +1,11 @@
-ebtables-translate -A INPUT --mark-set 42
-nft 'add rule bridge filter INPUT meta mark set 0x2a accept counter'
+ebtables-translate -A INPUT -j mark --mark-set 42
+nft 'add rule bridge filter INPUT counter meta mark set 0x2a accept'
-ebtables-translate -A INPUT --mark-or 42 --mark-target RETURN
-nft 'add rule bridge filter INPUT meta mark set meta mark or 0x2a return counter'
+ebtables-translate -A INPUT -j mark --mark-or 42 --mark-target RETURN
+nft 'add rule bridge filter INPUT counter meta mark set meta mark or 0x2a return'
-ebtables-translate -A INPUT --mark-and 42 --mark-target ACCEPT
-nft 'add rule bridge filter INPUT meta mark set meta mark and 0x2a accept counter'
+ebtables-translate -A INPUT -j mark --mark-and 42 --mark-target ACCEPT
+nft 'add rule bridge filter INPUT counter meta mark set meta mark and 0x2a accept'
-ebtables-translate -A INPUT --mark-xor 42 --mark-target DROP
-nft 'add rule bridge filter INPUT meta mark set meta mark xor 0x2a drop counter'
+ebtables-translate -A INPUT -j mark --mark-xor 42 --mark-target DROP
+nft 'add rule bridge filter INPUT counter meta mark set meta mark xor 0x2a drop'
diff --git a/extensions/libebt_nflog.c b/extensions/libebt_nflog.c
index 115e15da..762d6d5d 100644
--- a/extensions/libebt_nflog.c
+++ b/extensions/libebt_nflog.c
@@ -146,6 +146,7 @@ static int brnflog_xlate(struct xt_xlate *xl,
static struct xtables_target brnflog_watcher = {
.name = "nflog",
.revision = 0,
+ .ext_flags = XTABLES_EXT_WATCHER,
.version = XTABLES_VERSION,
.family = NFPROTO_BRIDGE,
.size = XT_ALIGN(sizeof(struct ebt_nflog_info)),
diff --git a/extensions/libebt_redirect.c b/extensions/libebt_redirect.c
index 4d4c7a02..7821935e 100644
--- a/extensions/libebt_redirect.c
+++ b/extensions/libebt_redirect.c
@@ -83,8 +83,8 @@ static int brredir_xlate(struct xt_xlate *xl,
{
const struct ebt_redirect_info *red = (const void*)params->target->data;
- xt_xlate_add(xl, "meta set pkttype host");
- if (red->target != EBT_ACCEPT)
+ xt_xlate_add(xl, "meta pkttype set host");
+ if (red->target != EBT_CONTINUE)
xt_xlate_add(xl, " %s ", brredir_verdict(red->target));
return 1;
}
diff --git a/extensions/libebt_redirect.txlate b/extensions/libebt_redirect.txlate
new file mode 100644
index 00000000..d073ec77
--- /dev/null
+++ b/extensions/libebt_redirect.txlate
@@ -0,0 +1,8 @@
+ebtables-translate -t nat -A PREROUTING -d de:ad:00:00:be:ef -j redirect
+nft 'add rule bridge nat PREROUTING ether daddr de:ad:00:00:be:ef counter meta pkttype set host accept'
+
+ebtables-translate -t nat -A PREROUTING -d de:ad:00:00:be:ef -j redirect --redirect-target RETURN
+nft 'add rule bridge nat PREROUTING ether daddr de:ad:00:00:be:ef counter meta pkttype set host return'
+
+ebtables-translate -t nat -A PREROUTING -d de:ad:00:00:be:ef -j redirect --redirect-target CONTINUE
+nft 'add rule bridge nat PREROUTING ether daddr de:ad:00:00:be:ef counter meta pkttype set host'
diff --git a/extensions/libebt_snat.txlate b/extensions/libebt_snat.txlate
index 857a6052..37343d3a 100644
--- a/extensions/libebt_snat.txlate
+++ b/extensions/libebt_snat.txlate
@@ -1,5 +1,5 @@
-ebtables-translate -t nat -A POSTROUTING -s 0:0:0:0:0:0 -o someport+ --to-source de:ad:00:be:ee:ff
-nft 'add rule bridge nat POSTROUTING oifname "someport*" ether saddr 00:00:00:00:00:00 ether saddr set de:ad:0:be:ee:ff accept counter'
+ebtables-translate -t nat -A POSTROUTING -s 0:0:0:0:0:0 -o someport+ -j snat --to-source de:ad:00:be:ee:ff
+nft 'add rule bridge nat POSTROUTING oifname "someport*" ether saddr 00:00:00:00:00:00 counter ether saddr set de:ad:0:be:ee:ff accept'
-ebtables-translate -t nat -A POSTROUTING -o someport --to-src de:ad:00:be:ee:ff --snat-target CONTINUE
-nft 'add rule bridge nat POSTROUTING oifname "someport" ether saddr set de:ad:0:be:ee:ff continue counter'
+ebtables-translate -t nat -A POSTROUTING -o someport -j snat --to-src de:ad:00:be:ee:ff --snat-target CONTINUE
+nft 'add rule bridge nat POSTROUTING oifname "someport" counter ether saddr set de:ad:0:be:ee:ff continue'
diff --git a/extensions/libebt_standard.t b/extensions/libebt_standard.t
index 97cb3baa..370a12f4 100644
--- a/extensions/libebt_standard.t
+++ b/extensions/libebt_standard.t
@@ -14,6 +14,10 @@
-o foobar;=;FAIL
--logical-in br0;=;OK
--logical-out br1;=;FAIL
+-i + -d 00:0f:ee:d0:ba:be;-d 00:0f:ee:d0:ba:be;OK
+-i + -p ip;-p IPv4;OK
+--logical-in + -d 00:0f:ee:d0:ba:be;-d 00:0f:ee:d0:ba:be;OK
+--logical-in + -p ip;-p IPv4;OK
:FORWARD
-i foobar;=;OK
-o foobar;=;OK
diff --git a/extensions/libip6t_icmp6.t b/extensions/libip6t_icmp6.t
index 028cfc16..b9a4dcd3 100644
--- a/extensions/libip6t_icmp6.t
+++ b/extensions/libip6t_icmp6.t
@@ -4,3 +4,7 @@
-p ipv6-icmp -m icmp6 --icmpv6-type 2;=;OK
# cannot use option twice:
-p ipv6-icmp -m icmp6 --icmpv6-type no-route --icmpv6-type packet-too-big;;FAIL
+-p ipv6-icmp -m icmp6 --icmpv6-type mld-listener-query;-p ipv6-icmp -m icmp6 --icmpv6-type 130;OK
+-p ipv6-icmp -m icmp6 --icmpv6-type mld-listener-report;-p ipv6-icmp -m icmp6 --icmpv6-type 131;OK
+-p ipv6-icmp -m icmp6 --icmpv6-type mld-listener-done;-p ipv6-icmp -m icmp6 --icmpv6-type 132;OK
+-p ipv6-icmp -m icmp6 --icmpv6-type mld-listener-reduction;-p ipv6-icmp -m icmp6 --icmpv6-type 132;OK
diff --git a/extensions/libip6t_standard.t b/extensions/libip6t_standard.t
index a528af10..0c559cc5 100644
--- a/extensions/libip6t_standard.t
+++ b/extensions/libip6t_standard.t
@@ -3,3 +3,6 @@
! -d ::;! -d ::/128;OK
! -s ::;! -s ::/128;OK
-s ::/64;=;OK
+:INPUT
+-i + -d c0::fe;-d c0::fe/128;OK
+-i + -p tcp;-p tcp;OK
diff --git a/extensions/libipt_CLUSTERIP.t b/extensions/libipt_CLUSTERIP.t
deleted file mode 100644
index 30b80167..00000000
--- a/extensions/libipt_CLUSTERIP.t
+++ /dev/null
@@ -1,4 +0,0 @@
-:INPUT
--d 10.31.3.236/32 -i lo -j CLUSTERIP --new --hashmode sourceip --clustermac 01:AA:7B:47:F7:D7 --total-nodes 2 --local-node 0 --hash-init 1;=;FAIL
--d 10.31.3.236/32 -i lo -j CLUSTERIP --new --hashmode sourceip --clustermac 01:AA:7B:47:F7:D7 --total-nodes 2 --local-node 1 --hash-init 1;=;OK;LEGACY
--d 10.31.3.236/32 -i lo -j CLUSTERIP --new --hashmode sourceip --clustermac 01:AA:7B:47:F7:D7 --total-nodes 2 --local-node 2 --hash-init 1;=;OK;LEGACY
diff --git a/extensions/libipt_icmp.c b/extensions/libipt_icmp.c
index b0318aeb..171b3b39 100644
--- a/extensions/libipt_icmp.c
+++ b/extensions/libipt_icmp.c
@@ -108,7 +108,8 @@ static void icmp_save(const void *ip, const struct xt_entry_match *match)
printf(" !");
/* special hack for 'any' case */
- if (icmp->type == 0xFF) {
+ if (icmp->type == 0xFF &&
+ icmp->code[0] == 0 && icmp->code[1] == 0xFF) {
printf(" --icmp-type any");
} else {
printf(" --icmp-type %u", icmp->type);
diff --git a/extensions/libipt_icmp.t b/extensions/libipt_icmp.t
index f4ba65c2..4ea93621 100644
--- a/extensions/libipt_icmp.t
+++ b/extensions/libipt_icmp.t
@@ -1,11 +1,8 @@
:INPUT,FORWARD,OUTPUT
-p icmp -m icmp --icmp-type any;=;OK
-# output uses the number, better use the name?
-# ERROR: cannot find: iptables -I INPUT -p icmp -m icmp --icmp-type echo-reply
-# -p icmp -m icmp --icmp-type echo-reply;=;OK
-# output uses the number, better use the name?
-# ERROR: annot find: iptables -I INPUT -p icmp -m icmp --icmp-type destination-unreachable
-# -p icmp -m icmp --icmp-type destination-unreachable;=;OK
+# XXX: output uses the number, better use the name?
+-p icmp -m icmp --icmp-type echo-reply;-p icmp -m icmp --icmp-type 0;OK
+-p icmp -m icmp --icmp-type destination-unreachable;-p icmp -m icmp --icmp-type 3;OK
# it does not acccept name/name, should we accept this?
# ERROR: cannot load: iptables -A INPUT -p icmp -m icmp --icmp-type destination-unreachable/network-unreachable
# -p icmp -m icmp --icmp-type destination-unreachable/network-unreachable;=;OK
@@ -13,3 +10,5 @@
# we accept "iptables -I INPUT -p tcp -m tcp", why not this below?
# ERROR: cannot load: iptables -A INPUT -p icmp -m icmp
# -p icmp -m icmp;=;OK
+-p icmp -m icmp --icmp-type 255/255;=;OK
+-p icmp -m icmp --icmp-type 255/0:255;-p icmp -m icmp --icmp-type any;OK
diff --git a/extensions/libxt_conntrack.c b/extensions/libxt_conntrack.c
index 09548c29..ffbc7467 100644
--- a/extensions/libxt_conntrack.c
+++ b/extensions/libxt_conntrack.c
@@ -346,14 +346,13 @@ static void conntrack_parse(struct xt_option_call *cb)
sinfo->invflags |= XT_CONNTRACK_STATE;
break;
case O_CTPROTO:
+ if (cb->val.protocol == 0)
+ xtables_error(PARAMETER_PROBLEM, cb->invert ?
+ "condition would always match protocol" :
+ "rule would never match protocol");
sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = cb->val.protocol;
if (cb->invert)
sinfo->invflags |= XT_CONNTRACK_PROTO;
- if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
- && (sinfo->invflags & XT_INV_PROTO))
- xtables_error(PARAMETER_PROBLEM,
- "rule would never match protocol");
-
sinfo->flags |= XT_CONNTRACK_PROTO;
break;
case O_CTORIGSRC:
@@ -411,11 +410,11 @@ static void conntrack_mt_parse(struct xt_option_call *cb, uint8_t rev)
info->invert_flags |= XT_CONNTRACK_STATE;
break;
case O_CTPROTO:
+ if (cb->val.protocol == 0)
+ xtables_error(PARAMETER_PROBLEM, cb->invert ?
+ "conntrack: condition would always match protocol" :
+ "conntrack: rule would never match protocol");
info->l4proto = cb->val.protocol;
- if (info->l4proto == 0 && (info->invert_flags & XT_INV_PROTO))
- xtables_error(PARAMETER_PROBLEM, "conntrack: rule would "
- "never match protocol");
-
info->match_flags |= XT_CONNTRACK_PROTO;
if (cb->invert)
info->invert_flags |= XT_CONNTRACK_PROTO;
diff --git a/extensions/libxt_conntrack.t b/extensions/libxt_conntrack.t
index db531475..2b3c5de9 100644
--- a/extensions/libxt_conntrack.t
+++ b/extensions/libxt_conntrack.t
@@ -25,3 +25,5 @@
-m conntrack --ctstatus EXPECTED;=;OK
-m conntrack --ctstatus SEEN_REPLY;=;OK
-m conntrack;;FAIL
+-m conntrack --ctproto 0;;FAIL
+-m conntrack ! --ctproto 0;;FAIL
diff --git a/extensions/libxt_cpu.man b/extensions/libxt_cpu.man
index d9ea5c2f..c89ef08a 100644
--- a/extensions/libxt_cpu.man
+++ b/extensions/libxt_cpu.man
@@ -7,9 +7,9 @@ multiqueue NICs to spread network traffic on different queues.
Example:
.PP
iptables \-t nat \-A PREROUTING \-p tcp \-\-dport 80 \-m cpu \-\-cpu 0
-\-j REDIRECT \-\-to\-port 8080
+\-j REDIRECT \-\-to\-ports 8080
.PP
iptables \-t nat \-A PREROUTING \-p tcp \-\-dport 80 \-m cpu \-\-cpu 1
-\-j REDIRECT \-\-to\-port 8081
+\-j REDIRECT \-\-to\-ports 8081
.PP
Available since Linux 2.6.36.
diff --git a/extensions/libxt_icmp.h b/extensions/libxt_icmp.h
index a763e50c..7a45b4bd 100644
--- a/extensions/libxt_icmp.h
+++ b/extensions/libxt_icmp.h
@@ -83,6 +83,13 @@ static const struct xt_icmp_names {
{ "echo-reply", 129, 0, 0xFF },
/* Alias */ { "pong", 129, 0, 0xFF },
+ { "mld-listener-query", 130, 0, 0xFF },
+
+ { "mld-listener-report", 131, 0, 0xFF },
+
+ { "mld-listener-done", 132, 0, 0xFF },
+ /* Alias */ { "mld-listener-reduction", 132, 0, 0xFF },
+
{ "router-solicitation", 133, 0, 0xFF },
{ "router-advertisement", 134, 0, 0xFF },
diff --git a/extensions/libxt_nfacct.man b/extensions/libxt_nfacct.man
index b755f977..a818fedd 100644
--- a/extensions/libxt_nfacct.man
+++ b/extensions/libxt_nfacct.man
@@ -26,5 +26,5 @@ nfacct get http\-traffic
.PP
You can obtain
.B nfacct(8)
-from http://www.netfilter.org or, alternatively, from the git.netfilter.org
+from https://www.netfilter.org or, alternatively, from the git.netfilter.org
repository.
diff --git a/extensions/libxt_sctp.c b/extensions/libxt_sctp.c
index fe5f5621..6e2b2745 100644
--- a/extensions/libxt_sctp.c
+++ b/extensions/libxt_sctp.c
@@ -50,7 +50,7 @@ static void sctp_help(void)
" --dport ...\n"
"[!] --chunk-types (all|any|none) (chunktype[:flags])+ match if all, any or none of\n"
" chunktypes are present\n"
-"chunktypes - DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK FORWARD_TSN ALL NONE\n");
+"chunktypes - DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE I_DATA RE_CONFIG PAD ASCONF ASCONF_ACK FORWARD_TSN I_FORWARD_TSN ALL NONE\n");
}
static const struct option sctp_opts[] = {
diff --git a/extensions/libxt_set.h b/extensions/libxt_set.h
index 597bf7eb..685bfab9 100644
--- a/extensions/libxt_set.h
+++ b/extensions/libxt_set.h
@@ -10,7 +10,7 @@
static int
get_version(unsigned *version)
{
- int res, sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+ int res, sockfd = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
struct ip_set_req_version req_version;
socklen_t size = sizeof(req_version);
@@ -18,12 +18,6 @@ get_version(unsigned *version)
xtables_error(OTHER_PROBLEM,
"Can't open socket to ipset.\n");
- if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) == -1) {
- xtables_error(OTHER_PROBLEM,
- "Could not set close on exec: %s\n",
- strerror(errno));
- }
-
req_version.op = IP_SET_OP_VERSION;
res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req_version, &size);
if (res != 0)
diff --git a/extensions/libxt_standard.t b/extensions/libxt_standard.t
index 56d6da2e..7c83cfa3 100644
--- a/extensions/libxt_standard.t
+++ b/extensions/libxt_standard.t
@@ -21,3 +21,8 @@
-s 10.11.12.13/255.128.0.0;-s 10.0.0.0/9;OK
-s 10.11.12.13/255.0.255.0;-s 10.0.12.0/255.0.255.0;OK
-s 10.11.12.13/255.0.12.0;-s 10.0.12.0/255.0.12.0;OK
+:FORWARD
+--protocol=tcp --source=1.2.3.4 --destination=5.6.7.8/32 --in-interface=eth0 --out-interface=eth1 --jump=ACCEPT;-s 1.2.3.4/32 -d 5.6.7.8/32 -i eth0 -o eth1 -p tcp -j ACCEPT;OK
+-ptcp -s1.2.3.4 -d5.6.7.8/32 -ieth0 -oeth1 -jACCEPT;-s 1.2.3.4/32 -d 5.6.7.8/32 -i eth0 -o eth1 -p tcp -j ACCEPT;OK
+-i + -d 1.2.3.4;-d 1.2.3.4/32;OK
+-i + -p tcp;-p tcp;OK
diff --git a/extensions/libxt_string.man b/extensions/libxt_string.man
index 5f1a993c..2a470ece 100644
--- a/extensions/libxt_string.man
+++ b/extensions/libxt_string.man
@@ -29,3 +29,18 @@ iptables \-A INPUT \-p tcp \-\-dport 80 \-m string \-\-algo bm \-\-string 'GET /
# The hex string pattern can be used for non-printable characters, like |0D 0A| or |0D0A|.
.br
iptables \-p udp \-\-dport 53 \-m string \-\-algo bm \-\-from 40 \-\-to 57 \-\-hex\-string '|03|www|09|netfilter|03|org|00|'
+.P
+Note: Since Boyer-Moore (BM) performs searches for matches from right to left and
+the kernel may store a packet in multiple discontiguous blocks, it's possible
+that a match could be spread over multiple blocks, in which case this algorithm
+won't find it.
+.P
+If you wish to ensure that such thing won't ever happen, use the
+Knuth-Pratt-Morris (KMP) algorithm instead. In conclusion, choose the proper
+string search algorithm depending on your use-case.
+.P
+For example, if you're using the module for filtering, NIDS or any similar
+security-focused purpose, then choose KMP. On the other hand, if you really care
+about performance \(em for example, you're classifying packets to apply Quality
+of Service (QoS) policies \(em and you don't mind about missing possible matches
+spread over multiple fragments, then choose BM.
diff --git a/include/linux/const.h b/include/linux/const.h
new file mode 100644
index 00000000..1eb84b50
--- /dev/null
+++ b/include/linux/const.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* const.h: Macros for dealing with constants. */
+
+#ifndef _LINUX_CONST_H
+#define _LINUX_CONST_H
+
+/* Some constant macros are used in both assembler and
+ * C code. Therefore we cannot annotate them always with
+ * 'UL' and other type specifiers unilaterally. We
+ * use the following macros to deal with this.
+ *
+ * Similarly, _AT() will cast an expression with a type in C, but
+ * leave it unchanged in asm.
+ */
+
+#ifdef __ASSEMBLY__
+#define _AC(X,Y) X
+#define _AT(T,X) X
+#else
+#define __AC(X,Y) (X##Y)
+#define _AC(X,Y) __AC(X,Y)
+#define _AT(T,X) ((T)(X))
+#endif
+
+#define _UL(x) (_AC(x, UL))
+#define _ULL(x) (_AC(x, ULL))
+
+#define _BITUL(x) (_UL(1) << (x))
+#define _BITULL(x) (_ULL(1) << (x))
+
+#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (__typeof__(x))(a) - 1)
+#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
+
+#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+
+#endif /* _LINUX_CONST_H */
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index d4c59f65..5413a8c5 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -1,29 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _LINUX_KERNEL_H
#define _LINUX_KERNEL_H
-/*
- * 'kernel.h' contains some often-used function prototypes etc
- */
-#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
-#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
+#include <linux/sysinfo.h>
+#include <linux/const.h>
-
-#define SI_LOAD_SHIFT 16
-struct sysinfo {
- long uptime; /* Seconds since boot */
- unsigned long loads[3]; /* 1, 5, and 15 minute load averages */
- unsigned long totalram; /* Total usable main memory size */
- unsigned long freeram; /* Available memory size */
- unsigned long sharedram; /* Amount of shared memory */
- unsigned long bufferram; /* Memory used by buffers */
- unsigned long totalswap; /* Total swap space size */
- unsigned long freeswap; /* swap space still available */
- unsigned short procs; /* Number of current processes */
- unsigned short pad; /* explicit padding for m68k */
- unsigned long totalhigh; /* Total high memory size */
- unsigned long freehigh; /* Available high memory size */
- unsigned int mem_unit; /* Memory unit size in bytes */
- char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding: libc5 uses this.. */
-};
-
-#endif
+#endif /* _LINUX_KERNEL_H */
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index e94d1fa5..c4d4d8e4 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -97,6 +97,14 @@ enum nft_verdicts {
* @NFT_MSG_NEWFLOWTABLE: add new flow table (enum nft_flowtable_attributes)
* @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes)
* @NFT_MSG_DELFLOWTABLE: delete flow table (enum nft_flowtable_attributes)
+ * @NFT_MSG_GETRULE_RESET: get rules and reset stateful expressions (enum nft_obj_attributes)
+ * @NFT_MSG_DESTROYTABLE: destroy a table (enum nft_table_attributes)
+ * @NFT_MSG_DESTROYCHAIN: destroy a chain (enum nft_chain_attributes)
+ * @NFT_MSG_DESTROYRULE: destroy a rule (enum nft_rule_attributes)
+ * @NFT_MSG_DESTROYSET: destroy a set (enum nft_set_attributes)
+ * @NFT_MSG_DESTROYSETELEM: destroy a set element (enum nft_set_elem_attributes)
+ * @NFT_MSG_DESTROYOBJ: destroy a stateful object (enum nft_object_attributes)
+ * @NFT_MSG_DESTROYFLOWTABLE: destroy flow table (enum nft_flowtable_attributes)
*/
enum nf_tables_msg_types {
NFT_MSG_NEWTABLE,
@@ -124,6 +132,14 @@ enum nf_tables_msg_types {
NFT_MSG_NEWFLOWTABLE,
NFT_MSG_GETFLOWTABLE,
NFT_MSG_DELFLOWTABLE,
+ NFT_MSG_GETRULE_RESET,
+ NFT_MSG_DESTROYTABLE,
+ NFT_MSG_DESTROYCHAIN,
+ NFT_MSG_DESTROYRULE,
+ NFT_MSG_DESTROYSET,
+ NFT_MSG_DESTROYSETELEM,
+ NFT_MSG_DESTROYOBJ,
+ NFT_MSG_DESTROYFLOWTABLE,
NFT_MSG_MAX,
};
@@ -669,7 +685,7 @@ enum nft_range_ops {
* enum nft_range_attributes - nf_tables range expression netlink attributes
*
* @NFTA_RANGE_SREG: source register of data to compare (NLA_U32: nft_registers)
- * @NFTA_RANGE_OP: cmp operation (NLA_U32: nft_cmp_ops)
+ * @NFTA_RANGE_OP: cmp operation (NLA_U32: nft_range_ops)
* @NFTA_RANGE_FROM_DATA: data range from (NLA_NESTED: nft_data_attributes)
* @NFTA_RANGE_TO_DATA: data range to (NLA_NESTED: nft_data_attributes)
*/
@@ -753,11 +769,14 @@ enum nft_dynset_attributes {
* @NFT_PAYLOAD_LL_HEADER: link layer header
* @NFT_PAYLOAD_NETWORK_HEADER: network header
* @NFT_PAYLOAD_TRANSPORT_HEADER: transport header
+ * @NFT_PAYLOAD_INNER_HEADER: inner header / payload
*/
enum nft_payload_bases {
NFT_PAYLOAD_LL_HEADER,
NFT_PAYLOAD_NETWORK_HEADER,
NFT_PAYLOAD_TRANSPORT_HEADER,
+ NFT_PAYLOAD_INNER_HEADER,
+ NFT_PAYLOAD_TUN_HEADER,
};
/**
@@ -777,6 +796,32 @@ enum nft_payload_csum_flags {
NFT_PAYLOAD_L4CSUM_PSEUDOHDR = (1 << 0),
};
+enum nft_inner_type {
+ NFT_INNER_UNSPEC = 0,
+ NFT_INNER_VXLAN,
+ NFT_INNER_GENEVE,
+};
+
+enum nft_inner_flags {
+ NFT_INNER_HDRSIZE = (1 << 0),
+ NFT_INNER_LL = (1 << 1),
+ NFT_INNER_NH = (1 << 2),
+ NFT_INNER_TH = (1 << 3),
+};
+#define NFT_INNER_MASK (NFT_INNER_HDRSIZE | NFT_INNER_LL | \
+ NFT_INNER_NH | NFT_INNER_TH)
+
+enum nft_inner_attributes {
+ NFTA_INNER_UNSPEC,
+ NFTA_INNER_NUM,
+ NFTA_INNER_TYPE,
+ NFTA_INNER_FLAGS,
+ NFTA_INNER_HDRSIZE,
+ NFTA_INNER_EXPR,
+ __NFTA_INNER_MAX
+};
+#define NFTA_INNER_MAX (__NFTA_INNER_MAX - 1)
+
/**
* enum nft_payload_attributes - nf_tables payload expression netlink attributes
*
@@ -833,7 +878,7 @@ enum nft_exthdr_op {
* @NFTA_EXTHDR_LEN: extension header length (NLA_U32)
* @NFTA_EXTHDR_FLAGS: extension header flags (NLA_U32)
* @NFTA_EXTHDR_OP: option match type (NLA_U32)
- * @NFTA_EXTHDR_SREG: option match type (NLA_U32)
+ * @NFTA_EXTHDR_SREG: source register (NLA_U32: nft_registers)
*/
enum nft_exthdr_attributes {
NFTA_EXTHDR_UNSPEC,
@@ -886,6 +931,7 @@ enum nft_exthdr_attributes {
* @NFT_META_TIME_HOUR: hour of day (in seconds)
* @NFT_META_SDIF: slave device interface index
* @NFT_META_SDIFNAME: slave device interface name
+ * @NFT_META_BRI_BROUTE: packet br_netfilter_broute bit
*/
enum nft_meta_keys {
NFT_META_LEN,
@@ -896,7 +942,8 @@ enum nft_meta_keys {
NFT_META_OIF,
NFT_META_IIFNAME,
NFT_META_OIFNAME,
- NFT_META_IIFTYPE,
+ NFT_META_IFTYPE,
+#define NFT_META_IIFTYPE NFT_META_IFTYPE
NFT_META_OIFTYPE,
NFT_META_SKUID,
NFT_META_SKGID,
@@ -923,6 +970,8 @@ enum nft_meta_keys {
NFT_META_TIME_HOUR,
NFT_META_SDIF,
NFT_META_SDIFNAME,
+ NFT_META_BRI_BROUTE,
+ __NFT_META_IIFTYPE,
};
/**
@@ -1213,10 +1262,10 @@ enum nft_last_attributes {
/**
* enum nft_log_attributes - nf_tables log expression netlink attributes
*
- * @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U32)
+ * @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U16)
* @NFTA_LOG_PREFIX: prefix to prepend to log messages (NLA_STRING)
* @NFTA_LOG_SNAPLEN: length of payload to include in netlink message (NLA_U32)
- * @NFTA_LOG_QTHRESHOLD: queue threshold (NLA_U32)
+ * @NFTA_LOG_QTHRESHOLD: queue threshold (NLA_U16)
* @NFTA_LOG_LEVEL: log level (NLA_U32)
* @NFTA_LOG_FLAGS: logging flags (NLA_U32)
*/
diff --git a/include/linux/sysinfo.h b/include/linux/sysinfo.h
new file mode 100644
index 00000000..435d5c23
--- /dev/null
+++ b/include/linux/sysinfo.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _LINUX_SYSINFO_H
+#define _LINUX_SYSINFO_H
+
+#include <linux/types.h>
+
+#define SI_LOAD_SHIFT 16
+struct sysinfo {
+ __kernel_long_t uptime; /* Seconds since boot */
+ __kernel_ulong_t loads[3]; /* 1, 5, and 15 minute load averages */
+ __kernel_ulong_t totalram; /* Total usable main memory size */
+ __kernel_ulong_t freeram; /* Available memory size */
+ __kernel_ulong_t sharedram; /* Amount of shared memory */
+ __kernel_ulong_t bufferram; /* Memory used by buffers */
+ __kernel_ulong_t totalswap; /* Total swap space size */
+ __kernel_ulong_t freeswap; /* swap space still available */
+ __u16 procs; /* Number of current processes */
+ __u16 pad; /* Explicit padding for m68k */
+ __kernel_ulong_t totalhigh; /* Total high memory size */
+ __kernel_ulong_t freehigh; /* Available high memory size */
+ __u32 mem_unit; /* Memory unit size in bytes */
+ char _f[20-2*sizeof(__kernel_ulong_t)-sizeof(__u32)]; /* Padding: libc5 uses this.. */
+};
+
+#endif /* _LINUX_SYSINFO_H */
diff --git a/include/xtables.h b/include/xtables.h
index 4ffc8ec5..087a1d60 100644
--- a/include/xtables.h
+++ b/include/xtables.h
@@ -203,6 +203,7 @@ struct xtables_lmap {
enum xtables_ext_flags {
XTABLES_EXT_ALIAS = 1 << 0,
+ XTABLES_EXT_WATCHER = 1 << 1,
};
struct xt_xlate;
diff --git a/iptables-test.py b/iptables-test.py
index de1e1e95..6f63cdbe 100755
--- a/iptables-test.py
+++ b/iptables-test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#
# (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
#
@@ -136,7 +136,7 @@ def run_test(iptables, rule, rule_save, res, filename, lineno, netns):
# check for segfaults
#
if proc.returncode == -11:
- reason = "iptables-save segfaults: " + cmd
+ reason = command + " segfaults!"
print_error(reason, filename, lineno)
delete_rule(iptables, rule, filename, lineno, netns)
return -1
@@ -333,8 +333,11 @@ def run_test_file_fast(iptables, filename, netns):
out, err = proc.communicate(input = restore_data)
if proc.returncode == -11:
- reason = iptables + "-restore segfaults: " + cmd
+ reason = iptables + "-restore segfaults!"
print_error(reason, filename, lineno)
+ msg = [iptables + "-restore segfault from:"]
+ msg.extend(["input: " + l for l in restore_data.split("\n")])
+ print("\n".join(msg), file=log_file)
return -1
if proc.returncode != 0:
@@ -355,7 +358,7 @@ def run_test_file_fast(iptables, filename, netns):
out, err = proc.communicate()
if proc.returncode == -11:
- reason = iptables + "-save segfaults: " + cmd
+ reason = iptables + "-save segfaults!"
print_error(reason, filename, lineno)
return -1
diff --git a/iptables/Makefile.am b/iptables/Makefile.am
index 1f37640f..8a722702 100644
--- a/iptables/Makefile.am
+++ b/iptables/Makefile.am
@@ -46,6 +46,9 @@ xtables_nft_multi_SOURCES += nft.c nft.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 \
diff --git a/iptables/ebtables-nft.8 b/iptables/ebtables-nft.8
index d75aae24..0304b508 100644
--- a/iptables/ebtables-nft.8
+++ b/iptables/ebtables-nft.8
@@ -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
@@ -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.c b/iptables/ip6tables.c
index 345af451..9afc32c1 100644
--- a/iptables/ip6tables.c
+++ b/iptables/ip6tables.c
@@ -331,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, sizeof(fw));
+ mask = make_delete_mask(matches, target, sizeof(*fw));
for (i = 0; i < nsaddrs; i++) {
fw->ipv6.src = saddrs[i];
fw->ipv6.smsk = smasks[i];
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-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 6f7ddf93..53029738 100644
--- a/iptables/iptables-restore.c
+++ b/iptables/iptables-restore.c
@@ -223,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) {
@@ -283,23 +281,21 @@ ip46tables_restore_main(const struct iptables_restore_cb *cb,
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'",
- 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",
policy, chain, line,
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.8.in b/iptables/iptables.8.in
index f81c632f..ecaa5553 100644
--- a/iptables/iptables.8.in
+++ b/iptables/iptables.8.in
@@ -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/nft-arp.c b/iptables/nft-arp.c
index 210f43d2..aed39ebd 100644
--- a/iptables/nft-arp.c
+++ b/iptables/nft-arp.c
@@ -40,8 +40,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,12 +49,12 @@ 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 ||
@@ -160,141 +160,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,
- 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.iniface_mask,
- fw->arp.outiface, fw->arp.outiface_mask,
- &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;
- 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;
- 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;
- }
-}
-
static void nft_arp_print_header(unsigned int format, const char *chain,
const char *pol,
const struct xt_counters *counters,
@@ -408,7 +273,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);
@@ -432,7 +298,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
@@ -708,7 +575,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,
@@ -783,19 +650,17 @@ 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,
},
.rule_to_cs = nft_rule_to_iptables_command_state,
.init_cs = nft_arp_init_cs,
.clear_cs = xtables_clear_iptables_command_state,
- .parse_target = nft_ipv46_parse_target,
.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 83cbe315..d9a8ad2b 100644
--- a/iptables/nft-bridge.c
+++ b/iptables/nft-bridge.c
@@ -65,54 +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);
-
- 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);
-}
+ expr = nftnl_expr_alloc("immediate");
+ if (expr == NULL)
+ return -1;
-static void add_logical_outiface(struct nft_handle *h, struct nftnl_rule *r,
- char *iface, uint32_t op)
-{
- int iface_len;
- uint8_t 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);
- iface_len = strlen(iface);
+ 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);
- 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);
+ nftnl_rule_add_expr(r, expr);
+ return 0;
}
static int _add_action(struct nftnl_rule *r, struct iptables_command_state *cs)
{
+ const char *table = nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
+
+ if (cs->target &&
+ table && strcmp(table, "broute") == 0) {
+ if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0) {
+ int ret = add_meta_broute(r);
+
+ if (ret)
+ return ret;
+
+ cs->jumpto = "ACCEPT";
+ }
+ }
+
return add_action(r, cs, false);
}
static int
nft_bridge_add_match(struct nft_handle *h, const struct ebt_entry *fw,
- struct nftnl_rule *r, struct xt_entry_match *m)
+ struct nft_rule_ctx *ctx, struct nftnl_rule *r,
+ struct xt_entry_match *m)
{
- if (!strcmp(m->u.user.name, "802_3") &&
- !(fw->bitmask & EBT_802_3))
+ 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");
- return add_match(h, r, m);
+ 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)
{
@@ -120,38 +134,38 @@ static int nft_bridge_add(struct nft_handle *h,
struct ebt_entry *fw = &cs->eb;
uint32_t 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);
+ }
+
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) {
@@ -174,7 +188,7 @@ static int nft_bridge_add(struct nft_handle *h,
for (iter = cs->match_list; iter; iter = iter->next) {
if (iter->ismatch) {
- if (nft_bridge_add_match(h, fw, 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))
@@ -188,390 +202,6 @@ 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,
- 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, NULL, oifname, NULL, &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->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");
-}
-
-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 bool nft_rule_to_ebtables_command_state(struct nft_handle *h,
const struct nftnl_rule *r,
struct iptables_command_state *cs)
@@ -945,11 +575,7 @@ 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,
.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-cache.c b/iptables/nft-cache.c
index 76e99adc..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;
@@ -147,7 +155,7 @@ static int fetch_table_cache(struct nft_handle *h)
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;
@@ -417,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;
@@ -425,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,
@@ -464,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;
}
@@ -504,7 +520,7 @@ static int __fetch_chain_cache(struct nft_handle *h,
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;
}
@@ -598,7 +614,7 @@ static int nft_rule_list_update(struct nft_chain *nc, void *data)
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 f16ea0e6..8a824586 100644
--- a/iptables/nft-cmd.c
+++ b/iptables/nft-cmd.c
@@ -14,12 +14,16 @@
#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;
@@ -33,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;
@@ -92,7 +96,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;
diff --git a/iptables/nft-cmd.h b/iptables/nft-cmd.h
index c0f84636..ae5908d8 100644
--- a/iptables/nft-cmd.h
+++ b/iptables/nft-cmd.h
@@ -37,7 +37,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);
diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c
index dcc4a8ed..75912847 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,108 +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 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_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.iniface_mask,
- cs->fw.ip.outiface, cs->fw.ip.outiface_mask,
- &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 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;
- }
-}
-
static void nft_ipv4_set_goto_flag(struct iptables_command_state *cs)
{
cs->fw.ip.flags |= IPT_F_GOTO;
@@ -370,7 +271,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);
@@ -443,18 +344,16 @@ 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,
},
- .parse_target = nft_ipv46_parse_target,
.rule_to_cs = nft_rule_to_iptables_command_state,
.clear_cs = xtables_clear_iptables_command_state,
.xlate = nft_ipv4_xlate,
diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c
index e9892185..5aef365b 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,85 +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,
- 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.iniface_mask, cs->fw6.ipv6.outiface,
- cs->fw6.ipv6.outiface_mask, &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;
- }
-}
-
static void nft_ipv6_set_goto_flag(struct iptables_command_state *cs)
{
cs->fw6.ipv6.flags |= IP6T_F_GOTO;
@@ -331,7 +254,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);
@@ -412,18 +335,16 @@ 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,
},
- .parse_target = nft_ipv46_parse_target,
.rule_to_cs = nft_rule_to_iptables_command_state,
.clear_cs = xtables_clear_iptables_command_state,
.xlate = nft_ipv6_xlate,
diff --git a/iptables/nft-ruleparse-arp.c b/iptables/nft-ruleparse-arp.c
new file mode 100644
index 00000000..d80ca922
--- /dev/null
+++ b/iptables/nft-ruleparse-arp.c
@@ -0,0 +1,167 @@
+/*
+ * (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.iniface_mask,
+ fw->arp.outiface, fw->arp.outiface_mask,
+ &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;
+ 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_ARPHLN;
+ 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..c6cc9af5
--- /dev/null
+++ b/iptables/nft-ruleparse-bridge.c
@@ -0,0 +1,421 @@
+/*
+ * (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, NULL, oifname, NULL, &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..491cbf42
--- /dev/null
+++ b/iptables/nft-ruleparse-ipv4.c
@@ -0,0 +1,134 @@
+/*
+ * (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.iniface_mask,
+ cs->fw.ip.outiface, cs->fw.ip.outiface_mask,
+ &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..7581b863
--- /dev/null
+++ b/iptables/nft-ruleparse-ipv6.c
@@ -0,0 +1,111 @@
+/*
+ * (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.iniface_mask, cs->fw6.ipv6.outiface,
+ cs->fw6.ipv6.outiface_mask, &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..c8322f93
--- /dev/null
+++ b/iptables/nft-ruleparse.c
@@ -0,0 +1,1194 @@
+/*
+ * (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, unsigned char *mask)
+{
+ if (len == 0)
+ return;
+
+ memcpy(dst, name, len);
+ if (name[len - 1] == '\0') {
+ if (mask)
+ memset(mask, 0xff, strlen(name) + 1);
+ return;
+ }
+
+ if (len >= IFNAMSIZ)
+ return;
+
+ /* wildcard */
+ dst[len++] = '+';
+ if (len >= IFNAMSIZ)
+ return;
+ dst[len++] = 0;
+ if (mask)
+ memset(mask, 0xff, len - 2);
+}
+
+static void parse_invalid_iface(char *iface, unsigned char *mask,
+ 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';
+ mask[0] = 0xff;
+ mask[1] = 0xff;
+ memset(mask + 2, 0, IFNAMSIZ - 2);
+}
+
+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, unsigned char *iniface_mask,
+ char *outiface, unsigned char *outiface_mask, uint8_t *invflags)
+{
+ uint32_t value;
+ const void *ifname;
+ uint32_t len;
+
+ switch(key) {
+ case NFT_META_IIF:
+ value = 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);
+ parse_invalid_iface(iniface, iniface_mask,
+ 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, outiface_mask);
+ parse_invalid_iface(outiface, outiface_mask,
+ 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..25ce05d2
--- /dev/null
+++ b/iptables/nft-ruleparse.h
@@ -0,0 +1,137 @@
+#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, unsigned char *iniface_mask, char *outiface,
+ unsigned char *outiface_mask, 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 4a7b5406..6775578b 100644
--- a/iptables/nft-shared.c
+++ b/iptables/nft-shared.c
@@ -21,15 +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 <linux/netfilter/xt_pkttype.h>
-
-#include <linux/netfilter_ipv6/ip6t_hl.h>
-
#include <libmnl/libmnl.h>
#include <libnftnl/rule.h>
#include <libnftnl/expr.h>
@@ -156,44 +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);
- else if (op != NFT_CMP_EQ)
- add_cmp_ptr(r, NFT_CMP_EQ, "INVAL/D",
- strlen("INVAL/D") + 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 if (op != NFT_CMP_EQ)
- add_cmp_ptr(r, NFT_CMP_EQ, "INVAL/D",
- strlen("INVAL/D") + 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,
@@ -277,230 +253,6 @@ 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, strlen(name) + 1);
- 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 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;
- 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;
- 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;
- struct xtables_match *match;
- uint8_t value;
-
- match = nft_create_match(ctx, ctx->cs, "pkttype");
- if (!match)
- return -1;
-
- pkttype = (void*)match->m->data;
-
- 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;
-}
-
-static void parse_invalid_iface(char *iface, unsigned char *mask,
- 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';
- mask[0] = 0xff;
- mask[1] = 0xff;
- memset(mask + 2, 0, IFNAMSIZ - 2);
-}
-
-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)
-{
- 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);
- parse_invalid_iface(iniface, iniface_mask,
- 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, outiface_mask);
- parse_invalid_iface(outiface, outiface_mask,
- 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;
-}
-
-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) {
- ctx->errmsg = "target extension not found";
- 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) {
- 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->parse_match != NULL)
- ctx->h->ops->parse_match(match, ctx->cs);
-}
-
void __get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, uint8_t *op)
{
uint32_t len;
@@ -517,898 +269,6 @@ void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv)
*inv = (op == NFT_CMP_NEQ);
}
-static void nft_meta_set_to_target(struct nft_xt_ctx *ctx,
- struct nftnl_expr *e)
-{
- struct xtables_target *target;
- struct nft_xt_ctx_reg *sreg;
- enum nft_registers sregnum;
- struct xt_entry_target *t;
- unsigned int size;
- const char *targname;
-
- 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 ((sreg->type != NFT_XT_REG_IMMEDIATE)) {
- ctx->errmsg = "meta nftrace but reg not immediate";
- return;
- }
-
- if (sreg->immediate.data[0] == 0) {
- ctx->errmsg = "trace is cleared";
- return;
- }
-
- targname = "TRACE";
- break;
- default:
- ctx->errmsg = "meta sreg key not supported";
- return;
- }
-
- target = xtables_find_target(targname, XTF_TRY_LOAD);
- if (target == NULL) {
- ctx->errmsg = "target TRACE not found";
- 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)
-{
- struct nft_xt_ctx_reg *reg;
-
- if (nftnl_expr_is_set(e, NFTNL_EXPR_META_SREG)) {
- nft_meta_set_to_target(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_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 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 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) {
- ctx->errmsg = "tcp match extension not found";
- 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 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_udp_match(ctx, cs);
-
- if (!udp)
- 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_tcp_match(ctx, cs);
-
- if (!tcp)
- 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_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 xtables_match *match;
- struct ipt_icmp icmp = {
- .type = UINT8_MAX,
- .code = { 0, UINT8_MAX },
- };
-
- 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:
- match = nft_create_match(ctx, cs, "icmp");
- break;
- case NFPROTO_IPV6:
- if (icmp.type == UINT8_MAX) {
- ctx->errmsg = "icmp6 code with any type match not supported";
- return;
- }
- match = nft_create_match(ctx, cs, "icmp6");
- break;
- default:
- ctx->errmsg = "unexpected family for icmp match";
- return;
- }
-
- if (!match) {
- ctx->errmsg = "icmp match extension not found";
- return;
- }
- memcpy(match->m->data, &icmp, sizeof(icmp));
- return;
-
-out_err_len:
- ctx->errmsg = "unexpected RHS data length";
-}
-
-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_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)
-{
- 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_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_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->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->parse_payload(ctx, sreg, e, ctx->cs);
- break;
- case NFT_PAYLOAD_NETWORK_HEADER:
- ctx->h->ops->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_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)) {
- 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:
- 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) {
- ctx->errmsg = "verdict extension not found";
- 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) {
- ctx->errmsg = "limit match extension not found";
- 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) {
- ctx->errmsg = "NFLOG target extension not found";
- 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)
-{
- 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;
-}
-
void nft_ipv46_save_chain(const struct nftnl_chain *c, const char *policy)
{
const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
@@ -1506,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");
@@ -1517,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;
}
}
@@ -1552,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";
@@ -1593,72 +451,6 @@ void nft_check_xt_legacy(int family, bool is_ipt_save)
fclose(fp);
}
-int nft_parse_hl(struct nft_xt_ctx *ctx,
- struct nftnl_expr *e,
- struct iptables_command_state *cs)
-{
- struct xtables_match *match;
- 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:
- match = nft_create_match(ctx, ctx->cs, "ttl");
- break;
- case NFPROTO_IPV6:
- match = nft_create_match(ctx, ctx->cs, "hl");
- break;
- default:
- return -1;
- }
-
- if (!match)
- return -1;
-
- info = (void*)match->m->data;
- info->hop_limit = hl;
- info->mode = mode;
-
- return 0;
-}
-
enum nft_registers nft_get_next_reg(enum nft_registers reg, size_t size)
{
/* convert size to NETLINK_ALIGN-sized chunks */
diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h
index 07d39131..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,113 +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_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 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;
- union {
- struct xt_tcp *tcp;
- struct xt_udp *udp;
- } tcpudp;
-
- 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_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,
- const struct nft_xt_ctx_reg *sreg,
- struct nftnl_expr *e,
- struct iptables_command_state *cs);
- void (*parse_payload)(struct nft_xt_ctx *ctx,
- const struct nft_xt_ctx_reg *sreg,
- 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);
@@ -153,11 +59,8 @@ 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);
bool (*rule_to_cs)(struct nft_handle *h, const struct nftnl_rule *r,
struct iptables_command_state *cs);
@@ -192,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,
@@ -208,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);
-bool nft_rule_to_iptables_command_state(struct nft_handle *h,
- const struct nftnl_rule *r,
- 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);
@@ -225,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);
@@ -264,11 +158,6 @@ void xtables_restore_parse(struct nft_handle *h,
void nft_check_xt_legacy(int family, bool is_ipt_save);
-int nft_parse_hl(struct nft_xt_ctx *ctx, struct nftnl_expr *e, struct iptables_command_state *cs);
-
-#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
diff --git a/iptables/nft.c b/iptables/nft.c
index 63468cf3..97fd4f49 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)
@@ -434,7 +436,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 +573,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 +596,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 +645,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 +701,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 +964,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 +989,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;
}
@@ -1449,22 +1466,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)
@@ -1692,7 +1717,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;
@@ -1705,7 +1731,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;
@@ -2343,7 +2369,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);
@@ -2864,6 +2891,9 @@ 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;
@@ -2882,7 +2912,7 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
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)
@@ -2949,6 +2979,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,
@@ -3195,6 +3231,7 @@ retry:
case NFT_COMPAT_RULE_ZERO:
case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
assert(0);
+ return 0;
}
mnl_nft_batch_continue(h->batch);
@@ -3260,6 +3297,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;
@@ -3287,7 +3327,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);
@@ -3469,7 +3509,7 @@ static int nft_prepare(struct nft_handle *h)
case NFT_COMPAT_TABLE_ADD:
case NFT_COMPAT_CHAIN_ADD:
assert(0);
- break;
+ return 0;
}
nft_cmd_free(cmd);
@@ -3846,7 +3886,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);
diff --git a/iptables/nft.h b/iptables/nft.h
index caff1fde..5acbbf82 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;
@@ -117,10 +118,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 +168,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 +190,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);
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/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/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 1091a4e8..b4f9728b 100755
--- a/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0
+++ b/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0
@@ -38,7 +38,7 @@ $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
@@ -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
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/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/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/xshared.c b/iptables/xshared.c
index 60955147..67fa2cfa 100644
--- a/iptables/xshared.c
+++ b/iptables/xshared.c
@@ -111,20 +111,13 @@ 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);
}
@@ -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);
}
@@ -1317,7 +1310,7 @@ 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:
@@ -1336,12 +1329,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)
@@ -1521,6 +1513,7 @@ 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':
@@ -1554,16 +1547,16 @@ void do_parse(int argc, char *argv[],
* Option selection
*/
case 'p':
- check_inverse(args, optarg, &invert, &optind, argc);
+ check_inverse(args, optarg, &invert, argc, argv);
set_option(&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];
+ cs->protocol = optarg;
args->proto = xtables_parse_protocol(cs->protocol);
if (args->proto == 0 &&
@@ -1577,17 +1570,17 @@ void do_parse(int argc, char *argv[],
break;
case 's':
- check_inverse(args, optarg, &invert, &optind, argc);
+ check_inverse(args, optarg, &invert, argc, argv);
set_option(&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);
+ check_inverse(args, optarg, &invert, argc, argv);
set_option(&cs->options, OPT_DESTINATION,
&args->invflags, invert);
- args->dhostnetworkmask = argv[optind - 1];
+ args->dhostnetworkmask = optarg;
break;
#ifdef IPT_F_GOTO
@@ -1600,71 +1593,71 @@ void do_parse(int argc, char *argv[],
#endif
case 2:/* src-mac */
- check_inverse(args, optarg, &invert, &optind, argc);
+ check_inverse(args, optarg, &invert, argc, argv);
set_option(&cs->options, OPT_S_MAC, &args->invflags,
invert);
- args->src_mac = argv[optind - 1];
+ args->src_mac = optarg;
break;
case 3:/* dst-mac */
- check_inverse(args, optarg, &invert, &optind, argc);
+ check_inverse(args, optarg, &invert, argc, argv);
set_option(&cs->options, OPT_D_MAC, &args->invflags,
invert);
- args->dst_mac = argv[optind - 1];
+ args->dst_mac = optarg;
break;
case 'l':/* hardware length */
- check_inverse(args, optarg, &invert, &optind, argc);
+ check_inverse(args, optarg, &invert, argc, argv);
set_option(&cs->options, OPT_H_LENGTH, &args->invflags,
invert);
- args->arp_hlen = argv[optind - 1];
+ 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);
+ check_inverse(args, optarg, &invert, argc, argv);
set_option(&cs->options, OPT_OPCODE, &args->invflags,
invert);
- args->arp_opcode = argv[optind - 1];
+ args->arp_opcode = optarg;
break;
case 5:/* h-type */
- check_inverse(args, optarg, &invert, &optind, argc);
+ check_inverse(args, optarg, &invert, argc, argv);
set_option(&cs->options, OPT_H_TYPE, &args->invflags,
invert);
- args->arp_htype = argv[optind - 1];
+ args->arp_htype = optarg;
break;
case 6:/* proto-type */
- check_inverse(args, optarg, &invert, &optind, argc);
+ check_inverse(args, optarg, &invert, argc, argv);
set_option(&cs->options, OPT_P_TYPE, &args->invflags,
invert);
- args->arp_ptype = argv[optind - 1];
+ args->arp_ptype = optarg;
break;
case 'j':
set_option(&cs->options, OPT_JUMP, &args->invflags,
invert);
- command_jump(cs, argv[optind - 1]);
+ command_jump(cs, optarg);
break;
case 'i':
check_empty_interface(args, optarg);
- check_inverse(args, optarg, &invert, &optind, argc);
+ check_inverse(args, optarg, &invert, argc, argv);
set_option(&cs->options, OPT_VIANAMEIN,
&args->invflags, invert);
- xtables_parse_interface(argv[optind - 1],
+ xtables_parse_interface(optarg,
args->iniface,
args->iniface_mask);
break;
case 'o':
check_empty_interface(args, optarg);
- check_inverse(args, optarg, &invert, &optind, argc);
+ check_inverse(args, optarg, &invert, argc, argv);
set_option(&cs->options, OPT_VIANAMEOUT,
&args->invflags, invert);
- xtables_parse_interface(argv[optind - 1],
+ xtables_parse_interface(optarg,
args->outiface,
args->outiface_mask);
break;
@@ -1992,6 +1985,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;
diff --git a/iptables/xshared.h b/iptables/xshared.h
index 0ed9f3c2..a200e0d6 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 {
diff --git a/iptables/xtables-eb-translate.c b/iptables/xtables-eb-translate.c
index 13b6b864..da7e5e3d 100644
--- a/iptables/xtables-eb-translate.c
+++ b/iptables/xtables-eb-translate.c
@@ -24,9 +24,6 @@
/*
* 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;
@@ -71,19 +68,6 @@ static int parse_rule_number(const char *rule)
/* 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[];
@@ -172,7 +156,6 @@ static int nft_rule_eb_xlate_add(struct nft_handle *h, const struct xt_cmd_parse
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;
@@ -187,13 +170,13 @@ static int do_commandeb_xlate(struct nft_handle *h, int argc, char *argv[], char
};
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,
};
+ bool table_set = false;
/* prevent getopt to spoil our error reporting */
opterr = false;
@@ -201,7 +184,7 @@ static int do_commandeb_xlate(struct nft_handle *h, int argc, char *argv[], char
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) {
+ "-: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 */
@@ -264,13 +247,6 @@ static int do_commandeb_xlate(struct nft_handle *h, int argc, char *argv[], char
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'))
@@ -292,9 +268,6 @@ print_zero:
if (OPT_COMMANDS)
xtables_error(PARAMETER_PROBLEM,
"Multiple commands are not allowed");
- if (exec_style == EXEC_STYLE_DAEMON)
- xtables_error(PARAMETER_PROBLEM,
- "%s %s", prog_name, prog_vers);
printf("%s %s\n", prog_name, prog_vers);
exit(0);
case 'h':
@@ -307,13 +280,16 @@ print_zero:
if (OPT_COMMANDS)
xtables_error(PARAMETER_PROBLEM,
"Please put the -t option first");
- ebt_check_option2(&flags, OPT_TABLE);
+ if (table_set)
+ xtables_error(PARAMETER_PROBLEM,
+ "Multiple use of same option not allowed");
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;
+ table_set = true;
break;
case 'i': /* Input interface */
case 2 : /* Logical input interface */
@@ -331,7 +307,7 @@ print_zero:
xtables_error(PARAMETER_PROBLEM,
"Command and option do not match");
if (c == 'i') {
- ebt_check_option2(&flags, OPT_IN);
+ ebt_check_option2(&flags, OPT_VIANAMEIN);
if (selected_chain > 2 && selected_chain < NF_BR_BROUTING)
xtables_error(PARAMETER_PROBLEM,
"Use -i only in INPUT, FORWARD, PREROUTING and BROUTING chains");
@@ -351,7 +327,7 @@ print_zero:
ebtables_parse_interface(optarg, cs.eb.logical_in);
break;
} else if (c == 'o') {
- ebt_check_option2(&flags, OPT_OUT);
+ ebt_check_option2(&flags, OPT_VIANAMEOUT);
if (selected_chain < 2 || selected_chain == NF_BR_BROUTING)
xtables_error(PARAMETER_PROBLEM,
"Use -o only in OUTPUT, FORWARD and POSTROUTING chains");
@@ -372,7 +348,9 @@ print_zero:
break;
} else if (c == 'j') {
ebt_check_option2(&flags, OPT_JUMP);
- command_jump(&cs, optarg);
+ if (strcmp(optarg, "CONTINUE") != 0) {
+ command_jump(&cs, optarg);
+ }
break;
} else if (c == 's') {
ebt_check_option2(&flags, OPT_SOURCE);
@@ -386,7 +364,7 @@ print_zero:
cs.eb.bitmask |= EBT_SOURCEMAC;
break;
} else if (c == 'd') {
- ebt_check_option2(&flags, OPT_DEST);
+ ebt_check_option2(&flags, OPT_DESTINATION);
if (ebt_check_inverse2(optarg, argc, argv))
cs.eb.invflags |= EBT_IDEST;
@@ -397,7 +375,7 @@ print_zero:
cs.eb.bitmask |= EBT_DESTMAC;
break;
} else if (c == 'c') {
- ebt_check_option2(&flags, OPT_COUNT);
+ ebt_check_option2(&flags, OPT_COUNTERS);
if (ebt_check_inverse2(optarg, argc, argv))
xtables_error(PARAMETER_PROBLEM,
"Unexpected '!' after -c");
@@ -491,11 +469,7 @@ print_zero:
continue;
default:
ebt_check_inverse2(optarg, argc, argv);
-
- if (ebt_command_default(&cs))
- xtables_error(PARAMETER_PROBLEM,
- "Unknown argument: '%s'",
- argv[optind - 1]);
+ ebt_command_default(&cs);
if (command != 'A' && command != 'I' &&
command != 'D')
@@ -525,6 +499,13 @@ print_zero:
if (command == 'P') {
return 0;
+ } else if (command == 'F') {
+ 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;
} else if (command == 'A') {
ret = nft_rule_eb_xlate_add(h, &p, &cs, true);
if (!ret)
diff --git a/iptables/xtables-eb.c b/iptables/xtables-eb.c
index 7214a767..08eec79d 100644
--- a/iptables/xtables-eb.c
+++ b/iptables/xtables-eb.c
@@ -42,6 +42,10 @@
#include "nft.h"
#include "nft-bridge.h"
+/* from linux/netfilter_bridge/ebtables.h */
+#define EBT_TABLE_MAXNAMELEN 32
+#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+
/*
* From include/ebtables_u.h
*/
@@ -74,6 +78,26 @@ static int ebt_check_inverse2(const char option[], int argc, char **argv)
return ebt_invert;
}
+/* XXX: merge with assert_valid_chain_name()? */
+static void ebt_assert_valid_chain_name(const char *chainname)
+{
+ if (strlen(chainname) >= EBT_CHAIN_MAXNAMELEN)
+ xtables_error(PARAMETER_PROBLEM,
+ "Chain name length can't exceed %d",
+ EBT_CHAIN_MAXNAMELEN - 1);
+
+ if (*chainname == '-' || *chainname == '!')
+ xtables_error(PARAMETER_PROBLEM, "No chain name specified");
+
+ if (xtables_find_target(chainname, XTF_TRY_LOAD))
+ xtables_error(PARAMETER_PROBLEM,
+ "Target with name %s exists", chainname);
+
+ if (strchr(chainname, ' ') != NULL)
+ xtables_error(PARAMETER_PROBLEM,
+ "Use of ' ' not allowed in chain names");
+}
+
/*
* Glue code to use libxtables
*/
@@ -99,7 +123,7 @@ append_entry(struct nft_handle *h,
int ret = 1;
if (append)
- ret = nft_cmd_rule_append(h, chain, table, cs, NULL, verbose);
+ ret = nft_cmd_rule_append(h, chain, table, cs, verbose);
else
ret = nft_cmd_rule_insert(h, chain, table, cs, rule_nr, verbose);
@@ -468,14 +492,14 @@ static void ebt_load_match(const char *name)
xtables_error(OTHER_PROBLEM, "Can't alloc memory");
}
-static void __ebt_load_watcher(const char *name, const char *typename)
+static void ebt_load_watcher(const char *name)
{
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;
}
@@ -496,16 +520,6 @@ static void __ebt_load_watcher(const char *name, const char *typename)
xtables_error(OTHER_PROBLEM, "Can't alloc memory");
}
-static void ebt_load_watcher(const char *name)
-{
- 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;
@@ -522,13 +536,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,
@@ -633,6 +640,9 @@ int ebt_command_default(struct iptables_command_state *cs)
/* Is it a watcher option? */
for (t = xtables_targets; t; t = t->next) {
+ if (!(t->ext_flags & XTABLES_EXT_WATCHER))
+ continue;
+
if (t->parse &&
t->parse(cs->c - t->option_offset, cs->argv,
ebt_invert, &t->tflags, NULL, &t->t)) {
@@ -640,7 +650,16 @@ int ebt_command_default(struct iptables_command_state *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)
@@ -680,7 +699,8 @@ void nft_fini_eb(struct nft_handle *h)
free(target->t);
}
- free(opts);
+ if (opts != ebt_original_options)
+ free(opts);
nft_fini(h);
xtables_fini();
@@ -717,6 +737,11 @@ int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table,
optind = 0;
opterr = false;
+ for (t = xtables_targets; t; t = t->next) {
+ t->tflags = 0;
+ t->used = 0;
+ }
+
/* Getopt saves the day */
while ((c = getopt_long(argc, argv, EBT_OPTSTRING,
opts, NULL)) != -1) {
@@ -750,6 +775,7 @@ int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table,
flags |= OPT_COMMAND;
if (c == 'N') {
+ ebt_assert_valid_chain_name(chain);
ret = nft_cmd_chain_user_add(h, chain, *table);
break;
} else if (c == 'X') {
@@ -763,14 +789,12 @@ int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table,
}
if (c == 'E') {
- if (optind >= argc)
+ if (!xs_has_arg(argc, argv))
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");
+
+ ebt_assert_valid_chain_name(argv[optind]);
errno = 0;
ret = nft_cmd_chain_user_rename(h, chain, *table,
@@ -1084,11 +1108,7 @@ print_zero:
continue;
default:
ebt_check_inverse2(optarg, argc, argv);
-
- if (ebt_command_default(&cs))
- xtables_error(PARAMETER_PROBLEM,
- "Unknown argument: '%s'",
- argv[optind]);
+ ebt_command_default(&cs);
if (command != 'A' && command != 'I' &&
command != 'D' && command != 'C' && command != 14)
diff --git a/iptables/xtables-multi.h b/iptables/xtables-multi.h
index 94c24d5a..833c11a2 100644
--- a/iptables/xtables-multi.h
+++ b/iptables/xtables-multi.h
@@ -20,7 +20,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-restore.c b/iptables/xtables-restore.c
index abe56374..23cd3498 100644
--- a/iptables/xtables-restore.c
+++ b/iptables/xtables-restore.c
@@ -166,19 +166,17 @@ static void xtables_restore_parse_line(struct nft_handle *h,
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'",
- 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",
policy, chain, line,
diff --git a/iptables/xtables-translate.c b/iptables/xtables-translate.c
index 22b2fbc8..88e0a6b6 100644
--- a/iptables/xtables-translate.c
+++ b/iptables/xtables-translate.c
@@ -173,6 +173,8 @@ static int nft_rule_xlate_add(struct nft_handle *h,
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);
diff --git a/libiptc/libiptc.c b/libiptc/libiptc.c
index f9b7779e..e4750633 100644
--- a/libiptc/libiptc.c
+++ b/libiptc/libiptc.c
@@ -822,7 +822,7 @@ static int __iptcc_p_del_policy(struct xtc_handle *h, unsigned int num)
/* save counter and counter_map information */
h->chain_iterator_cur->counter_map.maptype =
- COUNTER_MAP_ZEROED;
+ COUNTER_MAP_NORMAL_MAP;
h->chain_iterator_cur->counter_map.mappos = num-1;
memcpy(&h->chain_iterator_cur->counters, &pr->entry->counters,
sizeof(h->chain_iterator_cur->counters));
@@ -1318,16 +1318,10 @@ retry:
return NULL;
}
- sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
+ sockfd = socket(TC_AF, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
if (sockfd < 0)
return NULL;
- if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) == -1) {
- fprintf(stderr, "Could not set close on exec: %s\n",
- strerror(errno));
- abort();
- }
-
s = sizeof(info);
strcpy(info.name, tablename);
diff --git a/libxtables/xtables.c b/libxtables/xtables.c
index 52d83af0..c8ddadec 100644
--- a/libxtables/xtables.c
+++ b/libxtables/xtables.c
@@ -481,14 +481,9 @@ static char *get_modprobe(void)
char *ret;
int count;
- procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+ procfile = open(PROC_SYS_MODPROBE, O_RDONLY | O_CLOEXEC);
if (procfile < 0)
return NULL;
- if (fcntl(procfile, F_SETFD, FD_CLOEXEC) == -1) {
- fprintf(stderr, "Could not set close on exec: %s\n",
- strerror(errno));
- exit(1);
- }
ret = malloc(PATH_MAX);
if (ret) {
@@ -1025,7 +1020,7 @@ int xtables_compatible_revision(const char *name, uint8_t revision, int opt)
socklen_t s = sizeof(rev);
int max_rev, sockfd;
- sockfd = socket(afinfo->family, SOCK_RAW, IPPROTO_RAW);
+ sockfd = socket(afinfo->family, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
if (sockfd < 0) {
if (errno == EPERM) {
/* revision 0 is always supported. */
@@ -1041,12 +1036,6 @@ int xtables_compatible_revision(const char *name, uint8_t revision, int opt)
exit(1);
}
- if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) == -1) {
- fprintf(stderr, "Could not set close on exec: %s\n",
- strerror(errno));
- exit(1);
- }
-
xtables_load_ko(xtables_modprobe_program, true);
strncpy(rev.name, name, XT_EXTENSION_MAXNAMELEN - 1);
diff --git a/utils/Makefile.am b/utils/Makefile.am
index e9eec48f..34056514 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -2,7 +2,7 @@
AM_CFLAGS = ${regular_CFLAGS}
AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include \
- -I${top_srcdir}/include ${libnfnetlink_CFLAGS}
+ -I${top_srcdir}/include ${libnfnetlink_CFLAGS} ${libpcap_CFLAGS}
AM_LDFLAGS = ${regular_LDFLAGS}
sbin_PROGRAMS =
@@ -25,12 +25,12 @@ endif
if ENABLE_BPFC
man_MANS += nfbpf_compile.8
sbin_PROGRAMS += nfbpf_compile
-nfbpf_compile_LDADD = -lpcap
+nfbpf_compile_LDADD = ${libpcap_LIBS}
endif
if ENABLE_SYNCONF
sbin_PROGRAMS += nfsynproxy
-nfsynproxy_LDADD = -lpcap
+nfsynproxy_LDADD = ${libpcap_LIBS}
endif
CLEANFILES = nfnl_osf.8 nfbpf_compile.8
diff --git a/utils/nfbpf_compile.c b/utils/nfbpf_compile.c
index 2c46c7b0..c9e763dc 100644
--- a/utils/nfbpf_compile.c
+++ b/utils/nfbpf_compile.c
@@ -17,6 +17,7 @@ int main(int argc, char **argv)
struct bpf_program program;
struct bpf_insn *ins;
int i, dlt = DLT_RAW;
+ pcap_t *pcap;
if (argc < 2 || argc > 3) {
fprintf(stderr, "Usage: %s [link] '<program>'\n\n"
@@ -36,9 +37,15 @@ int main(int argc, char **argv)
}
}
- if (pcap_compile_nopcap(65535, dlt, &program, argv[argc - 1], 1,
+ pcap = pcap_open_dead(dlt, 65535);
+ if (!pcap) {
+ fprintf(stderr, "Memory allocation failure\n");
+ return 1;
+ }
+ if (pcap_compile(pcap, &program, argv[argc - 1], 1,
PCAP_NETMASK_UNKNOWN)) {
fprintf(stderr, "Compilation error\n");
+ pcap_close(pcap);
return 1;
}
@@ -50,6 +57,7 @@ int main(int argc, char **argv)
printf("%u %u %u %u\n", ins->code, ins->jt, ins->jf, ins->k);
pcap_freecode(&program);
+ pcap_close(pcap);
return 0;
}
diff --git a/xlate-test.py b/xlate-test.py
index 4cb1401b..6a116598 100755
--- a/xlate-test.py
+++ b/xlate-test.py
@@ -64,7 +64,7 @@ def test_one_replay(name, sourceline, expected, result):
if sourceline.find(';') >= 0:
sourceline, searchline = sourceline.split(';')
- srcwords = sourceline.split()
+ srcwords = shlex.split(sourceline)
srccmd = srcwords[0]
ipt = srccmd.split('-')[0]
@@ -176,7 +176,7 @@ def run_test(name, payload):
result.append(name + ": " + red("Fail"))
result.append("nft flush ruleset call failed: " + error)
- if (passed == tests) and not args.test:
+ if (passed == tests):
print(name + ": " + green("OK"))
if not test_passed:
print("\n".join(result), file=sys.stderr)
@@ -241,17 +241,22 @@ def main():
+ '/iptables/' + xtables_nft_multi
files = tests = passed = failed = errors = 0
- if args.test:
- if not args.test.endswith(".txlate"):
- args.test += ".txlate"
+ for test in args.test:
+ if not test.endswith(".txlate"):
+ test += ".txlate"
try:
- with open(args.test, "r") as payload:
- files = 1
- tests, passed, failed, errors = run_test(args.test, payload)
+ with open(test, "r") as payload:
+ t, p, f, e = run_test(test, payload)
+ files += 1
+ tests += t
+ passed += p
+ failed += f
+ errors += e
except IOError:
print(red("Error: ") + "test file does not exist", file=sys.stderr)
return 99
- else:
+
+ if files == 0:
files, tests, passed, failed, errors = load_test_files()
if files > 1:
@@ -272,6 +277,6 @@ parser.add_argument('-n', '--nft', type=str, default='nft',
help='Replay using given nft binary (default: \'%(default)s\')')
parser.add_argument('--no-netns', action='store_true',
help='Do not run testsuite in own network namespace')
-parser.add_argument("test", nargs="?", help="run only the specified test file")
+parser.add_argument("test", nargs="*", help="run only the specified test file(s)")
args = parser.parse_args()
sys.exit(main())