summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am3
-rw-r--r--configure.ac6
-rw-r--r--doc/additional-commands.txt116
-rw-r--r--doc/libnftables-json.adoc11
-rw-r--r--doc/nft.txt80
-rw-r--r--doc/payload-expression.txt38
-rw-r--r--doc/stateful-objects.txt2
-rw-r--r--doc/statements.txt47
-rw-r--r--include/cache.h13
-rw-r--r--include/datatype.h17
-rw-r--r--include/expression.h2
-rw-r--r--include/iface.h2
-rw-r--r--include/linux/netfilter_decnet.h72
-rw-r--r--include/mnl.h2
-rw-r--r--include/netlink.h5
-rw-r--r--include/nftables.h3
-rw-r--r--include/parser.h4
-rw-r--r--include/rule.h4
-rw-r--r--include/statement.h2
-rw-r--r--src/cache.c263
-rw-r--r--src/cmd.c41
-rw-r--r--src/datatype.c37
-rw-r--r--src/evaluate.c147
-rw-r--r--src/expression.c18
-rw-r--r--src/iface.c17
-rw-r--r--src/intervals.c31
-rw-r--r--src/json.c31
-rw-r--r--src/libnftables.c30
-rw-r--r--src/mnl.c228
-rw-r--r--src/netlink.c96
-rw-r--r--src/netlink_delinearize.c8
-rw-r--r--src/netlink_linearize.c15
-rw-r--r--src/optimize.c38
-rw-r--r--src/parser_bison.y235
-rw-r--r--src/parser_json.c181
-rw-r--r--src/preprocess.c168
-rw-r--r--src/proto.c2
-rw-r--r--src/rule.c81
-rw-r--r--src/scanner.l110
-rw-r--r--src/segtree.c8
-rw-r--r--src/statement.c10
-rw-r--r--tests/py/inet/udp.t.payload4
-rw-r--r--tests/py/ip/icmp.t.json.output24
-rw-r--r--tests/py/ip/sets.t2
-rw-r--r--tests/py/ip/sets.t.json37
-rw-r--r--tests/py/ip/sets.t.payload.inet11
-rw-r--r--tests/py/ip/sets.t.payload.ip8
-rw-r--r--tests/py/ip/sets.t.payload.netdev10
-rw-r--r--tests/py/ip6/icmpv6.t.json.output62
-rw-r--r--tests/py/ip6/sets.t3
-rw-r--r--tests/py/ip6/sets.t.json37
-rw-r--r--tests/py/ip6/sets.t.payload.inet11
-rw-r--r--tests/py/ip6/sets.t.payload.ip68
-rw-r--r--tests/py/ip6/sets.t.payload.netdev10
-rwxr-xr-xtests/py/nft-test.py4
-rwxr-xr-xtests/shell/features/elem_timeout_update.sh22
-rw-r--r--tests/shell/features/ip_options.nft8
-rw-r--r--tests/shell/features/ipsec.nft7
-rwxr-xr-xtests/shell/features/position_id.sh23
-rw-r--r--tests/shell/features/table_flag_persist.nft3
-rw-r--r--tests/shell/testcases/bogons/nft-f/flowtable-no-priority-crash5
-rwxr-xr-xtests/shell/testcases/cache/0011_index_02
-rwxr-xr-xtests/shell/testcases/chains/0003jump_loop_13
-rwxr-xr-xtests/shell/testcases/chains/0010endless_jump_loop_12
-rwxr-xr-xtests/shell/testcases/chains/0011endless_jump_loop_12
-rwxr-xr-xtests/shell/testcases/chains/0018check_jump_loop_12
-rw-r--r--tests/shell/testcases/chains/dumps/0003jump_loop_1.json-nft6
-rw-r--r--tests/shell/testcases/chains/dumps/0003jump_loop_1.nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.json-nft6
-rw-r--r--tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.json-nft6
-rw-r--r--tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0018check_jump_loop_1.json-nft6
-rw-r--r--tests/shell/testcases/chains/dumps/0018check_jump_loop_1.nft1
-rw-r--r--tests/shell/testcases/chains/dumps/jump_to_base_chain.nodump0
-rw-r--r--tests/shell/testcases/chains/dumps/netdev_chain_dormant_autoremove.json-nft32
-rw-r--r--tests/shell/testcases/chains/dumps/netdev_chain_dormant_autoremove.nft7
-rwxr-xr-xtests/shell/testcases/chains/jump_to_base_chain25
-rwxr-xr-xtests/shell/testcases/chains/netdev_chain_dormant_autoremove11
-rwxr-xr-xtests/shell/testcases/listing/reset_objects104
-rwxr-xr-xtests/shell/testcases/maps/0024named_objects_131
-rwxr-xr-xtests/shell/testcases/maps/0024named_objects_223
-rwxr-xr-xtests/shell/testcases/maps/anonymous_snat_map_116
-rwxr-xr-xtests/shell/testcases/maps/anonymous_snat_map_223
-rwxr-xr-xtests/shell/testcases/maps/delete_element28
-rwxr-xr-xtests/shell/testcases/maps/delete_element_catchall35
-rw-r--r--tests/shell/testcases/maps/dumps/0024named_objects_1.json-nft147
-rw-r--r--tests/shell/testcases/maps/dumps/0024named_objects_1.nft23
-rw-r--r--tests/shell/testcases/maps/dumps/0024named_objects_2.nodump0
-rw-r--r--tests/shell/testcases/maps/dumps/anonymous_snat_map_1.json-nft58
-rw-r--r--tests/shell/testcases/maps/dumps/anonymous_snat_map_1.nft5
-rw-r--r--tests/shell/testcases/maps/dumps/anonymous_snat_map_2.nodump0
-rw-r--r--tests/shell/testcases/maps/dumps/delete_element.nft12
-rw-r--r--tests/shell/testcases/maps/dumps/delete_element_catchall.nft12
-rw-r--r--tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft2
-rw-r--r--tests/shell/testcases/maps/dumps/typeof_maps_add_delete.nft2
-rwxr-xr-xtests/shell/testcases/maps/named_ct_objects1
-rwxr-xr-xtests/shell/testcases/maps/nat_addr_port5
-rwxr-xr-xtests/shell/testcases/maps/typeof_maps_02
-rwxr-xr-xtests/shell/testcases/maps/typeof_maps_add_delete4
-rwxr-xr-xtests/shell/testcases/maps/vmap_timeout48
-rw-r--r--tests/shell/testcases/nft-i/dumps/index_0.nft8
-rw-r--r--tests/shell/testcases/nft-i/dumps/set_0.nft7
-rwxr-xr-xtests/shell/testcases/nft-i/index_011
-rwxr-xr-xtests/shell/testcases/nft-i/set_09
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_counter.nft8
-rwxr-xr-xtests/shell/testcases/optimizations/merge_counter20
-rwxr-xr-xtests/shell/testcases/optimizations/ruleset1
-rwxr-xr-xtests/shell/testcases/optimizations/variables52
-rwxr-xr-xtests/shell/testcases/owner/0002-persist78
-rw-r--r--tests/shell/testcases/owner/dumps/0002-persist.json-nft8
-rw-r--r--tests/shell/testcases/owner/dumps/0002-persist.nft3
-rw-r--r--tests/shell/testcases/packetpath/dumps/flowtables.nodump0
-rw-r--r--tests/shell/testcases/packetpath/dumps/policy.json-nft121
-rw-r--r--tests/shell/testcases/packetpath/dumps/policy.nft11
-rw-r--r--tests/shell/testcases/packetpath/dumps/tcp_reset.json-nft168
-rw-r--r--tests/shell/testcases/packetpath/dumps/tcp_reset.nft13
-rw-r--r--tests/shell/testcases/packetpath/dumps/vlan_mangling.nodump0
-rw-r--r--tests/shell/testcases/packetpath/dumps/vlan_qinq.nodump0
-rwxr-xr-xtests/shell/testcases/packetpath/flowtables4
-rwxr-xr-xtests/shell/testcases/packetpath/match_l4proto149
-rwxr-xr-xtests/shell/testcases/packetpath/payload169
-rwxr-xr-xtests/shell/testcases/packetpath/policy42
-rwxr-xr-xtests/shell/testcases/packetpath/tcp_reset31
-rwxr-xr-xtests/shell/testcases/packetpath/vlan_mangling77
-rwxr-xr-xtests/shell/testcases/packetpath/vlan_qinq73
-rwxr-xr-xtests/shell/testcases/rule_management/0004replace_08
-rwxr-xr-xtests/shell/testcases/rule_management/0011reset_031
-rw-r--r--tests/shell/testcases/rule_management/dumps/0004replace_0.json-nft49
-rw-r--r--tests/shell/testcases/rule_management/dumps/0004replace_0.nft11
-rwxr-xr-xtests/shell/testcases/sets/collapse_elem_06
-rw-r--r--tests/shell/testcases/sets/dumps/collapse_elem_0.json-nft5
-rw-r--r--tests/shell/testcases/sets/dumps/collapse_elem_0.nft2
-rw-r--r--tests/shell/testcases/sets/dumps/elem_limit_0.nft7
-rw-r--r--tests/shell/testcases/sets/dumps/set_element_timeout_updates.json-nft43
-rw-r--r--tests/shell/testcases/sets/dumps/set_element_timeout_updates.nft10
-rwxr-xr-xtests/shell/testcases/sets/elem_limit_017
-rwxr-xr-xtests/shell/testcases/sets/set_element_timeout_updates120
-rwxr-xr-xtests/shell/testcases/sets/typeof_sets_02
-rwxr-xr-xtests/shell/testcases/transactions/0023rule_12
-rwxr-xr-xtests/shell/testcases/transactions/0024rule_02
-rwxr-xr-xtests/shell/testcases/transactions/0049huge_05
-rwxr-xr-xtests/shell/testcases/transactions/30s-stress42
-rwxr-xr-xtests/shell/testcases/transactions/anon_chain_loop2
-rwxr-xr-xtests/shell/testcases/transactions/bad_rule_graphs262
-rw-r--r--tests/shell/testcases/transactions/dumps/bad_rule_graphs.json-nft201
-rw-r--r--tests/shell/testcases/transactions/dumps/bad_rule_graphs.nft30
-rw-r--r--tests/shell/testcases/transactions/dumps/handle_bad_family.json-nft18
-rw-r--r--tests/shell/testcases/transactions/dumps/handle_bad_family.nft2
-rw-r--r--tests/shell/testcases/transactions/dumps/validation_recursion.sh.nodump0
-rwxr-xr-xtests/shell/testcases/transactions/handle_bad_family9
-rwxr-xr-xtests/shell/testcases/transactions/validation_recursion.sh39
152 files changed, 4018 insertions, 1188 deletions
diff --git a/Makefile.am b/Makefile.am
index fef1d8d1..fb64105d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -57,7 +57,6 @@ noinst_HEADERS = \
include/linux/netfilter_arp/arp_tables.h \
include/linux/netfilter_bridge.h \
include/linux/netfilter_bridge/ebtables.h \
- include/linux/netfilter_decnet.h \
include/linux/netfilter_ipv4.h \
include/linux/netfilter_ipv4/ip_tables.h \
include/linux/netfilter_ipv6.h \
@@ -232,6 +231,7 @@ src_libnftables_la_SOURCES = \
src/osf.c \
src/owner.c \
src/payload.c \
+ src/preprocess.c \
src/print.c \
src/proto.c \
src/rt.c \
@@ -321,6 +321,7 @@ A2X_OPTS_MANPAGE = \
ASCIIDOC_MAIN = doc/nft.txt
ASCIIDOC_INCLUDES = \
+ doc/additional-commands.txt \
doc/data-types.txt \
doc/payload-expression.txt \
doc/primary-expression.txt \
diff --git a/configure.ac b/configure.ac
index 724a4ae7..3f944996 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
-AC_INIT([nftables], [1.0.9], [netfilter-devel@vger.kernel.org])
-AC_DEFINE([RELEASE_NAME], ["Old Doc Yak #3"], [Release name])
+AC_INIT([nftables], [1.1.0], [netfilter-devel@vger.kernel.org])
+AC_DEFINE([RELEASE_NAME], ["Commodore Bullmoose"], [Release name])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
@@ -57,7 +57,7 @@ AS_IF([test "x$enable_man_doc" = "xyes"], [
])
PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.4])
-PKG_CHECK_MODULES([LIBNFTNL], [libnftnl >= 1.2.6])
+PKG_CHECK_MODULES([LIBNFTNL], [libnftnl >= 1.2.7])
AC_ARG_WITH([mini-gmp], [AS_HELP_STRING([--with-mini-gmp],
[Use builtin mini-gmp (for embedded builds)])],
diff --git a/doc/additional-commands.txt b/doc/additional-commands.txt
new file mode 100644
index 00000000..2ebc2993
--- /dev/null
+++ b/doc/additional-commands.txt
@@ -0,0 +1,116 @@
+LIST HOOKS
+~~~~~~~~~~
+
+This shows the list of functions that have been registered for the
+given protocol family, including functions that have been
+registered implicitly by kernel modules such as nf_conntrack. +
+
+[verse]
+____
+*list hooks* ['family']
+*list hooks netdev* [ *device* 'DEVICE_NAME' ]
+____
+
+*list hooks* is enough to display everything that is active
+on the system. Hooks in the netdev family are tied to a network
+device. If no device name is given, nft will query all network
+devices in the current network namespace.
+Example Usage:
+
+.List all active netfilter hooks in either the ip or ip6 stack
+--------------------------------------------------------------
+% nft list hooks inet
+family ip {
+ hook prerouting {
+ -0000000400 ipv4_conntrack_defrag [nf_defrag_ipv4]
+ -0000000200 ipv4_conntrack_in [nf_conntrack]
+ -0000000100 nf_nat_ipv4_pre_routing [nf_nat]
+ }
+ hook input {
+ 0000000000 chain inet filter input [nf_tables]
+ +0000000100 nf_nat_ipv4_local_in [nf_nat]
+[..]
+--------------------------------------------------------------
+
+The above shows a host that has nat, conntrack and ipv4 packet
+defragmentation enabled.
+For each hook location for the queried family a list of active hooks
+using the format +
+
+*priority* *identifier* [*module_name*]
+
+will be shown.
+
+The *priority* value dictates the order in which the hooks are called.
+The list is sorted, the lowest number is run first.
+
+The priority value of hooks registered by the kernel cannot be changed.
+For basechains registered by nftables, this value corresponds to the
+*priority* value specified in the base chain definition.
+
+After the numerical value, information about the hook is shown.
+For basechains defined in nftables this includes the table family,
+the table name and the basechains name.
+For hooks coming from kernel modules, the function name is used
+instead.
+
+If a *module name* is given, the hook was registered by the kernel
+module with this name. You can use 'modinfo *module name*' to
+obtain more information about the module.
+
+This functionality requires a kernel built with the option +
+CONFIG_NETFILTER_NETLINK_HOOK
+enabled, either as a module or builtin. The module is named
+*nfnetlink_hook*.
+
+MONITOR
+~~~~~~~
+The monitor command allows you to listen to Netlink events produced by the
+nf_tables subsystem. These are either related to creation and deletion of
+objects or to packets for which *meta nftrace* was enabled. When they
+occur, nft will print to stdout the monitored events in either JSON or
+native nft format. +
+
+[verse]
+____
+*monitor* [*new* | *destroy*] 'MONITOR_OBJECT'
+*monitor* *trace*
+
+'MONITOR_OBJECT' := *tables* | *chains* | *sets* | *rules* | *elements* | *ruleset*
+____
+
+To filter events related to a concrete object, use one of the keywords in
+'MONITOR_OBJECT'.
+
+To filter events related to a concrete action, use keyword *new* or *destroy*.
+
+The second form of invocation takes no further options and exclusively prints
+events generated for packets with *nftrace* enabled.
+
+Hit ^C to finish the monitor operation.
+
+.Listen to all events, report in native nft format
+--------------------------------------------------
+% nft monitor
+--------------------------------------------------
+
+.Listen to deleted rules, report in JSON format
+-----------------------------------------------
+% nft -j monitor destroy rules
+-----------------------------------------------
+
+.Listen to both new and destroyed chains, in native nft format
+-----------------------------------------------------------------
+% nft monitor chains
+-------------------------------
+
+.Listen to ruleset events such as table, chain, rule, set, counters and quotas, in native nft format
+----------------------------------------------------------------------------------------------------
+% nft monitor ruleset
+---------------------
+
+.Trace incoming packets from host 10.0.0.1
+------------------------------------------
+% nft add rule filter input ip saddr 10.0.0.1 meta nftrace set 1
+% nft monitor trace
+------------------------------------------
diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc
index e3b24cc4..a8a6165f 100644
--- a/doc/libnftables-json.adoc
+++ b/doc/libnftables-json.adoc
@@ -202,12 +202,19 @@ Rename a chain. The new name is expected in a dedicated property named
=== TABLE
[verse]
+____
*{ "table": {
"family":* 'STRING'*,
"name":* 'STRING'*,
- "handle":* 'NUMBER'
+ "handle":* 'NUMBER'*,
+ "flags":* 'TABLE_FLAGS'
*}}*
+'TABLE_FLAGS' := 'TABLE_FLAG' | *[* 'TABLE_FLAG_LIST' *]*
+'TABLE_FLAG_LIST' := 'TABLE_FLAG' [*,* 'TABLE_FLAG_LIST' ]
+'TABLE_FLAG' := *"dormant"* | *"owner"* | *"persist"*
+____
+
This object describes a table.
*family*::
@@ -217,6 +224,8 @@ This object describes a table.
*handle*::
The table's handle. In input, it is used only in *delete* command as
alternative to *name*.
+*flags*::
+ The table's flags.
=== CHAIN
[verse]
diff --git a/doc/nft.txt b/doc/nft.txt
index 248b29af..846ccfb2 100644
--- a/doc/nft.txt
+++ b/doc/nft.txt
@@ -43,6 +43,8 @@ understanding of their meaning. You can get information about options by running
*-f*::
*--file 'filename'*::
Read input from 'filename'. If 'filename' is -, read from stdin.
+ The directory path to this file is inserted at the beginning the list of
+ directories to be searched for included files (see *-I/--includepath*).
*-D*::
*--define 'name=value'*::
@@ -343,8 +345,17 @@ return an error.
|Flag | Description
|dormant |
table is not evaluated any more (base chains are unregistered).
+|owner |
+table is owned by the creating process.
+|persist |
+table shall outlive the owning process.
|=================
+Creating a table with flag *owner* excludes other processes from manipulating
+it or its contents. By default, it will be removed when the process exits.
+Setting flag *persist* will prevent this and the resulting orphaned table will
+accept a new owner, e.g. a restarting daemon maintaining the table.
+
.*Add, change, delete a table*
---------------------------------------
# start nft in interactive mode
@@ -412,7 +423,7 @@ Chains of this type perform Native Address Translation based on conntrack
entries. Only the first packet of a connection actually traverses this chain -
its rules usually define details of the created conntrack entry (NAT
statements for instance).
-|route | ip, ip6 | output |
+|route | ip, ip6, inet | output |
If a packet has traversed a chain of this type and is about to be accepted, a
new route lookup is performed if relevant parts of the IP header have changed.
This allows one to e.g. implement policy routing selectors in nftables.
@@ -738,8 +749,8 @@ protocols. Each entry also caches the destination interface and the gateway
address - to update the destination link-layer address - to forward packets.
The ttl and hoplimit fields are also decremented. Hence, flowtables provides an
alternative path that allow packets to bypass the classic forwarding path.
-Flowtables reside in the ingress hook that is located before the prerouting
-hook. You can select which flows you want to offload through the flow
+Flowtables reside in the ingress *hook* that is located before the prerouting
+*hook*. You can select which flows you want to offload through the flow
expression from the forward chain. Flowtables are identified by their address
family and their name. The address family must be one of ip, ip6, or inet. The inet
address family is a dummy family which is used to create hybrid IPv4/IPv6
@@ -755,17 +766,6 @@ and subtraction can be used to set relative priority, e.g. filter + 5 equals to
*destroy*:: Delete the specified flowtable, it does not fail if it does not exist.
*list*:: List all flowtables.
-LISTING
--------
-[verse]
-*list { secmarks | synproxys | flow tables | meters | hooks }* ['family']
-*list { secmarks | synproxys | flow tables | meters | hooks } table* ['family'] 'table'
-*list ct { timeout | expectation | helper | helpers } table* ['family'] 'table'
-
-Inspect configured objects.
-*list hooks* shows the full hook pipeline, including those registered by
-kernel modules, such as nf_conntrack.
-
STATEFUL OBJECTS
----------------
[verse]
@@ -897,57 +897,7 @@ ADDITIONAL COMMANDS
-------------------
These are some additional commands included in nft.
-MONITOR
-~~~~~~~~
-The monitor command allows you to listen to Netlink events produced by the
-nf_tables subsystem. These are either related to creation and deletion of
-objects or to packets for which *meta nftrace* was enabled. When they
-occur, nft will print to stdout the monitored events in either JSON or
-native nft format. +
-
-[verse]
-____
-*monitor* [*new* | *destroy*] 'MONITOR_OBJECT'
-*monitor* *trace*
-
-'MONITOR_OBJECT' := *tables* | *chains* | *sets* | *rules* | *elements* | *ruleset*
-____
-
-To filter events related to a concrete object, use one of the keywords in
-'MONITOR_OBJECT'.
-
-To filter events related to a concrete action, use keyword *new* or *destroy*.
-
-The second form of invocation takes no further options and exclusively prints
-events generated for packets with *nftrace* enabled.
-
-Hit ^C to finish the monitor operation.
-
-.Listen to all events, report in native nft format
---------------------------------------------------
-% nft monitor
---------------------------------------------------
-
-.Listen to deleted rules, report in JSON format
------------------------------------------------
-% nft -j monitor destroy rules
------------------------------------------------
-
-.Listen to both new and destroyed chains, in native nft format
------------------------------------------------------------------
-% nft monitor chains
--------------------------------
-
-.Listen to ruleset events such as table, chain, rule, set, counters and quotas, in native nft format
-----------------------------------------------------------------------------------------------------
-% nft monitor ruleset
----------------------
-
-.Trace incoming packets from host 10.0.0.1
-------------------------------------------
-% nft add rule filter input ip saddr 10.0.0.1 meta nftrace set 1
-% nft monitor trace
-------------------------------------------
+include::additional-commands.txt[]
ERROR REPORTING
---------------
diff --git a/doc/payload-expression.txt b/doc/payload-expression.txt
index c7c267da..7bc24a8a 100644
--- a/doc/payload-expression.txt
+++ b/doc/payload-expression.txt
@@ -670,44 +670,6 @@ integer (24 bit)
netdev filter ingress udp dport 4789 vxlan tcp dport 80 counter
----------------------------------------------------------
-ARP HEADER EXPRESSION
-~~~~~~~~~~~~~~~~~~~~~
-[verse]
-*arp* {*htype* | *ptype* | *hlen* | *plen* | *operation* | *saddr* { *ip* | *ether* } | *daddr* { *ip* | *ether* }
-
-.ARP header expression
-[options="header"]
-|==================
-|Keyword| Description| Type
-|htype|
-ARP hardware type|
-integer (16 bit)
-|ptype|
-EtherType|
-ether_type
-|hlen|
-Hardware address len|
-integer (8 bit)
-|plen|
-Protocol address len |
-integer (8 bit)
-|operation|
-Operation |
-arp_op
-|saddr ether|
-Ethernet sender address|
-ether_addr
-|daddr ether|
-Ethernet target address|
-ether_addr
-|saddr ip|
-IPv4 sender address|
-ipv4_addr
-|daddr ip|
-IPv4 target address|
-ipv4_addr
-|======================
-
RAW PAYLOAD EXPRESSION
~~~~~~~~~~~~~~~~~~~~~~
[verse]
diff --git a/doc/stateful-objects.txt b/doc/stateful-objects.txt
index 00d3c5f1..5824d53a 100644
--- a/doc/stateful-objects.txt
+++ b/doc/stateful-objects.txt
@@ -119,7 +119,7 @@ sport=41360 dport=22
CT EXPECTATION
~~~~~~~~~~~~~~
[verse]
-*add* *ct expectation* ['family'] 'table' 'name' *{ protocol* 'protocol' *; dport* 'dport' *; timeout* 'timeout' *; size* 'size' *; [*l3proto* 'family' *;*] *}*
+*add* *ct expectation* ['family'] 'table' 'name' *{ protocol* 'protocol' *; dport* 'dport' *; timeout* 'timeout' *; size* 'size' *;* [*l3proto* 'family' *;*] *}*
*delete* *ct expectation* ['family'] 'table' 'name'
*list* *ct expectations*
diff --git a/doc/statements.txt b/doc/statements.txt
index 39b31fd2..74af1d1a 100644
--- a/doc/statements.txt
+++ b/doc/statements.txt
@@ -56,7 +56,7 @@ set ip DSCP (diffserv) header field or ipv6 flow labels.
---------------------------------------
# redirect tcp:http from 192.160.0.0/16 to local machine for routing instead of bridging
# assumes 00:11:22:33:44:55 is local MAC address.
-bridge input meta iif eth0 ip saddr 192.168.0.0/16 tcp dport 80 meta pkttype set unicast ether daddr set 00:11:22:33:44:55
+bridge input meta iif eth0 ip saddr 192.168.0.0/16 tcp dport 80 meta pkttype set host ether daddr set 00:11:22:33:44:55
-------------------------------------------
.Set IPv4 DSCP header field
@@ -583,27 +583,58 @@ this case the rule will match for both families.
table ip x {
chain y {
type filter hook prerouting priority mangle; policy accept;
- tcp dport ntp tproxy to 1.1.1.1
- udp dport ssh tproxy to :2222
+ tcp dport ntp tproxy to 1.1.1.1 accept
+ udp dport ssh tproxy to :2222 accept
}
}
table ip6 x {
chain y {
type filter hook prerouting priority mangle; policy accept;
- tcp dport ntp tproxy to [dead::beef]
- udp dport ssh tproxy to :2222
+ tcp dport ntp tproxy to [dead::beef] accept
+ udp dport ssh tproxy to :2222 accept
}
}
table inet x {
chain y {
type filter hook prerouting priority mangle; policy accept;
- tcp dport 321 tproxy to :ssh
- tcp dport 99 tproxy ip to 1.1.1.1:999
- udp dport 155 tproxy ip6 to [dead::beef]:smux
+ tcp dport 321 tproxy to :22 accept
+ tcp dport 99 tproxy ip to 1.1.1.1:999 accept
+ udp dport 155 tproxy ip6 to [dead::beef]:smux accept
}
}
-------------------------------------
+Note that the tproxy statement is non-terminal to allow post-processing of
+packets. This allows packets to be logged for debugging as well as updating the
+mark to ensure that packets are delivered locally through policy routing rules.
+
+.Example ruleset for tproxy statement with logging and meta mark
+-------------------------------------
+table inet x {
+ chain y {
+ type filter hook prerouting priority mangle; policy accept;
+ udp dport 9999 goto {
+ tproxy to :1234 log prefix "packet tproxied: " meta mark set 1 accept
+ log prefix "no socket on port 1234 or not transparent?: " drop
+ }
+ }
+}
+-------------------------------------
+
+As packet headers are unchanged, packets might be forwarded instead of delivered
+locally. As mentioned above, this can be avoided by adding policy routing rules
+and the packet mark.
+
+.Example policy routing rules for local redirection
+----------------------------------------------------
+ip rule add fwmark 1 lookup 100
+ip route add local 0.0.0.0/0 dev lo table 100
+----------------------------------------------------
+
+This is a change in behavior compared to the legacy iptables TPROXY target
+which is terminal. To terminate the packet processing after the tproxy
+statement, remember to issue a verdict as in the example above.
+
SYNPROXY STATEMENT
~~~~~~~~~~~~~~~~~~
This statement will process TCP three-way-handshake parallel in netfilter
diff --git a/include/cache.h b/include/cache.h
index 8ca4a9a7..e6bde6c6 100644
--- a/include/cache.h
+++ b/include/cache.h
@@ -31,7 +31,6 @@ enum cache_level_flags {
NFT_CACHE_SET_BIT |
NFT_CACHE_SETELEM_BIT,
NFT_CACHE_RULE = NFT_CACHE_TABLE_BIT |
- NFT_CACHE_CHAIN_BIT |
NFT_CACHE_RULE_BIT,
NFT_CACHE_FULL = __NFT_CACHE_MAX_BIT - 1,
NFT_CACHE_TERSE = (1 << 27),
@@ -55,14 +54,22 @@ struct nft_cache_filter {
uint32_t family;
const char *table;
const char *chain;
+ const char *obj;
const char *set;
const char *ft;
+ int obj_type;
uint64_t rule_handle;
} list;
struct {
struct list_head head;
} obj[NFT_CACHE_HSIZE];
+
+ struct {
+ bool obj;
+ bool rule;
+ bool elem;
+ } reset;
};
struct nft_cache;
@@ -148,8 +155,4 @@ struct netlink_ctx;
void nft_chain_cache_update(struct netlink_ctx *ctx, struct table *table,
const char *chain);
-int rule_cache_dump(struct netlink_ctx *ctx, const struct handle *h,
- const struct nft_cache_filter *filter,
- bool dump, bool reset);
-
#endif /* _NFT_CACHE_H_ */
diff --git a/include/datatype.h b/include/datatype.h
index d4b4737c..df3bc385 100644
--- a/include/datatype.h
+++ b/include/datatype.h
@@ -120,17 +120,6 @@ enum byteorder {
struct expr;
-/**
- * enum datatype_flags
- *
- * @DTYPE_F_ALLOC: datatype is dynamically allocated
- * @DTYPE_F_PREFIX: preferred representation for ranges is a prefix
- */
-enum datatype_flags {
- DTYPE_F_ALLOC = (1 << 0),
- DTYPE_F_PREFIX = (1 << 1),
-};
-
struct parse_ctx;
/**
* struct datatype
@@ -147,11 +136,12 @@ struct parse_ctx;
* @print: function to print a constant of this type
* @parse: function to parse a symbol and return an expression
* @sym_tbl: symbol table for this type
- * @refcnt: reference counter (only for DTYPE_F_ALLOC)
+ * @refcnt: reference counter (only for dynamically allocated, see .alloc)
*/
struct datatype {
uint32_t type;
- enum byteorder byteorder;
+ enum byteorder byteorder:8;
+ uint32_t alloc:1;
unsigned int flags;
unsigned int size;
unsigned int subtypes;
@@ -179,6 +169,7 @@ extern void datatype_set(struct expr *expr, const struct datatype *dtype);
extern void __datatype_set(struct expr *expr, const struct datatype *dtype);
extern void datatype_free(const struct datatype *dtype);
struct datatype *datatype_clone(const struct datatype *orig_dtype);
+bool datatype_prefix_notation(const struct datatype *dtype);
struct parse_ctx {
struct symbol_tables *tbl;
diff --git a/include/expression.h b/include/expression.h
index 01b45b7c..8982110c 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -415,8 +415,6 @@ extern const struct datatype *expr_basetype(const struct expr *expr);
extern void expr_set_type(struct expr *expr, const struct datatype *dtype,
enum byteorder byteorder);
-void expr_to_string(const struct expr *expr, char *string);
-
struct eval_ctx;
extern int expr_binary_error(struct list_head *msgs,
const struct expr *e1, const struct expr *e2,
diff --git a/include/iface.h b/include/iface.h
index f41ee8be..786b1dfc 100644
--- a/include/iface.h
+++ b/include/iface.h
@@ -2,6 +2,7 @@
#define _NFTABLES_IFACE_H_
#include <net/if.h>
+#include <list.h>
struct iface {
struct list_head list;
@@ -15,4 +16,5 @@ char *nft_if_indextoname(unsigned int ifindex, char *name);
void iface_cache_update(void);
void iface_cache_release(void);
+const struct iface *iface_cache_get_next_entry(const struct iface *prev);
#endif
diff --git a/include/linux/netfilter_decnet.h b/include/linux/netfilter_decnet.h
deleted file mode 100644
index ca70c6cd..00000000
--- a/include/linux/netfilter_decnet.h
+++ /dev/null
@@ -1,72 +0,0 @@
-#ifndef __LINUX_DECNET_NETFILTER_H
-#define __LINUX_DECNET_NETFILTER_H
-
-/* DECnet-specific defines for netfilter.
- * This file (C) Steve Whitehouse 1999 derived from the
- * ipv4 netfilter header file which is
- * (C)1998 Rusty Russell -- This code is GPL.
- */
-
-#include <linux/netfilter.h>
-
-/* only for userspace compatibility */
-/* IP Cache bits. */
-/* Src IP address. */
-#define NFC_DN_SRC 0x0001
-/* Dest IP address. */
-#define NFC_DN_DST 0x0002
-/* Input device. */
-#define NFC_DN_IF_IN 0x0004
-/* Output device. */
-#define NFC_DN_IF_OUT 0x0008
-
-/* DECnet Hooks */
-/* After promisc drops, checksum checks. */
-#define NF_DN_PRE_ROUTING 0
-/* If the packet is destined for this box. */
-#define NF_DN_LOCAL_IN 1
-/* If the packet is destined for another interface. */
-#define NF_DN_FORWARD 2
-/* Packets coming from a local process. */
-#define NF_DN_LOCAL_OUT 3
-/* Packets about to hit the wire. */
-#define NF_DN_POST_ROUTING 4
-/* Input Hello Packets */
-#define NF_DN_HELLO 5
-/* Input Routing Packets */
-#define NF_DN_ROUTE 6
-#define NF_DN_NUMHOOKS 7
-
-enum nf_dn_hook_priorities {
- NF_DN_PRI_FIRST = INT_MIN,
- NF_DN_PRI_CONNTRACK = -200,
- NF_DN_PRI_MANGLE = -150,
- NF_DN_PRI_NAT_DST = -100,
- NF_DN_PRI_FILTER = 0,
- NF_DN_PRI_NAT_SRC = 100,
- NF_DN_PRI_DNRTMSG = 200,
- NF_DN_PRI_LAST = INT_MAX,
-};
-
-struct nf_dn_rtmsg {
- int nfdn_ifindex;
-};
-
-#define NFDN_RTMSG(r) ((unsigned char *)(r) + NLMSG_ALIGN(sizeof(struct nf_dn_rtmsg)))
-
-/* backwards compatibility for userspace */
-#define DNRMG_L1_GROUP 0x01
-#define DNRMG_L2_GROUP 0x02
-
-enum {
- DNRNG_NLGRP_NONE,
-#define DNRNG_NLGRP_NONE DNRNG_NLGRP_NONE
- DNRNG_NLGRP_L1,
-#define DNRNG_NLGRP_L1 DNRNG_NLGRP_L1
- DNRNG_NLGRP_L2,
-#define DNRNG_NLGRP_L2 DNRNG_NLGRP_L2
- __DNRNG_NLGRP_MAX
-};
-#define DNRNG_NLGRP_MAX (__DNRNG_NLGRP_MAX - 1)
-
-#endif /*__LINUX_DECNET_NETFILTER_H*/
diff --git a/include/mnl.h b/include/mnl.h
index cd5a2053..c9502f32 100644
--- a/include/mnl.h
+++ b/include/mnl.h
@@ -90,7 +90,7 @@ int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags);
int mnl_nft_flowtable_del(struct netlink_ctx *ctx, struct cmd *cmd);
-int mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, int family, int hook,
+int mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, int family,
const char *devname);
int mnl_nft_event_listener(struct mnl_socket *nf_sock, unsigned int debug_mask,
diff --git a/include/netlink.h b/include/netlink.h
index 27a62462..cf7ba369 100644
--- a/include/netlink.h
+++ b/include/netlink.h
@@ -176,8 +176,6 @@ extern int netlink_delinearize_setelem(struct nftnl_set_elem *nlse,
struct nft_cache *cache);
extern int netlink_list_objs(struct netlink_ctx *ctx, const struct handle *h);
-extern int netlink_reset_objs(struct netlink_ctx *ctx, const struct cmd *cmd,
- uint32_t type, bool dump);
extern struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
struct nftnl_obj *nlo);
@@ -186,9 +184,6 @@ extern int netlink_list_flowtables(struct netlink_ctx *ctx,
extern struct flowtable *netlink_delinearize_flowtable(struct netlink_ctx *ctx,
struct nftnl_flowtable *nlo);
-extern int netlink_reset_rules(struct netlink_ctx *ctx, const struct cmd *cmd,
- bool dump);
-
extern void netlink_dump_chain(const struct nftnl_chain *nlc,
struct netlink_ctx *ctx);
extern void netlink_dump_rule(const struct nftnl_rule *nlr,
diff --git a/include/nftables.h b/include/nftables.h
index 4b7c3359..c25deb36 100644
--- a/include/nftables.h
+++ b/include/nftables.h
@@ -241,4 +241,7 @@ int nft_optimize(struct nft_ctx *nft, struct list_head *cmds);
#define __NFT_OUTPUT_NOTSUPP UINT_MAX
+/* internal marker, not used by the kernel. */
+#define NFT_NEVER_TIMEOUT UINT64_MAX
+
#endif /* NFTABLES_NFTABLES_H */
diff --git a/include/parser.h b/include/parser.h
index f79a22f3..576e5e43 100644
--- a/include/parser.h
+++ b/include/parser.h
@@ -112,4 +112,8 @@ extern void scanner_push_buffer(void *scanner,
extern void scanner_pop_start_cond(void *scanner, enum startcond_type sc);
+const char *str_preprocess(struct parser_state *state, struct location *loc,
+ struct scope *scope, const char *x,
+ struct error_record **rec);
+
#endif /* NFTABLES_PARSER_H */
diff --git a/include/rule.h b/include/rule.h
index 3a833cf3..5b3e12b5 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -130,10 +130,12 @@ struct symbol *symbol_get(const struct scope *scope, const char *identifier);
enum table_flags {
TABLE_F_DORMANT = (1 << 0),
TABLE_F_OWNER = (1 << 1),
+ TABLE_F_PERSIST = (1 << 2),
};
-#define TABLE_FLAGS_MAX 2
+#define TABLE_FLAGS_MAX 3
const char *table_flag_name(uint32_t flag);
+unsigned int parse_table_flag(const char *name);
/**
* struct table - nftables table
diff --git a/include/statement.h b/include/statement.h
index 662f99dd..9376911b 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -90,7 +90,7 @@ enum {
};
struct log_stmt {
- struct expr *prefix;
+ const char *prefix;
unsigned int snaplen;
uint16_t group;
uint16_t qthreshold;
diff --git a/src/cache.c b/src/cache.c
index c000e32c..b75a5bf3 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -30,10 +30,7 @@ static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags)
break;
flags |= NFT_CACHE_TABLE |
- NFT_CACHE_CHAIN |
- NFT_CACHE_SET |
- NFT_CACHE_OBJECT |
- NFT_CACHE_FLOWTABLE;
+ NFT_CACHE_SET;
list_for_each_entry(set, &cmd->table->sets, list) {
if (set->automerge)
flags |= NFT_CACHE_SETELEM_MAYBE;
@@ -54,21 +51,15 @@ static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags)
break;
case CMD_OBJ_ELEMENTS:
flags |= NFT_CACHE_TABLE |
- NFT_CACHE_CHAIN |
NFT_CACHE_SET |
- NFT_CACHE_OBJECT |
NFT_CACHE_SETELEM_MAYBE;
break;
case CMD_OBJ_RULE:
flags |= NFT_CACHE_TABLE |
- NFT_CACHE_CHAIN |
- NFT_CACHE_SET |
- NFT_CACHE_OBJECT |
- NFT_CACHE_FLOWTABLE;
+ NFT_CACHE_SET;
- if (cmd->handle.index.id ||
- cmd->handle.position.id)
- flags |= NFT_CACHE_RULE | NFT_CACHE_UPDATE;
+ if (cmd->handle.index.id)
+ flags |= NFT_CACHE_FULL | NFT_CACHE_UPDATE;
break;
default:
break;
@@ -81,9 +72,11 @@ static unsigned int evaluate_cache_del(struct cmd *cmd, unsigned int flags)
{
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
- flags |= NFT_CACHE_SETELEM_MAYBE;
+ flags |= NFT_CACHE_SET |
+ NFT_CACHE_SETELEM_MAYBE;
break;
default:
+ flags = NFT_CACHE_TABLE;
break;
}
@@ -197,24 +190,39 @@ static unsigned int evaluate_cache_rename(struct cmd *cmd, unsigned int flags)
return flags;
}
+static void obj_filter_setup(const struct cmd *cmd, unsigned int *flags,
+ struct nft_cache_filter *filter, int type)
+{
+ assert(filter);
+
+ if (cmd->handle.family)
+ filter->list.family = cmd->handle.family;
+ if (cmd->handle.table.name)
+ filter->list.table = cmd->handle.table.name;
+ if (cmd->handle.obj.name)
+ filter->list.obj = cmd->handle.obj.name;
+
+ filter->list.obj_type = type;
+ *flags |= NFT_CACHE_TABLE | NFT_CACHE_OBJECT;
+}
+
static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
unsigned int flags,
struct nft_cache_filter *filter)
{
switch (cmd->obj) {
case CMD_OBJ_TABLE:
- if (filter)
- filter->list.family = cmd->handle.family;
+ filter->list.family = cmd->handle.family;
if (!cmd->handle.table.name) {
flags |= NFT_CACHE_TABLE;
break;
- } else if (filter) {
+ } else {
filter->list.table = cmd->handle.table.name;
}
flags |= NFT_CACHE_FULL;
break;
case CMD_OBJ_CHAIN:
- if (filter && cmd->handle.chain.name) {
+ if (cmd->handle.chain.name) {
filter->list.family = cmd->handle.family;
filter->list.table = cmd->handle.table.name;
filter->list.chain = cmd->handle.chain.name;
@@ -227,7 +235,7 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
break;
case CMD_OBJ_SET:
case CMD_OBJ_MAP:
- if (filter && cmd->handle.table.name && cmd->handle.set.name) {
+ if (cmd->handle.table.name && cmd->handle.set.name) {
filter->list.family = cmd->handle.family;
filter->list.table = cmd->handle.table.name;
filter->list.set = cmd->handle.set.name;
@@ -247,8 +255,7 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
flags |= NFT_CACHE_SETELEM;
break;
case CMD_OBJ_FLOWTABLE:
- if (filter &&
- cmd->handle.table.name &&
+ if (cmd->handle.table.name &&
cmd->handle.flowtable.name) {
filter->list.family = cmd->handle.family;
filter->list.table = cmd->handle.table.name;
@@ -258,6 +265,37 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
case CMD_OBJ_FLOWTABLES:
flags |= NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE;
break;
+ case CMD_OBJ_COUNTER:
+ case CMD_OBJ_COUNTERS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_COUNTER);
+ break;
+ case CMD_OBJ_QUOTA:
+ case CMD_OBJ_QUOTAS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_QUOTA);
+ break;
+ case CMD_OBJ_CT_HELPER:
+ case CMD_OBJ_CT_HELPERS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_CT_HELPER);
+ break;
+ case CMD_OBJ_LIMIT:
+ case CMD_OBJ_LIMITS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_LIMIT);
+ break;
+ case CMD_OBJ_CT_TIMEOUT:
+ case CMD_OBJ_CT_TIMEOUTS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_CT_TIMEOUT);
+ break;
+ case CMD_OBJ_SECMARK:
+ case CMD_OBJ_SECMARKS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_SECMARK);
+ break;
+ case CMD_OBJ_CT_EXPECT:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_CT_EXPECT);
+ break;
+ case CMD_OBJ_SYNPROXY:
+ case CMD_OBJ_SYNPROXYS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_SYNPROXY);
+ break;
case CMD_OBJ_RULESET:
default:
flags |= NFT_CACHE_FULL;
@@ -275,28 +313,49 @@ static unsigned int evaluate_cache_reset(struct cmd *cmd, unsigned int flags,
struct nft_cache_filter *filter)
{
switch (cmd->obj) {
+ case CMD_OBJ_TABLE:
+ case CMD_OBJ_CHAIN:
case CMD_OBJ_RULES:
case CMD_OBJ_RULE:
- if (filter) {
- if (cmd->handle.table.name) {
- filter->list.family = cmd->handle.family;
- filter->list.table = cmd->handle.table.name;
- }
- if (cmd->handle.chain.name)
- filter->list.chain = cmd->handle.chain.name;
+ if (cmd->handle.table.name) {
+ filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
}
- flags |= NFT_CACHE_SET | NFT_CACHE_FLOWTABLE |
- NFT_CACHE_OBJECT | NFT_CACHE_CHAIN;
+ if (cmd->handle.chain.name)
+ filter->list.chain = cmd->handle.chain.name;
+ if (cmd->handle.family)
+ filter->list.family = cmd->handle.family;
+ if (cmd->handle.handle.id)
+ filter->list.rule_handle = cmd->handle.handle.id;
+
+ filter->reset.rule = true;
+ flags |= NFT_CACHE_FULL;
+ break;
+ case CMD_OBJ_COUNTER:
+ case CMD_OBJ_COUNTERS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_COUNTER);
+ filter->reset.obj = true;
+ break;
+ case CMD_OBJ_QUOTA:
+ case CMD_OBJ_QUOTAS:
+ obj_filter_setup(cmd, &flags, filter, NFT_OBJECT_QUOTA);
+ filter->reset.obj = true;
break;
- case CMD_OBJ_ELEMENTS:
case CMD_OBJ_SET:
case CMD_OBJ_MAP:
- flags |= NFT_CACHE_SET;
+ if (cmd->handle.table.name && cmd->handle.set.name) {
+ filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
+ filter->list.set = cmd->handle.set.name;
+ }
+ flags |= NFT_CACHE_SETELEM;
+ filter->reset.elem = true;
break;
default:
- flags |= NFT_CACHE_TABLE;
+ flags |= NFT_CACHE_FULL;
break;
}
+ flags |= NFT_CACHE_REFRESH;
return flags;
}
@@ -406,52 +465,52 @@ err_name_too_long:
return -1;
}
+static void reset_filter(struct nft_cache_filter *filter)
+{
+ memset(&filter->list, 0, sizeof(filter->list));
+ memset(&filter->reset, 0, sizeof(filter->reset));
+}
+
int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds,
struct list_head *msgs, struct nft_cache_filter *filter,
unsigned int *pflags)
{
- unsigned int flags = NFT_CACHE_EMPTY;
+ unsigned int flags, batch_flags = NFT_CACHE_EMPTY;
struct cmd *cmd;
+ assert(filter);
+
list_for_each_entry(cmd, cmds, list) {
if (nft_handle_validate(cmd, msgs) < 0)
return -1;
- if (filter->list.table && cmd->op != CMD_LIST)
- memset(&filter->list, 0, sizeof(filter->list));
+ flags = NFT_CACHE_EMPTY;
+ reset_filter(filter);
switch (cmd->op) {
case CMD_ADD:
case CMD_INSERT:
case CMD_CREATE:
flags = evaluate_cache_add(cmd, flags);
- if (nft_output_echo(&nft->output))
- flags |= NFT_CACHE_FULL;
break;
- case CMD_REPLACE:
- flags = NFT_CACHE_FULL;
+ case CMD_REPLACE: /* only for rule */
+ flags = NFT_CACHE_TABLE | NFT_CACHE_SET;
break;
case CMD_DELETE:
case CMD_DESTROY:
- flags |= NFT_CACHE_TABLE |
- NFT_CACHE_CHAIN |
- NFT_CACHE_SET |
- NFT_CACHE_FLOWTABLE |
- NFT_CACHE_OBJECT;
-
flags = evaluate_cache_del(cmd, flags);
break;
case CMD_GET:
flags = evaluate_cache_get(cmd, flags);
break;
case CMD_RESET:
- flags |= evaluate_cache_reset(cmd, flags, filter);
+ flags = evaluate_cache_reset(cmd, flags, filter);
break;
case CMD_LIST:
- flags |= evaluate_cache_list(nft, cmd, flags, filter);
+ flags = evaluate_cache_list(nft, cmd, flags, filter);
break;
case CMD_MONITOR:
- flags |= NFT_CACHE_FULL;
+ flags = NFT_CACHE_FULL;
break;
case CMD_FLUSH:
flags = evaluate_cache_flush(cmd, flags, filter);
@@ -466,8 +525,9 @@ int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds,
default:
break;
}
+ batch_flags |= flags;
}
- *pflags = flags;
+ *pflags = batch_flags;
return 0;
}
@@ -646,23 +706,32 @@ static int list_rule_cb(struct nftnl_rule *nlr, void *data)
return 0;
}
-int rule_cache_dump(struct netlink_ctx *ctx, const struct handle *h,
- const struct nft_cache_filter *filter,
- bool dump, bool reset)
+static int rule_cache_dump(struct netlink_ctx *ctx, const struct handle *h,
+ const struct nft_cache_filter *filter)
{
struct nftnl_rule_list *rule_cache;
- const char *table = NULL;
+ const char *table = h->table.name;
const char *chain = NULL;
uint64_t rule_handle = 0;
+ int family = h->family;
+ bool dump = true;
if (filter) {
- table = filter->list.table;
- chain = filter->list.chain;
- rule_handle = filter->list.rule_handle;
+ if (filter->list.table)
+ table = filter->list.table;
+ if (filter->list.chain)
+ chain = filter->list.chain;
+ if (filter->list.rule_handle) {
+ rule_handle = filter->list.rule_handle;
+ dump = false;
+ }
+ if (filter->list.family)
+ family = filter->list.family;
}
- rule_cache = mnl_nft_rule_dump(ctx, h->family,
- table, chain, rule_handle, dump, reset);
+ rule_cache = mnl_nft_rule_dump(ctx, family,
+ table, chain, rule_handle, dump,
+ filter->reset.rule);
if (rule_cache == NULL) {
if (errno == EINTR)
return -1;
@@ -786,10 +855,19 @@ struct obj_cache_dump_ctx {
static int obj_cache_cb(struct nftnl_obj *nlo, void *arg)
{
struct obj_cache_dump_ctx *ctx = arg;
+ const char *obj_table;
const char *obj_name;
+ uint32_t obj_family;
struct obj *obj;
uint32_t hash;
+ obj_table = nftnl_obj_get_str(nlo, NFTNL_OBJ_TABLE);
+ obj_family = nftnl_obj_get_u32(nlo, NFTNL_OBJ_FAMILY);
+
+ if (obj_family != ctx->table->handle.family ||
+ strcmp(obj_table, ctx->table->handle.table.name))
+ return 0;
+
obj = netlink_delinearize_obj(ctx->nlctx, nlo);
if (!obj)
return -1;
@@ -798,6 +876,9 @@ static int obj_cache_cb(struct nftnl_obj *nlo, void *arg)
hash = djb_hash(obj_name) % NFT_CACHE_HSIZE;
cache_add(&obj->cache, &ctx->table->obj_cache, hash);
+ nftnl_obj_list_del(nlo);
+ nftnl_obj_free(nlo);
+
return 0;
}
@@ -814,13 +895,28 @@ static int obj_cache_init(struct netlink_ctx *ctx, struct table *table,
}
static struct nftnl_obj_list *obj_cache_dump(struct netlink_ctx *ctx,
- const struct table *table)
+ const struct nft_cache_filter *filter)
{
struct nftnl_obj_list *obj_list;
+ int type = NFT_OBJECT_UNSPEC;
+ int family = NFPROTO_UNSPEC;
+ const char *table = NULL;
+ const char *obj = NULL;
+ bool dump = true;
- obj_list = mnl_nft_obj_dump(ctx, table->handle.family,
- table->handle.table.name, NULL,
- 0, true, false);
+ if (filter) {
+ family = filter->list.family;
+ if (filter->list.table)
+ table = filter->list.table;
+ if (filter->list.obj) {
+ obj = filter->list.obj;
+ dump = false;
+ }
+ if (filter->list.obj_type)
+ type = filter->list.obj_type;
+ }
+ obj_list = mnl_nft_obj_dump(ctx, family, table, obj, type, dump,
+ filter->reset.obj);
if (!obj_list) {
if (errno == EINTR)
return NULL;
@@ -994,7 +1090,7 @@ static int rule_init_cache(struct netlink_ctx *ctx, struct table *table,
struct chain *chain;
int ret;
- ret = rule_cache_dump(ctx, &table->handle, filter, true, false);
+ ret = rule_cache_dump(ctx, &table->handle, filter);
list_for_each_entry_safe(rule, nrule, &ctx->list, list) {
chain = chain_cache_find(table, rule->handle.chain.name);
@@ -1022,15 +1118,14 @@ err_ctx_list:
static int implicit_chain_cache(struct netlink_ctx *ctx, struct table *table,
const char *chain_name)
{
- struct nft_cache_filter filter;
+ struct nft_cache_filter filter = {};
struct chain *chain;
int ret = 0;
list_for_each_entry(chain, &table->chain_bindings, cache.list) {
- filter.list = (typeof(filter.list)) {
- .table = table->handle.table.name,
- .chain = chain->handle.chain.name,
- };
+ filter.list.table = table->handle.table.name;
+ filter.list.chain = chain->handle.chain.name;
+
ret = rule_init_cache(ctx, table, &filter);
}
@@ -1043,7 +1138,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
struct nftnl_flowtable_list *ft_list = NULL;
struct nftnl_chain_list *chain_list = NULL;
struct nftnl_set_list *set_list = NULL;
- struct nftnl_obj_list *obj_list;
+ struct nftnl_obj_list *obj_list = NULL;
struct table *table;
struct set *set;
int ret = 0;
@@ -1060,6 +1155,13 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
goto cache_fails;
}
}
+ if (flags & NFT_CACHE_OBJECT_BIT) {
+ obj_list = obj_cache_dump(ctx, filter);
+ if (!obj_list) {
+ ret = -1;
+ goto cache_fails;
+ }
+ }
if (flags & NFT_CACHE_FLOWTABLE_BIT) {
ft_list = ft_cache_dump(ctx, filter);
if (!ft_list) {
@@ -1083,7 +1185,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
continue;
ret = netlink_list_setelems(ctx, &set->handle,
- set, false);
+ set, filter->reset.elem);
if (ret < 0)
goto cache_fails;
}
@@ -1096,7 +1198,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
continue;
ret = netlink_list_setelems(ctx, &set->handle,
- set, false);
+ set, filter->reset.elem);
if (ret < 0)
goto cache_fails;
}
@@ -1112,15 +1214,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
goto cache_fails;
}
if (flags & NFT_CACHE_OBJECT_BIT) {
- obj_list = obj_cache_dump(ctx, table);
- if (!obj_list) {
- ret = -1;
- goto cache_fails;
- }
ret = obj_cache_init(ctx, table, obj_list);
-
- nftnl_obj_list_free(obj_list);
-
if (ret < 0)
goto cache_fails;
}
@@ -1141,6 +1235,8 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
cache_fails:
if (set_list)
nftnl_set_list_free(set_list);
+ if (obj_list)
+ nftnl_obj_list_free(obj_list);
if (ft_list)
nftnl_flowtable_list_free(ft_list);
@@ -1177,9 +1273,10 @@ static bool nft_cache_is_complete(struct nft_cache *cache, unsigned int flags)
return (cache->flags & flags) == flags;
}
-static bool nft_cache_needs_refresh(struct nft_cache *cache)
+static bool nft_cache_needs_refresh(struct nft_cache *cache, unsigned int flags)
{
- return cache->flags & NFT_CACHE_REFRESH;
+ return (cache->flags & NFT_CACHE_REFRESH) ||
+ (flags & NFT_CACHE_REFRESH);
}
static bool nft_cache_is_updated(struct nft_cache *cache, uint16_t genid)
@@ -1207,7 +1304,7 @@ int nft_cache_update(struct nft_ctx *nft, unsigned int flags,
replay:
ctx.seqnum = cache->seqnum++;
genid = mnl_genid_get(&ctx);
- if (!nft_cache_needs_refresh(cache) &&
+ if (!nft_cache_needs_refresh(cache, flags) &&
nft_cache_is_complete(cache, flags) &&
nft_cache_is_updated(cache, genid))
return 0;
diff --git a/src/cmd.c b/src/cmd.c
index 14cb1b51..9a572b56 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -75,6 +75,10 @@ static int nft_cmd_enoent_chain(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!cmd->handle.chain.name)
return 0;
+ if (nft_cache_update(ctx->nft, NFT_CACHE_TABLE | NFT_CACHE_CHAIN,
+ ctx->msgs, NULL) < 0)
+ return 0;
+
chain = chain_lookup_fuzzy(&cmd->handle, &ctx->nft->cache, &table);
/* check table first. */
if (!table)
@@ -136,6 +140,10 @@ static int nft_cmd_enoent_set(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!cmd->handle.set.name)
return 0;
+ if (nft_cache_update(ctx->nft, NFT_CACHE_TABLE | NFT_CACHE_SET,
+ ctx->msgs, NULL) < 0)
+ return 0;
+
set = set_lookup_fuzzy(cmd->handle.set.name, &ctx->nft->cache, &table);
/* check table first. */
if (!table)
@@ -165,6 +173,10 @@ static int nft_cmd_enoent_obj(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!cmd->handle.obj.name)
return 0;
+ if (nft_cache_update(ctx->nft, NFT_CACHE_TABLE | NFT_CACHE_OBJECT,
+ ctx->msgs, NULL) < 0)
+ return 0;
+
obj = obj_lookup_fuzzy(cmd->handle.obj.name, &ctx->nft->cache, &table);
/* check table first. */
if (!table)
@@ -193,6 +205,10 @@ static int nft_cmd_enoent_flowtable(struct netlink_ctx *ctx,
if (!cmd->handle.flowtable.name)
return 0;
+ if (nft_cache_update(ctx->nft, NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE,
+ ctx->msgs, NULL) < 0)
+ return 0;
+
ft = flowtable_lookup_fuzzy(cmd->handle.flowtable.name,
&ctx->nft->cache, &table);
/* check table first. */
@@ -256,7 +272,8 @@ static void nft_cmd_enoent(struct netlink_ctx *ctx, const struct cmd *cmd,
static int nft_cmd_chain_error(struct netlink_ctx *ctx, struct cmd *cmd,
struct mnl_err *err)
{
- struct chain *chain = cmd->chain;
+ struct chain *chain = cmd->chain, *existing_chain;
+ const struct table *table;
int priority;
switch (err->err) {
@@ -270,6 +287,25 @@ static int nft_cmd_chain_error(struct netlink_ctx *ctx, struct cmd *cmd,
return netlink_io_error(ctx, &chain->priority.loc,
"Chains of type \"nat\" must have a priority value above -200");
+ if (nft_cache_update(ctx->nft, NFT_CACHE_TABLE | NFT_CACHE_CHAIN,
+ ctx->msgs, NULL) < 0) {
+ return netlink_io_error(ctx, &chain->loc,
+ "Chain of type \"%s\" is not supported, perhaps kernel support is missing?",
+ chain->type.str);
+ }
+
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name, cmd->handle.family);
+ if (table) {
+ existing_chain = chain_cache_find(table, cmd->handle.chain.name);
+ if (existing_chain && existing_chain != chain &&
+ !strcmp(existing_chain->handle.chain.name, chain->handle.chain.name))
+ return netlink_io_error(ctx, &chain->loc,
+ "Chain \"%s\" already exists in table %s '%s' with different declaration",
+ chain->handle.chain.name,
+ family2str(table->handle.family), table->handle.table.name);
+ }
+
return netlink_io_error(ctx, &chain->loc,
"Chain of type \"%s\" is not supported, perhaps kernel support is missing?",
chain->type.str);
@@ -442,6 +478,9 @@ bool nft_cmd_collapse(struct list_head *cmds)
continue;
}
+ if (cmd->expr->etype == EXPR_VARIABLE)
+ continue;
+
if (!elems) {
elems = cmd;
continue;
diff --git a/src/datatype.c b/src/datatype.c
index d398a9c8..ea73eaf9 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -123,6 +123,11 @@ void datatype_print(const struct expr *expr, struct output_ctx *octx)
expr->dtype->name);
}
+bool datatype_prefix_notation(const struct datatype *dtype)
+{
+ return dtype->type == TYPE_IPADDR || dtype->type == TYPE_IP6ADDR;
+}
+
struct error_record *symbol_parse(struct parse_ctx *ctx, const struct expr *sym,
struct expr **res)
{
@@ -642,7 +647,6 @@ const struct datatype ipaddr_type = {
.basetype = &integer_type,
.print = ipaddr_type_print,
.parse = ipaddr_type_parse,
- .flags = DTYPE_F_PREFIX,
};
static void ip6addr_type_print(const struct expr *expr, struct output_ctx *octx)
@@ -709,7 +713,6 @@ const struct datatype ip6addr_type = {
.basetype = &integer_type,
.print = ip6addr_type_print,
.parse = ip6addr_type_parse,
- .flags = DTYPE_F_PREFIX,
};
static void inet_protocol_type_print(const struct expr *expr,
@@ -1344,7 +1347,7 @@ static struct datatype *datatype_alloc(void)
struct datatype *dtype;
dtype = xzalloc(sizeof(*dtype));
- dtype->flags = DTYPE_F_ALLOC;
+ dtype->alloc = 1;
dtype->refcnt = 1;
return dtype;
@@ -1356,7 +1359,7 @@ const struct datatype *datatype_get(const struct datatype *ptr)
if (!dtype)
return NULL;
- if (!(dtype->flags & DTYPE_F_ALLOC))
+ if (!dtype->alloc)
return dtype;
dtype->refcnt++;
@@ -1386,7 +1389,7 @@ struct datatype *datatype_clone(const struct datatype *orig_dtype)
*dtype = *orig_dtype;
dtype->name = xstrdup(orig_dtype->name);
dtype->desc = xstrdup(orig_dtype->desc);
- dtype->flags = DTYPE_F_ALLOC | orig_dtype->flags;
+ dtype->alloc = 1;
dtype->refcnt = 1;
return dtype;
@@ -1398,7 +1401,7 @@ void datatype_free(const struct datatype *ptr)
if (!dtype)
return;
- if (!(dtype->flags & DTYPE_F_ALLOC))
+ if (!dtype->alloc)
return;
assert(dtype->refcnt != 0);
@@ -1477,7 +1480,7 @@ static struct error_record *time_unit_parse(const struct location *loc,
else if (strcmp(str, "week") == 0)
*unit = 1ULL * 60 * 60 * 24 * 7;
else
- return error(loc, "Wrong rate format");
+ return error(loc, "Wrong time format, expecting second, minute, hour, day or week");
return NULL;
}
@@ -1485,14 +1488,14 @@ static struct error_record *time_unit_parse(const struct location *loc,
struct error_record *data_unit_parse(const struct location *loc,
const char *str, uint64_t *rate)
{
- if (strncmp(str, "bytes", strlen("bytes")) == 0)
+ if (strcmp(str, "bytes") == 0)
*rate = 1ULL;
- else if (strncmp(str, "kbytes", strlen("kbytes")) == 0)
+ else if (strcmp(str, "kbytes") == 0)
*rate = 1024;
- else if (strncmp(str, "mbytes", strlen("mbytes")) == 0)
+ else if (strcmp(str, "mbytes") == 0)
*rate = 1024 * 1024;
else
- return error(loc, "Wrong rate format");
+ return error(loc, "Wrong unit format, expecting bytes, kbytes or mbytes");
return NULL;
}
@@ -1500,14 +1503,20 @@ struct error_record *data_unit_parse(const struct location *loc,
struct error_record *rate_parse(const struct location *loc, const char *str,
uint64_t *rate, uint64_t *unit)
{
+ const char *slash, *rate_str;
struct error_record *erec;
- const char *slash;
slash = strchr(str, '/');
if (!slash)
- return error(loc, "wrong rate format");
+ return error(loc, "wrong rate format, expecting {bytes,kbytes,mbytes}/{second,minute,hour,day,week}");
+
+ rate_str = strndup(str, slash - str);
+ if (!rate_str)
+ memory_allocation_error();
+
+ erec = data_unit_parse(loc, rate_str, rate);
+ free_const(rate_str);
- erec = data_unit_parse(loc, str, rate);
if (erec != NULL)
return erec;
diff --git a/src/evaluate.c b/src/evaluate.c
index 1682ba58..593a0140 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -775,6 +775,46 @@ static bool proto_is_dummy(const struct proto_desc *desc)
return desc == &proto_inet || desc == &proto_netdev;
}
+static int stmt_dep_conflict(struct eval_ctx *ctx, const struct stmt *nstmt)
+{
+ struct stmt *stmt;
+
+ list_for_each_entry(stmt, &ctx->rule->stmts, list) {
+ if (stmt == nstmt)
+ break;
+
+ if (stmt->ops->type != STMT_EXPRESSION ||
+ stmt->expr->etype != EXPR_RELATIONAL ||
+ stmt->expr->right->etype != EXPR_VALUE ||
+ stmt->expr->left->etype != EXPR_PAYLOAD ||
+ stmt->expr->left->etype != nstmt->expr->left->etype ||
+ stmt->expr->left->len != nstmt->expr->left->len)
+ continue;
+
+ if (stmt->expr->left->payload.desc != nstmt->expr->left->payload.desc ||
+ stmt->expr->left->payload.inner_desc != nstmt->expr->left->payload.inner_desc ||
+ stmt->expr->left->payload.base != nstmt->expr->left->payload.base ||
+ stmt->expr->left->payload.offset != nstmt->expr->left->payload.offset)
+ continue;
+
+ return stmt_binary_error(ctx, stmt, nstmt,
+ "conflicting statements");
+ }
+
+ return 0;
+}
+
+static int rule_stmt_dep_add(struct eval_ctx *ctx,
+ struct stmt *nstmt, struct stmt *stmt)
+{
+ rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+
+ if (stmt_dep_conflict(ctx, nstmt) < 0)
+ return -1;
+
+ return 0;
+}
+
static int resolve_ll_protocol_conflict(struct eval_ctx *ctx,
const struct proto_desc *desc,
struct expr *payload)
@@ -798,7 +838,8 @@ static int resolve_ll_protocol_conflict(struct eval_ctx *ctx,
return err;
desc = payload->payload.desc;
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
}
} else {
unsigned int i;
@@ -810,10 +851,6 @@ static int resolve_ll_protocol_conflict(struct eval_ctx *ctx,
}
}
- /* This payload and the existing context don't match, conflict. */
- if (pctx->protocol[base + 1].desc != NULL)
- return 1;
-
link = proto_find_num(desc, payload->payload.desc);
if (link < 0 ||
ll_conflict_resolution_gen_dependency(ctx, link, payload, &nstmt) < 0)
@@ -822,7 +859,8 @@ static int resolve_ll_protocol_conflict(struct eval_ctx *ctx,
for (i = 0; i < pctx->stacked_ll_count; i++)
payload->payload.offset += pctx->stacked_ll[i]->length;
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
return 0;
}
@@ -850,7 +888,8 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
if (payload_gen_dependency(ctx, payload, &nstmt) < 0)
return -1;
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
desc = pctx->protocol[base].desc;
@@ -870,7 +909,10 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
assert(pctx->stacked_ll_count);
payload->payload.offset += pctx->stacked_ll[0]->length;
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
+
return 1;
}
goto check_icmp;
@@ -911,8 +953,8 @@ check_icmp:
if (payload_gen_icmp_dependency(ctx, expr, &nstmt) < 0)
return -1;
- if (nstmt)
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ if (nstmt && rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
return 0;
}
@@ -988,7 +1030,8 @@ static int expr_evaluate_inner(struct eval_ctx *ctx, struct expr **exprp)
if (payload_gen_inner_dependency(ctx, expr, &nstmt) < 0)
return -1;
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
proto_ctx_update(pctx, PROTO_BASE_TRANSPORT_HDR, &expr->location, expr->payload.inner_desc);
}
@@ -1083,7 +1126,7 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
base = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (base == &proto_ip)
ct->ct.nfproto = NFPROTO_IPV4;
- else if (base == &proto_ip)
+ else if (base == &proto_ip6)
ct->ct.nfproto = NFPROTO_IPV6;
if (base)
@@ -1119,7 +1162,9 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
relational_expr_pctx_update(pctx, dep);
nstmt = expr_stmt_alloc(&dep->location, dep);
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
return 0;
}
@@ -1832,6 +1877,16 @@ err_missing_flag:
set_is_map(ctx->set->flags) ? "map" : "set", expr_name(key));
}
+static int expr_evaluate_set_elem_catchall(struct eval_ctx *ctx, struct expr **expr)
+{
+ struct expr *elem = *expr;
+
+ if (ctx->set)
+ elem->len = ctx->set->key->len;
+
+ return 0;
+}
+
static const struct expr *expr_set_elem(const struct expr *expr)
{
if (expr->etype == EXPR_MAPPING)
@@ -2061,6 +2116,7 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
mappings->set_flags |= NFT_SET_MAP;
switch (map->mappings->etype) {
+ case EXPR_VARIABLE:
case EXPR_SET:
if (ctx->ectx.key && ctx->ectx.key->etype == EXPR_CONCAT) {
key = expr_clone(ctx->ectx.key);
@@ -2104,6 +2160,11 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
if (expr_evaluate(ctx, &map->mappings->set->init) < 0)
return -1;
+ if (map->mappings->set->init->etype != EXPR_SET) {
+ return expr_error(ctx->msgs, map->mappings->set->init,
+ "Expression is not a map");
+ }
+
if (set_is_interval(map->mappings->set->init->set_flags) &&
!(map->mappings->set->init->set_flags & NFT_SET_CONCAT) &&
interval_set_eval(ctx, ctx->set, map->mappings->set->init) < 0)
@@ -2945,7 +3006,7 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
case EXPR_XFRM:
return expr_evaluate_xfrm(ctx, expr);
case EXPR_SET_ELEM_CATCHALL:
- return 0;
+ return expr_evaluate_set_elem_catchall(ctx, expr);
case EXPR_FLAGCMP:
return expr_evaluate_flagcmp(ctx, expr);
default:
@@ -4389,49 +4450,12 @@ static int stmt_evaluate_queue(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_log_prefix(struct eval_ctx *ctx, struct stmt *stmt)
{
- char tmp[NF_LOG_PREFIXLEN] = {};
- char prefix[NF_LOG_PREFIXLEN];
- size_t len = sizeof(prefix);
- size_t offset = 0;
- struct expr *expr;
-
- if (stmt->log.prefix->etype != EXPR_LIST) {
- if (stmt->log.prefix &&
- div_round_up(stmt->log.prefix->len, BITS_PER_BYTE) >= NF_LOG_PREFIXLEN)
- return expr_error(ctx->msgs, stmt->log.prefix, "log prefix is too long");
-
- return 0;
- }
-
- prefix[0] = '\0';
-
- list_for_each_entry(expr, &stmt->log.prefix->expressions, list) {
- int ret;
+ unsigned int len = strlen(stmt->log.prefix);
- switch (expr->etype) {
- case EXPR_VALUE:
- expr_to_string(expr, tmp);
- ret = snprintf(prefix + offset, len, "%s", tmp);
- break;
- case EXPR_VARIABLE:
- ret = snprintf(prefix + offset, len, "%s",
- expr->sym->expr->identifier);
- break;
- default:
- BUG("unknown expression type %s\n", expr_name(expr));
- break;
- }
- SNPRINTF_BUFFER_SIZE(ret, &len, &offset);
- }
-
- if (len == 0)
+ if (len >= NF_LOG_PREFIXLEN)
return stmt_error(ctx, stmt, "log prefix is too long");
-
- expr = constant_expr_alloc(&stmt->log.prefix->location, &string_type,
- BYTEORDER_HOST_ENDIAN,
- strlen(prefix) * BITS_PER_BYTE, prefix);
- expr_free(stmt->log.prefix);
- stmt->log.prefix = expr;
+ else if (len == 0)
+ return stmt_error(ctx, stmt, "log prefix must have a minimum length of 1 character");
return 0;
}
@@ -4576,6 +4600,7 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
mappings->set_flags |= NFT_SET_OBJECT;
switch (map->mappings->etype) {
+ case EXPR_VARIABLE:
case EXPR_SET:
key = constant_expr_alloc(&stmt->location,
ctx->ectx.dtype,
@@ -4595,6 +4620,11 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
if (expr_evaluate(ctx, &map->mappings->set->init) < 0)
return -1;
+ if (map->mappings->set->init->etype != EXPR_SET) {
+ return expr_error(ctx->msgs, map->mappings->set->init,
+ "Expression is not a map");
+ }
+
if (set_is_interval(map->mappings->set->init->set_flags) &&
!(map->mappings->set->init->set_flags & NFT_SET_CONCAT) &&
interval_set_eval(ctx, ctx->set, map->mappings->set->init) < 0)
@@ -4615,8 +4645,9 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
"Expression is not a map with objects");
break;
default:
- BUG("invalid mapping expression %s\n",
- expr_name(map->mappings));
+ return expr_binary_error(ctx->msgs, map->mappings, map->map,
+ "invalid mapping expression %s",
+ expr_name(map->mappings));
}
if (!datatype_compatible(map->mappings->set->key->dtype, map->map->dtype))
@@ -5817,6 +5848,8 @@ static int cmd_evaluate_reset(struct eval_ctx *ctx, struct cmd *cmd)
return 0;
case CMD_OBJ_ELEMENTS:
return setelem_evaluate(ctx, cmd);
+ case CMD_OBJ_TABLE:
+ case CMD_OBJ_CHAIN:
case CMD_OBJ_SET:
case CMD_OBJ_MAP:
return cmd_evaluate_list(ctx, cmd);
diff --git a/src/expression.c b/src/expression.c
index cb2573fe..c0cb7f22 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -183,15 +183,6 @@ void expr_describe(const struct expr *expr, struct output_ctx *octx)
}
}
-void expr_to_string(const struct expr *expr, char *string)
-{
- int len = expr->len / BITS_PER_BYTE;
-
- assert(expr->dtype == &string_type);
-
- mpz_export_data(string, expr->value, BYTEORDER_HOST_ENDIAN, len);
-}
-
void expr_set_type(struct expr *expr, const struct datatype *dtype,
enum byteorder byteorder)
{
@@ -1323,9 +1314,14 @@ static void set_elem_expr_print(const struct expr *expr,
}
if (expr->timeout) {
nft_print(octx, " timeout ");
- time_print(expr->timeout, octx);
+ if (expr->timeout == NFT_NEVER_TIMEOUT)
+ nft_print(octx, "never");
+ else
+ time_print(expr->timeout, octx);
}
- if (!nft_output_stateless(octx) && expr->expiration) {
+ if (!nft_output_stateless(octx) &&
+ expr->timeout != NFT_NEVER_TIMEOUT &&
+ expr->expiration) {
nft_print(octx, " expires ");
time_print(expr->expiration, octx);
}
diff --git a/src/iface.c b/src/iface.c
index 428acaae..a85341a1 100644
--- a/src/iface.c
+++ b/src/iface.c
@@ -171,3 +171,20 @@ char *nft_if_indextoname(unsigned int ifindex, char *name)
}
return NULL;
}
+
+const struct iface *iface_cache_get_next_entry(const struct iface *prev)
+{
+ if (!iface_cache_init)
+ iface_cache_update();
+
+ if (list_empty(&iface_list))
+ return NULL;
+
+ if (!prev)
+ return list_first_entry(&iface_list, struct iface, list);
+
+ if (list_is_last(&prev->list, &iface_list))
+ return NULL;
+
+ return list_next_entry(prev, list);
+}
diff --git a/src/intervals.c b/src/intervals.c
index 6c3f36fe..ff202be9 100644
--- a/src/intervals.c
+++ b/src/intervals.c
@@ -383,7 +383,7 @@ static int setelem_delete(struct list_head *msgs, struct set *set,
struct expr *purge, struct expr *elems,
unsigned int debug_mask)
{
- struct expr *i, *next, *prev = NULL;
+ struct expr *i, *next, *elem, *prev = NULL;
struct range range, prev_range;
int err = 0;
mpz_t rop;
@@ -394,21 +394,26 @@ static int setelem_delete(struct list_head *msgs, struct set *set,
mpz_init(range.high);
mpz_init(rop);
- list_for_each_entry_safe(i, next, &elems->expressions, list) {
- if (i->key->etype == EXPR_SET_ELEM_CATCHALL)
- continue;
+ list_for_each_entry_safe(elem, next, &elems->expressions, list) {
+ i = interval_expr_key(elem);
- range_expr_value_low(range.low, i);
- range_expr_value_high(range.high, i);
+ if (i->key->etype == EXPR_SET_ELEM_CATCHALL) {
+ /* Assume max value to simplify handling. */
+ mpz_bitmask(range.low, i->len);
+ mpz_bitmask(range.high, i->len);
+ } else {
+ range_expr_value_low(range.low, i);
+ range_expr_value_high(range.high, i);
+ }
- if (!prev && i->flags & EXPR_F_REMOVE) {
+ if (!prev && elem->flags & EXPR_F_REMOVE) {
expr_error(msgs, i, "element does not exist");
err = -1;
goto err;
}
- if (!(i->flags & EXPR_F_REMOVE)) {
- prev = i;
+ if (!(elem->flags & EXPR_F_REMOVE)) {
+ prev = elem;
mpz_set(prev_range.low, range.low);
mpz_set(prev_range.high, range.high);
continue;
@@ -416,12 +421,12 @@ static int setelem_delete(struct list_head *msgs, struct set *set,
if (mpz_cmp(prev_range.low, range.low) == 0 &&
mpz_cmp(prev_range.high, range.high) == 0) {
- if (i->flags & EXPR_F_REMOVE) {
+ if (elem->flags & EXPR_F_REMOVE) {
if (prev->flags & EXPR_F_KERNEL)
list_move_tail(&prev->list, &purge->expressions);
- list_del(&i->list);
- expr_free(i);
+ list_del(&elem->list);
+ expr_free(elem);
}
} else if (set->automerge) {
if (setelem_adjust(set, purge, &prev_range, &range, prev, i) < 0) {
@@ -429,7 +434,7 @@ static int setelem_delete(struct list_head *msgs, struct set *set,
err = -1;
goto err;
}
- } else if (i->flags & EXPR_F_REMOVE) {
+ } else if (elem->flags & EXPR_F_REMOVE) {
expr_error(msgs, i, "element does not exist");
err = -1;
goto err;
diff --git a/src/json.c b/src/json.c
index 37530171..b1531ff3 100644
--- a/src/json.c
+++ b/src/json.c
@@ -42,6 +42,15 @@
})
#endif
+static int json_array_extend_new(json_t *array, json_t *other_array)
+{
+ int ret;
+
+ ret = json_array_extend(array, other_array);
+ json_decref(other_array);
+ return ret;
+}
+
static json_t *expr_print_json(const struct expr *expr, struct output_ctx *octx)
{
const struct expr_ops *ops;
@@ -546,8 +555,10 @@ __binop_expr_json(int op, const struct expr *expr, struct output_ctx *octx)
json_t *a = json_array();
if (expr->etype == EXPR_BINOP && expr->op == op) {
- json_array_extend(a, __binop_expr_json(op, expr->left, octx));
- json_array_extend(a, __binop_expr_json(op, expr->right, octx));
+ json_array_extend_new(a,
+ __binop_expr_json(op, expr->left, octx));
+ json_array_extend_new(a,
+ __binop_expr_json(op, expr->right, octx));
} else {
json_array_append_new(a, expr_print_json(expr, octx));
}
@@ -1332,12 +1343,9 @@ json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
json_t *root = json_object(), *flags;
- if (stmt->log.flags & STMT_LOG_PREFIX) {
- char prefix[NF_LOG_PREFIXLEN] = {};
+ if (stmt->log.flags & STMT_LOG_PREFIX)
+ json_object_set_new(root, "prefix", json_string(stmt->log.prefix));
- expr_to_string(stmt->log.prefix, prefix);
- json_object_set_new(root, "prefix", json_string(prefix));
- }
if (stmt->log.flags & STMT_LOG_GROUP)
json_object_set_new(root, "group",
json_integer(stmt->log.group));
@@ -1743,8 +1751,7 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
}
}
- json_array_extend(root, rules);
- json_decref(rules);
+ json_array_extend_new(root, rules);
return root;
}
@@ -1752,7 +1759,7 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
static json_t *do_list_ruleset_json(struct netlink_ctx *ctx, struct cmd *cmd)
{
unsigned int family = cmd->handle.family;
- json_t *root = json_array(), *tmp;
+ json_t *root = json_array();
struct table *table;
list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
@@ -1760,9 +1767,7 @@ static json_t *do_list_ruleset_json(struct netlink_ctx *ctx, struct cmd *cmd)
table->handle.family != family)
continue;
- tmp = table_print_json_full(ctx, table);
- json_array_extend(root, tmp);
- json_decref(tmp);
+ json_array_extend_new(root, table_print_json_full(ctx, table));
}
return root;
diff --git a/src/libnftables.c b/src/libnftables.c
index 0dee1bac..2ae21501 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -17,6 +17,7 @@
#include <cmd.h>
#include <errno.h>
#include <sys/stat.h>
+#include <libgen.h>
static int nft_netlink(struct nft_ctx *nft,
struct list_head *cmds, struct list_head *msgs)
@@ -159,6 +160,7 @@ void nft_ctx_clear_vars(struct nft_ctx *ctx)
}
ctx->num_vars = 0;
free(ctx->vars);
+ ctx->vars = NULL;
}
EXPORT_SYMBOL(nft_ctx_add_include_path);
@@ -201,7 +203,6 @@ struct nft_ctx *nft_ctx_new(uint32_t flags)
nft_init(ctx);
ctx->state = xzalloc(sizeof(struct parser_state));
- nft_ctx_add_include_path(ctx, DEFAULT_INCLUDE_PATH);
ctx->parser_max_errors = 10;
cache_init(&ctx->cache.table_cache);
ctx->top_scope = scope_alloc();
@@ -664,6 +665,7 @@ retry:
/* need to use stat() to, fopen() will block for named fifos and
* libjansson makes no checks before or after open either.
+ * /dev/stdin is *never* used, read() from STDIN_FILENO is used instead.
*/
static struct error_record *filename_is_useable(struct nft_ctx *nft, const char *name)
{
@@ -671,6 +673,9 @@ static struct error_record *filename_is_useable(struct nft_ctx *nft, const char
struct stat sb;
int err;
+ if (!strcmp(name, "/dev/stdin"))
+ return NULL;
+
err = stat(name, &sb);
if (err)
return error(&internal_location, "Could not open file \"%s\": %s\n",
@@ -681,9 +686,6 @@ static struct error_record *filename_is_useable(struct nft_ctx *nft, const char
if (type == S_IFREG || type == S_IFIFO)
return NULL;
- if (type == S_IFCHR && 0 == strcmp(name, "/dev/stdin"))
- return NULL;
-
return error(&internal_location, "Not a regular file: \"%s\"\n", name);
}
@@ -786,6 +788,19 @@ static int nft_run_optimized_file(struct nft_ctx *nft, const char *filename)
return __nft_run_cmd_from_filename(nft, filename);
}
+static int nft_ctx_add_basedir_include_path(struct nft_ctx *nft,
+ const char *filename)
+{
+ char *basedir = xstrdup(filename);
+ int ret;
+
+ ret = nft_ctx_add_include_path(nft, dirname(basedir));
+
+ free(basedir);
+
+ return ret;
+}
+
EXPORT_SYMBOL(nft_run_cmd_from_filename);
int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
{
@@ -794,10 +809,13 @@ int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
if (!strcmp(filename, "-"))
filename = "/dev/stdin";
- if (!strcmp(filename, "/dev/stdin") &&
- !nft_output_json(&nft->output))
+ if (!strcmp(filename, "/dev/stdin"))
nft->stdin_buf = stdin_to_buffer();
+ if (!nft->stdin_buf &&
+ nft_ctx_add_basedir_include_path(nft, filename) < 0)
+ return -1;
+
if (nft->optimize_flags) {
ret = nft_run_optimized_file(nft, filename);
free_const(nft->stdin_buf);
diff --git a/src/mnl.c b/src/mnl.c
index 9e4bfcd9..db53a60b 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -9,6 +9,7 @@
*/
#include <nft.h>
+#include <iface.h>
#include <libmnl/libmnl.h>
#include <libnftnl/common.h>
@@ -45,6 +46,7 @@ struct basehook {
const char *hookfn;
const char *table;
const char *chain;
+ const char *devname;
int family;
int chain_family;
uint32_t num;
@@ -1161,8 +1163,11 @@ struct nftnl_table_list *mnl_nft_table_dump(struct netlink_ctx *ctx,
if (!nlt)
memory_allocation_error();
- nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, family);
- nftnl_table_set_str(nlt, NFTNL_TABLE_NAME, table);
+ if (family != NFPROTO_UNSPEC)
+ nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, family);
+ if (table)
+ nftnl_table_set_str(nlt, NFTNL_TABLE_NAME, table);
+
flags = NLM_F_ACK;
}
@@ -2179,9 +2184,24 @@ static void basehook_free(struct basehook *b)
free_const(b->hookfn);
free_const(b->chain);
free_const(b->table);
+ free_const(b->devname);
free(b);
}
+static bool basehook_eq(const struct basehook *prev, const struct basehook *hook)
+{
+ if (prev->num != hook->num)
+ return false;
+
+ if (prev->devname != NULL && hook->devname != NULL)
+ return strcmp(prev->devname, hook->devname) == 0;
+
+ if (prev->devname == NULL && prev->devname == NULL)
+ return true;
+
+ return false;
+}
+
static void basehook_list_add_tail(struct basehook *b, struct list_head *head)
{
struct basehook *hook;
@@ -2189,7 +2209,7 @@ static void basehook_list_add_tail(struct basehook *b, struct list_head *head)
list_for_each_entry(hook, head, list) {
if (hook->family != b->family)
continue;
- if (hook->num != b->num)
+ if (!basehook_eq(hook, b))
continue;
if (hook->prio < b->prio)
continue;
@@ -2310,6 +2330,7 @@ static int dump_nf_attr_bpf_cb(const struct nlattr *attr, void *data)
struct dump_nf_hook_data {
struct list_head *hook_list;
+ const char *devname;
int family;
};
@@ -2331,6 +2352,7 @@ static int dump_nf_hooks(const struct nlmsghdr *nlh, void *_data)
hook = basehook_alloc();
hook->prio = ntohl(mnl_attr_get_u32(tb[NFNLA_HOOK_PRIORITY]));
+ hook->devname = data->devname ? xstrdup(data->devname) : NULL;
if (tb[NFNLA_HOOK_FUNCTION_NAME])
hook->hookfn = xstrdup(mnl_attr_get_str(tb[NFNLA_HOOK_FUNCTION_NAME]));
@@ -2391,25 +2413,6 @@ static int dump_nf_hooks(const struct nlmsghdr *nlh, void *_data)
hook->family = nfg->nfgen_family;
- /* Netdev hooks potentially interfer with this family datapath. */
- if (hook->family == NFPROTO_NETDEV) {
- switch (data->family) {
- case NFPROTO_IPV4:
- case NFPROTO_IPV6:
- case NFPROTO_INET:
- case NFPROTO_BRIDGE:
- hook->family = data->family;
- hook->num = NF_INET_INGRESS;
- break;
- case NFPROTO_ARP:
- if (hook->chain_family == NFPROTO_NETDEV) {
- hook->family = data->family;
- hook->num = __NF_ARP_INGRESS;
- }
- break;
- }
- }
-
basehook_list_add_tail(hook, data->hook_list);
return MNL_CB_OK;
@@ -2439,6 +2442,7 @@ static int __mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, uint8_t query_family
char buf[MNL_SOCKET_BUFFER_SIZE];
struct dump_nf_hook_data data = {
.hook_list = hook_list,
+ .devname = devname,
.family = query_family,
};
struct nlmsghdr *nlh;
@@ -2478,7 +2482,7 @@ static void print_hooks(struct netlink_ctx *ctx, int family, struct list_head *h
continue;
if (prev) {
- if (prev->num == hook->num) {
+ if (basehook_eq(prev, hook)) {
fprintf(fp, "\n");
same = true;
} else {
@@ -2491,8 +2495,12 @@ static void print_hooks(struct netlink_ctx *ctx, int family, struct list_head *h
prev = hook;
if (!same) {
- fprintf(fp, "\thook %s {\n",
- hooknum2str(family, hook->num));
+ if (hook->devname)
+ fprintf(fp, "\thook %s device %s {\n",
+ hooknum2str(family, hook->num), hook->devname);
+ else
+ fprintf(fp, "\thook %s {\n",
+ hooknum2str(family, hook->num));
}
prio = hook->prio;
@@ -2518,90 +2526,42 @@ static void print_hooks(struct netlink_ctx *ctx, int family, struct list_head *h
fprintf(fp, "}\n");
}
-#define HOOK_FAMILY_MAX 5
-
-static uint8_t hook_family[HOOK_FAMILY_MAX] = {
- NFPROTO_IPV4,
- NFPROTO_IPV6,
- NFPROTO_BRIDGE,
- NFPROTO_ARP,
-};
-
-static int mnl_nft_dump_nf(struct netlink_ctx *ctx, int family, int hook,
- const char *devname, struct list_head *hook_list,
- int *ret)
+static int mnl_nft_dump_nf(struct netlink_ctx *ctx, int family,
+ const char *devname, struct list_head *hook_list)
{
int i, err;
- /* show ingress in first place in hook listing. */
- err = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_INGRESS, devname, hook_list);
- if (err < 0)
- *ret = err;
-
for (i = 0; i <= NF_INET_POST_ROUTING; i++) {
- err = __mnl_nft_dump_nf_hooks(ctx, family, family, i, devname, hook_list);
- if (err < 0)
- *ret = err;
+ int tmp;
+
+ tmp = __mnl_nft_dump_nf_hooks(ctx, family, family, i, devname, hook_list);
+ if (tmp == 0)
+ err = 0;
}
return err;
}
-static int mnl_nft_dump_nf_arp(struct netlink_ctx *ctx, int family, int hook,
- const char *devname, struct list_head *hook_list,
- int *ret)
+static int mnl_nft_dump_nf_arp(struct netlink_ctx *ctx, int family,
+ const char *devname, struct list_head *hook_list)
{
- int err;
-
- /* show ingress in first place in hook listing. */
- err = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_INGRESS, devname, hook_list);
- if (err < 0)
- *ret = err;
+ int err1, err2;
- err = __mnl_nft_dump_nf_hooks(ctx, family, family, NF_ARP_IN, devname, hook_list);
- if (err < 0)
- *ret = err;
- err = __mnl_nft_dump_nf_hooks(ctx, family, family, NF_ARP_OUT, devname, hook_list);
- if (err < 0)
- *ret = err;
+ err1 = __mnl_nft_dump_nf_hooks(ctx, family, family, NF_ARP_IN, devname, hook_list);
+ err2 = __mnl_nft_dump_nf_hooks(ctx, family, family, NF_ARP_OUT, devname, hook_list);
- return err;
+ return err1 ? err2 : err1;
}
-static int mnl_nft_dump_nf_netdev(struct netlink_ctx *ctx, int family, int hook,
- const char *devname, struct list_head *hook_list,
- int *ret)
+static int mnl_nft_dump_nf_netdev(struct netlink_ctx *ctx, int family,
+ const char *devname, struct list_head *hook_list)
{
- int err;
+ int err1, err2;
- err = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_INGRESS, devname, hook_list);
- if (err < 0)
- *ret = err;
+ err1 = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_INGRESS, devname, hook_list);
+ err2 = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_EGRESS, devname, hook_list);
- return err;
-}
-
-static int mnl_nft_dump_nf_decnet(struct netlink_ctx *ctx, int family, int hook,
- const char *devname, struct list_head *hook_list,
- int *ret)
-{
- int i, err;
-
- /* show ingress in first place in hook listing. */
- err = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_INGRESS, devname, hook_list);
- if (err < 0)
- *ret = err;
-
-#define NF_DN_NUMHOOKS 7
- for (i = 0; i < NF_DN_NUMHOOKS; i++) {
- err = __mnl_nft_dump_nf_hooks(ctx, family, family, i, devname, hook_list);
- if (err < 0) {
- *ret = err;
- return err;
- }
- }
-
- return err;
+ return err1 ? err2 : err1;
}
static void release_hook_list(struct list_head *hook_list)
@@ -2612,58 +2572,80 @@ static void release_hook_list(struct list_head *hook_list)
basehook_free(hook);
}
-int mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, int family, int hook, const char *devname)
+static void warn_if_device(struct nft_ctx *nft, const char *devname)
+{
+ if (devname)
+ nft_print(&nft->output, "# device keyword (%s) unexpected for this family\n", devname);
+}
+
+int mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, int family, const char *devname)
{
LIST_HEAD(hook_list);
- unsigned int i;
- int ret;
+ int ret = -1, tmp;
errno = 0;
- ret = 0;
switch (family) {
case NFPROTO_UNSPEC:
- mnl_nft_dump_nf(ctx, NFPROTO_IPV4, hook, devname, &hook_list, &ret);
- mnl_nft_dump_nf(ctx, NFPROTO_IPV6, hook, devname, &hook_list, &ret);
- mnl_nft_dump_nf(ctx, NFPROTO_BRIDGE, hook, devname, &hook_list, &ret);
- mnl_nft_dump_nf_decnet(ctx, NFPROTO_DECNET, hook, devname, &hook_list, &ret);
- break;
+ ret = mnl_nft_dump_nf_hooks(ctx, NFPROTO_ARP, NULL);
+ tmp = mnl_nft_dump_nf_hooks(ctx, NFPROTO_INET, NULL);
+ if (tmp == 0)
+ ret = 0;
+ tmp = mnl_nft_dump_nf_hooks(ctx, NFPROTO_BRIDGE, NULL);
+ if (tmp == 0)
+ ret = 0;
+
+ tmp = mnl_nft_dump_nf_hooks(ctx, NFPROTO_NETDEV, devname);
+ if (tmp == 0)
+ ret = 0;
+
+ return ret;
case NFPROTO_INET:
- mnl_nft_dump_nf(ctx, NFPROTO_IPV4, hook, devname, &hook_list, &ret);
- mnl_nft_dump_nf(ctx, NFPROTO_IPV6, hook, devname, &hook_list, &ret);
+ ret = 0;
+ if (devname)
+ ret = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV,
+ NF_NETDEV_INGRESS, devname, &hook_list);
+ tmp = mnl_nft_dump_nf_hooks(ctx, NFPROTO_IPV4, NULL);
+ if (tmp == 0)
+ ret = 0;
+ tmp = mnl_nft_dump_nf_hooks(ctx, NFPROTO_IPV6, NULL);
+ if (tmp == 0)
+ ret = 0;
+
break;
case NFPROTO_IPV4:
case NFPROTO_IPV6:
case NFPROTO_BRIDGE:
- mnl_nft_dump_nf(ctx, family, hook, devname, &hook_list, &ret);
+ warn_if_device(ctx->nft, devname);
+ ret = mnl_nft_dump_nf(ctx, family, devname, &hook_list);
break;
case NFPROTO_ARP:
- mnl_nft_dump_nf_arp(ctx, family, hook, devname, &hook_list, &ret);
+ warn_if_device(ctx->nft, devname);
+ ret = mnl_nft_dump_nf_arp(ctx, family, devname, &hook_list);
break;
case NFPROTO_NETDEV:
- mnl_nft_dump_nf_netdev(ctx, family, hook, devname, &hook_list, &ret);
- break;
- case NFPROTO_DECNET:
- mnl_nft_dump_nf_decnet(ctx, family, hook, devname, &hook_list, &ret);
- break;
- }
+ if (devname) {
+ ret = mnl_nft_dump_nf_netdev(ctx, family, devname, &hook_list);
+ } else {
+ const struct iface *iface;
+
+ iface = iface_cache_get_next_entry(NULL);
+ ret = 0;
+
+ while (iface) {
+ tmp = mnl_nft_dump_nf_netdev(ctx, family, iface->name, &hook_list);
+ if (tmp == 0)
+ ret = 0;
+
+ iface = iface_cache_get_next_entry(iface);
+ }
+ }
- switch (family) {
- case NFPROTO_UNSPEC:
- for (i = 0; i < HOOK_FAMILY_MAX; i++)
- print_hooks(ctx, hook_family[i], &hook_list);
- break;
- case NFPROTO_INET:
- print_hooks(ctx, NFPROTO_IPV4, &hook_list);
- print_hooks(ctx, NFPROTO_IPV6, &hook_list);
- break;
- default:
- print_hooks(ctx, family, &hook_list);
break;
}
+ print_hooks(ctx, family, &hook_list);
release_hook_list(&hook_list);
- ret = 0;
return ret;
}
diff --git a/src/netlink.c b/src/netlink.c
index 0088b742..25ee3419 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -155,9 +155,14 @@ struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
break;
}
- if (elem->timeout)
- nftnl_set_elem_set_u64(nlse, NFTNL_SET_ELEM_TIMEOUT,
- elem->timeout);
+ if (elem->timeout) {
+ uint64_t timeout = elem->timeout;
+
+ if (elem->timeout == NFT_NEVER_TIMEOUT)
+ timeout = 0;
+
+ nftnl_set_elem_set_u64(nlse, NFTNL_SET_ELEM_TIMEOUT, timeout);
+ }
if (elem->expiration)
nftnl_set_elem_set_u64(nlse, NFTNL_SET_ELEM_EXPIRATION,
elem->expiration);
@@ -1417,8 +1422,12 @@ key_end:
expr = set_elem_expr_alloc(&netlink_location, key);
expr->flags |= EXPR_F_KERNEL;
- if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_TIMEOUT))
+ if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_TIMEOUT)) {
expr->timeout = nftnl_set_elem_get_u64(nlse, NFTNL_SET_ELEM_TIMEOUT);
+ if (expr->timeout == 0)
+ expr->timeout = NFT_NEVER_TIMEOUT;
+ }
+
if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_EXPIRATION))
expr->expiration = nftnl_set_elem_get_u64(nlse, NFTNL_SET_ELEM_EXPIRATION);
if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_USERDATA))
@@ -1769,84 +1778,6 @@ void netlink_dump_flowtable(struct nftnl_flowtable *flo,
fprintf(fp, "\n");
}
-static int list_obj_cb(struct nftnl_obj *nls, void *arg)
-{
- struct netlink_ctx *ctx = arg;
- struct obj *obj;
-
- obj = netlink_delinearize_obj(ctx, nls);
- if (obj == NULL)
- return -1;
- list_add_tail(&obj->list, &ctx->list);
- return 0;
-}
-
-int netlink_reset_objs(struct netlink_ctx *ctx, const struct cmd *cmd,
- uint32_t type, bool dump)
-{
- const struct handle *h = &cmd->handle;
- struct nftnl_obj_list *obj_cache;
- int err;
-
- obj_cache = mnl_nft_obj_dump(ctx, h->family,
- h->table.name, h->obj.name, type, dump, true);
- if (obj_cache == NULL)
- return -1;
-
- err = nftnl_obj_list_foreach(obj_cache, list_obj_cb, ctx);
- nftnl_obj_list_free(obj_cache);
- return err;
-}
-
-int netlink_reset_rules(struct netlink_ctx *ctx, const struct cmd *cmd,
- bool dump)
-{
- const struct handle *h = &cmd->handle;
- struct nft_cache_filter f = {
- .list.table = h->table.name,
- .list.chain = h->chain.name,
- .list.rule_handle = h->handle.id,
- };
- struct rule *rule, *next, *crule, *cnext;
- struct table *table;
- struct chain *chain;
- int ret;
-
- ret = rule_cache_dump(ctx, h, &f, dump, true);
-
- list_for_each_entry_safe(rule, next, &ctx->list, list) {
- table = table_cache_find(&ctx->nft->cache.table_cache,
- rule->handle.table.name,
- rule->handle.family);
- if (!table)
- continue;
-
- chain = chain_cache_find(table, rule->handle.chain.name);
- if (!chain)
- continue;
-
- list_del(&rule->list);
- list_for_each_entry_safe(crule, cnext, &chain->rules, list) {
- if (crule->handle.handle.id != rule->handle.handle.id)
- continue;
-
- list_replace(&crule->list, &rule->list);
- rule_free(crule);
- rule = NULL;
- break;
- }
- if (rule) {
- list_add_tail(&rule->list, &chain->rules);
- }
- }
- list_for_each_entry_safe(rule, next, &ctx->list, list) {
- list_del(&rule->list);
- rule_free(rule);
- }
-
- return ret;
-}
-
struct flowtable *
netlink_delinearize_flowtable(struct netlink_ctx *ctx,
struct nftnl_flowtable *nlo)
@@ -2096,6 +2027,7 @@ restart:
/* Skip unknown and filtered expressions */
desc = lhs->payload.desc;
if (lhs->dtype == &invalid_type ||
+ lhs->payload.tmpl == &proto_unknown_template ||
desc->checksum_key == payload_hdr_field(lhs) ||
desc->format.filter & (1 << payload_hdr_field(lhs))) {
expr_free(lhs);
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index da9f7a91..e3d9cfbb 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -1090,11 +1090,7 @@ static void netlink_parse_log(struct netlink_parse_ctx *ctx,
stmt = log_stmt_alloc(loc);
prefix = nftnl_expr_get_str(nle, NFTNL_EXPR_LOG_PREFIX);
if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_PREFIX)) {
- stmt->log.prefix = constant_expr_alloc(&internal_location,
- &string_type,
- BYTEORDER_HOST_ENDIAN,
- (strlen(prefix) + 1) * BITS_PER_BYTE,
- prefix);
+ stmt->log.prefix = xstrdup(prefix);
stmt->log.flags |= STMT_LOG_PREFIX;
}
if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_GROUP)) {
@@ -2540,7 +2536,7 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
BUG("unknown operation type %d\n", expr->op);
}
expr_free(binop);
- } else if (binop->left->dtype->flags & DTYPE_F_PREFIX &&
+ } else if (datatype_prefix_notation(binop->left->dtype) &&
binop->op == OP_AND && expr->right->etype == EXPR_VALUE &&
expr_mask_is_prefix(binop->right)) {
expr->left = expr_get(binop->left);
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 6204d8fd..77bc5149 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -1117,6 +1117,8 @@ static void netlink_gen_payload_stmt(struct netlink_linearize_ctx *ctx,
}
if ((expr->payload.base == PROTO_BASE_NETWORK_HDR && desc &&
payload_needs_l4csum_update_pseudohdr(expr, desc)) ||
+ (expr->payload.base == PROTO_BASE_TRANSPORT_HDR && desc &&
+ desc == &proto_udp) ||
expr->payload.base == PROTO_BASE_INNER_HDR)
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_FLAGS,
NFT_PAYLOAD_L4CSUM_PSEUDOHDR);
@@ -1146,12 +1148,9 @@ static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx,
struct nftnl_expr *nle;
nle = alloc_nft_expr("log");
- if (stmt->log.prefix != NULL) {
- char prefix[NF_LOG_PREFIXLEN] = {};
+ if (stmt->log.prefix != NULL)
+ nftnl_expr_set_str(nle, NFTNL_EXPR_LOG_PREFIX, stmt->log.prefix);
- expr_to_string(stmt->log.prefix, prefix);
- nftnl_expr_set_str(nle, NFTNL_EXPR_LOG_PREFIX, prefix);
- }
if (stmt->log.flags & STMT_LOG_GROUP) {
nftnl_expr_set_u16(nle, NFTNL_EXPR_LOG_GROUP, stmt->log.group);
if (stmt->log.flags & STMT_LOG_SNAPLEN)
@@ -1592,11 +1591,11 @@ static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
sreg_key = get_register(ctx, stmt->map.key->key);
netlink_gen_expr(ctx, stmt->map.key->key, sreg_key);
- sreg_data = get_register(ctx, stmt->map.data);
- netlink_gen_expr(ctx, stmt->map.data, sreg_data);
+ sreg_data = get_register(ctx, stmt->map.data->key);
+ netlink_gen_expr(ctx, stmt->map.data->key, sreg_data);
release_register(ctx, stmt->map.key->key);
- release_register(ctx, stmt->map.data);
+ release_register(ctx, stmt->map.data->key);
nle = alloc_nft_expr("dynset");
netlink_put_register(nle, NFTNL_EXPR_DYNSET_SREG_KEY, sreg_key);
diff --git a/src/optimize.c b/src/optimize.c
index b90dd995..224c6a52 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -63,6 +63,8 @@ static bool __expr_cmp(const struct expr *expr_a, const struct expr *expr_b)
return false;
if (expr_a->meta.base != expr_b->meta.base)
return false;
+ if (expr_a->meta.inner_desc != expr_b->meta.inner_desc)
+ return false;
break;
case EXPR_CT:
if (expr_a->ct.key != expr_b->ct.key)
@@ -215,9 +217,7 @@ static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b,
if (!stmt_a->log.prefix)
return true;
- if (stmt_a->log.prefix->etype != EXPR_VALUE ||
- stmt_b->log.prefix->etype != EXPR_VALUE ||
- mpz_cmp(stmt_a->log.prefix->value, stmt_b->log.prefix->value))
+ if (strcmp(stmt_a->log.prefix, stmt_b->log.prefix))
return false;
break;
case STMT_REJECT:
@@ -406,13 +406,15 @@ static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
case STMT_LOG:
memcpy(&clone->log, &stmt->log, sizeof(clone->log));
if (stmt->log.prefix)
- clone->log.prefix = expr_get(stmt->log.prefix);
+ clone->log.prefix = xstrdup(stmt->log.prefix);
break;
case STMT_NAT:
if ((stmt->nat.addr &&
- stmt->nat.addr->etype == EXPR_MAP) ||
+ (stmt->nat.addr->etype == EXPR_MAP ||
+ stmt->nat.addr->etype == EXPR_VARIABLE)) ||
(stmt->nat.proto &&
- stmt->nat.proto->etype == EXPR_MAP)) {
+ (stmt->nat.proto->etype == EXPR_MAP ||
+ stmt->nat.proto->etype == EXPR_VARIABLE))) {
clone->ops = &unsupported_stmt_ops;
break;
}
@@ -694,29 +696,36 @@ static void build_verdict_map(struct expr *expr, struct stmt *verdict,
struct expr *set, struct stmt *counter)
{
struct expr *item, *elem, *mapping;
+ struct stmt *counter_elem;
switch (expr->etype) {
case EXPR_LIST:
list_for_each_entry(item, &expr->expressions, list) {
elem = set_elem_expr_alloc(&internal_location, expr_get(item));
- if (counter)
- list_add_tail(&counter->list, &elem->stmt_list);
+ if (counter) {
+ counter_elem = counter_stmt_alloc(&counter->location);
+ list_add_tail(&counter_elem->list, &elem->stmt_list);
+ }
mapping = mapping_expr_alloc(&internal_location, elem,
expr_get(verdict->expr));
compound_expr_add(set, mapping);
}
+ stmt_free(counter);
break;
case EXPR_SET:
list_for_each_entry(item, &expr->expressions, list) {
elem = set_elem_expr_alloc(&internal_location, expr_get(item->key));
- if (counter)
- list_add_tail(&counter->list, &elem->stmt_list);
+ if (counter) {
+ counter_elem = counter_stmt_alloc(&counter->location);
+ list_add_tail(&counter_elem->list, &elem->stmt_list);
+ }
mapping = mapping_expr_alloc(&internal_location, elem,
expr_get(verdict->expr));
compound_expr_add(set, mapping);
}
+ stmt_free(counter);
break;
case EXPR_PREFIX:
case EXPR_RANGE:
@@ -821,8 +830,8 @@ static void __merge_concat_stmts_vmap(const struct optimize_ctx *ctx,
struct expr *set, struct stmt *verdict)
{
struct expr *concat, *next, *elem, *mapping;
+ struct stmt *counter, *counter_elem;
LIST_HEAD(concat_list);
- struct stmt *counter;
counter = zap_counter(ctx, i);
__merge_concat(ctx, i, merge, &concat_list);
@@ -830,13 +839,16 @@ static void __merge_concat_stmts_vmap(const struct optimize_ctx *ctx,
list_for_each_entry_safe(concat, next, &concat_list, list) {
list_del(&concat->list);
elem = set_elem_expr_alloc(&internal_location, concat);
- if (counter)
- list_add_tail(&counter->list, &elem->stmt_list);
+ if (counter) {
+ counter_elem = counter_stmt_alloc(&counter->location);
+ list_add_tail(&counter_elem->list, &elem->stmt_list);
+ }
mapping = mapping_expr_alloc(&internal_location, elem,
expr_get(verdict->expr));
compound_expr_add(set, mapping);
}
+ stmt_free(counter);
}
static void merge_concat_stmts_vmap(const struct optimize_ctx *ctx,
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 61bed761..e2936d10 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -695,7 +695,7 @@ int nft_lex(void *, void *, void *);
%type <string> identifier type_identifier string comment_spec
%destructor { free_const($$); } identifier type_identifier string comment_spec
-%type <val> time_spec time_spec_or_num_s quota_used
+%type <val> time_spec time_spec_or_num_s set_elem_time_spec quota_used
%type <expr> data_type_expr data_type_atom_expr
%destructor { expr_free($$); } data_type_expr data_type_atom_expr
@@ -742,6 +742,8 @@ int nft_lex(void *, void *, void *);
%type <rule> rule rule_alloc
%destructor { rule_free($$); } rule
+%type <val> table_flags table_flag
+
%type <val> set_flag_list set_flag
%type <val> set_policy_spec
@@ -807,8 +809,8 @@ int nft_lex(void *, void *, void *);
%type <val> set_stmt_op
%type <stmt> map_stmt
%destructor { stmt_free($$); } map_stmt
-%type <stmt> meter_stmt meter_stmt_alloc flow_stmt_legacy_alloc
-%destructor { stmt_free($$); } meter_stmt meter_stmt_alloc flow_stmt_legacy_alloc
+%type <stmt> meter_stmt meter_stmt_alloc
+%destructor { stmt_free($$); } meter_stmt meter_stmt_alloc
%type <expr> symbol_expr verdict_expr integer_expr variable_expr chain_expr policy_expr
%destructor { expr_free($$); } symbol_expr verdict_expr integer_expr variable_expr chain_expr policy_expr
@@ -1755,21 +1757,21 @@ reset_cmd : COUNTERS ruleset_spec
}
| RULES table_spec
{
- $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$2, &@$, NULL);
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_TABLE, &$2, &@$, NULL);
}
| RULES TABLE table_spec
{
/* alias of previous rule. */
- $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$3, &@$, NULL);
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_TABLE, &$3, &@$, NULL);
}
| RULES chain_spec
{
- $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$2, &@$, NULL);
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_CHAIN, &$2, &@$, NULL);
}
| RULES CHAIN chain_spec
{
/* alias of previous rule. */
- $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$3, &@$, NULL);
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_CHAIN, &$3, &@$, NULL);
}
| RULE ruleid_spec
{
@@ -1905,20 +1907,9 @@ table_block_alloc : /* empty */
}
;
-table_options : FLAGS STRING
+table_options : FLAGS table_flags
{
- if (strcmp($2, "dormant") == 0) {
- $<table>0->flags |= TABLE_F_DORMANT;
- free_const($2);
- } else if (strcmp($2, "owner") == 0) {
- $<table>0->flags |= TABLE_F_OWNER;
- free_const($2);
- } else {
- erec_queue(error(&@2, "unknown table option %s", $2),
- state->msgs);
- free_const($2);
- YYERROR;
- }
+ $<table>0->flags |= $2;
}
| comment_spec
{
@@ -1930,6 +1921,24 @@ table_options : FLAGS STRING
}
;
+table_flags : table_flag
+ | table_flags COMMA table_flag
+ {
+ $$ = $1 | $3;
+ }
+ ;
+table_flag : STRING
+ {
+ $$ = parse_table_flag($1);
+ free_const($1);
+ if ($$ == 0) {
+ erec_queue(error(&@1, "unknown table option %s", $1),
+ state->msgs);
+ YYERROR;
+ }
+ }
+ ;
+
table_block : /* empty */ { $$ = $<table>-1; }
| table_block common_block
| table_block stmt_separator
@@ -3363,127 +3372,19 @@ log_args : log_arg
log_arg : PREFIX string
{
struct scope *scope = current_scope(state);
- bool done = false, another_var = false;
- char *start, *end, scratch = '\0';
- struct expr *expr, *item;
- struct symbol *sym;
- enum {
- PARSE_TEXT,
- PARSE_VAR,
- } prefix_state;
-
- /* No variables in log prefix, skip. */
- if (!strchr($2, '$')) {
- expr = constant_expr_alloc(&@$, &string_type,
- BYTEORDER_HOST_ENDIAN,
- (strlen($2) + 1) * BITS_PER_BYTE, $2);
- free_const($2);
- $<stmt>0->log.prefix = expr;
- $<stmt>0->log.flags |= STMT_LOG_PREFIX;
- break;
- }
-
- /* Parse variables in log prefix string using a
- * state machine parser with two states. This
- * parser creates list of expressions composed
- * of constant and variable expressions.
- */
- expr = compound_expr_alloc(&@$, EXPR_LIST);
-
- start = (char *)$2;
+ struct error_record *erec;
+ const char *prefix;
- if (*start != '$') {
- prefix_state = PARSE_TEXT;
- } else {
- prefix_state = PARSE_VAR;
- start++;
- }
- end = start;
-
- /* Not nice, but works. */
- while (!done) {
- switch (prefix_state) {
- case PARSE_TEXT:
- while (*end != '\0' && *end != '$')
- end++;
-
- if (*end == '\0')
- done = true;
-
- *end = '\0';
- item = constant_expr_alloc(&@$, &string_type,
- BYTEORDER_HOST_ENDIAN,
- (strlen(start) + 1) * BITS_PER_BYTE,
- start);
- compound_expr_add(expr, item);
-
- if (done)
- break;
-
- start = end + 1;
- end = start;
-
- /* fall through */
- case PARSE_VAR:
- while (isalnum(*end) || *end == '_')
- end++;
-
- if (*end == '\0')
- done = true;
- else if (*end == '$')
- another_var = true;
- else
- scratch = *end;
-
- *end = '\0';
-
- sym = symbol_get(scope, start);
- if (!sym) {
- sym = symbol_lookup_fuzzy(scope, start);
- if (sym) {
- erec_queue(error(&@2, "unknown identifier '%s'; "
- "did you mean identifier ‘%s’?",
- start, sym->identifier),
- state->msgs);
- } else {
- erec_queue(error(&@2, "unknown identifier '%s'",
- start),
- state->msgs);
- }
- expr_free(expr);
- free_const($2);
- YYERROR;
- }
- item = variable_expr_alloc(&@$, scope, sym);
- compound_expr_add(expr, item);
-
- if (done)
- break;
-
- /* Restore original byte after
- * symbol lookup.
- */
- if (scratch) {
- *end = scratch;
- scratch = '\0';
- }
-
- start = end;
- if (another_var) {
- another_var = false;
- start++;
- prefix_state = PARSE_VAR;
- } else {
- prefix_state = PARSE_TEXT;
- }
- end = start;
- break;
- }
+ prefix = str_preprocess(state, &@2, scope, $2, &erec);
+ if (!prefix) {
+ erec_queue(erec, state->msgs);
+ free_const($2);
+ YYERROR;
}
free_const($2);
- $<stmt>0->log.prefix = expr;
- $<stmt>0->log.flags |= STMT_LOG_PREFIX;
+ $<stmt>0->log.prefix = prefix;
+ $<stmt>0->log.flags |= STMT_LOG_PREFIX;
}
| GROUP NUM
{
@@ -4311,33 +4212,7 @@ map_stmt : set_stmt_op set_ref_expr '{' set_elem_expr_stmt COLON set_elem_expr_
}
;
-meter_stmt : flow_stmt_legacy_alloc flow_stmt_opts '{' meter_key_expr stmt '}'
- {
- $1->meter.key = $4;
- $1->meter.stmt = $5;
- $$->location = @$;
- $$ = $1;
- }
- | meter_stmt_alloc { $$ = $1; }
- ;
-
-flow_stmt_legacy_alloc : FLOW
- {
- $$ = meter_stmt_alloc(&@$);
- }
- ;
-
-flow_stmt_opts : flow_stmt_opt
- {
- $<stmt>$ = $<stmt>0;
- }
- | flow_stmt_opts flow_stmt_opt
- ;
-
-flow_stmt_opt : TABLE identifier
- {
- $<stmt>0->meter.name = $2;
- }
+meter_stmt : meter_stmt_alloc { $$ = $1; }
;
meter_stmt_alloc : METER identifier '{' meter_key_expr stmt '}'
@@ -4376,7 +4251,7 @@ variable_expr : '$' identifier
sym = symbol_lookup_fuzzy(scope, $2);
if (sym) {
erec_queue(error(&@2, "unknown identifier '%s'; "
- "did you mean identifier ‘%s’?",
+ "did you mean identifier '%s’?",
$2, sym->identifier),
state->msgs);
} else {
@@ -4670,7 +4545,28 @@ set_elem_options : set_elem_option
| set_elem_options set_elem_option
;
-set_elem_option : TIMEOUT time_spec
+set_elem_time_spec : STRING
+ {
+ struct error_record *erec;
+ uint64_t res;
+
+ if (!strcmp("never", $1)) {
+ free_const($1);
+ $$ = NFT_NEVER_TIMEOUT;
+ break;
+ }
+
+ erec = time_parse(&@1, $1, &res);
+ free_const($1);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+ $$ = res;
+ }
+ ;
+
+set_elem_option : TIMEOUT time_spec
{
$<expr>0->timeout = $2;
}
@@ -4734,11 +4630,6 @@ set_elem_stmt : COUNTER close_scope_counter
}
| LIMIT RATE limit_mode limit_rate_bytes limit_burst_bytes close_scope_limit
{
- if ($5 == 0) {
- erec_queue(error(&@6, "limit burst must be > 0"),
- state->msgs);
- YYERROR;
- }
$$ = limit_stmt_alloc(&@$);
$$->limit.rate = $4.rate;
$$->limit.unit = $4.unit;
@@ -4785,7 +4676,7 @@ set_elem_stmt : COUNTER close_scope_counter
}
;
-set_elem_expr_option : TIMEOUT time_spec
+set_elem_expr_option : TIMEOUT set_elem_time_spec
{
$<expr>0->timeout = $2;
}
diff --git a/src/parser_json.c b/src/parser_json.c
index 418d4ad7..bbe3b1c5 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -181,8 +181,11 @@ static int json_unpack_stmt(struct json_ctx *ctx, json_t *root,
assert(value);
if (json_object_size(root) != 1) {
+ const char *dump = json_dumps(root, 0);
+
json_error(ctx, "Malformed object (too many properties): '%s'.",
- json_dumps(root, 0));
+ dump);
+ free_const(dump);
return 1;
}
@@ -1305,6 +1308,7 @@ static struct expr *json_parse_range_expr(struct json_ctx *ctx,
expr_high = json_parse_primary_expr(ctx, high);
if (!expr_high) {
json_error(ctx, "Invalid high value in range expression.");
+ expr_free(expr_low);
return NULL;
}
return range_expr_alloc(int_loc, expr_low, expr_high);
@@ -1886,6 +1890,8 @@ static struct stmt *json_parse_mangle_stmt(struct json_ctx *ctx,
return stmt;
default:
json_error(ctx, "Invalid mangle statement key expression type.");
+ expr_free(key);
+ expr_free(value);
return NULL;
}
}
@@ -2374,7 +2380,7 @@ static void json_parse_set_stmt_list(struct json_ctx *ctx,
json_t *stmt_json)
{
struct list_head *head;
- struct stmt *tmp;
+ struct stmt *stmt;
json_t *value;
size_t index;
@@ -2386,9 +2392,14 @@ static void json_parse_set_stmt_list(struct json_ctx *ctx,
head = stmt_list;
json_array_foreach(stmt_json, index, value) {
- tmp = json_parse_stmt(ctx, value);
- list_add(&tmp->list, head);
- head = &tmp->list;
+ stmt = json_parse_stmt(ctx, value);
+ if (!stmt) {
+ json_error(ctx, "Parsing set statements array at index %zd failed.", index);
+ stmt_list_free(stmt_list);
+ return;
+ }
+ list_add(&stmt->list, head);
+ head = &stmt->list;
}
}
@@ -2565,9 +2576,7 @@ static struct stmt *json_parse_log_stmt(struct json_ctx *ctx,
stmt = log_stmt_alloc(int_loc);
if (!json_unpack(value, "{s:s}", "prefix", &tmpstr)) {
- stmt->log.prefix = constant_expr_alloc(int_loc, &string_type,
- BYTEORDER_HOST_ENDIAN,
- (strlen(tmpstr) + 1) * BITS_PER_BYTE, tmpstr);
+ stmt->log.prefix = xstrdup(tmpstr);
stmt->log.flags |= STMT_LOG_PREFIX;
}
if (!json_unpack(value, "{s:i}", "group", &tmp)) {
@@ -2887,6 +2896,7 @@ static struct stmt *json_parse_optstrip_stmt(struct json_ctx *ctx,
expr->etype != EXPR_EXTHDR ||
expr->exthdr.op != NFT_EXTHDR_OP_TCPOPT) {
json_error(ctx, "Illegal TCP optstrip argument");
+ expr_free(expr);
return NULL;
}
@@ -2966,6 +2976,45 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
return NULL;
}
+static int json_parse_table_flags(struct json_ctx *ctx, json_t *root,
+ enum table_flags *flags)
+{
+ json_t *tmp, *tmp2;
+ size_t index;
+ int flag;
+
+ if (json_unpack(root, "{s:o}", "flags", &tmp))
+ return 0;
+
+ if (json_is_string(tmp)) {
+ flag = parse_table_flag(json_string_value(tmp));
+ if (flag) {
+ *flags = flag;
+ return 0;
+ }
+ json_error(ctx, "Invalid table flag '%s'.",
+ json_string_value(tmp));
+ return 1;
+ }
+ if (!json_is_array(tmp)) {
+ json_error(ctx, "Unexpected table flags value.");
+ return 1;
+ }
+ json_array_foreach(tmp, index, tmp2) {
+ if (json_is_string(tmp2)) {
+ flag = parse_table_flag(json_string_value(tmp2));
+
+ if (flag) {
+ *flags |= flag;
+ continue;
+ }
+ }
+ json_error(ctx, "Invalid table flag at index %zu.", index);
+ return 1;
+ }
+ return 0;
+}
+
static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
enum cmd_ops op, enum cmd_obj obj)
{
@@ -2974,6 +3023,7 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
.table.location = *int_loc,
};
struct table *table = NULL;
+ enum table_flags flags = 0;
if (json_unpack_err(ctx, root, "{s:s}",
"family", &family))
@@ -2984,6 +3034,9 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
return NULL;
json_unpack(root, "{s:s}", "comment", &comment);
+ if (json_parse_table_flags(ctx, root, &flags))
+ return NULL;
+
} else if (op == CMD_DELETE &&
json_unpack(root, "{s:s}", "name", &h.table.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
@@ -2997,10 +3050,12 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
if (h.table.name)
h.table.name = xstrdup(h.table.name);
- if (comment) {
+ if (comment || flags) {
table = table_alloc();
handle_merge(&table->handle, &h);
- table->comment = xstrdup(comment);
+ if (comment)
+ table->comment = xstrdup(comment);
+ table->flags = flags;
}
if (op == CMD_ADD)
@@ -3118,8 +3173,7 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
chain->hook.name = chain_hookname_lookup(hookstr);
if (!chain->hook.name) {
json_error(ctx, "Invalid chain hook '%s'.", hookstr);
- chain_free(chain);
- return NULL;
+ goto err_free_chain;
}
json_unpack(root, "{s:o}", "dev", &devs);
@@ -3128,8 +3182,7 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
chain->dev_expr = json_parse_devs(ctx, devs);
if (!chain->dev_expr) {
json_error(ctx, "Invalid chain dev.");
- chain_free(chain);
- return NULL;
+ goto err_free_chain;
}
}
@@ -3137,8 +3190,7 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
chain->policy = parse_policy(policy);
if (!chain->policy) {
json_error(ctx, "Unknown policy '%s'.", policy);
- chain_free(chain);
- return NULL;
+ goto err_free_chain;
}
}
@@ -3147,6 +3199,11 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
handle_merge(&chain->handle, &h);
return cmd_alloc(op, obj, &h, int_loc, chain);
+
+err_free_chain:
+ chain_free(chain);
+ handle_free(&h);
+ return NULL;
}
static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
@@ -3186,6 +3243,7 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
if (!json_is_array(tmp)) {
json_error(ctx, "Value of property \"expr\" must be an array.");
+ handle_free(&h);
return NULL;
}
@@ -3205,16 +3263,14 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
if (!json_is_object(value)) {
json_error(ctx, "Unexpected expr array element of type %s, expected object.",
json_typename(value));
- rule_free(rule);
- return NULL;
+ goto err_free_rule;
}
stmt = json_parse_stmt(ctx, value);
if (!stmt) {
json_error(ctx, "Parsing expr array at index %zd failed.", index);
- rule_free(rule);
- return NULL;
+ goto err_free_rule;
}
rule_stmt_append(rule, stmt);
@@ -3224,6 +3280,11 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
json_object_del(root, "handle");
return cmd_alloc(op, obj, &h, int_loc, rule);
+
+err_free_rule:
+ rule_free(rule);
+ handle_free(&h);
+ return NULL;
}
static int string_to_nft_object(const char *str)
@@ -3335,8 +3396,10 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
} else if ((set->data = json_parse_dtype_expr(ctx, tmp))) {
set->flags |= NFT_SET_MAP;
} else {
- json_error(ctx, "Invalid map type '%s'.",
- json_dumps(tmp, 0));
+ const char *dump = json_dumps(tmp, 0);
+
+ json_error(ctx, "Invalid map type '%s'.", dump);
+ free_const(dump);
set_free(set);
handle_free(&h);
return NULL;
@@ -3602,8 +3665,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
if (ret < 0 || ret >= (int)sizeof(obj->secmark.ctx)) {
json_error(ctx, "Invalid secmark context '%s', max length is %zu.",
tmp, sizeof(obj->secmark.ctx));
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
}
break;
@@ -3619,8 +3681,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
ret >= (int)sizeof(obj->ct_helper.name)) {
json_error(ctx, "Invalid CT helper type '%s', max length is %zu.",
tmp, sizeof(obj->ct_helper.name));
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
}
if (!json_unpack(root, "{s:s}", "protocol", &tmp)) {
@@ -3630,15 +3691,13 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj->ct_helper.l4proto = IPPROTO_UDP;
} else {
json_error(ctx, "Invalid ct helper protocol '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
}
if (!json_unpack(root, "{s:s}", "l3proto", &tmp) &&
parse_family(tmp, &l3proto)) {
json_error(ctx, "Invalid ct helper l3proto '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
obj->ct_helper.l3proto = l3proto;
break;
@@ -3652,23 +3711,19 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj->ct_timeout.l4proto = IPPROTO_UDP;
} else {
json_error(ctx, "Invalid ct timeout protocol '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
}
if (!json_unpack(root, "{s:s}", "l3proto", &tmp) &&
parse_family(tmp, &l3proto)) {
json_error(ctx, "Invalid ct timeout l3proto '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
obj->ct_timeout.l3proto = l3proto;
init_list_head(&obj->ct_timeout.timeout_list);
- if (json_parse_ct_timeout_policy(ctx, root, obj)) {
- obj_free(obj);
- return NULL;
- }
+ if (json_parse_ct_timeout_policy(ctx, root, obj))
+ goto err_free_obj;
break;
case NFT_OBJECT_CT_EXPECT:
cmd_obj = CMD_OBJ_CT_EXPECT;
@@ -3676,8 +3731,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
if (!json_unpack(root, "{s:s}", "l3proto", &tmp) &&
parse_family(tmp, &l3proto)) {
json_error(ctx, "Invalid ct expectation l3proto '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
obj->ct_expect.l3proto = l3proto;
if (!json_unpack(root, "{s:s}", "protocol", &tmp)) {
@@ -3687,8 +3741,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj->ct_expect.l4proto = IPPROTO_UDP;
} else {
json_error(ctx, "Invalid ct expectation protocol '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
}
if (!json_unpack(root, "{s:i}", "dport", &i))
@@ -3702,10 +3755,9 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj->type = NFT_OBJECT_LIMIT;
if (json_unpack_err(ctx, root, "{s:I, s:s}",
"rate", &obj->limit.rate,
- "per", &tmp)) {
- obj_free(obj);
- return NULL;
- }
+ "per", &tmp))
+ goto err_free_obj;
+
json_unpack(root, "{s:s}", "rate_unit", &rate_unit);
json_unpack(root, "{s:b}", "inv", &inv);
json_unpack(root, "{s:i}", "burst", &obj->limit.burst);
@@ -3726,20 +3778,18 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
case CMD_OBJ_SYNPROXY:
obj->type = NFT_OBJECT_SYNPROXY;
if (json_unpack_err(ctx, root, "{s:i, s:i}",
- "mss", &i, "wscale", &j)) {
- obj_free(obj);
- return NULL;
- }
+ "mss", &i, "wscale", &j))
+ goto err_free_obj;
+
obj->synproxy.mss = i;
obj->synproxy.wscale = j;
obj->synproxy.flags |= NF_SYNPROXY_OPT_MSS;
obj->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE;
if (!json_unpack(root, "{s:o}", "flags", &jflags)) {
flags = json_parse_synproxy_flags(ctx, jflags);
- if (flags < 0) {
- obj_free(obj);
- return NULL;
- }
+ if (flags < 0)
+ goto err_free_obj;
+
obj->synproxy.flags |= flags;
}
break;
@@ -3751,6 +3801,11 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
json_object_del(root, "handle");
return cmd_alloc(op, cmd_obj, &h, int_loc, obj);
+
+err_free_obj:
+ obj_free(obj);
+ handle_free(&h);
+ return NULL;
}
static struct cmd *json_parse_cmd_add(struct json_ctx *ctx,
@@ -3865,8 +3920,7 @@ static struct cmd *json_parse_cmd_replace(struct json_ctx *ctx,
if (!json_is_object(value)) {
json_error(ctx, "Unexpected expr array element of type %s, expected object.",
json_typename(value));
- rule_free(rule);
- return NULL;
+ goto err_free_replace;
}
stmt = json_parse_stmt(ctx, value);
@@ -3874,8 +3928,7 @@ static struct cmd *json_parse_cmd_replace(struct json_ctx *ctx,
if (!stmt) {
json_error(ctx, "Parsing expr array at index %zd failed.",
index);
- rule_free(rule);
- return NULL;
+ goto err_free_replace;
}
rule_stmt_append(rule, stmt);
@@ -3885,6 +3938,11 @@ static struct cmd *json_parse_cmd_replace(struct json_ctx *ctx,
json_object_del(root, "handle");
return cmd_alloc(op, CMD_OBJ_RULE, &h, int_loc, rule);
+
+err_free_replace:
+ rule_free(rule);
+ handle_free(&h);
+ return NULL;
}
static struct cmd *json_parse_cmd_list_multiple(struct json_ctx *ctx,
@@ -4314,6 +4372,13 @@ int nft_parse_json_filename(struct nft_ctx *nft, const char *filename,
json_error_t err;
int ret;
+ if (nft->stdin_buf) {
+ json_indesc.type = INDESC_STDIN;
+ json_indesc.name = "/dev/stdin";
+
+ return nft_parse_json_buffer(nft, nft->stdin_buf, msgs, cmds);
+ }
+
json_indesc.type = INDESC_FILE;
json_indesc.name = filename;
diff --git a/src/preprocess.c b/src/preprocess.c
new file mode 100644
index 00000000..619f67a1
--- /dev/null
+++ b/src/preprocess.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2013-2024 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <utils.h>
+
+#include "list.h"
+#include "parser.h"
+#include "erec.h"
+
+struct str_buf {
+ uint8_t *str;
+ uint32_t len;
+ uint32_t size;
+};
+
+#define STR_BUF_LEN 128
+
+static struct str_buf *str_buf_alloc(void)
+{
+ struct str_buf *buf;
+
+ buf = xzalloc(sizeof(*buf));
+ buf->str = xzalloc_array(1, STR_BUF_LEN);
+ buf->size = STR_BUF_LEN;
+
+ return buf;
+}
+
+static int str_buf_add(struct str_buf *buf, const char *str, uint32_t len)
+{
+ uint8_t *tmp;
+
+ if (len + buf->len > buf->size) {
+ buf->size = (len + buf->len) * 2;
+ tmp = xrealloc(buf->str, buf->size);
+ buf->str = tmp;
+ }
+
+ memcpy(&buf->str[buf->len], str, len);
+ buf->len += len;
+
+ return 0;
+}
+
+struct str_chunk {
+ struct list_head list;
+ char *str;
+ uint32_t len;
+ bool is_sym;
+};
+
+static void add_str_chunk(const char *x, int from, int to, struct list_head *list, bool is_sym)
+{
+ struct str_chunk *chunk;
+ int len = to - from;
+
+ chunk = xzalloc_array(1, sizeof(*chunk));
+ chunk->str = xzalloc_array(1, len + 1);
+ chunk->is_sym = is_sym;
+ chunk->len = len;
+ memcpy(chunk->str, &x[from], len);
+
+ list_add_tail(&chunk->list, list);
+}
+
+static void free_str_chunk(struct str_chunk *chunk)
+{
+ free(chunk->str);
+ free(chunk);
+}
+
+const char *str_preprocess(struct parser_state *state, struct location *loc,
+ struct scope *scope, const char *x,
+ struct error_record **erec)
+{
+ struct str_chunk *chunk, *next;
+ struct str_buf *buf;
+ const char *str;
+ int i, j, start;
+ LIST_HEAD(list);
+
+ start = 0;
+ i = 0;
+ while (1) {
+ if (x[i] == '\0') {
+ i++;
+ break;
+ }
+
+ if (x[i] != '$') {
+ i++;
+ continue;
+ }
+
+ if (isdigit(x[++i]))
+ continue;
+
+ j = i;
+ while (1) {
+ if (isalpha(x[i]) ||
+ isdigit(x[i]) ||
+ x[i] == '_') {
+ i++;
+ continue;
+ }
+ break;
+ }
+ add_str_chunk(x, start, j-1, &list, false);
+ add_str_chunk(x, j, i, &list, true);
+ start = i;
+ }
+ if (start != i)
+ add_str_chunk(x, start, i, &list, false);
+
+ buf = str_buf_alloc();
+
+ list_for_each_entry_safe(chunk, next, &list, list) {
+ if (chunk->is_sym) {
+ struct symbol *sym;
+
+ sym = symbol_lookup(scope, chunk->str);
+ if (!sym) {
+ sym = symbol_lookup_fuzzy(scope, chunk->str);
+ if (sym) {
+ *erec = error(loc, "unknown identifier '%s'; "
+ "did you mean identifier '%s'?",
+ chunk->str, sym->identifier);
+ } else {
+ *erec = error(loc, "unknown identifier '%s'",
+ chunk->str);
+ }
+ goto err;
+ }
+ str_buf_add(buf, sym->expr->identifier,
+ strlen(sym->expr->identifier));
+ } else {
+ str_buf_add(buf, chunk->str, chunk->len);
+ }
+ list_del(&chunk->list);
+ free_str_chunk(chunk);
+ }
+
+ str = (char *)buf->str;
+
+ free(buf);
+
+ return (char *)str;
+err:
+ list_for_each_entry_safe(chunk, next, &list, list) {
+ list_del(&chunk->list);
+ free_str_chunk(chunk);
+ }
+ free(buf->str);
+ free(buf);
+
+ return NULL;
+}
diff --git a/src/proto.c b/src/proto.c
index 553b6a44..05ddb070 100644
--- a/src/proto.c
+++ b/src/proto.c
@@ -535,8 +535,6 @@ const struct proto_desc proto_udp = {
.name = "udp",
.id = PROTO_DESC_UDP,
.base = PROTO_BASE_TRANSPORT_HDR,
- .checksum_key = UDPHDR_CHECKSUM,
- .checksum_type = NFT_PAYLOAD_CSUM_INET,
.templates = {
[UDPHDR_SPORT] = INET_SERVICE("sport", struct udphdr, source),
[UDPHDR_DPORT] = INET_SERVICE("dport", struct udphdr, dest),
diff --git a/src/rule.c b/src/rule.c
index 45289cc0..9bc160ec 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -1215,6 +1215,7 @@ struct table *table_lookup_fuzzy(const struct handle *h,
static const char *table_flags_name[TABLE_FLAGS_MAX] = {
"dormant",
"owner",
+ "persist",
};
const char *table_flag_name(uint32_t flag)
@@ -1225,6 +1226,17 @@ const char *table_flag_name(uint32_t flag)
return table_flags_name[flag];
}
+unsigned int parse_table_flag(const char *name)
+{
+ int i;
+
+ for (i = 0; i < TABLE_FLAGS_MAX; i++) {
+ if (!strcmp(name, table_flags_name[i]))
+ return 1 << i;
+ }
+ return 0;
+}
+
static void table_print_flags(const struct table *table, const char **delim,
struct output_ctx *octx)
{
@@ -2095,12 +2107,15 @@ static void flowtable_print_declaration(const struct flowtable *flowtable,
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, flowtable->handle.handle.id);
nft_print(octx, "%s", opts->nl);
- nft_print(octx, "%s%shook %s priority %s%s",
- opts->tab, opts->tab,
- hooknum2str(NFPROTO_NETDEV, flowtable->hook.num),
- prio2str(octx, priobuf, sizeof(priobuf), NFPROTO_NETDEV,
- flowtable->hook.num, flowtable->priority.expr),
- opts->stmt_separator);
+
+ if (flowtable->priority.expr) {
+ nft_print(octx, "%s%shook %s priority %s%s",
+ opts->tab, opts->tab,
+ hooknum2str(NFPROTO_NETDEV, flowtable->hook.num),
+ prio2str(octx, priobuf, sizeof(priobuf), NFPROTO_NETDEV,
+ flowtable->hook.num, flowtable->priority.expr),
+ opts->stmt_separator);
+ }
if (flowtable->dev_array_len > 0) {
nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab);
@@ -2326,12 +2341,8 @@ static int do_list_set(struct netlink_ctx *ctx, struct cmd *cmd,
static int do_list_hooks(struct netlink_ctx *ctx, struct cmd *cmd)
{
const char *devname = cmd->handle.obj.name;
- int hooknum = -1;
- if (cmd->handle.chain.name)
- hooknum = cmd->handle.chain_id;
-
- return mnl_nft_dump_nf_hooks(ctx, cmd->handle.family, hooknum, devname);
+ return mnl_nft_dump_nf_hooks(ctx, cmd->handle.family, devname);
}
static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
@@ -2451,58 +2462,12 @@ static int do_command_get(struct netlink_ctx *ctx, struct cmd *cmd)
static int do_command_reset(struct netlink_ctx *ctx, struct cmd *cmd)
{
- struct obj *obj, *next;
- struct table *table;
- bool dump = false;
- uint32_t type;
- int ret;
-
switch (cmd->obj) {
- case CMD_OBJ_COUNTERS:
- dump = true;
- /* fall through */
- case CMD_OBJ_COUNTER:
- type = NFT_OBJECT_COUNTER;
- break;
- case CMD_OBJ_QUOTAS:
- dump = true;
- /* fall through */
- case CMD_OBJ_QUOTA:
- type = NFT_OBJECT_QUOTA;
- break;
- case CMD_OBJ_RULES:
- ret = netlink_reset_rules(ctx, cmd, true);
- if (ret < 0)
- return ret;
-
- return do_command_list(ctx, cmd);
- case CMD_OBJ_RULE:
- return netlink_reset_rules(ctx, cmd, false);
case CMD_OBJ_ELEMENTS:
return do_get_setelems(ctx, cmd, true);
- case CMD_OBJ_SET:
- case CMD_OBJ_MAP:
- ret = netlink_list_setelems(ctx, &cmd->handle, cmd->set, true);
- if (ret < 0)
- return ret;
-
- return do_command_list(ctx, cmd);
default:
- BUG("invalid command object type %u\n", cmd->obj);
- }
-
- ret = netlink_reset_objs(ctx, cmd, type, dump);
- list_for_each_entry_safe(obj, next, &ctx->list, list) {
- table = table_cache_find(&ctx->nft->cache.table_cache,
- obj->handle.table.name,
- obj->handle.family);
- if (!obj_cache_find(table, obj->handle.obj.name, obj->type)) {
- list_del(&obj->list);
- obj_cache_add(obj, table);
- }
+ break;
}
- if (ret < 0)
- return ret;
return do_command_list(ctx, cmd);
}
diff --git a/src/scanner.l b/src/scanner.l
index e4d20e69..c825fa79 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -132,48 +132,47 @@ slash \/
timestring ([0-9]+d)?([0-9]+h)?([0-9]+m)?([0-9]+s)?([0-9]+ms)?
hex4 ([[:xdigit:]]{1,4})
+rfc4291_broader (((:{hex4}){2})|(:{ip4addr}))
v680 (({hex4}:){7}{hex4})
-v670 ((:)((:{hex4}){7}))
-v671 ((({hex4}:){1})((:{hex4}){6}))
-v672 ((({hex4}:){2})((:{hex4}){5}))
-v673 ((({hex4}:){3})((:{hex4}){4}))
-v674 ((({hex4}:){4})((:{hex4}){3}))
-v675 ((({hex4}:){5})((:{hex4}){2}))
+v670 ((:)((:{hex4}){5}){rfc4291_broader})
+v671 ((({hex4}:){1})((:{hex4}){4}){rfc4291_broader})
+v672 ((({hex4}:){2})((:{hex4}){3}){rfc4291_broader})
+v673 ((({hex4}:){3})((:{hex4}){2}){rfc4291_broader})
+v674 ((({hex4}:){4})((:{hex4}){1}){rfc4291_broader})
+v675 ((({hex4}:){5}){rfc4291_broader})
v676 ((({hex4}:){6})(:{hex4}{1}))
v677 ((({hex4}:){7})(:))
v67 ({v670}|{v671}|{v672}|{v673}|{v674}|{v675}|{v676}|{v677})
-v660 ((:)((:{hex4}){6}))
-v661 ((({hex4}:){1})((:{hex4}){5}))
-v662 ((({hex4}:){2})((:{hex4}){4}))
-v663 ((({hex4}:){3})((:{hex4}){3}))
-v664 ((({hex4}:){4})((:{hex4}){2}))
+v660 ((:)((:{hex4}){4}){rfc4291_broader})
+v661 ((({hex4}:){1})((:{hex4}){3}){rfc4291_broader})
+v662 ((({hex4}:){2})((:{hex4}){2}){rfc4291_broader})
+v663 ((({hex4}:){3})((:{hex4}){1}){rfc4291_broader})
+v664 ((({hex4}:){4}){rfc4291_broader})
v665 ((({hex4}:){5})((:{hex4}){1}))
v666 ((({hex4}:){6})(:))
v66 ({v660}|{v661}|{v662}|{v663}|{v664}|{v665}|{v666})
-v650 ((:)((:{hex4}){5}))
-v651 ((({hex4}:){1})((:{hex4}){4}))
-v652 ((({hex4}:){2})((:{hex4}){3}))
-v653 ((({hex4}:){3})((:{hex4}){2}))
+v650 ((:)((:{hex4}){3}){rfc4291_broader})
+v651 ((({hex4}:){1})((:{hex4}){2}){rfc4291_broader})
+v652 ((({hex4}:){2})((:{hex4}){1}){rfc4291_broader})
+v653 ((({hex4}:){3}){rfc4291_broader})
v654 ((({hex4}:){4})(:{hex4}{1}))
v655 ((({hex4}:){5})(:))
v65 ({v650}|{v651}|{v652}|{v653}|{v654}|{v655})
-v640 ((:)((:{hex4}){4}))
-v641 ((({hex4}:){1})((:{hex4}){3}))
-v642 ((({hex4}:){2})((:{hex4}){2}))
+v640 ((:)((:{hex4}){2}){rfc4291_broader})
+v641 ((({hex4}:){1})((:{hex4}){1}){rfc4291_broader})
+v642 ((({hex4}:){2}){rfc4291_broader})
v643 ((({hex4}:){3})((:{hex4}){1}))
v644 ((({hex4}:){4})(:))
v64 ({v640}|{v641}|{v642}|{v643}|{v644})
-v630 ((:)((:{hex4}){3}))
-v631 ((({hex4}:){1})((:{hex4}){2}))
+v630 ((:)((:{hex4}){1}){rfc4291_broader})
+v631 ((({hex4}:){1}){rfc4291_broader})
v632 ((({hex4}:){2})((:{hex4}){1}))
v633 ((({hex4}:){3})(:))
v63 ({v630}|{v631}|{v632}|{v633})
-v620 ((:)((:{hex4}){2}))
-v620_rfc4291 ((:)(:{ip4addr}))
+v620 ((:){rfc4291_broader})
v621 ((({hex4}:){1})((:{hex4}){1}))
v622 ((({hex4}:){2})(:))
-v62_rfc4291 ((:)(:[fF]{4})(:{ip4addr}))
-v62 ({v620}|{v621}|{v622}|{v62_rfc4291}|{v620_rfc4291})
+v62 ({v620}|{v621}|{v622})
v610 ((:)(:{hex4}{1}))
v611 ((({hex4}:){1})(:))
v61 ({v610}|{v611})
@@ -1176,39 +1175,58 @@ static bool search_in_include_path(const char *filename)
filename[0] != '/');
}
+static int include_path_glob(struct nft_ctx *nft, void *scanner,
+ const char *include_path, const char *filename,
+ const struct location *loc)
+{
+ struct parser_state *state = yyget_extra(scanner);
+ struct error_record *erec;
+ char buf[PATH_MAX];
+ int ret;
+
+ ret = snprintf(buf, sizeof(buf), "%s/%s", include_path, filename);
+ if (ret < 0 || ret >= PATH_MAX) {
+ erec = error(loc, "Too long file path \"%s/%s\"\n",
+ include_path, filename);
+ erec_queue(erec, state->msgs);
+ return -1;
+ }
+
+ ret = include_glob(nft, scanner, buf, loc);
+
+ /* error was already handled */
+ if (ret == -1)
+ return -1;
+ /* no wildcards and file was processed: break early. */
+ if (ret == 0)
+ return 0;
+
+ /* else 1 (no wildcards) or 2 (wildcards): keep
+ * searching.
+ */
+ return ret;
+}
+
int scanner_include_file(struct nft_ctx *nft, void *scanner,
const char *filename, const struct location *loc)
{
struct parser_state *state = yyget_extra(scanner);
struct error_record *erec;
- char buf[PATH_MAX];
unsigned int i;
int ret = -1;
if (search_in_include_path(filename)) {
for (i = 0; i < nft->num_include_paths; i++) {
- ret = snprintf(buf, sizeof(buf), "%s/%s",
- nft->include_paths[i], filename);
- if (ret < 0 || ret >= PATH_MAX) {
- erec = error(loc, "Too long file path \"%s/%s\"\n",
- nft->include_paths[i], filename);
- erec_queue(erec, state->msgs);
- return -1;
- }
-
- ret = include_glob(nft, scanner, buf, loc);
-
- /* error was already handled */
- if (ret == -1)
- return -1;
- /* no wildcards and file was processed: break early. */
- if (ret == 0)
- return 0;
-
- /* else 1 (no wildcards) or 2 (wildcards): keep
- * searching.
- */
+ ret = include_path_glob(nft, scanner,
+ nft->include_paths[i],
+ filename, loc);
+ if (ret <= 0)
+ return ret;
}
+ ret = include_path_glob(nft, scanner, DEFAULT_INCLUDE_PATH,
+ filename, loc);
+ if (ret <= 0)
+ return ret;
} else {
/* an absolute path (starts with '/') */
ret = include_glob(nft, scanner, filename, loc);
diff --git a/src/segtree.c b/src/segtree.c
index 5e6f857f..2e32a329 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -402,7 +402,7 @@ void concat_range_aggregate(struct expr *set)
}
if (prefix_len < 0 ||
- !(r1->dtype->flags & DTYPE_F_PREFIX)) {
+ !datatype_prefix_notation(r1->dtype)) {
tmp = range_expr_alloc(&r1->location, r1,
r2);
@@ -517,7 +517,7 @@ add_interval(struct expr *set, struct expr *low, struct expr *i)
expr = expr_get(low);
} else if (range_is_prefix(range) && !mpz_cmp_ui(p, 0)) {
- if (i->dtype->flags & DTYPE_F_PREFIX)
+ if (datatype_prefix_notation(i->dtype))
expr = interval_to_prefix(low, i, range);
else if (expr_basetype(i)->type == TYPE_STRING)
expr = interval_to_string(low, i, range);
@@ -629,8 +629,10 @@ void interval_map_decompose(struct expr *set)
expr_free(i);
out:
- if (catchall)
+ if (catchall) {
+ catchall->flags |= EXPR_F_KERNEL;
compound_expr_add(set, catchall);
+ }
free(ranges);
free(elements);
diff --git a/src/statement.c b/src/statement.c
index ab144d63..551cd13f 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -377,12 +377,8 @@ int log_level_parse(const char *level)
static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
nft_print(octx, "log");
- if (stmt->log.flags & STMT_LOG_PREFIX) {
- char prefix[NF_LOG_PREFIXLEN] = {};
-
- expr_to_string(stmt->log.prefix, prefix);
- nft_print(octx, " prefix \"%s\"", prefix);
- }
+ if (stmt->log.flags & STMT_LOG_PREFIX)
+ nft_print(octx, " prefix \"%s\"", stmt->log.prefix);
if (stmt->log.flags & STMT_LOG_GROUP)
nft_print(octx, " group %u", stmt->log.group);
if (stmt->log.flags & STMT_LOG_SNAPLEN)
@@ -419,7 +415,7 @@ static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
static void log_stmt_destroy(struct stmt *stmt)
{
- expr_free(stmt->log.prefix);
+ free_const(stmt->log.prefix);
}
static const struct stmt_ops log_stmt_ops = {
diff --git a/tests/py/inet/udp.t.payload b/tests/py/inet/udp.t.payload
index e6beda7f..32f7f8c3 100644
--- a/tests/py/inet/udp.t.payload
+++ b/tests/py/inet/udp.t.payload
@@ -236,7 +236,7 @@ inet test-inet input
[ meta load l4proto => reg 1 ]
[ cmp eq reg 1 0x00000011 ]
[ immediate reg 1 0x00000000 ]
- [ payload write reg 1 => 2b @ transport header + 6 csum_type 1 csum_off 6 csum_flags 0x0 ]
+ [ payload write reg 1 => 2b @ transport header + 6 csum_type 0 csum_off 0 csum_flags 0x1 ]
# iif "lo" udp dport set 65535
inet test-inet input
@@ -245,4 +245,4 @@ inet test-inet input
[ meta load l4proto => reg 1 ]
[ cmp eq reg 1 0x00000011 ]
[ immediate reg 1 0x0000ffff ]
- [ payload write reg 1 => 2b @ transport header + 2 csum_type 1 csum_off 6 csum_flags 0x0 ]
+ [ payload write reg 1 => 2b @ transport header + 2 csum_type 0 csum_off 0 csum_flags 0x1 ]
diff --git a/tests/py/ip/icmp.t.json.output b/tests/py/ip/icmp.t.json.output
index 52fd6016..d79e72b5 100644
--- a/tests/py/ip/icmp.t.json.output
+++ b/tests/py/ip/icmp.t.json.output
@@ -1,27 +1,3 @@
-# icmp code { 2, 4, 54, 33, 56}
-[
- {
- "match": {
- "left": {
- "payload": {
- "field": "code",
- "protocol": "icmp"
- }
- },
- "op": "==",
- "right": {
- "set": [
- 2,
- 4,
- 33,
- 54,
- 56
- ]
- }
- }
- }
-]
-
# icmp id 1245 log
[
{
diff --git a/tests/py/ip/sets.t b/tests/py/ip/sets.t
index 46d9686b..ad2c8316 100644
--- a/tests/py/ip/sets.t
+++ b/tests/py/ip/sets.t
@@ -66,3 +66,5 @@ ip saddr @set6 drop;ok
ip saddr vmap { 1.1.1.1 : drop, * : accept };ok
meta mark set ip saddr map { 1.1.1.1 : 0x00000001, * : 0x00000002 };ok
+!map2 type ipv4_addr . ipv4_addr . inet_service : ipv4_addr . inet_service;ok
+add @map2 { ip saddr . ip daddr . th dport : 10.0.0.1 . 80 };ok
diff --git a/tests/py/ip/sets.t.json b/tests/py/ip/sets.t.json
index 44ca1528..f2637d93 100644
--- a/tests/py/ip/sets.t.json
+++ b/tests/py/ip/sets.t.json
@@ -303,3 +303,40 @@
}
]
+# add @map2 { ip saddr . ip daddr . th dport : 10.0.0.1 . 80 }
+[
+ {
+ "map": {
+ "data": {
+ "concat": [
+ "10.0.0.1",
+ 80
+ ]
+ },
+ "elem": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ },
+ {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip"
+ }
+ },
+ {
+ "payload": {
+ "field": "dport",
+ "protocol": "th"
+ }
+ }
+ ]
+ },
+ "map": "@map2",
+ "op": "add"
+ }
+ }
+]
diff --git a/tests/py/ip/sets.t.payload.inet b/tests/py/ip/sets.t.payload.inet
index fd6517a5..cc04b43d 100644
--- a/tests/py/ip/sets.t.payload.inet
+++ b/tests/py/ip/sets.t.payload.inet
@@ -104,3 +104,14 @@ inet
[ payload load 4b @ network header + 12 => reg 1 ]
[ lookup reg 1 set __map%d dreg 1 ]
[ meta set mark with reg 1 ]
+
+# add @map2 { ip saddr . ip daddr . th dport : 10.0.0.1 . 80 }
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x00000002 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ payload load 4b @ network header + 16 => reg 9 ]
+ [ payload load 2b @ transport header + 2 => reg 10 ]
+ [ immediate reg 11 0x0100000a ]
+ [ immediate reg 2 0x00005000 ]
+ [ dynset add reg_key 1 set map2 sreg_data 11 ]
diff --git a/tests/py/ip/sets.t.payload.ip b/tests/py/ip/sets.t.payload.ip
index d9cc32b6..f9ee1f98 100644
--- a/tests/py/ip/sets.t.payload.ip
+++ b/tests/py/ip/sets.t.payload.ip
@@ -81,3 +81,11 @@ ip test-ip4 input
[ meta load mark => reg 10 ]
[ dynset add reg_key 1 set map1 sreg_data 10 ]
+# add @map2 { ip saddr . ip daddr . th dport : 10.0.0.1 . 80 }
+ip test-ip4 input
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ payload load 4b @ network header + 16 => reg 9 ]
+ [ payload load 2b @ transport header + 2 => reg 10 ]
+ [ immediate reg 11 0x0100000a ]
+ [ immediate reg 2 0x00005000 ]
+ [ dynset add reg_key 1 set map2 sreg_data 11 ]
diff --git a/tests/py/ip/sets.t.payload.netdev b/tests/py/ip/sets.t.payload.netdev
index d41b9e8b..3d0dc79a 100644
--- a/tests/py/ip/sets.t.payload.netdev
+++ b/tests/py/ip/sets.t.payload.netdev
@@ -105,3 +105,13 @@ netdev test-netdev ingress
[ meta load mark => reg 10 ]
[ dynset add reg_key 1 set map1 sreg_data 10 ]
+# add @map2 { ip saddr . ip daddr . th dport : 10.0.0.1 . 80 }
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ payload load 4b @ network header + 16 => reg 9 ]
+ [ payload load 2b @ transport header + 2 => reg 10 ]
+ [ immediate reg 11 0x0100000a ]
+ [ immediate reg 2 0x00005000 ]
+ [ dynset add reg_key 1 set map2 sreg_data 11 ]
diff --git a/tests/py/ip6/icmpv6.t.json.output b/tests/py/ip6/icmpv6.t.json.output
index f29b346c..5d33780e 100644
--- a/tests/py/ip6/icmpv6.t.json.output
+++ b/tests/py/ip6/icmpv6.t.json.output
@@ -93,68 +93,6 @@
}
]
-# icmpv6 code 4
-[
- {
- "match": {
- "left": {
- "payload": {
- "field": "code",
- "protocol": "icmpv6"
- }
- },
- "op": "==",
- "right": 4
- }
- }
-]
-
-# icmpv6 code 3-66
-[
- {
- "match": {
- "left": {
- "payload": {
- "field": "code",
- "protocol": "icmpv6"
- }
- },
- "op": "==",
- "right": {
- "range": [
- 3,
- 66
- ]
- }
- }
- }
-]
-
-# icmpv6 code {5, 6, 7} accept
-[
- {
- "match": {
- "left": {
- "payload": {
- "field": "code",
- "protocol": "icmpv6"
- }
- },
- "op": "==",
- "right": {
- "set": [
- 5,
- 6,
- 7
- ]
- }
- }
- },
- {
- "accept": null
- }
-]
-
# icmpv6 code { 3-66}
[
{
diff --git a/tests/py/ip6/sets.t b/tests/py/ip6/sets.t
index 17fd62f5..cc26bd22 100644
--- a/tests/py/ip6/sets.t
+++ b/tests/py/ip6/sets.t
@@ -46,3 +46,6 @@ add @set5 { ip6 saddr . ip6 daddr };ok
add @map1 { ip6 saddr . ip6 daddr : meta mark };ok
delete @set5 { ip6 saddr . ip6 daddr };ok
+
+!map2 type ipv6_addr . ipv6_addr . inet_service : ipv6_addr . inet_service;ok
+add @map2 { ip6 saddr . ip6 daddr . th dport : 1234::1 . 80 };ok \ No newline at end of file
diff --git a/tests/py/ip6/sets.t.json b/tests/py/ip6/sets.t.json
index 2029d2b5..99236099 100644
--- a/tests/py/ip6/sets.t.json
+++ b/tests/py/ip6/sets.t.json
@@ -148,3 +148,40 @@
}
]
+# add @map2 { ip6 saddr . ip6 daddr . th dport : 1234::1 . 80 }
+[
+ {
+ "map": {
+ "data": {
+ "concat": [
+ "1234::1",
+ 80
+ ]
+ },
+ "elem": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip6"
+ }
+ },
+ {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip6"
+ }
+ },
+ {
+ "payload": {
+ "field": "dport",
+ "protocol": "th"
+ }
+ }
+ ]
+ },
+ "map": "@map2",
+ "op": "add"
+ }
+ }
+]
diff --git a/tests/py/ip6/sets.t.payload.inet b/tests/py/ip6/sets.t.payload.inet
index 2bbd5573..2dbb818a 100644
--- a/tests/py/ip6/sets.t.payload.inet
+++ b/tests/py/ip6/sets.t.payload.inet
@@ -47,3 +47,14 @@ inet test-inet input
[ payload load 16b @ network header + 8 => reg 1 ]
[ payload load 16b @ network header + 24 => reg 2 ]
[ dynset delete reg_key 1 set set5 ]
+
+# add @map2 { ip6 saddr . ip6 daddr . th dport : 1234::1 . 80 }
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x0000000a ]
+ [ payload load 16b @ network header + 8 => reg 1 ]
+ [ payload load 16b @ network header + 24 => reg 2 ]
+ [ payload load 2b @ transport header + 2 => reg 3 ]
+ [ immediate reg 17 0x00003412 0x00000000 0x00000000 0x01000000 ]
+ [ immediate reg 21 0x00005000 ]
+ [ dynset add reg_key 1 set map2 sreg_data 17 ]
diff --git a/tests/py/ip6/sets.t.payload.ip6 b/tests/py/ip6/sets.t.payload.ip6
index c59f7b5c..7234b989 100644
--- a/tests/py/ip6/sets.t.payload.ip6
+++ b/tests/py/ip6/sets.t.payload.ip6
@@ -36,3 +36,11 @@ ip6 test-ip6 input
[ meta load mark => reg 3 ]
[ dynset add reg_key 1 set map1 sreg_data 3 ]
+# add @map2 { ip6 saddr . ip6 daddr . th dport : 1234::1 . 80 }
+ip6 test-ip6 input
+ [ payload load 16b @ network header + 8 => reg 1 ]
+ [ payload load 16b @ network header + 24 => reg 2 ]
+ [ payload load 2b @ transport header + 2 => reg 3 ]
+ [ immediate reg 17 0x00003412 0x00000000 0x00000000 0x01000000 ]
+ [ immediate reg 21 0x00005000 ]
+ [ dynset add reg_key 1 set map2 sreg_data 17 ]
diff --git a/tests/py/ip6/sets.t.payload.netdev b/tests/py/ip6/sets.t.payload.netdev
index 1866d26b..2ad0f434 100644
--- a/tests/py/ip6/sets.t.payload.netdev
+++ b/tests/py/ip6/sets.t.payload.netdev
@@ -48,3 +48,13 @@ netdev test-netdev ingress
[ meta load mark => reg 3 ]
[ dynset add reg_key 1 set map1 sreg_data 3 ]
+# add @map2 { ip6 saddr . ip6 daddr . th dport : 1234::1 . 80 }
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ payload load 16b @ network header + 8 => reg 1 ]
+ [ payload load 16b @ network header + 24 => reg 2 ]
+ [ payload load 2b @ transport header + 2 => reg 3 ]
+ [ immediate reg 17 0x00003412 0x00000000 0x00000000 0x01000000 ]
+ [ immediate reg 21 0x00005000 ]
+ [ dynset add reg_key 1 set map2 sreg_data 17 ]
diff --git a/tests/py/nft-test.py b/tests/py/nft-test.py
index 1bc89558..00799e28 100755
--- a/tests/py/nft-test.py
+++ b/tests/py/nft-test.py
@@ -1164,6 +1164,10 @@ def set_process(set_line, filename, lineno):
set_data = tokens[i+1]
i += 2
+ while len(tokens) > i and tokens[i] == ".":
+ set_data += " . " + tokens[i+1]
+ i += 2
+
if parse_typeof and tokens[i] == "mark":
set_data += " " + tokens[i]
i += 1;
diff --git a/tests/shell/features/elem_timeout_update.sh b/tests/shell/features/elem_timeout_update.sh
new file mode 100755
index 00000000..6243170a
--- /dev/null
+++ b/tests/shell/features/elem_timeout_update.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# 4201f3938914 ("netfilter: nf_tables: set element timeout update support")
+
+$NFT -f - <<EOF
+table ip t {
+ set s {
+ typeof ip saddr
+ timeout 1m
+ elements = { 1.2.3.4 }
+ }
+}
+EOF
+
+$NFT add element t s { 1.2.3.4 expires 1ms }
+
+sleep 0.001
+$NFT get element t s { 1.2.3.4 }
+
+[ $? -eq 0 ] && exit 111
+
+exit 0
diff --git a/tests/shell/features/ip_options.nft b/tests/shell/features/ip_options.nft
new file mode 100644
index 00000000..0b8cb09c
--- /dev/null
+++ b/tests/shell/features/ip_options.nft
@@ -0,0 +1,8 @@
+# dbb5281a1f84 ("netfilter: nf_tables: add support for matching IPv4 options")
+# v5.3-rc1~140^2~153^2~1
+
+table ip x {
+ chain y {
+ ip option ra value 255
+ }
+}
diff --git a/tests/shell/features/ipsec.nft b/tests/shell/features/ipsec.nft
new file mode 100644
index 00000000..e7252271
--- /dev/null
+++ b/tests/shell/features/ipsec.nft
@@ -0,0 +1,7 @@
+# 6c47260250fc ("netfilter: nf_tables: add xfrm expression")
+# v4.20-rc1~14^2~125^2~25
+table ip x {
+ chain y {
+ ipsec in reqid 23
+ }
+}
diff --git a/tests/shell/features/position_id.sh b/tests/shell/features/position_id.sh
new file mode 100755
index 00000000..43ac97ac
--- /dev/null
+++ b/tests/shell/features/position_id.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# 75dd48e2e420 ("netfilter: nf_tables: Support RULE_ID reference in new rule")
+# v5.1-rc1~178^2~405^2~27
+
+EXPECTED="table inet t {
+ chain c {
+ tcp dport 1234 accept
+ udp dport 4321 accept
+ accept
+ }
+}"
+
+RULESET="add table inet t
+add chain inet t c
+add rule inet t c tcp dport 1234 accept
+add rule inet t c accept
+insert rule inet t c index 1 udp dport 4321 accept
+"
+
+$NFT -f - <<< $RULESET
+
+diff -u <($NFT list ruleset) - <<<"$EXPECTED"
diff --git a/tests/shell/features/table_flag_persist.nft b/tests/shell/features/table_flag_persist.nft
new file mode 100644
index 00000000..0da3e6d4
--- /dev/null
+++ b/tests/shell/features/table_flag_persist.nft
@@ -0,0 +1,3 @@
+table t {
+ flags persist;
+}
diff --git a/tests/shell/testcases/bogons/nft-f/flowtable-no-priority-crash b/tests/shell/testcases/bogons/nft-f/flowtable-no-priority-crash
new file mode 100644
index 00000000..627e66d6
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/flowtable-no-priority-crash
@@ -0,0 +1,5 @@
+table inet filter {
+ flowtable f {
+ devices = { lo }
+ }
+}
diff --git a/tests/shell/testcases/cache/0011_index_0 b/tests/shell/testcases/cache/0011_index_0
index c9eb8683..76f2615d 100755
--- a/tests/shell/testcases/cache/0011_index_0
+++ b/tests/shell/testcases/cache/0011_index_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_position_id)
+
set -e
RULESET="flush ruleset
diff --git a/tests/shell/testcases/chains/0003jump_loop_1 b/tests/shell/testcases/chains/0003jump_loop_1
index 80e243f0..1a8eaf68 100755
--- a/tests/shell/testcases/chains/0003jump_loop_1
+++ b/tests/shell/testcases/chains/0003jump_loop_1
@@ -5,8 +5,9 @@ set -e
MAX_JUMPS=16
$NFT add table t
+$NFT "add chain t c1 { type filter hook prerouting priority 0; }"
-for i in $(seq 1 $MAX_JUMPS)
+for i in $(seq 2 $MAX_JUMPS)
do
$NFT add chain t c${i}
done
diff --git a/tests/shell/testcases/chains/0010endless_jump_loop_1 b/tests/shell/testcases/chains/0010endless_jump_loop_1
index 5d3ef239..6000e5d7 100755
--- a/tests/shell/testcases/chains/0010endless_jump_loop_1
+++ b/tests/shell/testcases/chains/0010endless_jump_loop_1
@@ -3,7 +3,7 @@
set -e
$NFT add table t
-$NFT add chain t c
+$NFT add chain "t c { type filter hook input priority 0; }"
# kernel should return ELOOP
$NFT add rule t c tcp dport vmap {1 : jump c} 2>/dev/null || exit 0
diff --git a/tests/shell/testcases/chains/0011endless_jump_loop_1 b/tests/shell/testcases/chains/0011endless_jump_loop_1
index d75932d7..66abf8d0 100755
--- a/tests/shell/testcases/chains/0011endless_jump_loop_1
+++ b/tests/shell/testcases/chains/0011endless_jump_loop_1
@@ -3,7 +3,7 @@
set -e
$NFT add table t
-$NFT add chain t c1
+$NFT add chain "t c1 { type filter hook forward priority 0; }"
$NFT add chain t c2
$NFT add map t m {type inet_service : verdict \;}
$NFT add element t m {2 : jump c2}
diff --git a/tests/shell/testcases/chains/0018check_jump_loop_1 b/tests/shell/testcases/chains/0018check_jump_loop_1
index b87520f2..1e674d3d 100755
--- a/tests/shell/testcases/chains/0018check_jump_loop_1
+++ b/tests/shell/testcases/chains/0018check_jump_loop_1
@@ -3,7 +3,7 @@
set -e
$NFT add table ip filter
-$NFT add chain ip filter ap1
+$NFT add chain ip filter ap1 "{ type filter hook input priority 0; }"
$NFT add chain ip filter ap2
$NFT add rule ip filter ap1 jump ap2
diff --git a/tests/shell/testcases/chains/dumps/0003jump_loop_1.json-nft b/tests/shell/testcases/chains/dumps/0003jump_loop_1.json-nft
index ceef3224..d197e123 100644
--- a/tests/shell/testcases/chains/dumps/0003jump_loop_1.json-nft
+++ b/tests/shell/testcases/chains/dumps/0003jump_loop_1.json-nft
@@ -19,7 +19,11 @@
"family": "ip",
"table": "t",
"name": "c1",
- "handle": 0
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 0,
+ "policy": "accept"
}
},
{
diff --git a/tests/shell/testcases/chains/dumps/0003jump_loop_1.nft b/tests/shell/testcases/chains/dumps/0003jump_loop_1.nft
index 7054cde4..8d89bc40 100644
--- a/tests/shell/testcases/chains/dumps/0003jump_loop_1.nft
+++ b/tests/shell/testcases/chains/dumps/0003jump_loop_1.nft
@@ -1,5 +1,6 @@
table ip t {
chain c1 {
+ type filter hook prerouting priority filter; policy accept;
jump c2
}
diff --git a/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.json-nft b/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.json-nft
index db64cdbc..af99873d 100644
--- a/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.json-nft
+++ b/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.json-nft
@@ -19,7 +19,11 @@
"family": "ip",
"table": "t",
"name": "c",
- "handle": 0
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
}
}
]
diff --git a/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.nft b/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.nft
index 1e0d1d60..62fefaff 100644
--- a/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.nft
+++ b/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.nft
@@ -1,4 +1,5 @@
table ip t {
chain c {
+ type filter hook input priority filter; policy accept;
}
}
diff --git a/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.json-nft b/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.json-nft
index e1a2262f..75a4d895 100644
--- a/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.json-nft
+++ b/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.json-nft
@@ -19,7 +19,11 @@
"family": "ip",
"table": "t",
"name": "c1",
- "handle": 0
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 0,
+ "policy": "accept"
}
},
{
diff --git a/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.nft b/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.nft
index ca0a7378..d35736e8 100644
--- a/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.nft
+++ b/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.nft
@@ -5,6 +5,7 @@ table ip t {
}
chain c1 {
+ type filter hook forward priority filter; policy accept;
tcp dport vmap @m
}
diff --git a/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.json-nft b/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.json-nft
index 7294c841..ac7e1199 100644
--- a/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.json-nft
+++ b/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.json-nft
@@ -19,7 +19,11 @@
"family": "ip",
"table": "filter",
"name": "ap1",
- "handle": 0
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
}
},
{
diff --git a/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.nft b/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.nft
index 437900bc..bdd0ead7 100644
--- a/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.nft
+++ b/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.nft
@@ -1,5 +1,6 @@
table ip filter {
chain ap1 {
+ type filter hook input priority filter; policy accept;
jump ap2
}
diff --git a/tests/shell/testcases/chains/dumps/jump_to_base_chain.nodump b/tests/shell/testcases/chains/dumps/jump_to_base_chain.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/jump_to_base_chain.nodump
diff --git a/tests/shell/testcases/chains/dumps/netdev_chain_dormant_autoremove.json-nft b/tests/shell/testcases/chains/dumps/netdev_chain_dormant_autoremove.json-nft
new file mode 100644
index 00000000..9151d42f
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/netdev_chain_dormant_autoremove.json-nft
@@ -0,0 +1,32 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "netdev",
+ "name": "test",
+ "handle": 0,
+ "flags": "dormant"
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "test",
+ "name": "ingress",
+ "handle": 0,
+ "dev": "dummy1",
+ "type": "filter",
+ "hook": "ingress",
+ "prio": 0,
+ "policy": "drop"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/netdev_chain_dormant_autoremove.nft b/tests/shell/testcases/chains/dumps/netdev_chain_dormant_autoremove.nft
new file mode 100644
index 00000000..aad7cb63
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/netdev_chain_dormant_autoremove.nft
@@ -0,0 +1,7 @@
+table netdev test {
+ flags dormant
+
+ chain ingress {
+ type filter hook ingress device "dummy1" priority filter; policy drop;
+ }
+}
diff --git a/tests/shell/testcases/chains/jump_to_base_chain b/tests/shell/testcases/chains/jump_to_base_chain
new file mode 100755
index 00000000..d71da4cf
--- /dev/null
+++ b/tests/shell/testcases/chains/jump_to_base_chain
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+$NFT -f - <<EOF
+table t {
+ chain i {
+ type filter hook input priority 0
+ }
+
+ chain o {
+ type filter hook output priority 0
+ jump c
+ }
+
+ chain c {
+ jump i
+ }
+}
+EOF
+
+if [ $? -eq 0 ];then
+ echo "E: Accepted jump to a base chain"
+ exit 1
+fi
+
+exit 0
diff --git a/tests/shell/testcases/chains/netdev_chain_dormant_autoremove b/tests/shell/testcases/chains/netdev_chain_dormant_autoremove
new file mode 100755
index 00000000..3093ce25
--- /dev/null
+++ b/tests/shell/testcases/chains/netdev_chain_dormant_autoremove
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_netdev_chain_multidevice)
+
+set -e
+
+ip link add dummy0 type dummy
+ip link add dummy1 type dummy
+$NFT add table netdev test { flags dormant\; }
+$NFT add chain netdev test ingress { type filter hook ingress devices = { "dummy0", "dummy1" } priority 0\; policy drop\; }
+ip link del dummy0
diff --git a/tests/shell/testcases/listing/reset_objects b/tests/shell/testcases/listing/reset_objects
new file mode 100755
index 00000000..0b6720b6
--- /dev/null
+++ b/tests/shell/testcases/listing/reset_objects
@@ -0,0 +1,104 @@
+#!/bin/bash
+
+set -e
+
+load_ruleset()
+{
+ $NFT -f - <<EOF
+table ip test {
+ quota https-quota {
+ 25 mbytes used 10 mbytes
+ }
+ counter https-counter {
+ packets 10 bytes 4096
+ }
+}
+EOF
+}
+
+check_list_quota()
+{
+ EXPECT="table ip test {
+ quota https-quota {
+ 25 mbytes
+ }
+}"
+ $DIFF -u <(echo "$EXPECT") <($NFT list quotas)
+}
+
+check_list_counter()
+{
+ EXPECT="table ip test {
+ counter https-counter {
+ packets 0 bytes 0
+ }
+}"
+ $DIFF -u <(echo "$EXPECT") <($NFT list counters)
+}
+
+load_ruleset
+
+EXPECT="table ip test {
+ quota https-quota {
+ 25 mbytes used 10 mbytes
+ }
+}"
+$DIFF -u <(echo "$EXPECT") <($NFT reset quotas)
+
+check_list_quota
+$NFT flush ruleset
+load_ruleset
+
+EXPECT="table ip test {
+ quota https-quota {
+ 25 mbytes used 10 mbytes
+ }
+}"
+$DIFF -u <(echo "$EXPECT") <($NFT reset quotas ip)
+
+check_list_quota
+$NFT flush ruleset
+load_ruleset
+
+EXPECT="table ip test {
+ quota https-quota {
+ 25 mbytes used 10 mbytes
+ }
+}"
+$DIFF -u <(echo "$EXPECT") <($NFT reset quota ip test https-quota)
+
+check_list_quota
+$NFT flush ruleset
+load_ruleset
+
+EXPECT="table ip test {
+ counter https-counter {
+ packets 10 bytes 4096
+ }
+}"
+$DIFF -u <(echo "$EXPECT") <($NFT reset counters)
+
+check_list_counter
+$NFT flush ruleset
+load_ruleset
+
+EXPECT="table ip test {
+ counter https-counter {
+ packets 10 bytes 4096
+ }
+}"
+$DIFF -u <(echo "$EXPECT") <($NFT reset counters ip)
+
+check_list_counter
+$NFT flush ruleset
+load_ruleset
+
+EXPECT="table ip test {
+ counter https-counter {
+ packets 10 bytes 4096
+ }
+}"
+$DIFF -u <(echo "$EXPECT") <($NFT reset counter ip test https-counter)
+
+check_list_counter
+$NFT flush ruleset
diff --git a/tests/shell/testcases/maps/0024named_objects_1 b/tests/shell/testcases/maps/0024named_objects_1
new file mode 100755
index 00000000..a861e9e2
--- /dev/null
+++ b/tests/shell/testcases/maps/0024named_objects_1
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# This is the test-case:
+# * creating valid named objects and using map variables in statements
+
+RULESET='
+define counter_map = { 192.168.2.2 : "user123", 1.1.1.1 : "user123", 2.2.2.2 : "user123" }
+define quota_map = { 192.168.2.2 : "user124", 192.168.2.3 : "user124" }
+
+table inet x {
+ counter user123 {
+ packets 12 bytes 1433
+ }
+ counter user321 {
+ packets 12 bytes 1433
+ }
+ quota user123 {
+ over 2000 bytes
+ }
+ quota user124 {
+ over 2000 bytes
+ }
+ chain y {
+ type filter hook input priority 0; policy accept;
+ counter name ip saddr map $counter_map
+ quota name ip saddr map $quota_map drop
+ }
+}'
+
+set -e
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/maps/0024named_objects_2 b/tests/shell/testcases/maps/0024named_objects_2
new file mode 100755
index 00000000..584b5100
--- /dev/null
+++ b/tests/shell/testcases/maps/0024named_objects_2
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+#
+# Test some error conditions for using variables to define maps
+#
+
+set -e
+
+for m in "192.168.2.2" "{ 192.168.2.2, 1.1.1.1, 2.2.2.2 }"; do
+
+ RULESET="
+define m = $m"'
+table inet x {
+ chain y {
+ type filter hook input priority 0; policy accept;
+ counter name ip saddr map $m
+ }
+}'
+
+ $NFT -f - <<< "$RULESET" || rc=$?
+ test $rc = 1
+
+done
diff --git a/tests/shell/testcases/maps/anonymous_snat_map_1 b/tests/shell/testcases/maps/anonymous_snat_map_1
new file mode 100755
index 00000000..031de0c1
--- /dev/null
+++ b/tests/shell/testcases/maps/anonymous_snat_map_1
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# Variable containing anonymous map can be added to a snat rule
+
+set -e
+
+RULESET='
+define m = {1.1.1.1 : 2.2.2.2}
+table nat {
+ chain postrouting {
+ snat ip saddr map $m
+ }
+}
+'
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/maps/anonymous_snat_map_2 b/tests/shell/testcases/maps/anonymous_snat_map_2
new file mode 100755
index 00000000..90e02038
--- /dev/null
+++ b/tests/shell/testcases/maps/anonymous_snat_map_2
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+#
+# Test some error conditions for using variables to define maps
+#
+
+set -e
+
+for m in "1.1.1.1" "{1.1.1.1}"; do
+
+ RULESET="
+define m = $m"'
+table nat {
+ chain postrouting {
+ snat ip saddr map $m
+ }
+}
+'
+
+ $NFT -f - <<< "$RULESET" || rc=$?
+ test $rc = 1
+
+done
diff --git a/tests/shell/testcases/maps/delete_element b/tests/shell/testcases/maps/delete_element
new file mode 100755
index 00000000..75272f44
--- /dev/null
+++ b/tests/shell/testcases/maps/delete_element
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+set -e
+
+RULESET="flush ruleset
+
+table ip x {
+ map m {
+ typeof ct bytes : meta priority
+ flags interval
+ elements = {
+ 0-2048000 : 1:0001,
+ 2048001-4000000 : 1:0002,
+ }
+ }
+
+ chain y {
+ type filter hook output priority 0; policy accept;
+
+ meta priority set ct bytes map @m
+ }
+}"
+
+$NFT -f - <<< $RULESET
+
+$NFT delete element ip x m { 0-2048000 }
+$NFT add element ip x m { 0-2048000 : 1:0002 }
+$NFT delete element ip x m { 0-2048000 : 1:0002 }
diff --git a/tests/shell/testcases/maps/delete_element_catchall b/tests/shell/testcases/maps/delete_element_catchall
new file mode 100755
index 00000000..a6a0fc6f
--- /dev/null
+++ b/tests/shell/testcases/maps/delete_element_catchall
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_catchall_element)
+
+set -e
+
+RULESET="flush ruleset
+
+table ip x {
+ map m {
+ typeof ct bytes : meta priority
+ flags interval
+ elements = {
+ 0-2048000 : 1:0001,
+ * : 1:0002,
+ }
+ }
+
+ chain y {
+ type filter hook output priority 0; policy accept;
+
+ meta priority set ct bytes map @m
+ }
+}"
+
+$NFT -f - <<< $RULESET
+
+$NFT delete element ip x m { 0-2048000 }
+$NFT add element ip x m { 0-2048000 : 1:0002 }
+$NFT delete element ip x m { 0-2048000 : 1:0002 }
+
+$NFT 'delete element ip x m { * }'
+$NFT 'add element ip x m { * : 1:0003 }'
+$NFT 'delete element ip x m { * : 1:0003 }'
+$NFT 'add element ip x m { * : 1:0003 }'
diff --git a/tests/shell/testcases/maps/dumps/0024named_objects_1.json-nft b/tests/shell/testcases/maps/dumps/0024named_objects_1.json-nft
new file mode 100644
index 00000000..e3fab16d
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0024named_objects_1.json-nft
@@ -0,0 +1,147 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "y",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "counter": {
+ "family": "inet",
+ "name": "user123",
+ "table": "x",
+ "handle": 0,
+ "packets": 12,
+ "bytes": 1433
+ }
+ },
+ {
+ "counter": {
+ "family": "inet",
+ "name": "user321",
+ "table": "x",
+ "handle": 0,
+ "packets": 12,
+ "bytes": 1433
+ }
+ },
+ {
+ "quota": {
+ "family": "inet",
+ "name": "user123",
+ "table": "x",
+ "handle": 0,
+ "bytes": 2000,
+ "used": 0,
+ "inv": true
+ }
+ },
+ {
+ "quota": {
+ "family": "inet",
+ "name": "user124",
+ "table": "x",
+ "handle": 0,
+ "bytes": 2000,
+ "used": 0,
+ "inv": true
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "counter": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "1.1.1.1",
+ "user123"
+ ],
+ [
+ "2.2.2.2",
+ "user123"
+ ],
+ [
+ "192.168.2.2",
+ "user123"
+ ]
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "quota": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "192.168.2.2",
+ "user124"
+ ],
+ [
+ "192.168.2.3",
+ "user124"
+ ]
+ ]
+ }
+ }
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/0024named_objects_1.nft b/tests/shell/testcases/maps/dumps/0024named_objects_1.nft
new file mode 100644
index 00000000..a8e99a3c
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0024named_objects_1.nft
@@ -0,0 +1,23 @@
+table inet x {
+ counter user123 {
+ packets 12 bytes 1433
+ }
+
+ counter user321 {
+ packets 12 bytes 1433
+ }
+
+ quota user123 {
+ over 2000 bytes
+ }
+
+ quota user124 {
+ over 2000 bytes
+ }
+
+ chain y {
+ type filter hook input priority filter; policy accept;
+ counter name ip saddr map { 1.1.1.1 : "user123", 2.2.2.2 : "user123", 192.168.2.2 : "user123" }
+ quota name ip saddr map { 192.168.2.2 : "user124", 192.168.2.3 : "user124" } drop
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/0024named_objects_2.nodump b/tests/shell/testcases/maps/dumps/0024named_objects_2.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0024named_objects_2.nodump
diff --git a/tests/shell/testcases/maps/dumps/anonymous_snat_map_1.json-nft b/tests/shell/testcases/maps/dumps/anonymous_snat_map_1.json-nft
new file mode 100644
index 00000000..f4c55706
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/anonymous_snat_map_1.json-nft
@@ -0,0 +1,58 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "nat",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "nat",
+ "name": "postrouting",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "nat",
+ "chain": "postrouting",
+ "handle": 0,
+ "expr": [
+ {
+ "snat": {
+ "addr": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "1.1.1.1",
+ "2.2.2.2"
+ ]
+ ]
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/anonymous_snat_map_1.nft b/tests/shell/testcases/maps/dumps/anonymous_snat_map_1.nft
new file mode 100644
index 00000000..5009560c
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/anonymous_snat_map_1.nft
@@ -0,0 +1,5 @@
+table ip nat {
+ chain postrouting {
+ snat to ip saddr map { 1.1.1.1 : 2.2.2.2 }
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/anonymous_snat_map_2.nodump b/tests/shell/testcases/maps/dumps/anonymous_snat_map_2.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/anonymous_snat_map_2.nodump
diff --git a/tests/shell/testcases/maps/dumps/delete_element.nft b/tests/shell/testcases/maps/dumps/delete_element.nft
new file mode 100644
index 00000000..5275b4dc
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/delete_element.nft
@@ -0,0 +1,12 @@
+table ip x {
+ map m {
+ typeof ct bytes : meta priority
+ flags interval
+ elements = { 2048001-4000000 : 1:2 }
+ }
+
+ chain y {
+ type filter hook output priority filter; policy accept;
+ meta priority set ct bytes map @m
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/delete_element_catchall.nft b/tests/shell/testcases/maps/dumps/delete_element_catchall.nft
new file mode 100644
index 00000000..14054f4d
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/delete_element_catchall.nft
@@ -0,0 +1,12 @@
+table ip x {
+ map m {
+ typeof ct bytes : meta priority
+ flags interval
+ elements = { * : 1:3 }
+ }
+
+ chain y {
+ type filter hook output priority filter; policy accept;
+ meta priority set ct bytes map @m
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft b/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft
index 8130c46c..b3204a28 100644
--- a/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft
+++ b/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft
@@ -231,7 +231,7 @@
"elem": {
"elem": {
"val": "10.2.3.4",
- "timeout": 1
+ "timeout": 2
}
},
"data": 2,
diff --git a/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.nft b/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.nft
index 9134673c..e80366b8 100644
--- a/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.nft
+++ b/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.nft
@@ -16,7 +16,7 @@ table ip dynset {
chain input {
type filter hook input priority filter; policy accept;
- add @dynmark { 10.2.3.4 timeout 1s : 0x00000002 } comment "also check timeout-gc"
+ add @dynmark { 10.2.3.4 timeout 2s : 0x00000002 } comment "also check timeout-gc"
meta l4proto icmp ip daddr 127.0.0.42 jump test_ping
}
}
diff --git a/tests/shell/testcases/maps/named_ct_objects b/tests/shell/testcases/maps/named_ct_objects
index 61b87c1a..518140b0 100755
--- a/tests/shell/testcases/maps/named_ct_objects
+++ b/tests/shell/testcases/maps/named_ct_objects
@@ -1,6 +1,7 @@
#!/bin/bash
# NFT_TEST_REQUIRES(NFT_TEST_HAVE_cttimeout)
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_ctexpect)
$NFT -f /dev/stdin <<EOF || exit 1
table inet t {
diff --git a/tests/shell/testcases/maps/nat_addr_port b/tests/shell/testcases/maps/nat_addr_port
index 2804d48c..703a2ad9 100755
--- a/tests/shell/testcases/maps/nat_addr_port
+++ b/tests/shell/testcases/maps/nat_addr_port
@@ -84,6 +84,11 @@ $NFT add rule 'ip6 ip6foo c ip6 saddr f0:0b::a3 dnat to [1c::3]:42' && exit 1
# should fail: rule has no test for l4 protocol, but map has inet_service
$NFT add rule 'ip6 ip6foo c dnat to ip daddr map @y' && exit 1
+if [ "$NFT_TEST_HAVE_inet_nat" = n ]; then
+ echo "Test partially skipped due to NFT_TEST_HAVE_inet_nat=n"
+ exit 77
+fi
+
# skeleton inet
$NFT -f /dev/stdin <<EOF || exit 1
table inet inetfoo {
diff --git a/tests/shell/testcases/maps/typeof_maps_0 b/tests/shell/testcases/maps/typeof_maps_0
index 98517fd5..764206d2 100755
--- a/tests/shell/testcases/maps/typeof_maps_0
+++ b/tests/shell/testcases/maps/typeof_maps_0
@@ -4,6 +4,8 @@
# without typeof, this is 'type string' and 'type integer',
# but neither could be used because it lacks size information.
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_ipsec)
+
set -e
die() {
diff --git a/tests/shell/testcases/maps/typeof_maps_add_delete b/tests/shell/testcases/maps/typeof_maps_add_delete
index d2ac9f1c..2d718c5f 100755
--- a/tests/shell/testcases/maps/typeof_maps_add_delete
+++ b/tests/shell/testcases/maps/typeof_maps_add_delete
@@ -30,7 +30,7 @@ EXPECTED="table ip dynset {
chain input {
type filter hook input priority 0; policy accept;
- add @dynmark { 10.2.3.4 timeout 1s : 0x2 } comment \"also check timeout-gc\"
+ add @dynmark { 10.2.3.4 timeout 2s : 0x2 } comment \"also check timeout-gc\"
meta l4proto icmp ip daddr 127.0.0.42 jump test_ping
}
}"
@@ -45,7 +45,7 @@ ping -c 1 127.0.0.42
$NFT get element ip dynset dynmark { 10.2.3.4 }
# wait so that 10.2.3.4 times out.
-sleep 2
+sleep 3
set +e
$NFT get element ip dynset dynmark { 10.2.3.4 } && exit 1
diff --git a/tests/shell/testcases/maps/vmap_timeout b/tests/shell/testcases/maps/vmap_timeout
index 0cd965f7..3f0563af 100755
--- a/tests/shell/testcases/maps/vmap_timeout
+++ b/tests/shell/testcases/maps/vmap_timeout
@@ -11,18 +11,52 @@ port=23
for i in $(seq 1 100) ; do
timeout=$((RANDOM%5))
timeout=$((timeout+1))
+ expire=$((RANDOM%timeout))
j=1
batched="{ $port timeout 3s : jump other_input "
- batched_addr="{ 10.0.$((i%256)).$j . $port timeout ${timeout}s : jump other_input "
+ ubatched="$batched"
+
+ timeout_str="timeout ${timeout}s"
+ expire_str=""
+ if [ "$expire" -gt 0 ]; then
+ expire_str="expires ${expire}s"
+ fi
+
+ batched_addr="{ 10.0.$((i%256)).$j . $port ${timeout_str} ${expire_str} : jump other_input "
+ ubatched_addr="$batched_addr"
+
port=$((port + 1))
for j in $(seq 2 400); do
timeout=$((RANDOM%5))
timeout=$((timeout+1))
+ expire=$((RANDOM%timeout))
+ utimeout=$((RANDOM%5))
+ utimeout=$((timeout+1))
+
+ timeout_str="timeout ${timeout}s"
+ expire_str=""
+ if [ "$expire" -gt 0 ]; then
+ expire_str="expires ${expire}s"
+ fi
- batched="$batched, $port timeout ${timeout}s : jump other_input "
- batched_addr="$batched_addr, 10.0.$((i%256)).$((j%256)) . $port timeout ${timeout}s : jump other_input "
+ batched="$batched, $port ${timeout_str} ${expire_str} : jump other_input "
+ batched_addr="$batched_addr, 10.0.$((i%256)).$((j%256)) . $port ${timeout_str} ${expire_str} : jump other_input "
port=$((port + 1))
+
+ timeout_str="timeout ${utimeout}s"
+ expire=$((RANDOM%utimeout))
+
+ expire_str=""
+ if [ "$expires" -gt 0 ]; then
+ expire_str="expires ${expire}s"
+ fi
+
+ update=$((RANDOM%2))
+ if [ "$update" -ne 0 ]; then
+ ubatched="$batched, $port ${timeout_str} ${expire_str} : jump other_input "
+ ubatched_addr="$batched_addr, 10.0.$((i%256)).$((j%256)) . $port ${timeout_str} ${expire_str} : jump other_input "
+ fi
done
fail_addr="$batched_addr, 1.2.3.4 . 23 timeout 5m : jump other_input,
@@ -40,6 +74,14 @@ for i in $(seq 1 100) ; do
$NFT add element inet filter portmap "$batched"
$NFT add element inet filter portaddrmap "$batched_addr"
+
+ update=$((RANDOM%2))
+ if [ "$update" -ne 0 ]; then
+ ubatched="$ubatched }"
+ ubatched_addr="$ubatched_addr }"
+ $NFT add element inet filter portmap "$ubatched"
+ $NFT add element inet filter portaddrmap "$ubatched_addr"
+ fi
done
if [ "$NFT_TEST_HAVE_catchall_element" = n ] ; then
diff --git a/tests/shell/testcases/nft-i/dumps/index_0.nft b/tests/shell/testcases/nft-i/dumps/index_0.nft
new file mode 100644
index 00000000..abcd1b7c
--- /dev/null
+++ b/tests/shell/testcases/nft-i/dumps/index_0.nft
@@ -0,0 +1,8 @@
+table inet foo {
+ chain bar {
+ type filter hook input priority filter; policy accept;
+ accept
+ accept
+ accept
+ }
+}
diff --git a/tests/shell/testcases/nft-i/dumps/set_0.nft b/tests/shell/testcases/nft-i/dumps/set_0.nft
new file mode 100644
index 00000000..d3377d63
--- /dev/null
+++ b/tests/shell/testcases/nft-i/dumps/set_0.nft
@@ -0,0 +1,7 @@
+table inet foo {
+ set bar {
+ type ipv4_addr
+ flags interval
+ elements = { 10.1.1.1, 10.1.1.2 }
+ }
+}
diff --git a/tests/shell/testcases/nft-i/index_0 b/tests/shell/testcases/nft-i/index_0
new file mode 100755
index 00000000..f885fdeb
--- /dev/null
+++ b/tests/shell/testcases/nft-i/index_0
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+set -e
+
+RULESET="add table inet foo
+add chain inet foo bar { type filter hook input priority filter; }
+add rule inet foo bar accept
+insert rule inet foo bar index 0 accept
+add rule inet foo bar index 0 accept"
+
+$NFT -i <<< "$RULESET"
diff --git a/tests/shell/testcases/nft-i/set_0 b/tests/shell/testcases/nft-i/set_0
new file mode 100755
index 00000000..e87eef1d
--- /dev/null
+++ b/tests/shell/testcases/nft-i/set_0
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+set -e
+
+RULESET="add table inet foo
+add set inet foo bar { type ipv4_addr; flags interval; }; add element inet foo bar { 10.1.1.1/32 }
+add element inet foo bar { 10.1.1.2/32 }"
+
+$NFT -i <<< "$RULESET"
diff --git a/tests/shell/testcases/optimizations/dumps/merge_counter.nft b/tests/shell/testcases/optimizations/dumps/merge_counter.nft
new file mode 100644
index 00000000..72eed5d0
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_counter.nft
@@ -0,0 +1,8 @@
+table ip x {
+ chain y {
+ type filter hook input priority filter; policy drop;
+ ct state vmap { invalid counter packets 0 bytes 0 : drop, established counter packets 0 bytes 0 : accept, related counter packets 0 bytes 0 : accept }
+ tcp dport { 80, 123 } counter packets 0 bytes 0 accept
+ ip saddr . ip daddr vmap { 1.1.1.1 . 2.2.2.2 counter packets 0 bytes 0 : accept, 1.1.1.2 . 3.3.3.3 counter packets 0 bytes 0 : drop }
+ }
+}
diff --git a/tests/shell/testcases/optimizations/merge_counter b/tests/shell/testcases/optimizations/merge_counter
new file mode 100755
index 00000000..3b8bbadd
--- /dev/null
+++ b/tests/shell/testcases/optimizations/merge_counter
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_set_expr)
+
+set -e
+
+RULESET="table ip x {
+ chain y {
+ type filter hook input priority 0; policy drop;
+
+ ct state invalid counter drop
+ ct state established,related counter accept
+ tcp dport 80 counter accept
+ tcp dport 123 counter accept
+ ip saddr 1.1.1.1 ip daddr 2.2.2.2 counter accept
+ ip saddr 1.1.1.2 ip daddr 3.3.3.3 counter drop
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/ruleset b/tests/shell/testcases/optimizations/ruleset
index 2b2d80ff..f7c3b747 100755
--- a/tests/shell/testcases/optimizations/ruleset
+++ b/tests/shell/testcases/optimizations/ruleset
@@ -1,6 +1,7 @@
#!/bin/bash
# NFT_TEST_REQUIRES(NFT_TEST_HAVE_prerouting_reject)
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_inet_nat)
RULESET="table inet uni {
chain gtfo {
diff --git a/tests/shell/testcases/optimizations/variables b/tests/shell/testcases/optimizations/variables
index fa986065..4cb322db 100755
--- a/tests/shell/testcases/optimizations/variables
+++ b/tests/shell/testcases/optimizations/variables
@@ -2,14 +2,52 @@
set -e
-RULESET="define addrv4_vpnnet = 10.1.0.0/16
+RULESET='define addrv4_vpnnet = 10.1.0.0/16
+define wan = "eth0"
+define lan = "eth1"
+define vpn = "tun0"
+define server = "10.10.10.1"
-table ip nat {
- chain postrouting {
- type nat hook postrouting priority 0; policy accept;
+table inet filter {
+ chain input {
+ type filter hook input priority 0; policy drop;
+ }
+ chain forward {
+ type filter hook forward priority 1; policy drop;
- ip saddr \$addrv4_vpnnet counter masquerade fully-random comment \"masquerade ipv4\"
- }
-}"
+ iifname $lan oifname $lan accept;
+
+ iifname $lan oifname $wan ct state new accept
+ iifname $lan oifname $wan ct state {established, related} accept
+
+ iifname $wan oifname $lan ct state {established, related} accept
+
+ iifname $vpn oifname $wan accept
+ iifname $wan oifname $vpn accept
+ iifname $lan oifname $vpn accept
+ iifname $vpn oifname $lan accept
+
+ iifname $lan oifname $server accept
+ iifname $server oifname $lan accept
+ iifname $server oifname $wan accept
+ iifname $wan oifname $server accept
+ }
+ chain output {
+ type filter hook output priority 0; policy drop;
+ }
+}
+
+table nat {
+ chain prerouting {
+ type nat hook prerouting priority -100; policy accept;
+ iifname $wan tcp dport 10000 dnat to $server:10000;
+ }
+ chain postrouting {
+ type nat hook postrouting priority 100; policy accept;
+ ip saddr $addrv4_vpnnet counter masquerade fully-random comment "masquerade ipv4"
+ oifname $vpn masquerade
+ oifname $wan masquerade
+ }
+}'
$NFT -c -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/owner/0002-persist b/tests/shell/testcases/owner/0002-persist
new file mode 100755
index 00000000..98a8eb13
--- /dev/null
+++ b/tests/shell/testcases/owner/0002-persist
@@ -0,0 +1,78 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_table_flag_owner)
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_table_flag_persist)
+
+die() {
+ echo "$@"
+ exit 1
+}
+
+$NFT -f - <<EOF
+table ip t {
+ flags owner, persist
+}
+EOF
+[[ $? -eq 0 ]] || {
+ die "table add failed"
+}
+
+$NFT list ruleset | grep -q 'table ip t' || {
+ die "table does not persist"
+}
+$NFT list ruleset | grep -q 'flags persist$' || {
+ die "unexpected flags in orphaned table"
+}
+
+$NFT -f - <<EOF
+table ip t {
+ flags owner, persist
+}
+EOF
+[[ $? -eq 0 ]] || {
+ die "retake ownership failed"
+}
+
+EXPECT="table ip t {
+ flags persist
+}"
+diff -u <(echo "$EXPECT") <($NFT list ruleset) || {
+ die "unexpected ruleset before coproc setup"
+}
+
+coproc $NFT -i
+sleep 1
+
+cat >&"${COPROC[1]}" <<EOF
+add table ip t { flags owner, persist; }
+EOF
+
+EXPECT="table ip t { # progname nft
+ flags owner,persist
+}"
+diff -u <(echo "$EXPECT") <($NFT list ruleset) || {
+ die "unexpected ruleset after coproc setup"
+}
+
+$NFT flush ruleset
+$NFT list ruleset | grep -q 'table ip t' || {
+ die "flushed owned table"
+}
+
+$NFT add table 'ip t { flags owner, persist; }' && {
+ die "stole owned table"
+}
+
+cat >&"${COPROC[1]}" <<EOF
+delete table ip t
+EOF
+
+[[ -z $($NFT list ruleset) ]] || {
+ die "owner should be able to delete the table"
+}
+
+eval "exec ${COPROC[1]}>&-"
+wait $COPROC_PID
+
+
+exit 0
diff --git a/tests/shell/testcases/owner/dumps/0002-persist.json-nft b/tests/shell/testcases/owner/dumps/0002-persist.json-nft
index f0c336a8..546cc597 100644
--- a/tests/shell/testcases/owner/dumps/0002-persist.json-nft
+++ b/tests/shell/testcases/owner/dumps/0002-persist.json-nft
@@ -6,14 +6,6 @@
"release_name": "RELEASE_NAME",
"json_schema_version": 1
}
- },
- {
- "table": {
- "family": "ip",
- "name": "t",
- "handle": 0,
- "flags": "persist"
- }
}
]
}
diff --git a/tests/shell/testcases/owner/dumps/0002-persist.nft b/tests/shell/testcases/owner/dumps/0002-persist.nft
index b47027d3..e69de29b 100644
--- a/tests/shell/testcases/owner/dumps/0002-persist.nft
+++ b/tests/shell/testcases/owner/dumps/0002-persist.nft
@@ -1,3 +0,0 @@
-table ip t {
- flags persist
-}
diff --git a/tests/shell/testcases/packetpath/dumps/flowtables.nodump b/tests/shell/testcases/packetpath/dumps/flowtables.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/packetpath/dumps/flowtables.nodump
diff --git a/tests/shell/testcases/packetpath/dumps/policy.json-nft b/tests/shell/testcases/packetpath/dumps/policy.json-nft
new file mode 100644
index 00000000..26e8a052
--- /dev/null
+++ b/tests/shell/testcases/packetpath/dumps/policy.json-nft
@@ -0,0 +1,121 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "underflow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "drop"
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmp",
+ "field": "type"
+ }
+ },
+ "right": "echo-reply"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "127.0.0.1"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": "127.0.0.2"
+ }
+ },
+ {
+ "counter": {
+ "packets": 3,
+ "bytes": 252
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "underflow"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/packetpath/dumps/policy.nft b/tests/shell/testcases/packetpath/dumps/policy.nft
new file mode 100644
index 00000000..e625ea6c
--- /dev/null
+++ b/tests/shell/testcases/packetpath/dumps/policy.nft
@@ -0,0 +1,11 @@
+table inet filter {
+ chain underflow {
+ }
+
+ chain input {
+ type filter hook input priority filter; policy drop;
+ icmp type echo-reply accept
+ ip saddr 127.0.0.1 ip daddr 127.0.0.2 counter packets 3 bytes 252 accept
+ goto underflow
+ }
+}
diff --git a/tests/shell/testcases/packetpath/dumps/tcp_reset.json-nft b/tests/shell/testcases/packetpath/dumps/tcp_reset.json-nft
new file mode 100644
index 00000000..e1367cc1
--- /dev/null
+++ b/tests/shell/testcases/packetpath/dumps/tcp_reset.json-nft
@@ -0,0 +1,168 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "output",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "nftrace"
+ }
+ },
+ "value": 1
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": "127.0.0.1"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 5555
+ }
+ },
+ {
+ "reject": {
+ "type": "tcp reset"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "daddr"
+ }
+ },
+ "right": "::1"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 5555
+ }
+ },
+ {
+ "reject": {
+ "type": "tcp reset"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 5555
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/packetpath/dumps/tcp_reset.nft b/tests/shell/testcases/packetpath/dumps/tcp_reset.nft
new file mode 100644
index 00000000..fb3df1af
--- /dev/null
+++ b/tests/shell/testcases/packetpath/dumps/tcp_reset.nft
@@ -0,0 +1,13 @@
+table inet filter {
+ chain input {
+ type filter hook input priority filter; policy accept;
+ meta nftrace set 1
+ ip daddr 127.0.0.1 tcp dport 5555 reject with tcp reset
+ ip6 daddr ::1 tcp dport 5555 reject with tcp reset
+ tcp dport 5555 counter packets 0 bytes 0
+ }
+
+ chain output {
+ type filter hook output priority filter; policy accept;
+ }
+}
diff --git a/tests/shell/testcases/packetpath/dumps/vlan_mangling.nodump b/tests/shell/testcases/packetpath/dumps/vlan_mangling.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/packetpath/dumps/vlan_mangling.nodump
diff --git a/tests/shell/testcases/packetpath/dumps/vlan_qinq.nodump b/tests/shell/testcases/packetpath/dumps/vlan_qinq.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/packetpath/dumps/vlan_qinq.nodump
diff --git a/tests/shell/testcases/packetpath/flowtables b/tests/shell/testcases/packetpath/flowtables
index ec7dfeb7..2c4a7e1f 100755
--- a/tests/shell/testcases/packetpath/flowtables
+++ b/tests/shell/testcases/packetpath/flowtables
@@ -1,7 +1,9 @@
-#! /bin/bash -x
+#!/bin/bash
# NFT_TEST_SKIP(NFT_TEST_SKIP_slow)
+set -x
+
rnd=$(mktemp -u XXXXXXXX)
R="flowtable-router-$rnd"
C="flowtable-client-$rnd"
diff --git a/tests/shell/testcases/packetpath/match_l4proto b/tests/shell/testcases/packetpath/match_l4proto
new file mode 100755
index 00000000..31fbe6c2
--- /dev/null
+++ b/tests/shell/testcases/packetpath/match_l4proto
@@ -0,0 +1,149 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_netdev_egress)
+
+rnd=$(mktemp -u XXXXXXXX)
+ns1="nft1payload-$rnd"
+ns2="nft2payload-$rnd"
+
+cleanup()
+{
+ ip netns del "$ns1"
+ ip netns del "$ns2"
+}
+
+trap cleanup EXIT
+
+run_test()
+{
+ ns1_addr=$2
+ ns2_addr=$3
+ cidr=$4
+
+ # socat needs square brackets, ie. [abcd::2]
+ if [ $1 -eq 6 ]; then
+ nsx1_addr="["$ns1_addr"]"
+ nsx2_addr="["$ns2_addr"]"
+ else
+ nsx1_addr="$ns1_addr"
+ nsx2_addr="$ns2_addr"
+ fi
+
+ ip netns add "$ns1" || exit 111
+ ip netns add "$ns2" || exit 111
+
+ ip -net "$ns1" link set lo up
+ ip -net "$ns2" link set lo up
+
+ ip link add veth0 netns $ns1 type veth peer name veth0 netns $ns2
+
+ ip -net "$ns1" link set veth0 up
+ ip -net "$ns2" link set veth0 up
+ ip -net "$ns1" addr add $ns1_addr/$cidr dev veth0
+ ip -net "$ns2" addr add $ns2_addr/$cidr dev veth0
+
+ sleep 5
+
+RULESET="table netdev payload_netdev {
+ counter ingress {}
+ counter ingress_2 {}
+ counter egress {}
+ counter egress_2 {}
+
+ chain ingress {
+ type filter hook ingress device veth0 priority 0;
+ udp dport 7777 counter name ingress
+ meta l4proto udp counter name ingress_2
+ }
+
+ chain egress {
+ type filter hook egress device veth0 priority 0;
+ udp dport 7777 counter name egress
+ meta l4proto udp counter name egress_2
+ }
+}"
+
+ ip netns exec "$ns1" $NFT -f - <<< "$RULESET" || exit 1
+
+ ip netns exec "$ns1" bash -c "echo 'A' | socat -u STDIN UDP:$nsx2_addr:7777 > /dev/null"
+ ip netns exec "$ns1" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx2_addr:7777 > /dev/null"
+ ip netns exec "$ns1" bash -c "echo 'AAA' | socat -u STDIN UDP:$nsx2_addr:7777 > /dev/null"
+ ip netns exec "$ns1" bash -c "echo 'AAAA' | socat -u STDIN UDP:$nsx2_addr:7777 > /dev/null"
+ ip netns exec "$ns1" bash -c "echo 'AAAAA' | socat -u STDIN UDP:$nsx2_addr:7777 > /dev/null"
+
+ ip netns exec "$ns2" bash -c "echo 'A' | socat -u STDIN UDP:$nsx1_addr:7777 > /dev/null"
+ ip netns exec "$ns2" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx1_addr:7777 > /dev/null"
+ ip netns exec "$ns2" bash -c "echo 'AAA' | socat -u STDIN UDP:$nsx1_addr:7777 > /dev/null"
+ ip netns exec "$ns2" bash -c "echo 'AAAA' | socat -u STDIN UDP:$nsx1_addr:7777 > /dev/null"
+ ip netns exec "$ns2" bash -c "echo 'AAAAA' | socat -u STDIN UDP:$nsx1_addr:7777 > /dev/null"
+
+ ip netns exec "$ns1" $NFT list ruleset
+
+ ip netns exec "$ns1" $NFT list counter netdev payload_netdev ingress | grep "packets 5" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter netdev payload_netdev ingress_2 | grep "packets 5" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter netdev payload_netdev egress | grep "packets 5" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter netdev payload_netdev egress_2| grep "packets 5" > /dev/null || exit 1
+
+ #
+ # ... next stage
+ #
+ ip netns exec "$ns1" $NFT flush ruleset
+
+ #
+ # bridge
+ #
+
+ ip -net "$ns1" addr del $ns1_addr/$cidr dev veth0
+
+ ip -net "$ns1" link add name br0 type bridge
+ ip -net "$ns1" link set veth0 master br0
+ ip -net "$ns1" addr add $ns1_addr/$cidr dev br0
+ ip -net "$ns1" link set up dev br0
+
+ sleep 5
+
+RULESET="table bridge payload_bridge {
+ counter input {}
+ counter output {}
+ counter input_2 {}
+ counter output_2 {}
+
+ chain in {
+ type filter hook input priority 0;
+ udp dport 7777 counter name input
+ meta l4proto udp counter name input_2
+ }
+
+ chain out {
+ type filter hook output priority 0;
+ udp dport 7777 counter name output
+ meta l4proto udp counter name output_2
+ }
+}"
+
+ ip netns exec "$ns1" $NFT -f - <<< "$RULESET" || exit 1
+
+ ip netns exec "$ns1" bash -c "echo 'A' | socat -u STDIN UDP:$nsx2_addr:7777 > /dev/null"
+ ip netns exec "$ns1" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx2_addr:7777 > /dev/null"
+ ip netns exec "$ns1" bash -c "echo 'AAA' | socat -u STDIN UDP:$nsx2_addr:7777 > /dev/null"
+ ip netns exec "$ns1" bash -c "echo 'AAAA' | socat -u STDIN UDP:$nsx2_addr:7777 > /dev/null"
+ ip netns exec "$ns1" bash -c "echo 'AAAAA' | socat -u STDIN UDP:$nsx2_addr:7777 > /dev/null"
+
+ ip netns exec "$ns2" bash -c "echo 'A' | socat -u STDIN UDP:$nsx1_addr:7777 > /dev/null"
+ ip netns exec "$ns2" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx1_addr:7777 > /dev/null"
+ ip netns exec "$ns2" bash -c "echo 'AAA' | socat -u STDIN UDP:$nsx1_addr:7777 > /dev/null"
+ ip netns exec "$ns2" bash -c "echo 'AAAA' | socat -u STDIN UDP:$nsx1_addr:7777 > /dev/null"
+ ip netns exec "$ns2" bash -c "echo 'AAAAA' | socat -u STDIN UDP:$nsx1_addr:7777 > /dev/null"
+
+ ip netns exec "$ns1" $NFT list ruleset
+
+ ip netns exec "$ns1" $NFT list counter bridge payload_bridge input | grep "packets 5" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter bridge payload_bridge input_2 | grep "packets 5" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter bridge payload_bridge output | grep "packets 5" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter bridge payload_bridge output_2 | grep "packets 5" > /dev/null || exit 1
+}
+
+run_test "4" "10.141.10.2" "10.141.10.3" "24"
+cleanup
+run_test "6" "abcd::2" "abcd::3" "64"
+# trap calls cleanup
diff --git a/tests/shell/testcases/packetpath/payload b/tests/shell/testcases/packetpath/payload
index 4c5c42da..83e0b7fc 100755
--- a/tests/shell/testcases/packetpath/payload
+++ b/tests/shell/testcases/packetpath/payload
@@ -19,6 +19,28 @@ run_test()
ns1_addr=$2
ns2_addr=$3
cidr=$4
+ mode=$5
+
+ case $mode in
+ "udp")
+ l4proto="udp"
+ udp_checksum="udp checksum != 0"
+ udp_zero_checksum=""
+ ;;
+ "udp-zero-checksum")
+ l4proto="udp"
+ udp_checksum="udp checksum 0"
+ udp_zero_checksum="udp checksum set 0"
+ ;;
+ "tcp")
+ l4proto="tcp"
+ udp_checksum=""
+ udp_zero_checksum=""
+ ;;
+ *)
+ echo "unexpected, incorrect mode"
+ exit 0
+ esac
# socat needs square brackets, ie. [abcd::2]
if [ $1 -eq 6 ]; then
@@ -42,6 +64,8 @@ run_test()
ip -net "$ns1" addr add $ns1_addr/$cidr dev veth0
ip -net "$ns2" addr add $ns2_addr/$cidr dev veth0
+ sleep 3
+
RULESET="table netdev payload_netdev {
counter ingress {}
counter egress {}
@@ -52,16 +76,18 @@ RULESET="table netdev payload_netdev {
chain ingress {
type filter hook ingress device veth0 priority 0;
- tcp dport 7777 counter name ingress
- tcp dport 7778 tcp dport set 7779 counter name mangle_ingress
- tcp dport 7779 counter name mangle_ingress_match
+ $udp_zero_checksum
+ $l4proto dport 7777 counter name ingress
+ $l4proto dport 7778 $l4proto dport set 7779 $udp_checksum counter name mangle_ingress
+ $l4proto dport 7779 counter name mangle_ingress_match
}
chain egress {
type filter hook egress device veth0 priority 0;
- tcp dport 8887 counter name egress
- tcp dport 8888 tcp dport set 8889 counter name mangle_egress
- tcp dport 8889 counter name mangle_egress_match
+ $udp_zero_checksum
+ $l4proto dport 8887 counter name egress
+ $l4proto dport 8888 $l4proto dport set 8889 $udp_checksum counter name mangle_egress
+ $l4proto dport 8889 counter name mangle_egress_match
}
}
@@ -75,48 +101,67 @@ table inet payload_inet {
chain in {
type filter hook input priority 0;
- tcp dport 7770 counter name input
- tcp dport 7771 tcp dport set 7772 counter name mangle_input
- tcp dport 7772 counter name mangle_input_match
+ $udp_zero_checksum
+ $l4proto dport 7770 counter name input
+ $l4proto dport 7771 $l4proto dport set 7772 $udp_checksum counter name mangle_input
+ $l4proto dport 7772 counter name mangle_input_match
}
chain out {
type filter hook output priority 0;
- tcp dport 8880 counter name output
- tcp dport 8881 tcp dport set 8882 counter name mangle_output
- tcp dport 8882 counter name mangle_output_match
+ $udp_zero_checksum
+ $l4proto dport 8880 counter name output
+ $l4proto dport 8881 $l4proto dport set 8882 $udp_checksum counter name mangle_output
+ $l4proto dport 8882 counter name mangle_output_match
}
}"
ip netns exec "$ns1" $NFT -f - <<< "$RULESET" || exit 1
- ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8887,connect-timeout=2 < /dev/null > /dev/null
- ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8888,connect-timeout=2 < /dev/null > /dev/null
+ case $l4proto in
+ "tcp")
+ ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8887,connect-timeout=4 < /dev/null > /dev/null
+ ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8888,connect-timeout=4 < /dev/null > /dev/null
+
+ ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8880,connect-timeout=4 < /dev/null > /dev/null
+ ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8881,connect-timeout=4 < /dev/null > /dev/null
+
+ ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7777,connect-timeout=4 < /dev/null > /dev/null
+ ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7778,connect-timeout=4 < /dev/null > /dev/null
- ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8880,connect-timeout=2 < /dev/null > /dev/null
- ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8881,connect-timeout=2 < /dev/null > /dev/null
+ ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7770,connect-timeout=4 < /dev/null > /dev/null
+ ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7771,connect-timeout=4 < /dev/null > /dev/null
+ ;;
+ "udp")
+ ip netns exec "$ns1" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx2_addr:8887 > /dev/null"
+ ip netns exec "$ns1" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx2_addr:8888 > /dev/null"
- ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7777,connect-timeout=2 < /dev/null > /dev/null
- ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7778,connect-timeout=2 < /dev/null > /dev/null
+ ip netns exec "$ns1" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx2_addr:8880 > /dev/null"
+ ip netns exec "$ns1" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx2_addr:8881 > /dev/null"
- ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7770,connect-timeout=2 < /dev/null > /dev/null
- ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7771,connect-timeout=2 < /dev/null > /dev/null
+ ip netns exec "$ns2" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx1_addr:7777 > /dev/null"
+ ip netns exec "$ns2" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx1_addr:7778 > /dev/null"
+
+ ip netns exec "$ns2" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx1_addr:7770 > /dev/null"
+ ip netns exec "$ns2" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx1_addr:7771 > /dev/null"
+ ;;
+ esac
ip netns exec "$ns1" $NFT list ruleset
- ip netns exec "$ns1" $NFT list counter netdev payload_netdev ingress | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" $NFT list counter netdev payload_netdev mangle_ingress | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" $NFT list counter netdev payload_netdev mangle_ingress_match | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" $NFT list counter netdev payload_netdev egress | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" $NFT list counter netdev payload_netdev mangle_egress | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" $NFT list counter netdev payload_netdev mangle_egress_match | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter netdev payload_netdev ingress | grep -q "packets 0" && exit 1
+ ip netns exec "$ns1" $NFT list counter netdev payload_netdev mangle_ingress | grep -q "packets 0" && exit 1
+ ip netns exec "$ns1" $NFT list counter netdev payload_netdev mangle_ingress_match | grep -q "packets 0" && exit 1
+ ip netns exec "$ns1" $NFT list counter netdev payload_netdev egress | grep -q "packets 0" && exit 1
+ ip netns exec "$ns1" $NFT list counter netdev payload_netdev mangle_egress | grep -q "packets 0" && exit 1
+ ip netns exec "$ns1" $NFT list counter netdev payload_netdev mangle_egress_match | grep -q "packets 0" && exit 1
- ip netns exec "$ns1" $NFT list counter inet payload_inet input | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" $NFT list counter inet payload_inet mangle_input | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" $NFT list counter inet payload_inet mangle_input_match | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" $NFT list counter inet payload_inet output | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" $NFT list counter inet payload_inet mangle_output | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" $NFT list counter inet payload_inet mangle_output_match | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter inet payload_inet input | grep -q "packets 0" && exit 1
+ ip netns exec "$ns1" $NFT list counter inet payload_inet mangle_input | grep -q "packets 0" && exit 1
+ ip netns exec "$ns1" $NFT list counter inet payload_inet mangle_input_match | grep -q "packets 0" && exit 1
+ ip netns exec "$ns1" $NFT list counter inet payload_inet output | grep -q "packets 0" && exit 1
+ ip netns exec "$ns1" $NFT list counter inet payload_inet mangle_output | grep -q "packets 0" && exit 1
+ ip netns exec "$ns1" $NFT list counter inet payload_inet mangle_output_match | grep -q "packets 0" && exit 1
#
# ... next stage
@@ -135,6 +180,8 @@ table inet payload_inet {
ip -net "$ns1" addr add $ns1_addr/$cidr dev br0
ip -net "$ns1" link set up dev br0
+ sleep 3
+
RULESET="table bridge payload_bridge {
counter input {}
counter output {}
@@ -145,38 +192,60 @@ RULESET="table bridge payload_bridge {
chain in {
type filter hook input priority 0;
- tcp dport 7770 counter name input
- tcp dport 7771 tcp dport set 7772 counter name mangle_input
- tcp dport 7772 counter name mangle_input_match
+ $udp_zero_checksum
+ $l4proto dport 7770 counter name input
+ $l4proto dport 7771 $l4proto dport set 7772 $udp_checksum counter name mangle_input
+ $l4proto dport 7772 counter name mangle_input_match
}
chain out {
type filter hook output priority 0;
- tcp dport 8880 counter name output
- tcp dport 8881 tcp dport set 8882 counter name mangle_output
- tcp dport 8882 counter name mangle_output_match
+ $udp_zero_checksum
+ $l4proto dport 8880 counter name output
+ $l4proto dport 8881 $l4proto dport set 8882 $udp_checksum counter name mangle_output
+ $l4proto dport 8882 counter name mangle_output_match
}
}"
ip netns exec "$ns1" $NFT -f - <<< "$RULESET" || exit 1
- ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8880,connect-timeout=2 < /dev/null > /dev/null
- ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8881,connect-timeout=2 < /dev/null > /dev/null
+ case $l4proto in
+ "tcp")
+ ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8880,connect-timeout=4 < /dev/null > /dev/null
+ ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8881,connect-timeout=4 < /dev/null > /dev/null
- ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7770,connect-timeout=2 < /dev/null > /dev/null
- ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7771,connect-timeout=2 < /dev/null > /dev/null
+ ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7770,connect-timeout=4 < /dev/null > /dev/null
+ ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7771,connect-timeout=4 < /dev/null > /dev/null
+ ;;
+ "udp")
+ ip netns exec "$ns1" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx2_addr:8880 > /dev/null"
+ ip netns exec "$ns1" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx2_addr:8881 > /dev/null"
+
+ ip netns exec "$ns2" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx1_addr:7770 > /dev/null"
+ ip netns exec "$ns2" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx1_addr:7771 > /dev/null"
+ ;;
+ esac
ip netns exec "$ns1" $NFT list ruleset
- ip netns exec "$ns1" $NFT list counter bridge payload_bridge input | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" $NFT list counter bridge payload_bridge mangle_input | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" $NFT list counter bridge payload_bridge mangle_input_match | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" $NFT list counter bridge payload_bridge output | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" $NFT list counter bridge payload_bridge mangle_output | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" $NFT list counter bridge payload_bridge mangle_output_match | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter bridge payload_bridge input | grep -q "packets 0" && exit 1
+ ip netns exec "$ns1" $NFT list counter bridge payload_bridge mangle_input | grep -q "packets 0" && exit 1
+ ip netns exec "$ns1" $NFT list counter bridge payload_bridge mangle_input_match | grep -q "packets 0" && exit 1
+ ip netns exec "$ns1" $NFT list counter bridge payload_bridge output | grep -q "packets 0" && exit 1
+ ip netns exec "$ns1" $NFT list counter bridge payload_bridge mangle_output | grep -q "packets 0" && exit 1
+ ip netns exec "$ns1" $NFT list counter bridge payload_bridge mangle_output_match | grep -q "packets 0" && exit 1
}
-run_test "4" "10.141.10.2" "10.141.10.3" "24"
+run_test "4" "10.141.10.2" "10.141.10.3" "24" "tcp"
+cleanup
+run_test 6 "abcd::2" "abcd::3" "64" "tcp"
+cleanup
+run_test "4" "10.141.10.2" "10.141.10.3" "24" "udp"
+cleanup
+run_test 6 "abcd::2" "abcd::3" "64" "udp"
+cleanup
+run_test "4" "10.141.10.2" "10.141.10.3" "24" "udp-zero-checksum"
cleanup
-run_test 6 "abcd::2" "abcd::3" "64"
+run_test 6 "abcd::2" "abcd::3" "64" "udp-zero-checksum"
# trap calls cleanup
+exit 0
diff --git a/tests/shell/testcases/packetpath/policy b/tests/shell/testcases/packetpath/policy
new file mode 100755
index 00000000..0bb42a54
--- /dev/null
+++ b/tests/shell/testcases/packetpath/policy
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+ip link set lo up
+
+$NFT -f - <<EOF
+table inet filter {
+ chain underflow { }
+
+ chain input {
+ type filter hook input priority filter; policy accept;
+ icmp type echo-reply accept
+ ip saddr 127.0.0.1 ip daddr 127.0.0.2 counter accept
+ goto underflow
+ }
+}
+EOF
+[ $? -ne 0 ] && exit 1
+
+ping -q -c 1 127.0.0.2 >/dev/null || exit 2
+
+# should work, polict is accept.
+ping -q -c 1 127.0.0.1 >/dev/null || exit 1
+
+$NFT -f - <<EOF
+table inet filter {
+ chain input {
+ type filter hook input priority filter; policy drop;
+ }
+}
+EOF
+[ $? -ne 0 ] && exit 1
+
+$NFT list ruleset
+
+ping -W 1 -q -c 1 127.0.0.2
+
+ping -q -c 1 127.0.0.2 >/dev/null || exit 2
+
+# should fail, policy is set to drop
+ping -W 1 -q -c 1 127.0.0.1 >/dev/null 2>&1 && exit 1
+
+exit 0
diff --git a/tests/shell/testcases/packetpath/tcp_reset b/tests/shell/testcases/packetpath/tcp_reset
new file mode 100755
index 00000000..3dfcdde4
--- /dev/null
+++ b/tests/shell/testcases/packetpath/tcp_reset
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# regression check for kernel commit
+# netfilter: nf_reject: init skb->dev for reset packet
+
+socat -h > /dev/null || exit 77
+
+ip link set lo up
+
+$NFT -f - <<EOF
+table inet filter {
+ chain input {
+ type filter hook input priority filter; policy accept;
+ meta nftrace set 1
+ ip daddr 127.0.0.1 tcp dport 5555 reject with tcp reset
+ ip6 daddr ::1 tcp dport 5555 reject with tcp reset
+ tcp dport 5555 counter
+ }
+ chain output {
+ type filter hook output priority filter; policy accept;
+ # empty chain, so nf_hook_slow is called from ip_local_out.
+ }
+}
+EOF
+[ $? -ne 0 ] && exit 1
+
+socat -u STDIN TCP:127.0.0.1:5555,connect-timeout=2 < /dev/null > /dev/null
+socat -u STDIN TCP:[::1]:5555,connect-timeout=2 < /dev/null > /dev/null
+
+$NFT list ruleset |grep -q 'counter packets 0 bytes 0' || exit 1
+exit 0
diff --git a/tests/shell/testcases/packetpath/vlan_mangling b/tests/shell/testcases/packetpath/vlan_mangling
new file mode 100755
index 00000000..e3fd443e
--- /dev/null
+++ b/tests/shell/testcases/packetpath/vlan_mangling
@@ -0,0 +1,77 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_netdev_egress)
+
+rnd=$(mktemp -u XXXXXXXX)
+ns1="nft1ifname-$rnd"
+ns2="nft2ifname-$rnd"
+
+cleanup()
+{
+ ip netns del "$ns1"
+ ip netns del "$ns2"
+}
+
+trap cleanup EXIT
+
+set -e
+
+ip netns add "$ns1"
+ip netns add "$ns2"
+ip -net "$ns1" link set lo up
+ip -net "$ns2" link set lo up
+
+ip link add veth0 netns $ns1 type veth peer name veth0 netns $ns2
+
+ip -net "$ns1" link set veth0 addr da:d3:00:01:02:03
+
+ip -net "$ns1" link add vlan123 link veth0 type vlan id 123
+ip -net "$ns2" link add vlan321 link veth0 type vlan id 321
+
+
+for dev in veth0 ; do
+ ip -net "$ns1" link set $dev up
+ ip -net "$ns2" link set $dev up
+done
+ip -net "$ns1" link set vlan123 up
+ip -net "$ns2" link set vlan321 up
+
+ip -net "$ns1" addr add 10.1.1.1/24 dev vlan123
+ip -net "$ns2" addr add 10.1.1.2/24 dev vlan321
+
+ip netns exec "$ns2" $NFT -f /dev/stdin <<"EOF"
+table netdev t {
+ chain in_update_vlan {
+ vlan type arp vlan id set 321 counter
+ ip saddr 10.1.1.1 icmp type echo-request vlan id set 321 counter
+ }
+
+ chain in {
+ type filter hook ingress device veth0 priority filter;
+ ether saddr da:d3:00:01:02:03 vlan id 123 jump in_update_vlan
+ }
+
+ chain out_update_vlan {
+ vlan type arp vlan id set 123 counter
+ ip daddr 10.1.1.1 icmp type echo-reply vlan id set 123 counter
+ }
+
+ chain out {
+ type filter hook egress device veth0 priority filter;
+ ether daddr da:d3:00:01:02:03 vlan id 321 jump out_update_vlan
+ }
+}
+EOF
+
+ip netns exec "$ns1" ping -c 1 10.1.1.2
+
+set +e
+
+ip netns exec "$ns2" $NFT list ruleset
+ip netns exec "$ns2" $NFT list table netdev t | grep 'counter packets' | grep 'counter packets 0 bytes 0'
+if [ $? -eq 1 ]
+then
+ exit 0
+fi
+
+exit 1
diff --git a/tests/shell/testcases/packetpath/vlan_qinq b/tests/shell/testcases/packetpath/vlan_qinq
new file mode 100755
index 00000000..28655766
--- /dev/null
+++ b/tests/shell/testcases/packetpath/vlan_qinq
@@ -0,0 +1,73 @@
+#!/bin/bash
+
+rnd=$(mktemp -u XXXXXXXX)
+ns1="nft1ifname-$rnd"
+ns2="nft2ifname-$rnd"
+
+cleanup()
+{
+ ip netns del "$ns1"
+ ip netns del "$ns2"
+}
+
+trap cleanup EXIT
+
+set -e
+
+ip netns add "$ns1"
+ip netns add "$ns2"
+ip -net "$ns1" link set lo up
+ip -net "$ns2" link set lo up
+
+ip link add veth0 netns $ns1 type veth peer name veth0 netns $ns2
+
+ip -net "$ns1" link set veth0 addr da:d3:00:01:02:03
+
+ip -net "$ns1" link add link veth0 name vlan10 type vlan proto 802.1ad id 10
+ip -net "$ns1" link add link vlan10 name vlan10.100 type vlan proto 802.1q id 100
+
+ip -net "$ns2" link add link veth0 name vlan10 type vlan proto 802.1ad id 10
+ip -net "$ns2" link add link vlan10 name vlan10.100 type vlan proto 802.1q id 100
+
+for dev in veth0 vlan10 vlan10.100; do
+ ip -net "$ns1" link set $dev up
+ ip -net "$ns2" link set $dev up
+done
+
+ip -net "$ns1" addr add 10.1.1.1/24 dev vlan10.100
+ip -net "$ns2" addr add 10.1.1.2/24 dev vlan10.100
+
+ip netns exec "$ns2" $NFT -f /dev/stdin <<"EOF"
+table netdev t {
+ chain c1 {
+ type filter hook ingress device veth0 priority filter;
+ ether type 8021ad vlan id 10 vlan type 8021q vlan id 100 vlan type ip counter
+ }
+
+ chain c2 {
+ type filter hook ingress device vlan10 priority filter;
+ vlan id 100 ip daddr 10.1.1.2 counter
+ }
+
+ chain c3 {
+ type filter hook ingress device vlan10.100 priority filter;
+ ip daddr 10.1.1.2 counter
+ }
+}
+EOF
+
+ip netns exec "$ns1" ping -c 1 10.1.1.2
+ip netns exec "$ns2" $NFT list ruleset
+
+set +e
+
+ip netns exec "$ns2" $NFT list chain netdev t c1 | grep 'counter packets 0 bytes 0'
+[[ $? -eq 0 ]] && exit 1
+
+ip netns exec "$ns2" $NFT list chain netdev t c2 | grep 'counter packets 0 bytes 0'
+[[ $? -eq 0 ]] && exit 1
+
+ip netns exec "$ns2" $NFT list chain netdev t c3 | grep 'counter packets 0 bytes 0'
+[[ $? -eq 0 ]] && exit 1
+
+exit 0
diff --git a/tests/shell/testcases/rule_management/0004replace_0 b/tests/shell/testcases/rule_management/0004replace_0
index c3329af5..18dc4a9f 100755
--- a/tests/shell/testcases/rule_management/0004replace_0
+++ b/tests/shell/testcases/rule_management/0004replace_0
@@ -6,5 +6,9 @@
set -e
$NFT add table t
$NFT add chain t c
-$NFT add rule t c accept # should have handle 2
-$NFT replace rule t c handle 2 drop
+$NFT 'add set t s1 { type ipv4_addr; }'
+$NFT 'add set t s2 { type ipv4_addr; flags interval; }'
+$NFT add rule t c accept # should have handle 4
+$NFT replace rule t c handle 4 drop
+$NFT replace rule t c handle 4 ip saddr { 1.1.1.1, 2.2.2.2 }
+$NFT replace rule t c handle 4 ip saddr @s2 ip daddr { 3.3.3.3, 4.4.4.4 }
diff --git a/tests/shell/testcases/rule_management/0011reset_0 b/tests/shell/testcases/rule_management/0011reset_0
index 33eadd9e..2004b17d 100755
--- a/tests/shell/testcases/rule_management/0011reset_0
+++ b/tests/shell/testcases/rule_management/0011reset_0
@@ -4,6 +4,27 @@
set -e
+echo "loading ruleset with anonymous set"
+$NFT -f - <<EOF
+table t {
+ chain dns-nat-pre {
+ type nat hook prerouting priority filter; policy accept;
+ meta l4proto { tcp, udp } th dport 53 ip saddr 10.24.0.0/24 ip daddr != 10.25.0.1 counter packets 1000 bytes 1000 dnat to 10.25.0.1
+ }
+}
+EOF
+
+echo "resetting ruleset with anonymous set"
+$NFT reset rules
+EXPECT='table ip t {
+ chain dns-nat-pre {
+ type nat hook prerouting priority filter; policy accept;
+ meta l4proto { tcp, udp } th dport 53 ip saddr 10.24.0.0/24 ip daddr != 10.25.0.1 counter packets 0 bytes 0 dnat to 10.25.0.1
+ }
+}'
+$DIFF -u <(echo "$EXPECT") <($NFT list ruleset)
+$NFT flush ruleset
+
echo "loading ruleset"
$NFT -f - <<EOF
table ip t {
@@ -74,13 +95,6 @@ $DIFF -u <(echo "$EXPECT") <($NFT list ruleset)
echo "resetting specific chain"
EXPECT='table ip t {
- set s {
- type ipv4_addr
- size 65535
- flags dynamic
- counter
- }
-
chain c2 {
counter packets 3 bytes 13 accept
counter packets 4 bytes 14 drop
@@ -95,6 +109,7 @@ EXPECT='table ip t {
size 65535
flags dynamic
counter
+ elements = { 1.1.1.1 counter packets 1 bytes 11 }
}
chain c {
@@ -116,6 +131,7 @@ EXPECT='table ip t {
size 65535
flags dynamic
counter
+ elements = { 1.1.1.1 counter packets 1 bytes 11 }
}
chain c {
@@ -143,6 +159,7 @@ EXPECT='table ip t {
size 65535
flags dynamic
counter
+ elements = { 1.1.1.1 counter packets 1 bytes 11 }
}
chain c {
diff --git a/tests/shell/testcases/rule_management/dumps/0004replace_0.json-nft b/tests/shell/testcases/rule_management/dumps/0004replace_0.json-nft
index 5d0b7d06..767e80f1 100644
--- a/tests/shell/testcases/rule_management/dumps/0004replace_0.json-nft
+++ b/tests/shell/testcases/rule_management/dumps/0004replace_0.json-nft
@@ -23,6 +23,27 @@
}
},
{
+ "set": {
+ "family": "ip",
+ "name": "s1",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s2",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ]
+ }
+ },
+ {
"rule": {
"family": "ip",
"table": "t",
@@ -30,7 +51,33 @@
"handle": 0,
"expr": [
{
- "drop": null
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@s2"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": {
+ "set": [
+ "3.3.3.3",
+ "4.4.4.4"
+ ]
+ }
+ }
}
]
}
diff --git a/tests/shell/testcases/rule_management/dumps/0004replace_0.nft b/tests/shell/testcases/rule_management/dumps/0004replace_0.nft
index e20952ef..803c0deb 100644
--- a/tests/shell/testcases/rule_management/dumps/0004replace_0.nft
+++ b/tests/shell/testcases/rule_management/dumps/0004replace_0.nft
@@ -1,5 +1,14 @@
table ip t {
+ set s1 {
+ type ipv4_addr
+ }
+
+ set s2 {
+ type ipv4_addr
+ flags interval
+ }
+
chain c {
- drop
+ ip saddr @s2 ip daddr { 3.3.3.3, 4.4.4.4 }
}
}
diff --git a/tests/shell/testcases/sets/collapse_elem_0 b/tests/shell/testcases/sets/collapse_elem_0
index 7699e9da..52a42c2f 100755
--- a/tests/shell/testcases/sets/collapse_elem_0
+++ b/tests/shell/testcases/sets/collapse_elem_0
@@ -17,3 +17,9 @@ add element ip a x { 2 }
add element ip6 a x { 2 }"
$NFT -f - <<< $RULESET
+
+RULESET="define m = { 3, 4 }
+add element ip a x \$m
+add element ip a x { 5 }"
+
+$NFT -f - <<< $RULESET
diff --git a/tests/shell/testcases/sets/dumps/collapse_elem_0.json-nft b/tests/shell/testcases/sets/dumps/collapse_elem_0.json-nft
index c713828d..c8ff4347 100644
--- a/tests/shell/testcases/sets/dumps/collapse_elem_0.json-nft
+++ b/tests/shell/testcases/sets/dumps/collapse_elem_0.json-nft
@@ -23,7 +23,10 @@
"handle": 0,
"elem": [
1,
- 2
+ 2,
+ 3,
+ 4,
+ 5
]
}
},
diff --git a/tests/shell/testcases/sets/dumps/collapse_elem_0.nft b/tests/shell/testcases/sets/dumps/collapse_elem_0.nft
index a3244fc6..775f0ab1 100644
--- a/tests/shell/testcases/sets/dumps/collapse_elem_0.nft
+++ b/tests/shell/testcases/sets/dumps/collapse_elem_0.nft
@@ -1,7 +1,7 @@
table ip a {
set x {
type inet_service
- elements = { 1, 2 }
+ elements = { 1, 2, 3, 4, 5 }
}
}
table ip6 a {
diff --git a/tests/shell/testcases/sets/dumps/elem_limit_0.nft b/tests/shell/testcases/sets/dumps/elem_limit_0.nft
new file mode 100644
index 00000000..ca5b2b54
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/elem_limit_0.nft
@@ -0,0 +1,7 @@
+table netdev filter {
+ set test123 {
+ typeof ip saddr
+ limit rate over 1 mbytes/second
+ elements = { 1.2.3.4 limit rate over 1 mbytes/second }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/set_element_timeout_updates.json-nft b/tests/shell/testcases/sets/dumps/set_element_timeout_updates.json-nft
new file mode 100644
index 00000000..aa908297
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/set_element_timeout_updates.json-nft
@@ -0,0 +1,43 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "base",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "timeout"
+ ],
+ "timeout": 60
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/set_element_timeout_updates.nft b/tests/shell/testcases/sets/dumps/set_element_timeout_updates.nft
new file mode 100644
index 00000000..1edd2ec7
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/set_element_timeout_updates.nft
@@ -0,0 +1,10 @@
+table ip t {
+ set s {
+ typeof ip saddr
+ timeout 1m
+ }
+
+ chain base {
+ type filter hook input priority filter; policy accept;
+ }
+}
diff --git a/tests/shell/testcases/sets/elem_limit_0 b/tests/shell/testcases/sets/elem_limit_0
new file mode 100755
index 00000000..b57f9274
--- /dev/null
+++ b/tests/shell/testcases/sets/elem_limit_0
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+## requires EXPR
+
+set -e
+
+RULESET="table netdev filter {
+ set test123 {
+ typeof ip saddr
+ limit rate over 1024 kbytes/second
+ elements = { 1.2.3.4 limit rate over 1024 kbytes/second }
+ }
+}"
+
+$NFT -f - <<< $RULESET
+
+(echo "flush ruleset netdev"; $NFT --stateless list ruleset netdev) | $NFT -f -
diff --git a/tests/shell/testcases/sets/set_element_timeout_updates b/tests/shell/testcases/sets/set_element_timeout_updates
new file mode 100755
index 00000000..4bf6c7c3
--- /dev/null
+++ b/tests/shell/testcases/sets/set_element_timeout_updates
@@ -0,0 +1,120 @@
+#!/bin/bash
+#
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_elem_timeout_update)
+#
+
+assert_fail()
+{
+ ret=$1
+
+ if [ $ret -eq 0 ];then
+ echo "subtest should have failed: $2"
+ exit 111
+ fi
+}
+
+assert_ok()
+{
+ ret=$1
+
+ if [ $ret -ne 0 ];then
+ echo "subtest should have passed: $2"
+ exit 111
+ fi
+}
+
+
+$NFT -f - <<EOF
+table t {
+ set s {
+ typeof ip saddr
+ timeout 1m
+ elements = { 10.0.0.1, 10.0.0.2, 10.0.0.3 }
+ }
+
+ chain base {
+ type filter hook input priority 0
+ }
+}
+EOF
+
+for i in 1 2 3;do
+ $NFT get element t s "{ 10.0.0.$i }"
+ assert_ok $? "get element $i"
+done
+
+# first, bogus updates to trigger abort path with updates.
+$NFT -f - <<EOF
+add element t s { 10.0.0.2 timeout 2m }
+create element t s { 10.0.0.1 }
+add element t s { 10.0.0.3 timeout 3m }
+EOF
+assert_fail $? "abort due to existing element"
+
+$NFT -f - <<EOF
+add chain t a
+add element t s { 10.0.0.1 timeout 1m }
+add element t s { 10.0.0.2 timeout 2m }
+add element t s { 10.0.0.3 timeout 3m }
+add chain t b
+add rule t a jump b
+add rule t b jump a
+add rule t base jump a
+EOF
+assert_fail $? "abort due to chainloop"
+
+$NFT -f - <<EOF
+add element t s { 10.0.0.1 expires 2m }
+EOF
+assert_fail $? "expire larger than timeout"
+
+$NFT -f - <<EOF
+add element t s { 10.0.0.1 timeout 1s }
+add element t s { 10.0.0.2 timeout 1s }
+add element t s { 10.0.0.3 timeout 1s }
+add element t s { 10.0.0.4 expires 2m }
+EOF
+assert_fail $? "abort because expire too large"
+
+# check timeout update had no effect
+sleep 1
+for i in 1 2 3;do
+ $NFT get element t s "{ 10.0.0.$i }"
+ assert_ok $? "get element $i after aborted update"
+done
+
+# adjust timeouts upwards.
+$NFT -f - <<EOF
+add element t s { 10.0.0.1 timeout 1m }
+add element t s { 10.0.0.2 timeout 2m }
+add element t s { 10.0.0.3 timeout 3m }
+EOF
+assert_ok $? "upwards adjust"
+
+for i in 1 2 3;do
+ $NFT get element t s "{ 10.0.0.$i }"
+ assert_ok $? "get element $i"
+done
+
+# insert 4th element with timeout larger than set default
+$NFT -f - <<EOF
+add element t s { 10.0.0.4 timeout 2m expires 2m }
+EOF
+$NFT get element t s "{ 10.0.0.4 }"
+assert_ok $? "get element 4"
+
+# adjust timeouts downwards
+$NFT -f - <<EOF
+add element t s { 10.0.0.1 timeout 1s }
+add element t s { 10.0.0.2 timeout 2s expires 1s }
+add element t s { 10.0.0.3 expires 1s }
+add element t s { 10.0.0.4 timeout 4m expires 1s }
+EOF
+assert_ok $?
+
+sleep 1
+
+for i in 1 2 3;do
+ $NFT get element t s "{ 10.0.0.$i }"
+ assert_fail $?
+done
diff --git a/tests/shell/testcases/sets/typeof_sets_0 b/tests/shell/testcases/sets/typeof_sets_0
index 016227da..a105acff 100755
--- a/tests/shell/testcases/sets/typeof_sets_0
+++ b/tests/shell/testcases/sets/typeof_sets_0
@@ -4,6 +4,8 @@
# s1 and s2 are identical, they just use different
# ways for declaration.
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_ip_options)
+
set -e
die() {
diff --git a/tests/shell/testcases/transactions/0023rule_1 b/tests/shell/testcases/transactions/0023rule_1
index e58c088c..863bcde4 100755
--- a/tests/shell/testcases/transactions/0023rule_1
+++ b/tests/shell/testcases/transactions/0023rule_1
@@ -1,7 +1,7 @@
#!/bin/bash
RULESET="add table x
-add chain x y
+add chain x y { type filter hook input priority 0; }
add rule x y jump y"
# kernel must return ELOOP
diff --git a/tests/shell/testcases/transactions/0024rule_0 b/tests/shell/testcases/transactions/0024rule_0
index 4c1ac41d..645319e2 100755
--- a/tests/shell/testcases/transactions/0024rule_0
+++ b/tests/shell/testcases/transactions/0024rule_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_position_id)
+
RULESET="flush ruleset
add table x
add chain x y
diff --git a/tests/shell/testcases/transactions/0049huge_0 b/tests/shell/testcases/transactions/0049huge_0
index f66953c2..698716b2 100755
--- a/tests/shell/testcases/transactions/0049huge_0
+++ b/tests/shell/testcases/transactions/0049huge_0
@@ -42,6 +42,11 @@ if [ "$NFT_TEST_HAVE_json" != n ]; then
test $($NFT -j -e -a -f - <<< "$RULESET" |sed 's/\({"add":\)/\n\1/g' |grep '"handle"' |wc -l) -eq ${RULE_COUNT} || exit 1
fi
+if [ "$NFT_TEST_HAVE_inet_nat" = n ]; then
+ echo "Test partially skipped due to missing inet nat support."
+ exit 77
+fi
+
# Now an example from firewalld's testsuite
#
$NFT flush ruleset
diff --git a/tests/shell/testcases/transactions/30s-stress b/tests/shell/testcases/transactions/30s-stress
index e92b9226..e1e8b742 100755
--- a/tests/shell/testcases/transactions/30s-stress
+++ b/tests/shell/testcases/transactions/30s-stress
@@ -290,8 +290,34 @@ available_flags()
fi
}
-random_element_string=""
+random_timeout()
+{
+ local timeout=""
+ local expires
+ local r=$((RANDOM%3))
+
+ case "$r" in
+ 0)
+ timeout=$((RANDOM%60000))
+ timeout="timeout ${timeout}ms"
+ ;;
+ 1)
+ timeout=$((RANDOM%60000))
+ expires=$((timeout))
+ if [ $timeout -gt 0 ];then
+ expires=$((RANDOM%expires))
+ else
+ expires=0
+ fi
+ timeout="timeout ${timeout}ms expires ${expires}ms"
+ ;;
+ esac
+
+ echo -n "$timeout"
+}
+
+random_element_string=""
# create a random element. Could cause any of the following:
# 1. Invalid set/map
# 2. Element already exists in set/map w. create
@@ -354,17 +380,17 @@ random_elem()
r=$((RANDOM%7))
case "$r" in
0)
- random_element_string=" inet $table set_${cnt} { $element }"
+ random_element_string="inet $table set_${cnt} { $element }"
;;
- 1) random_element_string="inet $table sett${cnt} { $element timeout $((RANDOM%60))s }"
+ 1) random_element_string="inet $table sett${cnt} { $element $(random_timeout) }"
;;
2) random_element_string="inet $table dmap_${cnt} { $element : $RANDOM }"
;;
- 3) random_element_string="inet $table dmapt${cnt} { $element timeout $((RANDOM%60))s : $RANDOM }"
+ 3) random_element_string="inet $table dmapt${cnt} { $element $(random_timeout) : $RANDOM }"
;;
4) random_element_string="inet $table vmap_${cnt} { $element : `random_verdict $count` }"
;;
- 5) random_element_string="inet $table vmapt${cnt} { $element timeout $((RANDOM%60))s : `random_verdict $count` }"
+ 5) random_element_string="inet $table vmapt${cnt} { $element $(random_timeout) : `random_verdict $count` }"
;;
6) random_element_string="inet $table setc${cnt} { $element }"
;;
@@ -625,11 +651,11 @@ for table in $tables; do
esac
echo "add element inet $table set_${cnt} { $element }" >> "$tmp"
- echo "add element inet $table sett${cnt} { $element timeout $((RANDOM%60))s }" >> "$tmp"
+ echo "add element inet $table sett${cnt} { $element $(random_timeout) }" >> "$tmp"
echo "add element inet $table dmap_${cnt} { $element : $RANDOM }" >> "$tmp"
- echo "add element inet $table dmapt${cnt} { $element timeout $((RANDOM%60))s : $RANDOM }" >> "$tmp"
+ echo "add element inet $table dmapt${cnt} { $element $(random_timeout) : $RANDOM }" >> "$tmp"
echo "add element inet $table vmap_${cnt} { $element : `random_verdict $count` }" >> "$tmp"
- echo "add element inet $table vmapt${cnt} { $element timeout $((RANDOM%60))s : `random_verdict $count` }" >> "$tmp"
+ echo "add element inet $table vmapt${cnt} { $element $(random_timeout) : `random_verdict $count` }" >> "$tmp"
done
done
done
diff --git a/tests/shell/testcases/transactions/anon_chain_loop b/tests/shell/testcases/transactions/anon_chain_loop
index 2fd61810..3053d166 100755
--- a/tests/shell/testcases/transactions/anon_chain_loop
+++ b/tests/shell/testcases/transactions/anon_chain_loop
@@ -3,7 +3,7 @@
# anon chains with c1 -> c2 recursive jump, expect failure
$NFT -f - <<EOF
table ip t {
- chain c2 { }
+ chain c2 { type filter hook input priority 0; }
chain c1 { }
}
diff --git a/tests/shell/testcases/transactions/bad_rule_graphs b/tests/shell/testcases/transactions/bad_rule_graphs
new file mode 100755
index 00000000..53047c3c
--- /dev/null
+++ b/tests/shell/testcases/transactions/bad_rule_graphs
@@ -0,0 +1,262 @@
+#!/bin/bash
+
+# test case to attempt to fool ruleset validation.
+# Initial ruleset added here is fine, then we try to make the
+# ruleset exceed the jump chain depth via jumps, gotos, verdict
+# map entries etc, either by having the map loop back to itself,
+# jumping back to an earlier chain and so on.
+#
+# Also check that can't hook up a user-defined chain with a
+# restricted expression (here: tproxy, only valid from prerouting
+# hook) to the input hook, even if reachable indirectly via vmap.
+
+bad_ruleset()
+{
+ ret=$1
+ shift
+
+ if [ $ret -eq 0 ];then
+ echo "Accepted bad ruleset with $@"
+ $NFT list ruleset
+ exit 1
+ fi
+}
+
+good_ruleset()
+{
+ ret=$1
+ shift
+
+ if [ $ret -ne 0 ];then
+ echo "Rejected good ruleset with $@"
+ exit 1
+ fi
+}
+
+# add a loop with a vmap statement, either goto or jump,
+# both with single rule and delta-transaction that also
+# contains valid information.
+check_loop()
+{
+ what=$1
+
+ $NFT "add element t m { 1.2.3.9 : $what c1 }"
+ bad_ruleset $? "bound map with $what to backjump should exceed jump stack"
+
+ $NFT "add element t m { 1.2.3.9 : $what c7 }"
+ bad_ruleset $? "bound map with $what to backjump should exceed jump stack"
+
+ $NFT "add element t m { 1.2.3.9 : $what c8 }"
+ bad_ruleset $? "bound map with $what to self should exceed jump stack"
+
+ # rule bound to c8, this should not work -- jump stack should be exceeded.
+ $NFT "add element t m { 1.2.3.9 : jump c9 }"
+ bad_ruleset $? "bound map with $what should exceed jump stack"
+
+ # rule bound to c8, this should be within jump stack limit
+ $NFT "add element t m { 1.2.3.9 : jump c10 }"
+ good_ruleset $? "bound map with $what should not have exceeded jump stack"
+
+$NFT -f - <<EOF
+flush chain t c16
+flush chain t c15
+table t {
+ chain c9 {
+ ip protocol 6 goto c14
+ }
+
+ # calls @m again, but @m now runs c10, which is linked to c14 already.
+ chain c14 {
+ ip protocol 6 return
+ ip daddr vmap @m
+ }
+}
+EOF
+ bad_ruleset $? "delta with bound map with $what loop and rule deletions"
+
+ # delete mapping again
+ $NFT "delete element t m { 1.2.3.9 }"
+ good_ruleset $? "cannot delete mapping"
+}
+
+check_bad_expr()
+{
+$NFT -f -<<EOF
+table t {
+ chain c1 {
+ jump c9
+ }
+}
+EOF
+bad_ruleset $? "tproxy expr exposed to input hook"
+
+$NFT -f -<<EOF
+flush map t m
+
+table t {
+ chain c1 {
+ ip saddr vmap @m
+ }
+}
+EOF
+good_ruleset $? "bound vmap to c1"
+
+$NFT -f -<<EOF
+table t {
+ map m {
+ type ipv4_addr : verdict
+ elements = { 1.2.3.4 : jump c9 }
+ }
+}
+EOF
+bad_ruleset $? "tproxy expr exposed to input hook by vmap"
+
+$NFT -f -<<EOF
+flush chain t c10
+flush chain t c11
+add rule t c8 jump c9
+
+table t {
+ map m {
+ type ipv4_addr : verdict
+ elements = { 1.2.3.4 : goto c2 }
+ }
+}
+EOF
+bad_ruleset $? "tproxy expr exposed to input hook by vmap"
+
+$NFT -f -<<EOF
+flush chain t c2
+flush chain t c3
+flush chain t c4
+flush chain t c5
+flush chain t c6
+flush chain t c7
+flush chain t c10
+flush chain t c11
+flush chain t c12
+flush chain t c13
+flush chain t c14
+flush chain t c15
+flush chain t c16
+delete chain t c16
+delete chain t c15
+delete chain t c14
+delete chain t c13
+delete chain t c12
+delete chain t c11
+delete chain t c7
+delete chain t c6
+delete chain t c5
+delete chain t c4
+delete chain t c3
+add rule t c8 jump c9
+EOF
+good_ruleset $? "connect chain c8 to chain c9"
+
+$NFT -f -<<EOF
+table t {
+ map m {
+ type ipv4_addr : verdict
+ elements = { 1.2.3.4 : goto c8 }
+ }
+}
+EOF
+bad_ruleset $? "tproxy expr exposed to input hook by vmap c1 -> vmap -> c8 -> c9"
+}
+
+# 16 jump levels are permitted.
+# First ruleset is fine, there is no jump
+# from c8 to c9.
+$NFT -f - <<EOF
+table t {
+ map m {
+ type ipv4_addr : verdict
+ }
+
+ chain c16 { }
+ chain c15 { jump c16; }
+ chain c14 { jump c15; }
+ chain c13 { jump c14; }
+ chain c12 { jump c13; }
+ chain c11 { jump c12; }
+ chain c10 { jump c11; }
+ chain c9 { jump c10; }
+ chain c8 { }
+ chain c7 { jump c8; }
+ chain c6 { jump c7; }
+ chain c5 { jump c6; }
+ chain c4 { jump c5; }
+ chain c3 { jump c4; }
+ chain c2 { jump c3; }
+ chain c1 { jump c2; }
+ chain c0 { type filter hook input priority 0;
+ jump c1
+ }
+}
+EOF
+
+ret=$?
+if [ $ret -ne 0 ];then
+ exit 1
+fi
+
+# ensure kernel catches the exceeded jumpstack use, despite no new chains
+# are added here and cycle is acyclic.
+$NFT -f - <<EOF
+# unrelated rule.
+add rule t c14 accept
+add rule t c15 accept
+
+# close jump gap; after this jumpstack limit is exceeded.
+add rule t c8 goto c9
+
+# unrelated rules.
+add rule t c14 accept
+add rule t c15 accept
+EOF
+
+bad_ruleset $? "chain jump stack exhausted without cycle"
+
+$NFT -f - <<EOF
+# unrelated rule.
+add rule t c12 accept
+add rule t c13 accept
+
+add element t m { 1.2.3.1 : accept }
+add element t m { 1.2.3.16 : goto c16 }
+add element t m { 1.2.3.15 : goto c15 }
+
+# after this jumpstack limit is exceeded,
+# IFF @m was bound to c8, but it is not.
+add element t m { 1.2.3.9 : jump c9 }
+
+# unrelated rules.
+add rule t c12 accept
+add rule t c13 accept
+
+add element t m { 1.2.3.16 : goto c16 }
+EOF
+good_ruleset $? "unbounded map"
+
+# bind vmap to c8. This MUST fail, map jumps to c9.
+$NFT "add rule t c8 ip saddr vmap @m"
+bad_ruleset $? "jump c8->c9 via vmap expression"
+
+# delete the mapping again.
+$NFT "delete element t m { 1.2.3.9 }"
+$NFT "add rule t c8 ip saddr vmap @m"
+good_ruleset $? "bind empty map to c8"
+
+check_loop "jump"
+check_loop "goto"
+
+$NFT "flush chain t c8"
+good_ruleset $? "flush chain t c8"
+
+# should work, c9 not connected to c0 aka filter input.
+$NFT "add rule t c9 tcp dport 80 tproxy to :20000 meta mark set 1 accept"
+good_ruleset $? "add tproxy expression to c9"
+check_bad_expr
+
+exit $?
diff --git a/tests/shell/testcases/transactions/dumps/bad_rule_graphs.json-nft b/tests/shell/testcases/transactions/dumps/bad_rule_graphs.json-nft
new file mode 100644
index 00000000..30789211
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/bad_rule_graphs.json-nft
@@ -0,0 +1,201 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c10",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c9",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c8",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c2",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c0",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "m",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "verdict"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c9",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c10"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c9",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 80
+ }
+ },
+ {
+ "tproxy": {
+ "port": 20000
+ }
+ },
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": 1
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c8",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c9"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c1",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c2"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c1",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": "@m"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c0",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c1"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/bad_rule_graphs.nft b/tests/shell/testcases/transactions/dumps/bad_rule_graphs.nft
new file mode 100644
index 00000000..3a593650
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/bad_rule_graphs.nft
@@ -0,0 +1,30 @@
+table ip t {
+ map m {
+ type ipv4_addr : verdict
+ }
+
+ chain c10 {
+ }
+
+ chain c9 {
+ jump c10
+ tcp dport 80 tproxy to :20000 meta mark set 0x00000001 accept
+ }
+
+ chain c8 {
+ jump c9
+ }
+
+ chain c2 {
+ }
+
+ chain c1 {
+ jump c2
+ ip saddr vmap @m
+ }
+
+ chain c0 {
+ type filter hook input priority filter; policy accept;
+ jump c1
+ }
+}
diff --git a/tests/shell/testcases/transactions/dumps/handle_bad_family.json-nft b/tests/shell/testcases/transactions/dumps/handle_bad_family.json-nft
new file mode 100644
index 00000000..15ec0aac
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/handle_bad_family.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/handle_bad_family.nft b/tests/shell/testcases/transactions/dumps/handle_bad_family.nft
new file mode 100644
index 00000000..5d4d2caf
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/handle_bad_family.nft
@@ -0,0 +1,2 @@
+table ip x {
+}
diff --git a/tests/shell/testcases/transactions/dumps/validation_recursion.sh.nodump b/tests/shell/testcases/transactions/dumps/validation_recursion.sh.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/validation_recursion.sh.nodump
diff --git a/tests/shell/testcases/transactions/handle_bad_family b/tests/shell/testcases/transactions/handle_bad_family
new file mode 100755
index 00000000..59224189
--- /dev/null
+++ b/tests/shell/testcases/transactions/handle_bad_family
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+NFT=nft
+
+HANDLE=$($NFT -a -e add table ip x | cut -d '#' -f 2 | awk '{ print $2 }' | head -1)
+
+# should fail
+$NFT delete table inet handle $HANDLE
+[ $? -ne 0 ] && exit 0
diff --git a/tests/shell/testcases/transactions/validation_recursion.sh b/tests/shell/testcases/transactions/validation_recursion.sh
new file mode 100755
index 00000000..bc3ebcc1
--- /dev/null
+++ b/tests/shell/testcases/transactions/validation_recursion.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+# regression check for kernel commit
+# cff3bd012a95 ("netfilter: nf_tables: prefer nft_chain_validate")
+
+chains=100
+
+# first create skeleton, linear list
+# of 1k jumps, c1 -> c2 .. -> c100.
+#
+# not caught, commit phase validation doesn't care about
+# non-base chains.
+(
+ echo add table ip t
+
+ for i in $(seq 1 $chains);do
+ echo add chain t c$i
+ done
+
+ for i in $(seq 1 $((chains-1)) );do
+ echo add rule t c$i jump c$((i+1))
+ done
+) | $NFT -f -
+
+# now link up c0 to c1. This triggers register-store validation for
+# c1. Old algorithm is recursive and will blindly chase the entire
+# list of chains created above. On older kernels, this will cause kernel
+# stack overflow/guard page crash.
+$NFT -f - <<EOF
+add chain t c0 { type filter hook input priority 0; }
+add rule t c0 jump c1
+EOF
+
+if [ $? -eq 0 ] ; then
+ echo "E: loaded bogus ruleset" >&2
+ exit 1
+fi
+
+$NFT delete table ip t