summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Make_global.am2
-rw-r--r--configure.ac4
-rw-r--r--doc/libnftables-json.adoc2
-rw-r--r--doc/libnftables.adoc1
-rw-r--r--doc/nft.txt12
-rw-r--r--doc/stateful-objects.txt62
-rw-r--r--doc/statements.txt25
-rw-r--r--include/cache.h1
-rw-r--r--include/linux/netfilter/Makefile.am1
-rw-r--r--include/linux/netfilter/nfnetlink_hook.h14
-rw-r--r--include/netlink.h3
-rw-r--r--include/nftables.h11
-rw-r--r--include/nftables/libnftables.h6
-rw-r--r--include/rule.h1
-rw-r--r--src/cache.c52
-rw-r--r--src/cmd.c20
-rw-r--r--src/datatype.c5
-rw-r--r--src/evaluate.c189
-rw-r--r--src/expression.c7
-rw-r--r--src/json.c4
-rw-r--r--src/libnftables.c105
-rw-r--r--src/libnftables.map7
-rw-r--r--src/main.c22
-rw-r--r--src/meta.c33
-rw-r--r--src/mnl.c339
-rw-r--r--src/netlink.c220
-rw-r--r--src/netlink_delinearize.c188
-rw-r--r--src/netlink_linearize.c63
-rw-r--r--src/parser_bison.y114
-rw-r--r--src/parser_json.c20
-rw-r--r--src/rule.c80
-rw-r--r--src/statement.c23
-rw-r--r--src/tcpopt.c3
-rwxr-xr-xtests/json_echo/run-test.py14
-rwxr-xr-xtests/monitor/run-tests.sh60
-rw-r--r--tests/py/any/ct.t6
-rw-r--r--tests/py/any/meta.t1
-rw-r--r--tests/py/any/meta.t.json20
-rw-r--r--tests/py/any/meta.t.payload6
-rw-r--r--tests/py/any/queue.t23
-rw-r--r--tests/py/any/queue.t.json109
-rw-r--r--tests/py/any/queue.t.payload33
-rw-r--r--tests/py/any/tcpopt.t1
-rw-r--r--tests/py/any/tcpopt.t.json17
-rw-r--r--tests/py/any/tcpopt.t.payload5
-rw-r--r--tests/py/bridge/meta.t3
-rw-r--r--tests/py/bridge/meta.t.json54
-rw-r--r--tests/py/bridge/meta.t.payload18
-rw-r--r--tests/py/bridge/reject.t50
-rw-r--r--tests/py/bridge/reject.t.json36
-rw-r--r--tests/py/bridge/reject.t.payload36
-rw-r--r--tests/py/inet/dnat.t1
-rw-r--r--tests/py/inet/dnat.t.json20
-rw-r--r--tests/py/inet/dnat.t.payload7
-rw-r--r--tests/py/inet/meta.t4
-rw-r--r--tests/py/inet/meta.t.json54
-rw-r--r--tests/py/inet/meta.t.payload18
-rw-r--r--tests/py/inet/reject.t51
-rw-r--r--tests/py/inet/reject.t.json48
-rw-r--r--tests/py/inet/reject.t.json.output4
-rw-r--r--tests/py/inet/reject.t.payload.inet42
-rw-r--r--tests/py/inet/tcp.t15
-rw-r--r--tests/py/inet/tcp.t.json301
-rw-r--r--tests/py/inet/tcp.t.payload104
-rw-r--r--tests/py/ip/dnat.t7
-rw-r--r--tests/py/ip/dnat.t.payload.ip76
-rw-r--r--tests/py/ip/meta.t2
-rw-r--r--tests/py/ip/meta.t.json16
-rw-r--r--tests/py/ip/meta.t.payload7
-rw-r--r--tests/py/ip/reject.t19
-rw-r--r--tests/py/ip/reject.t.json24
-rw-r--r--tests/py/ip/reject.t.payload18
-rw-r--r--tests/py/ip/snat.t7
-rw-r--r--tests/py/ip/snat.t.json86
-rw-r--r--tests/py/ip/snat.t.payload38
-rw-r--r--tests/py/ip6/meta.t3
-rw-r--r--tests/py/ip6/meta.t.json54
-rw-r--r--tests/py/ip6/meta.t.payload16
-rw-r--r--tests/py/ip6/reject.t17
-rw-r--r--tests/py/ip6/reject.t.json22
-rw-r--r--tests/py/ip6/reject.t.payload.ip616
-rw-r--r--tests/py/netdev/reject.t54
-rw-r--r--tests/py/netdev/reject.t.json42
-rw-r--r--tests/py/netdev/reject.t.payload42
-rwxr-xr-xtests/py/nft-test.py40
-rwxr-xr-xtests/shell/testcases/maps/0010concat_map_019
-rwxr-xr-xtests/shell/testcases/maps/0011vmap_025
-rwxr-xr-xtests/shell/testcases/maps/0012map_017
-rwxr-xr-xtests/shell/testcases/maps/0013map_014
-rw-r--r--tests/shell/testcases/maps/dumps/0010concat_map_0.nft11
-rw-r--r--tests/shell/testcases/maps/dumps/0011vmap_0.nft19
-rw-r--r--tests/shell/testcases/maps/dumps/0012map_0.nft12
-rw-r--r--tests/shell/testcases/maps/dumps/0013map_0.nft13
-rw-r--r--tests/shell/testcases/maps/dumps/nat_addr_port.nft24
-rwxr-xr-xtests/shell/testcases/nft-f/0012different_defines_07
-rwxr-xr-xtests/shell/testcases/nft-f/0028variable_cmdline_017
-rw-r--r--tests/shell/testcases/nft-f/dumps/0012different_defines_0.nft5
-rw-r--r--tests/shell/testcases/nft-f/dumps/0022priority_variable_0.nft5
-rw-r--r--tests/shell/testcases/nft-f/dumps/0022variables_0.nft14
-rw-r--r--tests/shell/testcases/nft-f/dumps/0028variable_cmdline_0.nft8
-rwxr-xr-xtests/shell/testcases/optimizations/dependency_kill48
-rw-r--r--tests/shell/testcases/optimizations/dumps/dependency_kill.nft42
-rwxr-xr-xtests/shell/testcases/sets/0031set_timeout_size_04
-rwxr-xr-xtests/shell/testcases/sets/0047nat_02
-rwxr-xr-xtests/shell/testcases/sets/0065_icmp_postprocessing13
-rwxr-xr-xtests/shell/testcases/sets/0067nat_concat_interval_033
-rw-r--r--tests/shell/testcases/sets/dumps/0047nat_0.nft2
-rw-r--r--tests/shell/testcases/sets/dumps/0067nat_concat_interval_0.nft19
108 files changed, 2996 insertions, 783 deletions
diff --git a/Make_global.am b/Make_global.am
index 713d9583..5bb541f6 100644
--- a/Make_global.am
+++ b/Make_global.am
@@ -18,4 +18,4 @@
# set age to 0.
# </snippet>
#
-libnftables_LIBVERSION=1:0:0
+libnftables_LIBVERSION=2:0:1
diff --git a/configure.ac b/configure.ac
index dc6f4648..6069b871 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
-AC_INIT([nftables], [0.9.9], [netfilter-devel@vger.kernel.org])
-AC_DEFINE([RELEASE_NAME], ["Prudence Pimpleton"], [Release name])
+AC_INIT([nftables], [1.0.0], [netfilter-devel@vger.kernel.org])
+AC_DEFINE([RELEASE_NAME], ["Fearless Fosdick #2"], [Release name])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc
index fba4cb08..c152dc05 100644
--- a/doc/libnftables-json.adoc
+++ b/doc/libnftables-json.adoc
@@ -904,7 +904,7 @@ Reject the packet and send the given error reply.
*type*::
Type of reject, either *"tcp reset"*, *"icmpx"*, *"icmp"* or *"icmpv6"*.
*expr*::
- ICMP type to reject with.
+ ICMP code to reject with.
All properties are optional.
diff --git a/doc/libnftables.adoc b/doc/libnftables.adoc
index ce4a361b..3abb9595 100644
--- a/doc/libnftables.adoc
+++ b/doc/libnftables.adoc
@@ -93,6 +93,7 @@ enum {
NFT_CTX_OUTPUT_NUMERIC_TIME = (1 << 10),
NFT_CTX_OUTPUT_NUMERIC_ALL = (NFT_CTX_OUTPUT_NUMERIC_PROTO |
NFT_CTX_OUTPUT_NUMERIC_PRIO |
+ NFT_CTX_OUTPUT_NUMERIC_SYMBOL |
NFT_CTX_OUTPUT_NUMERIC_TIME),
NFT_CTX_OUTPUT_TERSE = (1 << 11),
};
diff --git a/doc/nft.txt b/doc/nft.txt
index 46e8dc53..c9bb901b 100644
--- a/doc/nft.txt
+++ b/doc/nft.txt
@@ -44,6 +44,10 @@ understanding of their meaning. You can get information about options by running
*--file 'filename'*::
Read input from 'filename'. If 'filename' is -, read from stdin.
+*-D*::
+*--define 'name=value'*::
+ Define a variable. You can only combine this option with '-f'.
+
*-i*::
*--interactive*::
Read input from an interactive readline CLI. You can use quit to exit, or use the EOF marker,
@@ -287,7 +291,7 @@ Effectively, this is the nft-equivalent of *iptables-save* and
TABLES
------
[verse]
-{*add* | *create*} *table* ['family'] 'table' [*{ flags* 'flags' *; }*]
+{*add* | *create*} *table* ['family'] 'table' [ {*comment* 'comment' *;*'} *{ flags* 'flags' *; }*]
{*delete* | *list* | *flush*} *table* ['family'] 'table'
*list tables* ['family']
*delete table* ['family'] *handle* 'handle'
@@ -340,7 +344,7 @@ add table inet mytable
CHAINS
------
[verse]
-{*add* | *create*} *chain* ['family'] 'table' 'chain' [*{ type* 'type' *hook* 'hook' [*device* 'device'] *priority* 'priority' *;* [*policy* 'policy' *;*] *}*]
+{*add* | *create*} *chain* ['family'] 'table' 'chain' [*{ type* 'type' *hook* 'hook' [*device* 'device'] *priority* 'priority' *;* [*policy* 'policy' *;*] [*comment* 'comment' *;*'] *}*]
{*delete* | *list* | *flush*} *chain* ['family'] 'table' 'chain'
*list chains* ['family']
*delete chain* ['family'] 'table' *handle* 'handle'
@@ -523,7 +527,7 @@ The sets allowed_hosts and allowed_ports need to be created first. The next
section describes nft set syntax in more detail.
[verse]
-*add set* ['family'] 'table' 'set' *{ type* 'type' | *typeof* 'expression' *;* [*flags* 'flags' *;*] [*timeout* 'timeout' *;*] [*gc-interval* 'gc-interval' *;*] [*elements = {* 'element'[*,* ...] *} ;*] [*size* 'size' *;*] [*policy* 'policy' *;*] [*auto-merge ;*] *}*
+*add set* ['family'] 'table' 'set' *{ type* 'type' | *typeof* 'expression' *;* [*flags* 'flags' *;*] [*timeout* 'timeout' *;*] [*gc-interval* 'gc-interval' *;*] [*elements = {* 'element'[*,* ...] *} ;*] [*size* 'size' *;*] [*comment* 'comment' *;*'] [*policy* 'policy' *;*] [*auto-merge ;*] *}*
{*delete* | *list* | *flush*} *set* ['family'] 'table' 'set'
*list sets* ['family']
*delete set* ['family'] 'table' *handle* 'handle'
@@ -576,7 +580,7 @@ automatic merge of adjacent/overlapping set elements (only for interval sets) |
MAPS
-----
[verse]
-*add map* ['family'] 'table' 'map' *{ type* 'type' | *typeof* 'expression' [*flags* 'flags' *;*] [*elements = {* 'element'[*,* ...] *} ;*] [*size* 'size' *;*] [*policy* 'policy' *;*] *}*
+*add map* ['family'] 'table' 'map' *{ type* 'type' | *typeof* 'expression' [*flags* 'flags' *;*] [*elements = {* 'element'[*,* ...] *} ;*] [*size* 'size' *;*] [*comment* 'comment' *;*'] [*policy* 'policy' *;*] *}*
{*delete* | *list* | *flush*} *map* ['family'] 'table' 'map'
*list maps* ['family']
diff --git a/doc/stateful-objects.txt b/doc/stateful-objects.txt
index c7488b28..4972969e 100644
--- a/doc/stateful-objects.txt
+++ b/doc/stateful-objects.txt
@@ -1,7 +1,9 @@
CT HELPER
~~~~~~~~~
[verse]
-*ct helper* 'helper' *{ type* 'type' *protocol* 'protocol' *;* [*l3proto* 'family' *;*] *}*
+*add* *ct helper* ['family'] 'table' 'name' *{ type* 'type' *protocol* 'protocol' *;* [*l3proto* 'family' *;*] *}*
+*delete* *ct helper* ['family'] 'table' 'name'
+*list* *ct helpers*
Ct helper is used to define connection tracking helpers that can then be used in
combination with the *ct helper set* statement. 'type' and 'protocol' are
@@ -22,6 +24,9 @@ string (e.g. ip)
|l3proto |
layer 3 protocol of the helper |
address family (e.g. ip)
+|comment |
+per ct helper comment field |
+string
|=================
.defining and assigning ftp helper
@@ -43,7 +48,9 @@ table inet myhelpers {
CT TIMEOUT
~~~~~~~~~~
[verse]
-*ct timeout* 'name' *{ protocol* 'protocol' *; policy = {* 'state'*:* 'value' [*,* ...] *} ;* [*l3proto* 'family' *;*] *}*
+*add* *ct timeout* ['family'] 'table' 'name' *{ protocol* 'protocol' *; policy = {* 'state'*:* 'value' [*,* ...] *} ;* [*l3proto* 'family' *;*] *}*
+*delete* *ct timeout* ['family'] 'table' 'name'
+*list* *ct timeouts*
Ct timeout is used to update connection tracking timeout values.Timeout policies are assigned
with the *ct timeout set* statement. 'protocol' and 'policy' are
@@ -65,6 +72,9 @@ unsigned integer
|l3proto |
layer 3 protocol of the timeout object |
address family (e.g. ip)
+|comment |
+per ct timeout comment field |
+string
|=================
.defining and assigning ct timeout policy
@@ -98,7 +108,9 @@ sport=41360 dport=22
CT EXPECTATION
~~~~~~~~~~~~~~
[verse]
-*ct expectation* '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*
Ct expectation is used to create connection expectations. Expectations are
assigned with the *ct expectation set* statement. 'protocol', 'dport',
@@ -124,6 +136,9 @@ unsigned integer
|l3proto |
layer 3 protocol of the expectation object |
address family (e.g. ip)
+|comment |
+per ct expectation comment field |
+string
|=================
.defining and assigning ct expectation policy
@@ -147,7 +162,9 @@ table ip filter {
COUNTER
~~~~~~~
[verse]
-*counter* ['packets bytes']
+*add* *counter* ['family'] 'table' 'name' [*{* [ *packets* 'packets' *bytes* 'bytes' ';' ] [ *comment* 'comment' ';' *}*]
+*delete* *counter* ['family'] 'table' 'name'
+*list* *counters*
.Counter specifications
[options="header"]
@@ -159,12 +176,31 @@ unsigned integer (64 bit)
|bytes |
initial count of bytes |
unsigned integer (64 bit)
+|comment |
+per counter comment field |
+string
|=================
+.*Using named counters*
+------------------
+nft add counter filter http
+nft add rule filter input tcp dport 80 counter name \"http\"
+------------------
+
+.*Using named counters with maps*
+------------------
+nft add counter filter http
+nft add counter filter https
+nft add rule filter input counter name tcp dport map { 80 : \"http\", 443 : \"https\" }
+------------------
+
QUOTA
~~~~~
[verse]
-*quota* [*over* | *until*] ['used']
+*add* *quota* ['family'] 'table' 'name' *{* [*over*|*until*] 'bytes' 'BYTE_UNIT' [ *used* 'bytes' 'BYTE_UNIT' ] ';' [ *comment* 'comment' ';' ] *}*
+BYTE_UNIT := bytes | kbytes | mbytes
+*delete* *quota* ['family'] 'table' 'name'
+*list* *quotas*
.Quota specifications
[options="header"]
@@ -177,4 +213,20 @@ Two arguments, unsigned integer (64 bit) and string: bytes, kbytes, mbytes.
|used |
initial value of used quota |
Two arguments, unsigned integer (64 bit) and string: bytes, kbytes, mbytes
+|comment |
+per quota comment field |
+string
|=================
+
+.*Using named quotas*
+------------------
+nft add quota filter user123 { over 20 mbytes }
+nft add rule filter input ip saddr 192.168.10.123 quota name \"user123\"
+------------------
+
+.*Using named quotas with maps*
+------------------
+nft add quota filter user123 { over 20 mbytes }
+nft add quota filter user124 { over 20 mbytes }
+nft add rule filter input quota name ip saddr map { 192.168.10.123 : \"user123\", 192.168.10.124 : \"user124\" }
+------------------
diff --git a/doc/statements.txt b/doc/statements.txt
index c2a61659..d402da70 100644
--- a/doc/statements.txt
+++ b/doc/statements.txt
@@ -93,10 +93,11 @@ packets, such as header fields, via the kernel log (where it can be read with
dmesg(1) or read in the syslog).
In the second form of invocation (if 'nflog_group' is specified), the Linux
-kernel will pass the packet to nfnetlink_log which will multicast the packet
-through a netlink socket to the specified multicast group. One or more userspace
-processes may subscribe to the group to receive the packets, see
-libnetfilter_queue documentation for details.
+kernel will pass the packet to nfnetlink_log which will send the log through a
+netlink socket to the specified group. One userspace process may subscribe to
+the group to receive the logs, see man(8) ulogd for the Netfilter userspace log
+daemon and libnetfilter_log documentation for details in case you would like to
+develop a custom program to digest your logs.
In the third form of invocation (if level audit is specified), the Linux
kernel writes a message into the audit buffer suitably formatted for reading
@@ -163,9 +164,9 @@ REJECT STATEMENT
____
*reject* [ *with* 'REJECT_WITH' ]
-'REJECT_WITH' := *icmp type* 'icmp_code' |
- *icmpv6 type* 'icmpv6_code' |
- *icmpx type* 'icmpx_code' |
+'REJECT_WITH' := *icmp* 'icmp_code' |
+ *icmpv6* 'icmpv6_code' |
+ *icmpx* 'icmpx_code' |
*tcp reset*
____
@@ -589,17 +590,19 @@ for details.
[verse]
____
-*queue* [*flags* 'QUEUE_FLAGS'] [*num* 'queue_number']
-*queue* [*flags* 'QUEUE_FLAGS'] [*num* 'queue_number_from' - 'queue_number_to']
+*queue* [*flags* 'QUEUE_FLAGS'] [*to* 'queue_number']
+*queue* [*flags* 'QUEUE_FLAGS'] [*to* 'queue_number_from' - 'queue_number_to']
*queue* [*flags* 'QUEUE_FLAGS'] [*to* 'QUEUE_EXPRESSION' ]
'QUEUE_FLAGS' := 'QUEUE_FLAG' [*,* 'QUEUE_FLAGS']
'QUEUE_FLAG' := *bypass* | *fanout*
-'QUEUE_EXPRESSION' := *numgen* | *hash* | *symhash*
+'QUEUE_EXPRESSION' := *numgen* | *hash* | *symhash* | *MAP STATEMENT*
____
QUEUE_EXPRESSION can be used to compute a queue number
-at run-time with the hash or numgen expressions.
+at run-time with the hash or numgen expressions. It also
+allows to use the map statement to assign fixed queue numbers
+based on external inputs such as the source ip address or interface names.
.queue statement values
[options="header"]
diff --git a/include/cache.h b/include/cache.h
index ad907843..70aaf735 100644
--- a/include/cache.h
+++ b/include/cache.h
@@ -32,6 +32,7 @@ enum cache_level_flags {
NFT_CACHE_CHAIN_BIT |
NFT_CACHE_RULE_BIT,
NFT_CACHE_FULL = __NFT_CACHE_MAX_BIT - 1,
+ NFT_CACHE_SETELEM_MAYBE = (1 << 28),
NFT_CACHE_REFRESH = (1 << 29),
NFT_CACHE_UPDATE = (1 << 30),
NFT_CACHE_FLUSHED = (1 << 31),
diff --git a/include/linux/netfilter/Makefile.am b/include/linux/netfilter/Makefile.am
index 04c9ab80..22f66a7e 100644
--- a/include/linux/netfilter/Makefile.am
+++ b/include/linux/netfilter/Makefile.am
@@ -6,4 +6,5 @@ noinst_HEADERS = nf_conntrack_common.h \
nf_tables_compat.h \
nf_synproxy.h \
nfnetlink_osf.h \
+ nfnetlink_hook.h \
nfnetlink.h
diff --git a/include/linux/netfilter/nfnetlink_hook.h b/include/linux/netfilter/nfnetlink_hook.h
index d8ac8278..bbcd285b 100644
--- a/include/linux/netfilter/nfnetlink_hook.h
+++ b/include/linux/netfilter/nfnetlink_hook.h
@@ -8,10 +8,10 @@ enum nfnl_hook_msg_types {
};
/**
- * enum nfnl_hook_attributes - nf_tables netfilter hook netlink attributes
+ * enum nfnl_hook_attributes - netfilter hook netlink attributes
*
* @NFNLA_HOOK_HOOKNUM: netfilter hook number (NLA_U32)
- * @NFNLAA_HOOK_PRIORITY: netfilter hook priority (NLA_U32)
+ * @NFNLA_HOOK_PRIORITY: netfilter hook priority (NLA_U32)
* @NFNLA_HOOK_DEV: netdevice name (NLA_STRING)
* @NFNLA_HOOK_FUNCTION_NAME: hook function name (NLA_STRING)
* @NFNLA_HOOK_MODULE_NAME: kernel module that registered this hook (NLA_STRING)
@@ -43,6 +43,15 @@ enum nfnl_hook_chain_info_attributes {
};
#define NFNLA_HOOK_INFO_MAX (__NFNLA_HOOK_INFO_MAX - 1)
+enum nfnl_hook_chain_desc_attributes {
+ NFNLA_CHAIN_UNSPEC,
+ NFNLA_CHAIN_TABLE,
+ NFNLA_CHAIN_FAMILY,
+ NFNLA_CHAIN_NAME,
+ __NFNLA_CHAIN_MAX,
+};
+#define NFNLA_CHAIN_MAX (__NFNLA_CHAIN_MAX - 1)
+
/**
* enum nfnl_hook_chaintype - chain type
*
@@ -51,4 +60,5 @@ enum nfnl_hook_chain_info_attributes {
enum nfnl_hook_chaintype {
NFNL_HOOK_TYPE_NFTABLES = 0x1,
};
+
#endif /* _NFNL_HOOK_H */
diff --git a/include/netlink.h b/include/netlink.h
index 0c8655ca..2467ff82 100644
--- a/include/netlink.h
+++ b/include/netlink.h
@@ -215,9 +215,6 @@ int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type,
enum nft_data_types dtype_map_to_kernel(const struct datatype *dtype);
-void expr_handler_init(void);
-void expr_handler_exit(void);
-
void netlink_linearize_init(struct netlink_linearize_ctx *lctx,
struct nftnl_rule *nlr);
void netlink_linearize_fini(struct netlink_linearize_ctx *lctx);
diff --git a/include/nftables.h b/include/nftables.h
index f239fcf0..7b633905 100644
--- a/include/nftables.h
+++ b/include/nftables.h
@@ -100,12 +100,23 @@ struct mnl_socket;
struct parser_state;
struct scope;
+struct nft_vars {
+ const char *key;
+ const char *value;
+};
+
#define MAX_INCLUDE_DEPTH 16
struct nft_ctx {
struct mnl_socket *nf_sock;
char **include_paths;
unsigned int num_include_paths;
+ struct nft_vars *vars;
+ struct {
+ const char *buf;
+ struct list_head indesc_list;
+ } vars_ctx;
+ unsigned int num_vars;
unsigned int parser_max_errors;
unsigned int debug_mask;
struct output_ctx output;
diff --git a/include/nftables/libnftables.h b/include/nftables/libnftables.h
index 765b20dd..957b5fbe 100644
--- a/include/nftables/libnftables.h
+++ b/include/nftables/libnftables.h
@@ -55,7 +55,8 @@ enum {
NFT_CTX_OUTPUT_NUMERIC_TIME = (1 << 10),
NFT_CTX_OUTPUT_NUMERIC_ALL = (NFT_CTX_OUTPUT_NUMERIC_PROTO |
NFT_CTX_OUTPUT_NUMERIC_PRIO |
- NFT_CTX_OUTPUT_NUMERIC_SYMBOL),
+ NFT_CTX_OUTPUT_NUMERIC_SYMBOL |
+ NFT_CTX_OUTPUT_NUMERIC_TIME),
NFT_CTX_OUTPUT_TERSE = (1 << 11),
};
@@ -78,6 +79,9 @@ const char *nft_ctx_get_error_buffer(struct nft_ctx *ctx);
int nft_ctx_add_include_path(struct nft_ctx *ctx, const char *path);
void nft_ctx_clear_include_paths(struct nft_ctx *ctx);
+int nft_ctx_add_var(struct nft_ctx *ctx, const char *var);
+void nft_ctx_clear_vars(struct nft_ctx *ctx);
+
int nft_run_cmd_from_buffer(struct nft_ctx *nft, const char *buf);
int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename);
diff --git a/include/rule.h b/include/rule.h
index 357326a3..be316956 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -270,6 +270,7 @@ extern struct chain *chain_binding_lookup(const struct table *table,
const char *chain_name);
extern const char *family2str(unsigned int family);
+#define __NF_ARP_INGRESS 255
extern const char *hooknum2str(unsigned int family, unsigned int hooknum);
extern const char *chain_policy2str(uint32_t policy);
extern void chain_print_plain(const struct chain *chain,
diff --git a/src/cache.c b/src/cache.c
index ff63e59e..42e6b65c 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -38,7 +38,7 @@ static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags)
NFT_CACHE_CHAIN |
NFT_CACHE_SET |
NFT_CACHE_OBJECT |
- NFT_CACHE_SETELEM;
+ NFT_CACHE_SETELEM_MAYBE;
break;
case CMD_OBJ_RULE:
flags |= NFT_CACHE_TABLE |
@@ -62,7 +62,7 @@ static unsigned int evaluate_cache_del(struct cmd *cmd, unsigned int flags)
{
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
- flags |= NFT_CACHE_SETELEM;
+ flags |= NFT_CACHE_SETELEM_MAYBE;
break;
default:
break;
@@ -415,8 +415,7 @@ 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,
- int *err)
+ const struct table *table)
{
struct nftnl_obj_list *obj_list;
@@ -424,12 +423,15 @@ static struct nftnl_obj_list *obj_cache_dump(struct netlink_ctx *ctx,
table->handle.table.name, NULL,
0, true, false);
if (!obj_list) {
- if (errno == EINTR) {
- *err = -1;
+ if (errno == EINTR)
return NULL;
- }
- *err = 0;
- return NULL;
+
+ /* old kernels do not support this, provide an empty list. */
+ obj_list = nftnl_obj_list_alloc();
+ if (!obj_list)
+ memory_allocation_error();
+
+ return obj_list;
}
return obj_list;
@@ -500,20 +502,22 @@ static int ft_cache_init(struct netlink_ctx *ctx, struct table *table,
}
static struct nftnl_flowtable_list *ft_cache_dump(struct netlink_ctx *ctx,
- const struct table *table,
- int *err)
+ const struct table *table)
{
struct nftnl_flowtable_list *ft_list;
ft_list = mnl_nft_flowtable_dump(ctx, table->handle.family,
table->handle.table.name);
if (!ft_list) {
- if (errno == EINTR) {
- *err = -1;
+ if (errno == EINTR)
return NULL;
- }
- *err = 0;
- return NULL;
+
+ /* old kernels do not support this, provide an empty list. */
+ ft_list = nftnl_flowtable_list_alloc();
+ if (!ft_list)
+ memory_allocation_error();
+
+ return ft_list;
}
return ft_list;
@@ -607,6 +611,18 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags)
goto cache_fails;
}
}
+ } else if (flags & NFT_CACHE_SETELEM_MAYBE) {
+ list_for_each_entry(set, &table->set_cache.list, cache.list) {
+ if (!set_is_non_concat_range(set))
+ continue;
+
+ ret = netlink_list_setelems(ctx, &set->handle,
+ set);
+ if (ret < 0) {
+ ret = -1;
+ goto cache_fails;
+ }
+ }
}
if (flags & NFT_CACHE_CHAIN_BIT) {
ret = chain_cache_init(ctx, table, chain_list);
@@ -616,7 +632,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags)
}
}
if (flags & NFT_CACHE_FLOWTABLE_BIT) {
- ft_list = ft_cache_dump(ctx, table, &ret);
+ ft_list = ft_cache_dump(ctx, table);
if (!ft_list) {
ret = -1;
goto cache_fails;
@@ -631,7 +647,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags)
}
}
if (flags & NFT_CACHE_OBJECT_BIT) {
- obj_list = obj_cache_dump(ctx, table, &ret);
+ obj_list = obj_cache_dump(ctx, table);
if (!obj_list) {
ret = -1;
goto cache_fails;
diff --git a/src/cmd.c b/src/cmd.c
index a69767c5..d956919b 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -28,12 +28,16 @@ static int nft_cmd_enoent_table(struct netlink_ctx *ctx, const struct cmd *cmd,
}
static int table_fuzzy_check(struct netlink_ctx *ctx, const struct cmd *cmd,
- const struct table *table,
- const struct location *loc)
+ const struct table *table)
{
+ if (table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name, cmd->handle.family))
+ return 0;
+
if (strcmp(cmd->handle.table.name, table->handle.table.name) ||
cmd->handle.family != table->handle.family) {
- netlink_io_error(ctx, loc, "%s; did you mean table ā€˜%sā€™ in family %s?",
+ netlink_io_error(ctx, &cmd->handle.table.location,
+ "%s; did you mean table ā€˜%sā€™ in family %s?",
strerror(ENOENT), table->handle.table.name,
family2str(table->handle.family));
return 1;
@@ -56,7 +60,7 @@ static int nft_cmd_enoent_chain(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!table)
return 0;
- if (table_fuzzy_check(ctx, cmd, table, loc))
+ if (table_fuzzy_check(ctx, cmd, table))
return 1;
if (!chain)
@@ -85,7 +89,7 @@ static int nft_cmd_enoent_rule(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!table)
return 0;
- if (table_fuzzy_check(ctx, cmd, table, loc))
+ if (table_fuzzy_check(ctx, cmd, table))
return 1;
if (!chain)
@@ -117,7 +121,7 @@ static int nft_cmd_enoent_set(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!table)
return 0;
- if (table_fuzzy_check(ctx, cmd, table, loc))
+ if (table_fuzzy_check(ctx, cmd, table))
return 1;
if (!set)
@@ -146,7 +150,7 @@ static int nft_cmd_enoent_obj(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!table)
return 0;
- if (table_fuzzy_check(ctx, cmd, table, loc))
+ if (table_fuzzy_check(ctx, cmd, table))
return 1;
if (!obj)
@@ -175,7 +179,7 @@ static int nft_cmd_enoent_flowtable(struct netlink_ctx *ctx,
if (!table)
return 0;
- if (table_fuzzy_check(ctx, cmd, table, loc))
+ if (table_fuzzy_check(ctx, cmd, table))
return 1;
if (!ft)
diff --git a/src/datatype.c b/src/datatype.c
index 7267d608..b849f708 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -911,6 +911,11 @@ void time_print(uint64_t ms, struct output_ctx *octx)
{
uint64_t days, hours, minutes, seconds;
+ if (nft_output_seconds(octx)) {
+ nft_print(octx, "%" PRIu64 "s", ms / 1000);
+ return;
+ }
+
days = ms / 86400000;
ms %= 86400000;
diff --git a/src/evaluate.c b/src/evaluate.c
index 4f19dc43..8ebc7561 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1240,8 +1240,7 @@ static int list_member_evaluate(struct eval_ctx *ctx, struct expr **expr)
return err;
}
-static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr,
- bool eval)
+static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
{
const struct datatype *dtype = ctx->ectx.dtype, *tmp;
uint32_t type = dtype ? dtype->type : 0, ntype = 0;
@@ -1270,7 +1269,7 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr,
tmp = concat_subtype_lookup(type, --off);
expr_set_context(&ctx->ectx, tmp, tmp->size);
- if (eval && list_member_evaluate(ctx, &i) < 0)
+ if (list_member_evaluate(ctx, &i) < 0)
return -1;
flags &= i->flags;
@@ -1352,10 +1351,12 @@ static int __expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr *elem)
"but element has %d", num_set_exprs,
num_elem_exprs);
} else if (num_set_exprs == 0) {
- if (!(set->flags & NFT_SET_EVAL))
- return expr_error(ctx->msgs, elem,
- "missing statements in %s definition",
+ if (!(set->flags & NFT_SET_EVAL)) {
+ elem_stmt = list_first_entry(&elem->stmt_list, struct stmt, list);
+ return stmt_error(ctx, elem_stmt,
+ "missing statement in %s declaration",
set_is_map(set->flags) ? "map" : "set");
+ }
return 0;
}
@@ -1430,6 +1431,23 @@ static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
if (list_member_evaluate(ctx, &i) < 0)
return -1;
+ if (i->etype == EXPR_MAPPING &&
+ i->left->etype == EXPR_SET_ELEM &&
+ i->left->key->etype == EXPR_SET) {
+ struct expr *new, *j;
+
+ list_for_each_entry(j, &i->left->key->expressions, list) {
+ new = mapping_expr_alloc(&i->location,
+ expr_get(j),
+ expr_clone(i->right));
+ list_add_tail(&new->list, &set->expressions);
+ set->size++;
+ }
+ list_del(&i->list);
+ expr_free(i);
+ continue;
+ }
+
elem = expr_set_elem(i);
if (elem->etype == EXPR_SET_ELEM &&
@@ -1580,6 +1598,9 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
return expr_error(ctx->msgs, map->mappings,
"Expression is not a map");
break;
+ case EXPR_SET_REF:
+ /* symbol has been already evaluated to set reference */
+ break;
default:
BUG("invalid mapping expression %s\n",
expr_name(map->mappings));
@@ -1603,6 +1624,26 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
return 0;
}
+static bool data_mapping_has_interval(struct expr *data)
+{
+ struct expr *i;
+
+ if (data->etype == EXPR_RANGE ||
+ data->etype == EXPR_PREFIX)
+ return true;
+
+ if (data->etype != EXPR_CONCAT)
+ return false;
+
+ list_for_each_entry(i, &data->expressions, list) {
+ if (i->etype == EXPR_RANGE ||
+ i->etype == EXPR_PREFIX)
+ return true;
+ }
+
+ return false;
+}
+
static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *mapping = *expr;
@@ -1642,8 +1683,7 @@ static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr)
"Value must be a constant");
if (set_is_anonymous(set->flags) &&
- (mapping->right->etype == EXPR_RANGE ||
- mapping->right->etype == EXPR_PREFIX))
+ data_mapping_has_interval(mapping->right))
set->data->flags |= EXPR_F_INTERVAL;
if (!(set->data->flags & EXPR_F_INTERVAL) &&
@@ -1993,12 +2033,16 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
/* fall through */
case OP_NEQ:
case OP_NEG:
- if (rel->op == OP_NEG &&
- (right->etype != EXPR_VALUE ||
- right->dtype->basetype == NULL ||
- right->dtype->basetype->type != TYPE_BITMASK))
- return expr_binary_error(ctx->msgs, left, right,
- "negation can only be used with singleton bitmask values");
+ if (rel->op == OP_NEG) {
+ if (left->etype == EXPR_BINOP)
+ return expr_binary_error(ctx->msgs, left, right,
+ "cannot combine negation with binary expression");
+ if (right->etype != EXPR_VALUE ||
+ right->dtype->basetype == NULL ||
+ right->dtype->basetype->type != TYPE_BITMASK)
+ return expr_binary_error(ctx->msgs, left, right,
+ "negation can only be used with singleton bitmask values");
+ }
switch (right->etype) {
case EXPR_RANGE:
@@ -2240,7 +2284,7 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
case EXPR_BINOP:
return expr_evaluate_binop(ctx, expr);
case EXPR_CONCAT:
- return expr_evaluate_concat(ctx, expr, true);
+ return expr_evaluate_concat(ctx, expr);
case EXPR_LIST:
return expr_evaluate_list(ctx, expr);
case EXPR_SET:
@@ -2976,9 +3020,10 @@ static int nat_evaluate_family(struct eval_ctx *ctx, struct stmt *stmt)
stmt->nat.family = ctx->pctx.family;
return 0;
case NFPROTO_INET:
- if (!stmt->nat.addr)
+ if (!stmt->nat.addr) {
+ stmt->nat.family = NFPROTO_INET;
return 0;
-
+ }
if (stmt->nat.family != NFPROTO_UNSPEC)
return 0;
@@ -3138,6 +3183,9 @@ static int stmt_evaluate_nat_map(struct eval_ctx *ctx, struct stmt *stmt)
return 0;
data = stmt->nat.addr->mappings->set->data;
+ if (data->flags & EXPR_F_INTERVAL)
+ stmt->nat.type_flags |= STMT_NAT_F_INTERVAL;
+
datatype_set(data, dtype);
if (expr_ops(data)->type != EXPR_CONCAT)
@@ -3173,6 +3221,40 @@ static int stmt_evaluate_nat_map(struct eval_ctx *ctx, struct stmt *stmt)
return err;
}
+static bool nat_concat_map(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ struct expr *i;
+
+ if (stmt->nat.addr->etype != EXPR_MAP)
+ return false;
+
+ switch (stmt->nat.addr->mappings->etype) {
+ case EXPR_SET:
+ list_for_each_entry(i, &stmt->nat.addr->mappings->expressions, list) {
+ if (i->etype == EXPR_MAPPING &&
+ i->right->etype == EXPR_CONCAT) {
+ stmt->nat.type_flags |= STMT_NAT_F_CONCAT;
+ return true;
+ }
+ }
+ break;
+ case EXPR_SYMBOL:
+ /* expr_evaluate_map() see EXPR_SET_REF after this evaluation. */
+ if (expr_evaluate(ctx, &stmt->nat.addr->mappings))
+ return false;
+
+ if (stmt->nat.addr->mappings->set->data->etype == EXPR_CONCAT) {
+ stmt->nat.type_flags |= STMT_NAT_F_CONCAT;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
{
int err;
@@ -3186,7 +3268,9 @@ static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
if (err < 0)
return err;
- if (stmt->nat.type_flags & STMT_NAT_F_CONCAT) {
+ if (nat_concat_map(ctx, stmt) ||
+ stmt->nat.type_flags & STMT_NAT_F_CONCAT) {
+
err = stmt_evaluate_nat_map(ctx, stmt);
if (err < 0)
return err;
@@ -3201,26 +3285,6 @@ static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
return err;
}
- if (stmt->nat.type_flags & STMT_NAT_F_INTERVAL) {
- switch (stmt->nat.addr->etype) {
- case EXPR_MAP:
- if (!(stmt->nat.addr->mappings->set->data->flags & EXPR_F_INTERVAL))
- return expr_error(ctx->msgs, stmt->nat.addr,
- "map is not defined as interval");
- break;
- case EXPR_RANGE:
- case EXPR_PREFIX:
- break;
- default:
- return expr_error(ctx->msgs, stmt->nat.addr,
- "neither prefix, range nor map expression");
- }
-
- stmt->flags |= STMT_F_TERMINAL;
-
- return 0;
- }
-
if (stmt->nat.proto != NULL) {
err = nat_evaluate_transport(ctx, stmt, &stmt->nat.proto);
if (err < 0)
@@ -3797,6 +3861,45 @@ static int set_key_data_error(struct eval_ctx *ctx, const struct set *set,
dtype->name, name, hint);
}
+static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
+{
+ unsigned int flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON;
+ struct expr *i, *next;
+ uint32_t ntype = 0;
+
+ list_for_each_entry_safe(i, next, &(*expr)->expressions, list) {
+ unsigned dsize_bytes;
+
+ if (i->etype == EXPR_CT &&
+ (i->ct.key == NFT_CT_SRC ||
+ i->ct.key == NFT_CT_DST))
+ return expr_error(ctx->msgs, i,
+ "specify either ip or ip6 for address matching");
+
+ if (i->dtype->size == 0)
+ return expr_binary_error(ctx->msgs, i, *expr,
+ "can not use variable sized "
+ "data types (%s) in concat "
+ "expressions",
+ i->dtype->name);
+
+ flags &= i->flags;
+
+ ntype = concat_subtype_add(ntype, i->dtype->type);
+
+ dsize_bytes = div_round_up(i->dtype->size, BITS_PER_BYTE);
+ (*expr)->field_len[(*expr)->field_count++] = dsize_bytes;
+ }
+
+ (*expr)->flags |= flags;
+ datatype_set(*expr, concat_type_alloc(ntype));
+ (*expr)->len = (*expr)->dtype->size;
+
+ expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->len);
+
+ return 0;
+}
+
static int set_evaluate(struct eval_ctx *ctx, struct set *set)
{
unsigned int num_stmts = 0;
@@ -3826,7 +3929,7 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
if (set->key->len == 0) {
if (set->key->etype == EXPR_CONCAT &&
- expr_evaluate_concat(ctx, &set->key, false) < 0)
+ set_expr_evaluate_concat(ctx, &set->key) < 0)
return -1;
if (set->key->len == 0)
@@ -3846,13 +3949,13 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
return set_error(ctx, set, "map definition does not "
"specify mapping data type");
- if (set->data->flags & EXPR_F_INTERVAL)
- set->data->len *= 2;
-
if (set->data->etype == EXPR_CONCAT &&
- expr_evaluate_concat(ctx, &set->data, false) < 0)
+ set_expr_evaluate_concat(ctx, &set->data) < 0)
return -1;
+ if (set->data->flags & EXPR_F_INTERVAL)
+ set->data->len *= 2;
+
if (set->data->len == 0 && set->data->dtype->type != TYPE_VERDICT)
return set_key_data_error(ctx, set,
set->data->dtype, type);
diff --git a/src/expression.c b/src/expression.c
index c6be0001..4c0874fe 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -1358,7 +1358,12 @@ struct expr *set_elem_catchall_expr_alloc(const struct location *loc)
static void flagcmp_expr_print(const struct expr *expr, struct output_ctx *octx)
{
expr_print(expr->flagcmp.expr, octx);
- nft_print(octx, " ");
+
+ if (expr->op == OP_NEQ)
+ nft_print(octx, " != ");
+ else
+ nft_print(octx, " ");
+
expr_print(expr->flagcmp.value, octx);
nft_print(octx, " / ");
expr_print(expr->flagcmp.mask, octx);
diff --git a/src/json.c b/src/json.c
index f111ad67..63b325af 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1329,12 +1329,8 @@ static json_t *nat_type_flags_json(uint32_t type_flags)
{
json_t *array = json_array();
- if (type_flags & STMT_NAT_F_INTERVAL)
- json_array_append_new(array, json_string("interval"));
if (type_flags & STMT_NAT_F_PREFIX)
json_array_append_new(array, json_string("prefix"));
- if (type_flags & STMT_NAT_F_CONCAT)
- json_array_append_new(array, json_string("concat"));
return array;
}
diff --git a/src/libnftables.c b/src/libnftables.c
index e080eb03..fc52fbc3 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -89,6 +89,12 @@ static int nft_netlink(struct nft_ctx *nft,
last_seqnum = UINT32_MAX;
}
}
+ /* nfnetlink uses the first netlink message header in the batch whose
+ * sequence number is zero to report for EOPNOTSUPP and EPERM errors in
+ * some scenarios. Now it is safe to release pending errors here.
+ */
+ list_for_each_entry_safe(err, tmp, &err_list, head)
+ mnl_err_list_free(err);
out:
mnl_batch_reset(ctx.batch);
return ret;
@@ -100,19 +106,56 @@ static void nft_init(struct nft_ctx *ctx)
realm_table_rt_init(ctx);
devgroup_table_init(ctx);
ct_label_table_init(ctx);
- expr_handler_init();
}
static void nft_exit(struct nft_ctx *ctx)
{
cache_free(&ctx->cache.table_cache);
- expr_handler_exit();
ct_label_table_exit(ctx);
realm_table_rt_exit(ctx);
devgroup_table_exit(ctx);
mark_table_exit(ctx);
}
+EXPORT_SYMBOL(nft_ctx_add_var);
+int nft_ctx_add_var(struct nft_ctx *ctx, const char *var)
+{
+ char *separator = strchr(var, '=');
+ int pcount = ctx->num_vars;
+ struct nft_vars *tmp;
+ const char *value;
+
+ if (!separator)
+ return -1;
+
+ tmp = realloc(ctx->vars, (pcount + 1) * sizeof(struct nft_vars));
+ if (!tmp)
+ return -1;
+
+ *separator = '\0';
+ value = separator + 1;
+
+ ctx->vars = tmp;
+ ctx->vars[pcount].key = xstrdup(var);
+ ctx->vars[pcount].value = xstrdup(value);
+ ctx->num_vars++;
+
+ return 0;
+}
+
+EXPORT_SYMBOL(nft_ctx_clear_vars);
+void nft_ctx_clear_vars(struct nft_ctx *ctx)
+{
+ unsigned int i;
+
+ for (i = 0; i < ctx->num_vars; i++) {
+ xfree(ctx->vars[i].key);
+ xfree(ctx->vars[i].value);
+ }
+ ctx->num_vars = 0;
+ xfree(ctx->vars);
+}
+
EXPORT_SYMBOL(nft_ctx_add_include_path);
int nft_ctx_add_include_path(struct nft_ctx *ctx, const char *path)
{
@@ -172,6 +215,7 @@ struct nft_ctx *nft_ctx_new(uint32_t flags)
ctx->flags = flags;
ctx->output.output_fp = stdout;
ctx->output.error_fp = stderr;
+ init_list_head(&ctx->vars_ctx.indesc_list);
if (flags == NFT_CTX_DEFAULT)
nft_ctx_netlink_init(ctx);
@@ -305,6 +349,7 @@ void nft_ctx_free(struct nft_ctx *ctx)
exit_cookie(&ctx->output.error_cookie);
iface_cache_release();
nft_cache_release(&ctx->cache);
+ nft_ctx_clear_vars(ctx);
nft_ctx_clear_include_paths(ctx);
scope_free(ctx->top_scope);
xfree(ctx->state);
@@ -501,6 +546,47 @@ err:
return rc;
}
+static int load_cmdline_vars(struct nft_ctx *ctx, struct list_head *msgs)
+{
+ unsigned int bufsize, ret, i, offset = 0;
+ LIST_HEAD(cmds);
+ char *buf;
+ int rc;
+
+ if (ctx->num_vars == 0)
+ return 0;
+
+ bufsize = 1024;
+ buf = xzalloc(bufsize + 1);
+ for (i = 0; i < ctx->num_vars; i++) {
+retry:
+ ret = snprintf(buf + offset, bufsize - offset,
+ "define %s=%s; ",
+ ctx->vars[i].key, ctx->vars[i].value);
+ if (ret >= bufsize - offset) {
+ bufsize *= 2;
+ buf = xrealloc(buf, bufsize + 1);
+ goto retry;
+ }
+ offset += ret;
+ }
+ snprintf(buf + offset, bufsize - offset, "\n");
+
+ rc = nft_parse_bison_buffer(ctx, buf, msgs, &cmds);
+
+ assert(list_empty(&cmds));
+ /* Stash the buffer that contains the variable definitions and zap the
+ * list of input descriptors before releasing the scanner state,
+ * otherwise error reporting path walks over released objects.
+ */
+ ctx->vars_ctx.buf = buf;
+ list_splice_init(&ctx->state->indesc_list, &ctx->vars_ctx.indesc_list);
+ scanner_destroy(ctx);
+ ctx->scanner = NULL;
+
+ return rc;
+}
+
EXPORT_SYMBOL(nft_run_cmd_from_filename);
int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
{
@@ -509,6 +595,10 @@ int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
LIST_HEAD(msgs);
LIST_HEAD(cmds);
+ rc = load_cmdline_vars(nft, &msgs);
+ if (rc < 0)
+ goto err;
+
if (!strcmp(filename, "-"))
filename = "/dev/stdin";
@@ -542,6 +632,17 @@ err:
scanner_destroy(nft);
nft->scanner = NULL;
}
+ if (!list_empty(&nft->vars_ctx.indesc_list)) {
+ struct input_descriptor *indesc, *next;
+
+ list_for_each_entry_safe(indesc, next, &nft->vars_ctx.indesc_list, list) {
+ if (indesc->name)
+ xfree(indesc->name);
+
+ xfree(indesc);
+ }
+ }
+ xfree(nft->vars_ctx.buf);
if (!rc &&
nft_output_json(&nft->output) &&
diff --git a/src/libnftables.map b/src/libnftables.map
index 955af380..d3a795ce 100644
--- a/src/libnftables.map
+++ b/src/libnftables.map
@@ -1,7 +1,7 @@
LIBNFTABLES_1 {
global:
nft_ctx_add_include_path;
- nft_ctx_clear_include_pat;
+ nft_ctx_clear_include_paths;
nft_ctx_new;
nft_ctx_buffer_output;
nft_ctx_unbuffer_output;
@@ -23,3 +23,8 @@ global:
local: *;
};
+
+LIBNFTABLES_2 {
+ nft_ctx_add_var;
+ nft_ctx_clear_vars;
+} LIBNFTABLES_1;
diff --git a/src/main.c b/src/main.c
index 8c470644..21096fc7 100644
--- a/src/main.c
+++ b/src/main.c
@@ -32,6 +32,7 @@ enum opt_indices {
/* Ruleset input handling */
IDX_FILE,
#define IDX_RULESET_INPUT_START IDX_FILE
+ IDX_DEFINE,
IDX_INTERACTIVE,
IDX_INCLUDEPATH,
IDX_CHECK,
@@ -63,6 +64,7 @@ enum opt_vals {
OPT_VERSION_LONG = 'V',
OPT_CHECK = 'c',
OPT_FILE = 'f',
+ OPT_DEFINE = 'D',
OPT_INTERACTIVE = 'i',
OPT_INCLUDEPATH = 'I',
OPT_JSON = 'j',
@@ -100,6 +102,8 @@ static const struct nft_opt nft_options[] = {
"Show extended version information"),
[IDX_FILE] = NFT_OPT("file", OPT_FILE, "<filename>",
"Read input from <filename>"),
+ [IDX_DEFINE] = NFT_OPT("define", OPT_DEFINE, "<name=value>",
+ "Define variable, e.g. --define foo=1.2.3.4"),
[IDX_INTERACTIVE] = NFT_OPT("interactive", OPT_INTERACTIVE, NULL,
"Read input from interactive CLI"),
[IDX_INCLUDEPATH] = NFT_OPT("includepath", OPT_INCLUDEPATH, "<directory>",
@@ -332,8 +336,10 @@ static bool nft_options_check(int argc, char * const argv[])
} else if (argv[i][1] == 'd' ||
argv[i][1] == 'I' ||
argv[i][1] == 'f' ||
+ argv[i][1] == 'D' ||
!strcmp(argv[i], "--debug") ||
!strcmp(argv[i], "--includepath") ||
+ !strcmp(argv[i], "--define") ||
!strcmp(argv[i], "--file")) {
skip = true;
continue;
@@ -349,10 +355,10 @@ static bool nft_options_check(int argc, char * const argv[])
int main(int argc, char * const *argv)
{
const struct option *options = get_options();
+ bool interactive = false, define = false;
const char *optstring = get_optstring();
char *buf = NULL, *filename = NULL;
unsigned int output_flags = 0;
- bool interactive = false;
unsigned int debug_mask;
unsigned int len;
int i, val, rc;
@@ -378,6 +384,15 @@ int main(int argc, char * const *argv)
case OPT_VERSION_LONG:
show_version();
exit(EXIT_SUCCESS);
+ case OPT_DEFINE:
+ if (nft_ctx_add_var(nft, optarg)) {
+ fprintf(stderr,
+ "Failed to define variable '%s'\n",
+ optarg);
+ exit(EXIT_FAILURE);
+ }
+ define = true;
+ break;
case OPT_CHECK:
nft_ctx_set_dry_run(nft, true);
break;
@@ -470,6 +485,11 @@ int main(int argc, char * const *argv)
}
}
+ if (!filename && define) {
+ fprintf(stderr, "Error: -D/--define can only be used with -f/--filename\n");
+ exit(EXIT_FAILURE);
+ }
+
nft_ctx_output_set_flags(nft, output_flags);
if (optind != argc) {
diff --git a/src/meta.c b/src/meta.c
index fdbeba26..bdd10269 100644
--- a/src/meta.c
+++ b/src/meta.c
@@ -388,27 +388,17 @@ static void date_type_print(const struct expr *expr, struct output_ctx *octx)
/* Convert from nanoseconds to seconds */
tstamp /= 1000000000L;
- if (!nft_output_seconds(octx)) {
- /* Obtain current tm, to add tm_gmtoff to the timestamp */
- cur_tm = localtime((time_t *) &tstamp);
+ /* Obtain current tm, to add tm_gmtoff to the timestamp */
+ cur_tm = localtime((time_t *) &tstamp);
- if (cur_tm)
- tstamp += cur_tm->tm_gmtoff;
-
- if ((tm = gmtime((time_t *) &tstamp)) != NULL &&
- strftime(timestr, sizeof(timestr) - 1, "%F %T", tm))
- nft_print(octx, "\"%s\"", timestr);
- else
- nft_print(octx, "Error converting timestamp to printed time");
-
- return;
- }
+ if (cur_tm)
+ tstamp += cur_tm->tm_gmtoff;
- /*
- * Do our own printing. The default print function will print in
- * nanoseconds, which is ugly.
- */
- nft_print(octx, "%" PRIu64, tstamp);
+ if ((tm = gmtime((time_t *) &tstamp)) != NULL &&
+ strftime(timestr, sizeof(timestr) - 1, "%F %T", tm))
+ nft_print(octx, "\"%s\"", timestr);
+ else
+ nft_print(octx, "Error converting timestamp to printed time");
}
static time_t parse_iso_date(const char *sym)
@@ -498,11 +488,6 @@ static void hour_type_print(const struct expr *expr, struct output_ctx *octx)
struct tm *cur_tm;
time_t ts;
- if (nft_output_seconds(octx)) {
- expr_basetype(expr)->print(expr, octx);
- return;
- }
-
/* Obtain current tm, so that we can add tm_gmtoff */
ts = time(NULL);
cur_tm = localtime(&ts);
diff --git a/src/mnl.c b/src/mnl.c
index f28d6605..2d5afdfe 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -44,6 +44,9 @@ struct basehook {
const char *hookfn;
const char *table;
const char *chain;
+ int family;
+ int chain_family;
+ uint32_t num;
int prio;
};
@@ -1948,6 +1951,20 @@ static void basehook_free(struct basehook *b)
static void basehook_list_add_tail(struct basehook *b, struct list_head *head)
{
+ struct basehook *hook;
+
+ list_for_each_entry(hook, head, list) {
+ if (hook->family != b->family)
+ continue;
+ if (hook->num != b->num)
+ continue;
+ if (hook->prio < b->prio)
+ continue;
+
+ list_add(&b->list, &hook->list);
+ return;
+ }
+
list_add_tail(&b->list, head);
}
@@ -2016,15 +2033,19 @@ static int dump_nf_attr_chain_cb(const struct nlattr *attr, void *data)
int type = mnl_attr_get_type(attr);
const struct nlattr **tb = data;
- if (mnl_attr_type_valid(attr, NFTA_CHAIN_MAX) < 0)
+ if (mnl_attr_type_valid(attr, NFNLA_CHAIN_MAX) < 0)
return MNL_CB_OK;
switch(type) {
- case NFTA_CHAIN_TABLE:
- case NFTA_CHAIN_NAME:
+ case NFNLA_CHAIN_TABLE:
+ case NFNLA_CHAIN_NAME:
if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0)
return MNL_CB_ERROR;
break;
+ case NFNLA_CHAIN_FAMILY:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ return MNL_CB_ERROR;
+ break;
default:
return MNL_CB_OK;
}
@@ -2033,11 +2054,16 @@ static int dump_nf_attr_chain_cb(const struct nlattr *attr, void *data)
return MNL_CB_OK;
}
-static int dump_nf_hooks(const struct nlmsghdr *nlh, void *data)
+struct dump_nf_hook_data {
+ struct list_head *hook_list;
+ int family;
+};
+
+static int dump_nf_hooks(const struct nlmsghdr *nlh, void *_data)
{
const struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
struct nlattr *tb[NFNLA_HOOK_MAX + 1] = {};
- struct list_head *head = data;
+ struct dump_nf_hook_data *data = _data;
struct basehook *hook;
/* NB: Don't check the nft generation ID, this is not
@@ -2067,22 +2093,47 @@ static int dump_nf_hooks(const struct nlmsghdr *nlh, void *data)
type = ntohl(mnl_attr_get_u32(nested[NFNLA_HOOK_INFO_TYPE]));
if (type == NFNL_HOOK_TYPE_NFTABLES) {
- struct nlattr *info[NFTA_CHAIN_MAX + 1] = {};
+ struct nlattr *info[NFNLA_CHAIN_MAX + 1] = {};
const char *tablename, *chainname;
if (mnl_attr_parse_nested(nested[NFNLA_HOOK_INFO_DESC], dump_nf_attr_chain_cb, info) < 0)
return -1;
- tablename = mnl_attr_get_str(info[NFTA_CHAIN_TABLE]);
- chainname = mnl_attr_get_str(info[NFTA_CHAIN_NAME]);
+ tablename = mnl_attr_get_str(info[NFNLA_CHAIN_TABLE]);
+ chainname = mnl_attr_get_str(info[NFNLA_CHAIN_NAME]);
if (tablename && chainname) {
hook->table = xstrdup(tablename);
hook->chain = xstrdup(chainname);
}
+ hook->chain_family = mnl_attr_get_u8(info[NFNLA_CHAIN_FAMILY]);
+ }
+ }
+ if (tb[NFNLA_HOOK_HOOKNUM])
+ hook->num = ntohl(mnl_attr_get_u32(tb[NFNLA_HOOK_HOOKNUM]));
+
+ 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, head);
+ basehook_list_add_tail(hook, data->hook_list);
+
return MNL_CB_OK;
}
@@ -2102,14 +2153,17 @@ static struct nlmsghdr *nf_hook_dump_request(char *buf, uint8_t family, uint32_t
return nlh;
}
-static int __mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, uint8_t family, uint8_t hooknum, const char *devname)
+static int __mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, uint8_t query_family,
+ uint8_t family, uint8_t hooknum,
+ const char *devname,
+ struct list_head *hook_list)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
- struct basehook *hook, *tmp;
+ struct dump_nf_hook_data data = {
+ .hook_list = hook_list,
+ .family = query_family,
+ };
struct nlmsghdr *nlh;
- LIST_HEAD(hook_list);
- FILE *fp;
- int ret;
nlh = nf_hook_dump_request(buf, family, ctx->seqnum);
if (devname)
@@ -2117,130 +2171,219 @@ static int __mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, uint8_t family, uint
mnl_attr_put_u32(nlh, NFNLA_HOOK_HOOKNUM, htonl(hooknum));
- ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, dump_nf_hooks, &hook_list);
- if (ret)
- return ret;
+ return nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, dump_nf_hooks, &data);
+}
- if (list_empty(&hook_list))
- return 0;
+static void print_hooks(struct netlink_ctx *ctx, int family, struct list_head *hook_list)
+{
+ struct basehook *hook, *tmp, *prev = NULL;
+ bool same, family_in_use = false;
+ int prio;
+ FILE *fp;
fp = ctx->nft->output.output_fp;
- fprintf(fp, "family %s hook %s", family2str(family), hooknum2str(family, hooknum));
- if (devname)
- fprintf(fp, " device %s", devname);
- fprintf(fp, " {\n");
+ list_for_each_entry_safe(hook, tmp, hook_list, list) {
+ if (hook->family == family) {
+ family_in_use = true;
+ break;
+ }
+ }
+
+ if (!family_in_use)
+ return;
+
+ fprintf(fp, "family %s {\n", family2str(family));
+
+ list_for_each_entry_safe(hook, tmp, hook_list, list) {
+ if (hook->family != family)
+ continue;
+
+ if (prev) {
+ if (prev->num == hook->num) {
+ fprintf(fp, "\n");
+ same = true;
+ } else {
+ same = false;
+ fprintf(fp, "\n\t}\n");
+ }
+ } else {
+ same = false;
+ }
+ prev = hook;
- list_for_each_entry_safe(hook, tmp, &hook_list, list) {
- int prio = hook->prio;
+ if (!same) {
+ fprintf(fp, "\thook %s {\n",
+ hooknum2str(family, hook->num));
+ }
+ prio = hook->prio;
if (prio < 0)
- fprintf(fp, "\t%011d", prio); /* outputs a '-' sign */
+ fprintf(fp, "\t\t%011d", prio); /* outputs a '-' sign */
+ else if (prio == 0)
+ fprintf(fp, "\t\t %010u", prio);
else
- fprintf(fp, "\t+%010u", prio);
+ fprintf(fp, "\t\t+%010u", prio);
- if (hook->hookfn) {
+ if (hook->table && hook->chain)
+ fprintf(fp, " chain %s %s %s", family2str(hook->chain_family), hook->table, hook->chain);
+ else if (hook->hookfn) {
fprintf(fp, " %s", hook->hookfn);
- if (hook->module_name)
- fprintf(fp, " [%s]", hook->module_name);
}
+ if (hook->module_name)
+ fprintf(fp, " [%s]", hook->module_name);
+ }
- if (hook->table && hook->chain)
- fprintf(fp, "\t# nft table %s %s chain %s", family2str(family), hook->table, hook->chain);
+ fprintf(fp, "\n\t}\n");
+ fprintf(fp, "}\n");
+}
- fprintf(fp, "\n");
- basehook_free(hook);
+#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)
+{
+ 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;
}
- fprintf(fp, "}\n");
- return ret;
+ 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)
+{
+ 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;
+
+ 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;
+
+ return err;
+}
+
+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)
+{
+ int err;
+
+ err = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_INGRESS, devname, hook_list);
+ if (err < 0)
+ *ret = err;
+
+ 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;
+}
+
+static void release_hook_list(struct list_head *hook_list)
+{
+ struct basehook *hook, *next;
+
+ list_for_each_entry_safe(hook, next, hook_list, list)
+ basehook_free(hook);
}
int mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, int family, int hook, const char *devname)
{
+ LIST_HEAD(hook_list);
unsigned int i;
- int ret, err;
+ int ret;
errno = 0;
ret = 0;
switch (family) {
case NFPROTO_UNSPEC:
- if (devname)
- return mnl_nft_dump_nf_hooks(ctx, NFPROTO_NETDEV, NF_INET_INGRESS, devname);
-
- err = mnl_nft_dump_nf_hooks(ctx, NFPROTO_INET, hook, NULL);
- if (err < 0 && errno != EPROTONOSUPPORT)
- ret = err;
- err = mnl_nft_dump_nf_hooks(ctx, NFPROTO_ARP, hook, NULL);
- if (err < 0 && errno != EPROTONOSUPPORT)
- ret = err;
- err = mnl_nft_dump_nf_hooks(ctx, NFPROTO_BRIDGE, hook, NULL);
- if (err < 0 && errno != EPROTONOSUPPORT)
- ret = err;
- err = mnl_nft_dump_nf_hooks(ctx, NFPROTO_DECNET, hook, NULL);
- if (err < 0 && errno != EPROTONOSUPPORT)
- ret = err;
+ 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;
case NFPROTO_INET:
- if (devname) {
- err = __mnl_nft_dump_nf_hooks(ctx, family, NF_INET_INGRESS, devname);
- if (err < 0)
- ret = err;
- }
-
- err = mnl_nft_dump_nf_hooks(ctx, NFPROTO_IPV4, hook, NULL);
- if (err < 0)
- ret = err;
- err = mnl_nft_dump_nf_hooks(ctx, NFPROTO_IPV6, hook, NULL);
- if (err < 0)
- ret = err;
-
+ mnl_nft_dump_nf(ctx, NFPROTO_IPV4, hook, devname, &hook_list, &ret);
+ mnl_nft_dump_nf(ctx, NFPROTO_IPV6, hook, devname, &hook_list, &ret);
break;
case NFPROTO_IPV4:
case NFPROTO_IPV6:
case NFPROTO_BRIDGE:
- if (hook >= 0)
- return __mnl_nft_dump_nf_hooks(ctx, family, hook, devname);
-
- for (i = 0; i <= NF_INET_POST_ROUTING; i++) {
- err = __mnl_nft_dump_nf_hooks(ctx, family, i, NULL);
- if (err < 0)
- err = ret;
- }
+ mnl_nft_dump_nf(ctx, family, hook, devname, &hook_list, &ret);
break;
case NFPROTO_ARP:
- if (hook >= 0)
- return __mnl_nft_dump_nf_hooks(ctx, family, hook, devname);
-
- err = __mnl_nft_dump_nf_hooks(ctx, family, NF_ARP_IN, devname);
- if (err < 0)
- ret = err;
- err = __mnl_nft_dump_nf_hooks(ctx, family, NF_ARP_OUT, devname);
- if (err < 0)
- ret = err;
+ mnl_nft_dump_nf_arp(ctx, family, hook, devname, &hook_list, &ret);
break;
case NFPROTO_NETDEV:
- if (hook >= 0)
- return __mnl_nft_dump_nf_hooks(ctx, family, hook, devname);
-
- err = __mnl_nft_dump_nf_hooks(ctx, family, NF_INET_INGRESS, devname);
- if (err < 0)
- ret = err;
+ mnl_nft_dump_nf_netdev(ctx, family, hook, devname, &hook_list, &ret);
break;
case NFPROTO_DECNET:
- if (hook >= 0)
- return __mnl_nft_dump_nf_hooks(ctx, family, hook, devname);
-#define NF_DN_NUMHOOKS 7
- for (i = 0; i < NF_DN_NUMHOOKS; i++) {
- err = __mnl_nft_dump_nf_hooks(ctx, family, i, devname);
- if (err < 0) {
- ret = err;
- break;
- }
- }
+ mnl_nft_dump_nf_decnet(ctx, family, hook, devname, &hook_list, &ret);
+ break;
+ }
+
+ 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;
}
+ release_hook_list(&hook_list);
+ ret = 0;
+
return ret;
}
diff --git a/src/netlink.c b/src/netlink.c
index c5fd3804..28a5514a 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -97,6 +97,9 @@ struct nftnl_expr *alloc_nft_expr(const char *name)
return nle;
}
+void __netlink_gen_data(const struct expr *expr,
+ struct nft_data_linearize *data, bool expand);
+
static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
const struct expr *expr)
{
@@ -130,11 +133,11 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
case EXPR_SET_ELEM_CATCHALL:
break;
default:
- netlink_gen_data(key, &nld);
+ __netlink_gen_data(key, &nld, false);
nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
if (set->set_flags & NFT_SET_INTERVAL && key->field_count > 1) {
key->flags |= EXPR_F_INTERVAL_END;
- netlink_gen_data(key, &nld);
+ __netlink_gen_data(key, &nld, false);
key->flags &= ~EXPR_F_INTERVAL_END;
nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY_END,
@@ -185,7 +188,7 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
nftnl_udata_buf_free(udbuf);
}
if (set_is_datamap(set->set_flags) && data != NULL) {
- netlink_gen_data(data, &nld);
+ __netlink_gen_data(data, &nld, !(data->flags & EXPR_F_SINGLETON));
switch (data->etype) {
case EXPR_VERDICT:
nftnl_set_elem_set_u32(nlse, NFTNL_SET_ELEM_VERDICT,
@@ -270,8 +273,8 @@ static int netlink_gen_concat_data_expr(int end, const struct expr *i,
return netlink_export_pad(data, i->value, i);
}
-static void netlink_gen_concat_data(const struct expr *expr,
- struct nft_data_linearize *nld)
+static void __netlink_gen_concat(const struct expr *expr,
+ struct nft_data_linearize *nld)
{
unsigned int len = expr->len / BITS_PER_BYTE, offset = 0;
int end = expr->flags & EXPR_F_INTERVAL_END;
@@ -287,6 +290,35 @@ static void netlink_gen_concat_data(const struct expr *expr,
nld->len = len;
}
+static void __netlink_gen_concat_expand(const struct expr *expr,
+ struct nft_data_linearize *nld)
+{
+ unsigned int len = div_round_up(expr->len, BITS_PER_BYTE) * 2, offset = 0;
+ unsigned char data[len];
+ const struct expr *i;
+
+ memset(data, 0, len);
+
+ list_for_each_entry(i, &expr->expressions, list)
+ offset += netlink_gen_concat_data_expr(false, i, data + offset);
+
+ list_for_each_entry(i, &expr->expressions, list)
+ offset += netlink_gen_concat_data_expr(true, i, data + offset);
+
+ memcpy(nld->value, data, len);
+ nld->len = len;
+}
+
+static void netlink_gen_concat_data(const struct expr *expr,
+ struct nft_data_linearize *nld,
+ bool expand)
+{
+ if (expand)
+ __netlink_gen_concat_expand(expr, nld);
+ else
+ __netlink_gen_concat(expr, nld);
+}
+
static void netlink_gen_constant_data(const struct expr *expr,
struct nft_data_linearize *data)
{
@@ -366,13 +398,14 @@ static void netlink_gen_prefix(const struct expr *expr,
nld->len = len;
}
-void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data)
+void __netlink_gen_data(const struct expr *expr,
+ struct nft_data_linearize *data, bool expand)
{
switch (expr->etype) {
case EXPR_VALUE:
return netlink_gen_constant_data(expr, data);
case EXPR_CONCAT:
- return netlink_gen_concat_data(expr, data);
+ return netlink_gen_concat_data(expr, data, expand);
case EXPR_VERDICT:
return netlink_gen_verdict(expr, data);
case EXPR_RANGE:
@@ -384,6 +417,11 @@ void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data)
}
}
+void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data)
+{
+ __netlink_gen_data(expr, data, false);
+}
+
struct expr *netlink_alloc_value(const struct location *loc,
const struct nft_data_delinearize *nld)
{
@@ -1010,53 +1048,80 @@ void alloc_setelem_cache(const struct expr *set, struct nftnl_set *nls)
}
}
-static bool mpz_bitmask_is_prefix(mpz_t bitmask, uint32_t len)
+static bool range_expr_is_prefix(const struct expr *range, uint32_t *prefix_len)
{
+ const struct expr *right = range->right;
+ const struct expr *left = range->left;
+ uint32_t len = left->len;
unsigned long n1, n2;
+ uint32_t plen;
+ mpz_t bitmask;
- n1 = mpz_scan0(bitmask, 0);
- if (n1 == ULONG_MAX)
- return false;
+ mpz_init2(bitmask, left->len);
+ mpz_xor(bitmask, left->value, right->value);
- n2 = mpz_scan1(bitmask, n1 + 1);
- if (n2 < len)
- return false;
+ n1 = mpz_scan0(bitmask, 0);
+ if (n1 == ULONG_MAX)
+ goto not_a_prefix;
- return true;
-}
+ n2 = mpz_scan1(bitmask, n1 + 1);
+ if (n2 < len)
+ goto not_a_prefix;
-static uint32_t mpz_bitmask_to_prefix(mpz_t bitmask, uint32_t len)
-{
- return len - mpz_scan0(bitmask, 0);
+ plen = len - n1;
+
+ if (mpz_scan1(left->value, 0) < len - plen)
+ goto not_a_prefix;
+
+ mpz_clear(bitmask);
+ *prefix_len = plen;
+
+ return true;
+
+not_a_prefix:
+ mpz_clear(bitmask);
+
+ return false;
}
struct expr *range_expr_to_prefix(struct expr *range)
{
- struct expr *left = range->left, *right = range->right, *prefix;
- uint32_t len = left->len, prefix_len;
- mpz_t bitmask;
-
- mpz_init2(bitmask, len);
- mpz_xor(bitmask, left->value, right->value);
+ struct expr *prefix;
+ uint32_t prefix_len;
- if (mpz_bitmask_is_prefix(bitmask, len)) {
- prefix_len = mpz_bitmask_to_prefix(bitmask, len);
- prefix = prefix_expr_alloc(&range->location, expr_get(left),
+ if (range_expr_is_prefix(range, &prefix_len)) {
+ prefix = prefix_expr_alloc(&range->location,
+ expr_get(range->left),
prefix_len);
- mpz_clear(bitmask);
expr_free(range);
-
return prefix;
}
- mpz_clear(bitmask);
return range;
}
-static struct expr *netlink_parse_interval_elem(const struct datatype *dtype,
+static struct expr *range_expr_reduce(struct expr *range)
+{
+ struct expr *expr;
+
+ if (!mpz_cmp(range->left->value, range->right->value)) {
+ expr = expr_get(range->left);
+ expr_free(range);
+ return expr;
+ }
+
+ if (range->left->dtype->type != TYPE_IPADDR &&
+ range->left->dtype->type != TYPE_IP6ADDR)
+ return range;
+
+ return range_expr_to_prefix(range);
+}
+
+static struct expr *netlink_parse_interval_elem(const struct set *set,
struct expr *expr)
{
unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
+ const struct datatype *dtype = set->data->dtype;
struct expr *range, *left, *right;
char data[len];
@@ -1073,31 +1138,84 @@ static struct expr *netlink_parse_interval_elem(const struct datatype *dtype,
return range_expr_to_prefix(range);
}
-static struct expr *netlink_parse_concat_elem(const struct datatype *dtype,
- struct expr *data)
+static struct expr *concat_elem_expr(struct expr *expr,
+ const struct datatype *dtype,
+ struct expr *data, int *off)
{
const struct datatype *subtype;
+
+ subtype = concat_subtype_lookup(dtype->type, --(*off));
+
+ expr = constant_expr_splice(data, subtype->size);
+ expr->dtype = subtype;
+ expr->byteorder = subtype->byteorder;
+
+ if (expr->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
+
+ if (expr->dtype->basetype != NULL &&
+ expr->dtype->basetype->type == TYPE_BITMASK)
+ expr = bitmask_expr_to_binops(expr);
+
+ data->len -= netlink_padding_len(expr->len);
+
+ return expr;
+}
+
+static struct expr *netlink_parse_concat_elem_key(const struct set *set,
+ struct expr *data)
+{
+ const struct datatype *dtype = set->key->dtype;
struct expr *concat, *expr;
int off = dtype->subtypes;
concat = concat_expr_alloc(&data->location);
while (off > 0) {
- subtype = concat_subtype_lookup(dtype->type, --off);
+ expr = concat_elem_expr(expr, dtype, data, &off);
+ compound_expr_add(concat, expr);
+ }
- expr = constant_expr_splice(data, subtype->size);
- expr->dtype = subtype;
- expr->byteorder = subtype->byteorder;
+ expr_free(data);
- if (expr->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
+ return concat;
+}
- if (expr->dtype->basetype != NULL &&
- expr->dtype->basetype->type == TYPE_BITMASK)
- expr = bitmask_expr_to_binops(expr);
+static struct expr *netlink_parse_concat_elem(const struct set *set,
+ struct expr *data)
+{
+ const struct datatype *dtype = set->data->dtype;
+ struct expr *concat, *expr, *left, *range;
+ struct list_head expressions;
+ int off = dtype->subtypes;
- compound_expr_add(concat, expr);
- data->len -= netlink_padding_len(expr->len);
+ init_list_head(&expressions);
+
+ concat = concat_expr_alloc(&data->location);
+ while (off > 0) {
+ expr = concat_elem_expr(expr, dtype, data, &off);
+ list_add_tail(&expr->list, &expressions);
+ }
+
+ if (set->data->flags & EXPR_F_INTERVAL) {
+ assert(!list_empty(&expressions));
+
+ off = dtype->subtypes;
+
+ while (off > 0) {
+ left = list_first_entry(&expressions, struct expr, list);
+
+ expr = concat_elem_expr(expr, dtype, data, &off);
+ list_del(&left->list);
+
+ range = range_expr_alloc(&data->location, left, expr);
+ range = range_expr_reduce(range);
+ compound_expr_add(concat, range);
+ }
+ assert(list_empty(&expressions));
+ } else {
+ list_splice_tail(&expressions, &concat->expressions);
}
+
expr_free(data);
return concat;
@@ -1169,7 +1287,7 @@ key_end:
datatype_set(key, set->key->dtype);
key->byteorder = set->key->byteorder;
if (set->key->dtype->subtypes)
- key = netlink_parse_concat_elem(set->key->dtype, key);
+ key = netlink_parse_concat_elem_key(set, key);
if (!(set->flags & NFT_SET_INTERVAL) &&
key->byteorder == BYTEORDER_HOST_ENDIAN)
@@ -1206,7 +1324,7 @@ key_end:
nftnl_set_elem_expr_foreach(nlse, set_elem_parse_expressions,
&setelem_parse_ctx);
}
- list_splice_tail(&setelem_parse_ctx.stmt_list, &expr->stmt_list);
+ list_splice_tail_init(&setelem_parse_ctx.stmt_list, &expr->stmt_list);
if (flags & NFT_SET_ELEM_INTERVAL_END) {
expr->flags |= EXPR_F_INTERVAL_END;
@@ -1232,10 +1350,10 @@ key_end:
datatype_set(data, set->data->dtype);
data->byteorder = set->data->byteorder;
- if (set->data->flags & EXPR_F_INTERVAL)
- data = netlink_parse_interval_elem(set->data->dtype, data);
- else if (set->data->dtype->subtypes)
- data = netlink_parse_concat_elem(set->data->dtype, data);
+ if (set->data->dtype->subtypes) {
+ data = netlink_parse_concat_elem(set, data);
+ } else if (set->data->flags & EXPR_F_INTERVAL)
+ data = netlink_parse_interval_elem(set, data);
if (data->byteorder == BYTEORDER_HOST_ENDIAN)
mpz_switch_byteorder(data->value, data->len / BITS_PER_BYTE);
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 6a6f7747..bd75ad5c 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -637,7 +637,7 @@ static void netlink_parse_exthdr(struct netlink_parse_ctx *ctx,
sreg = netlink_parse_register(nle, NFTNL_EXPR_EXTHDR_SREG);
val = netlink_get_register(ctx, loc, sreg);
if (val == NULL) {
- xfree(expr);
+ expr_free(expr);
return netlink_error(ctx, loc,
"exthdr statement has no expression");
}
@@ -679,7 +679,7 @@ static void netlink_parse_hash(struct netlink_parse_ctx *ctx,
len = nftnl_expr_get_u32(nle,
NFTNL_EXPR_HASH_LEN) * BITS_PER_BYTE;
if (hexpr->len < len) {
- xfree(hexpr);
+ expr_free(hexpr);
hexpr = netlink_parse_concat_expr(ctx, loc, sreg, len);
if (hexpr == NULL)
goto out_err;
@@ -691,7 +691,7 @@ static void netlink_parse_hash(struct netlink_parse_ctx *ctx,
netlink_set_register(ctx, dreg, expr);
return;
out_err:
- xfree(expr);
+ expr_free(expr);
}
static void netlink_parse_fib(struct netlink_parse_ctx *ctx,
@@ -1015,7 +1015,8 @@ static void netlink_parse_reject(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
}
-static bool is_nat_addr_map(const struct expr *addr, uint8_t family)
+static bool is_nat_addr_map(const struct expr *addr, uint8_t family,
+ struct stmt *stmt)
{
const struct expr *mappings, *data;
const struct set *set;
@@ -1034,14 +1035,31 @@ static bool is_nat_addr_map(const struct expr *addr, uint8_t family)
if (!(data->flags & EXPR_F_INTERVAL))
return false;
+ stmt->nat.family = family;
+
/* if we're dealing with an address:address map,
* the length will be bit_sizeof(addr) + 32 (one register).
*/
switch (family) {
case NFPROTO_IPV4:
- return data->len == 32 + 32;
+ if (data->len == 32 + 32) {
+ stmt->nat.type_flags |= STMT_NAT_F_INTERVAL;
+ return true;
+ } else if (data->len == 32 + 32 + 32 + 32) {
+ stmt->nat.type_flags |= STMT_NAT_F_INTERVAL |
+ STMT_NAT_F_CONCAT;
+ return true;
+ }
+ break;
case NFPROTO_IPV6:
- return data->len == 128 + 128;
+ if (data->len == 128 + 128) {
+ stmt->nat.type_flags |= STMT_NAT_F_INTERVAL;
+ return true;
+ } else if (data->len == 128 + 32 + 128 + 32) {
+ stmt->nat.type_flags |= STMT_NAT_F_INTERVAL |
+ STMT_NAT_F_CONCAT;
+ return true;
+ }
}
return false;
@@ -1117,9 +1135,8 @@ static void netlink_parse_nat(struct netlink_parse_ctx *ctx,
stmt->nat.addr = addr;
}
- if (is_nat_addr_map(addr, family)) {
+ if (is_nat_addr_map(addr, family, stmt)) {
stmt->nat.family = family;
- stmt->nat.type_flags |= STMT_NAT_F_INTERVAL;
ctx->stmt = stmt;
return;
}
@@ -1183,7 +1200,7 @@ static void netlink_parse_nat(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
return;
out_err:
- xfree(stmt);
+ stmt_free(stmt);
}
static void netlink_parse_synproxy(struct netlink_parse_ctx *ctx,
@@ -1247,7 +1264,7 @@ static void netlink_parse_tproxy(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
return;
err:
- xfree(stmt);
+ stmt_free(stmt);
}
static void netlink_parse_masq(struct netlink_parse_ctx *ctx,
@@ -1294,7 +1311,7 @@ static void netlink_parse_masq(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
return;
out_err:
- xfree(stmt);
+ stmt_free(stmt);
}
static void netlink_parse_redir(struct netlink_parse_ctx *ctx,
@@ -1345,7 +1362,7 @@ static void netlink_parse_redir(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
return;
out_err:
- xfree(stmt);
+ stmt_free(stmt);
}
static void netlink_parse_dup(struct netlink_parse_ctx *ctx,
@@ -1398,7 +1415,7 @@ static void netlink_parse_dup(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
return;
out_err:
- xfree(stmt);
+ stmt_free(stmt);
}
static void netlink_parse_fwd(struct netlink_parse_ctx *ctx,
@@ -1460,7 +1477,7 @@ static void netlink_parse_fwd(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
return;
out_err:
- xfree(stmt);
+ stmt_free(stmt);
}
static void netlink_parse_queue(struct netlink_parse_ctx *ctx,
@@ -1623,7 +1640,7 @@ out_err:
list_for_each_entry_safe(dstmt, next, &dynset_parse_ctx.stmt_list, list)
stmt_free(dstmt);
- xfree(expr);
+ expr_free(expr);
}
static void netlink_parse_objref(struct netlink_parse_ctx *ctx,
@@ -1733,46 +1750,26 @@ static const struct expr_handler netlink_parsers[] = {
{ .name = "synproxy", .parse = netlink_parse_synproxy },
};
-static const struct expr_handler **expr_handle_ht;
-
-#define NFT_EXPR_HSIZE 4096
-
-void expr_handler_init(void)
-{
- unsigned int i;
- uint32_t hash;
-
- expr_handle_ht = xzalloc_array(NFT_EXPR_HSIZE,
- sizeof(expr_handle_ht[0]));
-
- for (i = 0; i < array_size(netlink_parsers); i++) {
- hash = djb_hash(netlink_parsers[i].name) % NFT_EXPR_HSIZE;
- assert(expr_handle_ht[hash] == NULL);
- expr_handle_ht[hash] = &netlink_parsers[i];
- }
-}
-
-void expr_handler_exit(void)
-{
- xfree(expr_handle_ht);
-}
-
static int netlink_parse_expr(const struct nftnl_expr *nle,
struct netlink_parse_ctx *ctx)
{
const char *type = nftnl_expr_get_str(nle, NFTNL_EXPR_NAME);
struct location loc;
- uint32_t hash;
+ unsigned int i;
memset(&loc, 0, sizeof(loc));
loc.indesc = &indesc_netlink;
loc.nle = nle;
- hash = djb_hash(type) % NFT_EXPR_HSIZE;
- if (expr_handle_ht[hash])
- expr_handle_ht[hash]->parse(ctx, &loc, nle);
- else
- netlink_error(ctx, &loc, "unknown expression type '%s'", type);
+ for (i = 0; i < array_size(netlink_parsers); i++) {
+ if (strcmp(type, netlink_parsers[i].name))
+ continue;
+
+ netlink_parsers[i].parse(ctx, &loc, nle);
+
+ return 0;
+ }
+ netlink_error(ctx, &loc, "unknown expression type '%s'", type);
return 0;
}
@@ -1945,6 +1942,7 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx,
struct set *set = expr->right->set;
if (set_is_anonymous(set->flags) &&
+ set->init &&
!list_empty(&set->init->expressions)) {
struct expr *elem;
@@ -1965,6 +1963,55 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx,
}
}
+static uint8_t ether_type_to_nfproto(uint16_t l3proto)
+{
+ switch(l3proto) {
+ case ETH_P_IP:
+ return NFPROTO_IPV4;
+ case ETH_P_IPV6:
+ return NFPROTO_IPV6;
+ default:
+ break;
+ }
+
+ return NFPROTO_UNSPEC;
+}
+
+static bool __meta_dependency_may_kill(const struct expr *dep, uint8_t *nfproto)
+{
+ uint16_t l3proto;
+
+ switch (dep->left->etype) {
+ case EXPR_META:
+ switch (dep->left->meta.key) {
+ case NFT_META_NFPROTO:
+ *nfproto = mpz_get_uint8(dep->right->value);
+ break;
+ case NFT_META_PROTOCOL:
+ l3proto = mpz_get_uint16(dep->right->value);
+ *nfproto = ether_type_to_nfproto(l3proto);
+ break;
+ default:
+ return true;
+ }
+ break;
+ case EXPR_PAYLOAD:
+ if (dep->left->payload.base != PROTO_BASE_LL_HDR)
+ return true;
+
+ if (dep->left->dtype != &ethertype_type)
+ return true;
+
+ l3proto = mpz_get_uint16(dep->right->value);
+ *nfproto = ether_type_to_nfproto(l3proto);
+ break;
+ default:
+ return true;
+ }
+
+ return false;
+}
+
/* We have seen a protocol key expression that restricts matching at the network
* base, leave it in place since this is meaninful in bridge, inet and netdev
* families. Exceptions are ICMP and ICMPv6 where this code assumes that can
@@ -1974,51 +2021,34 @@ static bool meta_may_dependency_kill(struct payload_dep_ctx *ctx,
unsigned int family,
const struct expr *expr)
{
+ uint8_t l4proto, nfproto = NFPROTO_UNSPEC;
struct expr *dep = ctx->pdep->expr;
- uint16_t l3proto;
- uint8_t l4proto;
if (ctx->pbase != PROTO_BASE_NETWORK_HDR)
return true;
+ if (__meta_dependency_may_kill(dep, &nfproto))
+ return true;
+
switch (family) {
case NFPROTO_INET:
case NFPROTO_NETDEV:
case NFPROTO_BRIDGE:
break;
default:
+ if (family == NFPROTO_IPV4 &&
+ nfproto != NFPROTO_IPV4)
+ return false;
+ else if (family == NFPROTO_IPV6 &&
+ nfproto != NFPROTO_IPV6)
+ return false;
+
return true;
}
if (expr->left->meta.key != NFT_META_L4PROTO)
return true;
- l3proto = mpz_get_uint16(dep->right->value);
-
- switch (dep->left->etype) {
- case EXPR_META:
- if (dep->left->meta.key != NFT_META_NFPROTO)
- return true;
- break;
- case EXPR_PAYLOAD:
- if (dep->left->payload.base != PROTO_BASE_LL_HDR)
- return true;
-
- switch(l3proto) {
- case ETH_P_IP:
- l3proto = NFPROTO_IPV4;
- break;
- case ETH_P_IPV6:
- l3proto = NFPROTO_IPV6;
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
-
l4proto = mpz_get_uint8(expr->right->value);
switch (l4proto) {
@@ -2029,8 +2059,8 @@ static bool meta_may_dependency_kill(struct payload_dep_ctx *ctx,
return false;
}
- if ((l3proto == NFPROTO_IPV4 && l4proto == IPPROTO_ICMPV6) ||
- (l3proto == NFPROTO_IPV6 && l4proto == IPPROTO_ICMP))
+ if ((nfproto == NFPROTO_IPV4 && l4proto == IPPROTO_ICMPV6) ||
+ (nfproto == NFPROTO_IPV6 && l4proto == IPPROTO_ICMP))
return false;
return true;
@@ -2267,6 +2297,14 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
BUG("unknown operation type %d\n", expr->op);
}
expr_free(binop);
+ } else if (binop->right->etype == EXPR_VALUE &&
+ value->etype == EXPR_VALUE &&
+ !mpz_cmp(value->value, binop->right->value)) {
+ /* Skip flag / flag representation for:
+ * data & flag == flag
+ * data & flag != flag
+ */
+ ;
} else {
*exprp = flagcmp_expr_alloc(&expr->location, expr->op,
expr_get(binop->left),
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index b1f3feee..454b9ba3 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -481,23 +481,31 @@ static void netlink_gen_flagcmp(struct netlink_linearize_ctx *ctx,
netlink_gen_raw_data(zero, expr->right->byteorder, len, &nld);
netlink_gen_data(expr->right, &nld2);
- nle = alloc_nft_expr("bitwise");
- netlink_put_register(nle, NFTNL_EXPR_BITWISE_SREG, sreg);
- netlink_put_register(nle, NFTNL_EXPR_BITWISE_DREG, sreg);
- nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, len);
- nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_MASK, &nld2.value, nld2.len);
- nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_XOR, &nld.value, nld.len);
- nft_rule_add_expr(ctx, nle, &expr->location);
-
- nle = alloc_nft_expr("cmp");
- netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg);
- if (expr->op == OP_NEG)
+ if (expr->left->etype == EXPR_BINOP) {
+ nle = alloc_nft_expr("cmp");
+ netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg);
nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_EQ);
- else
- nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_NEQ);
+ nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld2.value, nld2.len);
+ nft_rule_add_expr(ctx, nle, &expr->location);
+ } else {
+ nle = alloc_nft_expr("bitwise");
+ netlink_put_register(nle, NFTNL_EXPR_BITWISE_SREG, sreg);
+ netlink_put_register(nle, NFTNL_EXPR_BITWISE_DREG, sreg);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, len);
+ nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_MASK, &nld2.value, nld2.len);
+ nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_XOR, &nld.value, nld.len);
+ nft_rule_add_expr(ctx, nle, &expr->location);
- nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len);
- nft_rule_add_expr(ctx, nle, &expr->location);
+ nle = alloc_nft_expr("cmp");
+ netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg);
+ if (expr->op == OP_NEG)
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_EQ);
+ else
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_NEQ);
+
+ nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len);
+ nft_rule_add_expr(ctx, nle, &expr->location);
+ }
mpz_clear(zero);
release_register(ctx, expr->left);
@@ -540,7 +548,8 @@ static void netlink_gen_relational(struct netlink_linearize_ctx *ctx,
case EXPR_PREFIX:
sreg = get_register(ctx, expr->left);
if (expr_basetype(expr->left)->type != TYPE_STRING &&
- (!expr->right->prefix_len ||
+ (expr->right->byteorder != BYTEORDER_BIG_ENDIAN ||
+ !expr->right->prefix_len ||
expr->right->prefix_len % BITS_PER_BYTE)) {
len = div_round_up(expr->right->len, BITS_PER_BYTE);
netlink_gen_expr(ctx, expr->left, sreg);
@@ -1165,11 +1174,14 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
amin_reg);
if (stmt->nat.addr->etype == EXPR_MAP &&
stmt->nat.addr->mappings->set->data->flags & EXPR_F_INTERVAL) {
- amax_reg = get_register(ctx, NULL);
- registers++;
amin_reg += netlink_register_space(nat_addrlen(family));
- netlink_put_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MAX,
- amin_reg);
+ if (stmt->nat.type_flags & STMT_NAT_F_CONCAT) {
+ netlink_put_register(nle, nftnl_reg_pmin,
+ amin_reg);
+ } else {
+ netlink_put_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MAX,
+ amin_reg);
+ }
}
}
@@ -1181,6 +1193,12 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
pmin_reg = amin_reg;
+ if (stmt->nat.type_flags & STMT_NAT_F_INTERVAL) {
+ pmin_reg += netlink_register_space(nat_addrlen(family));
+ netlink_put_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MAX,
+ pmin_reg);
+ }
+
/* if STMT_NAT_F_CONCAT is set, the mapped type is a
* concatenation of 'addr . inet_service'.
* The map lookup will then return the
@@ -1189,7 +1207,10 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
* will hold the inet_service part.
*/
pmin_reg += netlink_register_space(nat_addrlen(family));
- netlink_put_register(nle, nftnl_reg_pmin, pmin_reg);
+ if (stmt->nat.type_flags & STMT_NAT_F_INTERVAL)
+ netlink_put_register(nle, nftnl_reg_pmax, pmin_reg);
+ else
+ netlink_put_register(nle, nftnl_reg_pmin, pmin_reg);
}
}
diff --git a/src/parser_bison.y b/src/parser_bison.y
index cb3e80e3..c25af6ba 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -705,8 +705,8 @@ int nft_lex(void *, void *, void *);
%type <stmt> queue_stmt queue_stmt_alloc queue_stmt_compat
%destructor { stmt_free($$); } queue_stmt queue_stmt_alloc queue_stmt_compat
-%type <expr> queue_stmt_expr_simple queue_stmt_expr
-%destructor { expr_free($$); } queue_stmt_expr_simple queue_stmt_expr
+%type <expr> queue_stmt_expr_simple queue_stmt_expr queue_expr reject_with_expr
+%destructor { expr_free($$); } queue_stmt_expr_simple queue_stmt_expr queue_expr reject_with_expr
%type <val> queue_stmt_flags queue_stmt_flag
%type <stmt> dup_stmt
%destructor { stmt_free($$); } dup_stmt
@@ -972,6 +972,7 @@ common_block : INCLUDE QUOTED_STRING stmt_separator
if (symbol_unbind(scope, $2) < 0) {
erec_queue(error(&@2, "undefined symbol '%s'", $2),
state->msgs);
+ xfree($2);
YYERROR;
}
xfree($2);
@@ -1312,6 +1313,8 @@ delete_cmd : TABLE table_or_id_spec
| CT ct_obj_type obj_spec ct_obj_alloc close_scope_ct
{
$$ = cmd_alloc_obj_ct(CMD_DELETE, $2, &$3, &@$, $4);
+ if ($2 == NFT_OBJECT_CT_TIMEOUT)
+ init_list_head(&$4->ct_timeout.timeout_list);
}
| LIMIT obj_or_id_spec close_scope_limit
{
@@ -1471,11 +1474,7 @@ list_cmd : TABLE table_spec
}
;
-basehook_device_name : /* NULL */
- {
- $$ = NULL;
- }
- | DEVICE STRING
+basehook_device_name : DEVICE STRING
{
$$ = $2;
}
@@ -1485,22 +1484,11 @@ basehook_spec : ruleset_spec
{
$$ = $1;
}
- | ruleset_spec STRING basehook_device_name
+ | ruleset_spec basehook_device_name
{
- const char *name = chain_hookname_lookup($2);
-
- if (name == NULL) {
- erec_queue(error(&@2, "unknown chain hook"),
- state->msgs);
- xfree($3);
- YYERROR;
- }
-
- $1.chain.name = $2;
- $1.chain.location = @2;
- if ($3) {
- $1.obj.name = $3;
- $1.obj.location = @3;
+ if ($2) {
+ $1.obj.name = $2;
+ $1.obj.location = @2;
}
$$ = $1;
}
@@ -2049,6 +2037,12 @@ map_block : /* empty */ { $$ = $<set>-1; }
$1->flags |= $3;
$$ = $1;
}
+ | map_block stateful_stmt_list stmt_separator
+ {
+ list_splice_tail($2, &$1->stmt_list);
+ $$ = $1;
+ free($2);
+ }
| map_block ELEMENTS '=' set_block_expr
{
$1->init = $4;
@@ -2162,6 +2156,7 @@ data_type_atom_expr : type_identifier
if (dtype == NULL) {
erec_queue(error(&@1, "unknown datatype %s", $1),
state->msgs);
+ xfree($1);
YYERROR;
}
$$ = constant_expr_alloc(&@1, dtype, dtype->byteorder,
@@ -2717,6 +2712,7 @@ comment_spec : COMMENT string
erec_queue(error(&@2, "comment too long, %d characters maximum allowed",
NFTNL_UDATA_COMMENT_MAXLEN),
state->msgs);
+ xfree($2);
YYERROR;
}
$$ = $2;
@@ -3287,42 +3283,59 @@ reject_stmt_alloc : _REJECT
}
;
+reject_with_expr : STRING
+ {
+ $$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
+ current_scope(state), $1);
+ xfree($1);
+ }
+ | integer_expr { $$ = $1; }
+ ;
+
reject_opts : /* empty */
{
$<stmt>0->reject.type = -1;
$<stmt>0->reject.icmp_code = -1;
}
- | WITH ICMP TYPE STRING
+ | WITH ICMP TYPE reject_with_expr
{
$<stmt>0->reject.family = NFPROTO_IPV4;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
- $<stmt>0->reject.expr =
- symbol_expr_alloc(&@$, SYMBOL_VALUE,
- current_scope(state),
- $4);
+ $<stmt>0->reject.expr = $4;
datatype_set($<stmt>0->reject.expr, &icmp_code_type);
- xfree($4);
}
- | WITH ICMP6 TYPE STRING
+ | WITH ICMP reject_with_expr
+ {
+ $<stmt>0->reject.family = NFPROTO_IPV4;
+ $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
+ $<stmt>0->reject.expr = $3;
+ datatype_set($<stmt>0->reject.expr, &icmp_code_type);
+ }
+ | WITH ICMP6 TYPE reject_with_expr
{
$<stmt>0->reject.family = NFPROTO_IPV6;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
- $<stmt>0->reject.expr =
- symbol_expr_alloc(&@$, SYMBOL_VALUE,
- current_scope(state),
- $4);
+ $<stmt>0->reject.expr = $4;
datatype_set($<stmt>0->reject.expr, &icmpv6_code_type);
- xfree($4);
}
- | WITH ICMPX TYPE STRING
+ | WITH ICMP6 reject_with_expr
+ {
+ $<stmt>0->reject.family = NFPROTO_IPV6;
+ $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
+ $<stmt>0->reject.expr = $3;
+ datatype_set($<stmt>0->reject.expr, &icmpv6_code_type);
+ }
+ | WITH ICMPX TYPE reject_with_expr
{
$<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH;
- $<stmt>0->reject.expr =
- symbol_expr_alloc(&@$, SYMBOL_VALUE,
- current_scope(state),
- $4);
+ $<stmt>0->reject.expr = $4;
+ datatype_set($<stmt>0->reject.expr, &icmpx_code_type);
+ }
+ | WITH ICMPX reject_with_expr
+ {
+ $<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH;
+ $<stmt>0->reject.expr = $3;
datatype_set($<stmt>0->reject.expr, &icmpx_code_type);
- xfree($4);
}
| WITH TCP RESET
{
@@ -3620,28 +3633,24 @@ nat_stmt_args : stmt_expr
{
$<stmt>0->nat.family = $1;
$<stmt>0->nat.addr = $4;
- $<stmt>0->nat.type_flags = STMT_NAT_F_INTERVAL;
}
| INTERVAL TO stmt_expr
{
$<stmt>0->nat.addr = $3;
- $<stmt>0->nat.type_flags = STMT_NAT_F_INTERVAL;
}
| nf_key_proto PREFIX TO stmt_expr
{
$<stmt>0->nat.family = $1;
$<stmt>0->nat.addr = $4;
$<stmt>0->nat.type_flags =
- STMT_NAT_F_PREFIX |
- STMT_NAT_F_INTERVAL;
+ STMT_NAT_F_PREFIX;
$<stmt>0->nat.flags |= NF_NAT_RANGE_NETMAP;
}
| PREFIX TO stmt_expr
{
$<stmt>0->nat.addr = $3;
$<stmt>0->nat.type_flags =
- STMT_NAT_F_PREFIX |
- STMT_NAT_F_INTERVAL;
+ STMT_NAT_F_PREFIX;
$<stmt>0->nat.flags |= NF_NAT_RANGE_NETMAP;
}
;
@@ -3781,12 +3790,22 @@ queue_stmt_arg : QUEUENUM queue_stmt_expr_simple
}
;
+queue_expr : variable_expr
+ | integer_expr
+ ;
+
queue_stmt_expr_simple : integer_expr
- | range_rhs_expr
+ | variable_expr
+ | queue_expr DASH queue_expr
+ {
+ $$ = range_expr_alloc(&@$, $1, $3);
+ }
;
queue_stmt_expr : numgen_expr
| hash_expr
+ | map_expr
+ | queue_stmt_expr_simple
;
queue_stmt_flags : queue_stmt_flag
@@ -4549,6 +4568,7 @@ limit_config : RATE limit_mode NUM SLASH time_unit limit_burst_pkts
uint64_t rate, unit;
erec = rate_parse(&@$, $4, &rate, &unit);
+ xfree($4);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
diff --git a/src/parser_json.c b/src/parser_json.c
index e03b5169..3cd21175 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -44,7 +44,6 @@
#define CTX_F_CONCAT (1 << 8) /* inside concat_expr */
struct json_ctx {
- struct input_descriptor indesc;
struct nft_ctx *nft;
struct list_head *msgs;
struct list_head *cmds;
@@ -107,11 +106,12 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root);
/* parsing helpers */
const struct location *int_loc = &internal_location;
+static struct input_descriptor json_indesc;
static void json_lib_error(struct json_ctx *ctx, json_error_t *err)
{
struct location loc = {
- .indesc = &ctx->indesc,
+ .indesc = &json_indesc,
.line_offset = err->position - err->column,
.first_line = err->line,
.last_line = err->line,
@@ -3204,7 +3204,6 @@ static int json_parse_ct_timeout_policy(struct json_ctx *ctx,
return 1;
}
- init_list_head(&obj->ct_timeout.timeout_list);
json_object_foreach(tmp, key, val) {
struct timeout_state *ts;
@@ -3351,6 +3350,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
}
obj->ct_helper.l3proto = l3proto;
+ init_list_head(&obj->ct_timeout.timeout_list);
if (json_parse_ct_timeout_policy(ctx, root, obj)) {
obj_free(obj);
return NULL;
@@ -3923,16 +3923,15 @@ int nft_parse_json_buffer(struct nft_ctx *nft, const char *buf,
struct list_head *msgs, struct list_head *cmds)
{
struct json_ctx ctx = {
- .indesc = {
- .type = INDESC_BUFFER,
- .data = buf,
- },
.nft = nft,
.msgs = msgs,
.cmds = cmds,
};
int ret;
+ json_indesc.type = INDESC_BUFFER;
+ json_indesc.data = buf;
+
parser_init(nft, nft->state, msgs, cmds, nft->top_scope);
nft->json_root = json_loads(buf, 0, NULL);
if (!nft->json_root)
@@ -3951,10 +3950,6 @@ int nft_parse_json_filename(struct nft_ctx *nft, const char *filename,
struct list_head *msgs, struct list_head *cmds)
{
struct json_ctx ctx = {
- .indesc = {
- .type = INDESC_FILE,
- .name = filename,
- },
.nft = nft,
.msgs = msgs,
.cmds = cmds,
@@ -3962,6 +3957,9 @@ int nft_parse_json_filename(struct nft_ctx *nft, const char *filename,
json_error_t err;
int ret;
+ json_indesc.type = INDESC_FILE;
+ json_indesc.name = filename;
+
parser_init(nft, nft->state, msgs, cmds, nft->top_scope);
nft->json_root = json_load_file(filename, 0, &err);
if (!nft->json_root)
diff --git a/src/rule.c b/src/rule.c
index 10569aa7..6091067f 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -822,6 +822,8 @@ const char *hooknum2str(unsigned int family, unsigned int hooknum)
return "forward";
case NF_ARP_OUT:
return "output";
+ case __NF_ARP_INGRESS:
+ return "ingress";
default:
break;
}
@@ -1714,6 +1716,7 @@ void obj_free(struct obj *obj)
list_for_each_entry_safe(ts, next, &obj->ct_timeout.timeout_list, head) {
list_del(&ts->head);
+ xfree(ts->timeout_str);
xfree(ts);
}
}
@@ -2754,49 +2757,80 @@ static void payload_do_merge(struct stmt *sa[], unsigned int n)
}
/**
- * payload_try_merge - try to merge consecutive payload match statements
+ * stmt_reduce - reduce statements in rule
*
* @rule: nftables rule
*
+ * This function aims to:
+ *
+ * - remove redundant statement, e.g. remove 'meta protocol ip' if family is ip
+ * - merge consecutive payload match statements
+ *
* Locate sequences of payload match statements referring to adjacent
* header locations and merge those using only equality relations.
*
* As a side-effect, payload match statements are ordered in ascending
* order according to the location of the payload.
*/
-static void payload_try_merge(const struct rule *rule)
+static void stmt_reduce(const struct rule *rule)
{
+ struct stmt *stmt, *dstmt = NULL, *next;
struct stmt *sa[rule->num_stmts];
- struct stmt *stmt, *next;
unsigned int idx = 0;
list_for_each_entry_safe(stmt, next, &rule->stmts, list) {
+ /* delete this redundant statement */
+ if (dstmt) {
+ list_del(&dstmt->list);
+ stmt_free(dstmt);
+ dstmt = NULL;
+ }
+
/* Must not merge across other statements */
- if (stmt->ops->type != STMT_EXPRESSION)
- goto do_merge;
+ if (stmt->ops->type != STMT_EXPRESSION) {
+ if (idx < 2)
+ continue;
- if (stmt->expr->etype != EXPR_RELATIONAL)
+ payload_do_merge(sa, idx);
+ idx = 0;
continue;
- if (stmt->expr->left->etype != EXPR_PAYLOAD)
+ }
+
+ if (stmt->expr->etype != EXPR_RELATIONAL)
continue;
if (stmt->expr->right->etype != EXPR_VALUE)
continue;
- switch (stmt->expr->op) {
- case OP_EQ:
- case OP_IMPLICIT:
- case OP_NEQ:
- break;
- default:
- continue;
- }
- sa[idx++] = stmt;
- continue;
-do_merge:
- if (idx < 2)
- continue;
- payload_do_merge(sa, idx);
- idx = 0;
+ if (stmt->expr->left->etype == EXPR_PAYLOAD) {
+ switch (stmt->expr->op) {
+ case OP_EQ:
+ case OP_IMPLICIT:
+ case OP_NEQ:
+ break;
+ default:
+ continue;
+ }
+
+ sa[idx++] = stmt;
+ } else if (stmt->expr->left->etype == EXPR_META) {
+ switch (stmt->expr->op) {
+ case OP_EQ:
+ case OP_IMPLICIT:
+ if (stmt->expr->left->meta.key == NFT_META_PROTOCOL) {
+ uint16_t protocol;
+
+ protocol = mpz_get_uint16(stmt->expr->right->value);
+ if ((rule->handle.family == NFPROTO_IPV4 &&
+ protocol == ETH_P_IP) ||
+ (rule->handle.family == NFPROTO_IPV6 &&
+ protocol == ETH_P_IPV6))
+ dstmt = stmt;
+ }
+ break;
+ default:
+ break;
+ }
+ }
}
if (idx > 1)
@@ -2805,6 +2839,6 @@ do_merge:
struct error_record *rule_postprocess(struct rule *rule)
{
- payload_try_merge(rule);
+ stmt_reduce(rule);
return NULL;
}
diff --git a/src/statement.c b/src/statement.c
index dfd27510..03c0acf6 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -507,15 +507,10 @@ static void queue_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
nft_print(octx, "%sfanout", delim);
if (e) {
- if (e->etype == EXPR_VALUE || e->etype == EXPR_RANGE) {
- nft_print(octx, " num ");
- expr_print(stmt->queue.queue, octx);
- } else {
- nft_print(octx, " to ");
- expr_print(stmt->queue.queue, octx);
- }
+ nft_print(octx, " to ");
+ expr_print(stmt->queue.queue, octx);
} else {
- nft_print(octx, " num 0");
+ nft_print(octx, " to 0");
}
}
@@ -585,7 +580,7 @@ static void reject_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
case NFT_REJECT_ICMPX_UNREACH:
if (stmt->reject.icmp_code == NFT_REJECT_ICMPX_PORT_UNREACH)
break;
- nft_print(octx, " with icmpx type ");
+ nft_print(octx, " with icmpx ");
expr_print(stmt->reject.expr, octx);
break;
case NFT_REJECT_ICMP_UNREACH:
@@ -594,14 +589,14 @@ static void reject_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
if (!stmt->reject.verbose_print &&
stmt->reject.icmp_code == ICMP_PORT_UNREACH)
break;
- nft_print(octx, " with icmp type ");
+ nft_print(octx, " with icmp ");
expr_print(stmt->reject.expr, octx);
break;
case NFPROTO_IPV6:
if (!stmt->reject.verbose_print &&
stmt->reject.icmp_code == ICMP6_DST_UNREACH_NOPORT)
break;
- nft_print(octx, " with icmpv6 type ");
+ nft_print(octx, " with icmpv6 ");
expr_print(stmt->reject.expr, octx);
break;
}
@@ -673,12 +668,8 @@ static void nat_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
break;
}
- if (stmt->nat.type_flags & STMT_NAT_F_CONCAT)
- nft_print(octx, " addr . port");
- else if (stmt->nat.type_flags & STMT_NAT_F_PREFIX)
+ if (stmt->nat.type_flags & STMT_NAT_F_PREFIX)
nft_print(octx, " prefix");
- else if (stmt->nat.type_flags & STMT_NAT_F_INTERVAL)
- nft_print(octx, " interval");
nft_print(octx, " to");
}
diff --git a/src/tcpopt.c b/src/tcpopt.c
index 05b5ee6e..53fe9bc8 100644
--- a/src/tcpopt.c
+++ b/src/tcpopt.c
@@ -200,7 +200,8 @@ void tcpopt_init_raw(struct expr *expr, uint8_t type, unsigned int off,
else
datatype_set(expr, &integer_type);
- if (type >= array_size(tcpopt_protocols))
+ if (type >= array_size(tcpopt_protocols) ||
+ !tcpopt_protocols[type])
return;
expr->exthdr.desc = tcpopt_protocols[type];
diff --git a/tests/json_echo/run-test.py b/tests/json_echo/run-test.py
index 36a377ac..a6bdfc61 100755
--- a/tests/json_echo/run-test.py
+++ b/tests/json_echo/run-test.py
@@ -95,25 +95,25 @@ add_quota = { "add": {
# helper functions
def exit_err(msg):
- print("Error: %s" %msg)
+ print("Error: %s" %msg, file=sys.stderr)
sys.exit(1)
def exit_dump(e, obj):
- print("FAIL: {}".format(e))
- print("Output was:")
- json.dumps(out, sort_keys = True, indent = 4, separators = (',', ': '))
- sys.exit(1)
+ msg = "{}\n".format(e)
+ msg += "Output was:\n"
+ msg += json.dumps(obj, sort_keys = True, indent = 4, separators = (',', ': '))
+ exit_err(msg)
def do_flush():
rc, out, err = nftables.json_cmd({ "nftables": [flush_ruleset] })
- if not rc is 0:
+ if rc != 0:
exit_err("flush ruleset failed: {}".format(err))
def do_command(cmd):
if not type(cmd) is list:
cmd = [cmd]
rc, out, err = nftables.json_cmd({ "nftables": cmd })
- if not rc is 0:
+ if rc != 0:
exit_err("command failed: {}".format(err))
return out
diff --git a/tests/monitor/run-tests.sh b/tests/monitor/run-tests.sh
index 1fe613c7..ff00450b 100755
--- a/tests/monitor/run-tests.sh
+++ b/tests/monitor/run-tests.sh
@@ -9,15 +9,22 @@ mydiff() {
diff -w -I '^# ' "$@"
}
-if [ "$(id -u)" != "0" ] ; then
- echo "this requires root!"
+err() {
+ echo "$*" >&2
+}
+
+die() {
+ err "$*"
exit 1
+}
+
+if [ "$(id -u)" != "0" ] ; then
+ die "this requires root!"
fi
testdir=$(mktemp -d)
if [ ! -d $testdir ]; then
- echo "Failed to create test directory" >&2
- exit 1
+ die "Failed to create test directory"
fi
trap 'rm -rf $testdir; $nft flush ruleset' EXIT
@@ -56,6 +63,7 @@ monitor_run_test() {
monitor_output=$(mktemp -p $testdir)
monitor_args=""
$test_json && monitor_args="vm json"
+ local rc=0
$nft -nn monitor $monitor_args >$monitor_output &
monitor_pid=$!
@@ -67,45 +75,49 @@ monitor_run_test() {
cat $command_file
}
$nft -f $command_file || {
- echo "nft command failed!"
- kill $monitor_pid
- wait >/dev/null 2>&1
- exit 1
+ err "nft command failed!"
+ rc=1
}
sleep 0.5
kill $monitor_pid
wait >/dev/null 2>&1
$test_json && json_output_filter $monitor_output
- if ! mydiff -q $monitor_output $output_file >/dev/null 2>&1; then
- echo "monitor output differs!"
- mydiff -u $output_file $monitor_output
- exit 1
+ mydiff -q $monitor_output $output_file >/dev/null 2>&1
+ if [[ $rc == 0 && $? != 0 ]]; then
+ err "monitor output differs!"
+ mydiff -u $output_file $monitor_output >&2
+ rc=1
fi
rm $command_file
rm $output_file
touch $command_file
touch $output_file
+ return $rc
}
echo_run_test() {
echo_output=$(mktemp -p $testdir)
+ local rc=0
+
$debug && {
echo "command file:"
cat $command_file
}
$nft -nn -e -f $command_file >$echo_output || {
- echo "nft command failed!"
- exit 1
+ err "nft command failed!"
+ rc=1
}
- if ! mydiff -q $echo_output $output_file >/dev/null 2>&1; then
- echo "echo output differs!"
- mydiff -u $output_file $echo_output
- exit 1
+ mydiff -q $echo_output $output_file >/dev/null 2>&1
+ if [[ $rc == 0 && $? != 0 ]]; then
+ err "echo output differs!"
+ mydiff -u $output_file $echo_output >&2
+ rc=1
fi
rm $command_file
rm $output_file
touch $command_file
touch $output_file
+ return $rc
}
testcases=""
@@ -143,6 +155,7 @@ else
variants="monitor echo"
fi
+rc=0
for variant in $variants; do
run_test=${variant}_run_test
output_append=${variant}_output_append
@@ -162,7 +175,10 @@ for variant in $variants; do
while read dir line; do
case $dir in
I)
- $input_complete && $run_test
+ $input_complete && {
+ $run_test
+ let "rc += $?"
+ }
input_complete=false
cmd_append "$line"
;;
@@ -179,6 +195,10 @@ for variant in $variants; do
;;
esac
done <$testcase
- $input_complete && $run_test
+ $input_complete && {
+ $run_test
+ let "rc += $?"
+ }
done
done
+exit $rc
diff --git a/tests/py/any/ct.t b/tests/py/any/ct.t
index a44142ac..8b8e68ab 100644
--- a/tests/py/any/ct.t
+++ b/tests/py/any/ct.t
@@ -69,7 +69,7 @@ ct event set {new, related, destroy, label};fail
ct expiration 30s;ok
ct expiration 30000ms;ok;ct expiration 30s
-ct expiration 1m-1h;ok
+ct expiration 1m-1h;ok;ct expiration 60s-3600s
ct expiration 1d-1h;fail
ct expiration > 4d23h59m59s;ok
ct expiration != 233;ok;ct expiration != 3m53s
@@ -77,8 +77,8 @@ ct expiration 33-45;ok;ct expiration 33s-45s
ct expiration != 33-45;ok;ct expiration != 33s-45s
ct expiration {33, 55, 67, 88};ok;ct expiration { 1m7s, 33s, 55s, 1m28s}
ct expiration != {33, 55, 67, 88};ok;ct expiration != { 1m7s, 33s, 55s, 1m28s}
-ct expiration {33-55, 66-88};ok;ct expiration { 33s-55s, 1m6s-1m28s}
-ct expiration != {33-55, 66-88};ok;ct expiration != { 33s-55s, 1m6s-1m28s}
+ct expiration {33-55, 66-88};ok;ct expiration { 33s-55s, 66s-88s}
+ct expiration != {33-55, 66-88};ok;ct expiration != { 33s-55s, 66s-88s}
ct helper "ftp";ok
ct helper "12345678901234567";fail
diff --git a/tests/py/any/meta.t b/tests/py/any/meta.t
index 125b0a3f..6ec4853e 100644
--- a/tests/py/any/meta.t
+++ b/tests/py/any/meta.t
@@ -55,6 +55,7 @@ meta mark and 0x03 == 0x01;ok;meta mark & 0x00000003 == 0x00000001
meta mark and 0x03 != 0x01;ok;meta mark & 0x00000003 != 0x00000001
meta mark 0x10;ok;meta mark 0x00000010
meta mark != 0x10;ok;meta mark != 0x00000010
+meta mark 0xffffff00/24;ok
meta mark or 0x03 == 0x01;ok;meta mark | 0x00000003 == 0x00000001
meta mark or 0x03 != 0x01;ok;meta mark | 0x00000003 != 0x00000001
diff --git a/tests/py/any/meta.t.json b/tests/py/any/meta.t.json
index fd4d1c2a..b140aaaa 100644
--- a/tests/py/any/meta.t.json
+++ b/tests/py/any/meta.t.json
@@ -662,6 +662,26 @@
}
]
+# meta mark 0xffffff00/24
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "op": "==",
+ "right": {
+ "prefix": {
+ "addr": 4294967040,
+ "len": 24
+ }
+ }
+ }
+ }
+]
+
# meta mark or 0x03 == 0x01
[
{
diff --git a/tests/py/any/meta.t.payload b/tests/py/any/meta.t.payload
index b79a0255..d8351c27 100644
--- a/tests/py/any/meta.t.payload
+++ b/tests/py/any/meta.t.payload
@@ -155,6 +155,12 @@ ip test-ip4 input
[ meta load mark => reg 1 ]
[ cmp neq reg 1 0x00000010 ]
+# meta mark 0xffffff00/24
+ip test-ip4 input
+ [ meta load mark => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0xffffff00 ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0xffffff00 ]
+
# meta mark or 0x03 == 0x01
ip test-ip4 input
[ meta load mark => reg 1 ]
diff --git a/tests/py/any/queue.t b/tests/py/any/queue.t
index 670dfd92..f12acfaf 100644
--- a/tests/py/any/queue.t
+++ b/tests/py/any/queue.t
@@ -6,15 +6,15 @@
*arp;test-arp;output
*bridge;test-bridge;output
-queue;ok;queue num 0
-queue num 2;ok
-queue num 65535;ok
+queue;ok;queue to 0
+queue num 2;ok;queue to 2
+queue num 65535;ok;queue to 65535
queue num 65536;fail
-queue num 2-3;ok
-queue num 1-65535;ok
-queue num 4-5 fanout bypass;ok;queue flags bypass,fanout num 4-5
-queue num 4-5 fanout;ok;queue flags fanout num 4-5
-queue num 4-5 bypass;ok;queue flags bypass num 4-5
+queue num 2-3;ok;queue to 2-3
+queue num 1-65535;ok;queue to 1-65535
+queue num 4-5 fanout bypass;ok;queue flags bypass,fanout to 4-5
+queue num 4-5 fanout;ok;queue flags fanout to 4-5
+queue num 4-5 bypass;ok;queue flags bypass to 4-5
queue to symhash mod 2 offset 65536;fail
queue num symhash mod 65536;fail
@@ -23,5 +23,12 @@ queue flags fanout to symhash mod 65536;fail
queue flags bypass,fanout to symhash mod 65536;fail
queue flags bypass to numgen inc mod 65536;ok
queue to jhash oif . meta mark mod 32;ok
+queue to 2;ok
+queue to 65535;ok
+queue flags bypass to 65535;ok
+queue flags bypass to 1-65535;ok
+queue flags bypass,fanout to 1-65535;ok
+queue to 1-65535;ok
queue to oif;fail
queue num oif;fail
+queue flags bypass to oifname map { "eth0" : 0, "ppp0" : 2, "eth1" : 2 };ok
diff --git a/tests/py/any/queue.t.json b/tests/py/any/queue.t.json
index 18ed3c81..5f7f9014 100644
--- a/tests/py/any/queue.t.json
+++ b/tests/py/any/queue.t.json
@@ -140,3 +140,112 @@
}
]
+# queue flags bypass to oifname map { "eth0" : 0, "ppp0" : 2, "eth1" : 2 }
+[
+ {
+ "queue": {
+ "flags": "bypass",
+ "num": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ "eth0",
+ 0
+ ],
+ [
+ "ppp0",
+ 2
+ ],
+ [
+ "eth1",
+ 2
+ ]
+ ]
+ },
+ "key": {
+ "meta": {
+ "key": "oifname"
+ }
+ }
+ }
+ }
+ }
+ }
+]
+
+# queue to 2
+[
+ {
+ "queue": {
+ "num": 2
+ }
+ }
+]
+
+# queue to 65535
+[
+ {
+ "queue": {
+ "num": 65535
+ }
+ }
+]
+
+# queue flags bypass to 65535
+[
+ {
+ "queue": {
+ "flags": "bypass",
+ "num": 65535
+ }
+ }
+]
+
+# queue flags bypass to 1-65535
+[
+ {
+ "queue": {
+ "flags": "bypass",
+ "num": {
+ "range": [
+ 1,
+ 65535
+ ]
+ }
+ }
+ }
+]
+
+# queue flags bypass,fanout to 1-65535
+[
+ {
+ "queue": {
+ "flags": [
+ "bypass",
+ "fanout"
+ ],
+ "num": {
+ "range": [
+ 1,
+ 65535
+ ]
+ }
+ }
+ }
+]
+
+# queue to 1-65535
+[
+ {
+ "queue": {
+ "num": {
+ "range": [
+ 1,
+ 65535
+ ]
+ }
+ }
+ }
+]
+
diff --git a/tests/py/any/queue.t.payload b/tests/py/any/queue.t.payload
index 35e757ee..2f221930 100644
--- a/tests/py/any/queue.t.payload
+++ b/tests/py/any/queue.t.payload
@@ -46,3 +46,36 @@ ip
ip
[ numgen reg 1 = inc mod 65536 ]
[ queue sreg_qnum 1 bypass ]
+
+# queue flags bypass to oifname map { "eth0" : 0, "ppp0" : 2, "eth1" : 2 }
+__map%d test-ip4 b size 3
+__map%d test-ip4 0
+ element 30687465 00000000 00000000 00000000 : 00000000 0 [end] element 30707070 00000000 00000000 00000000 : 00000002 0 [end] element 31687465 00000000 00000000 00000000 : 00000002 0 [end]
+ip
+ [ meta load oifname => reg 1 ]
+ [ lookup reg 1 set __map%d dreg 1 ]
+ [ queue sreg_qnum 1 bypass ]
+
+# queue to 2
+ip
+ [ queue num 2 ]
+
+# queue to 65535
+ip
+ [ queue num 65535 ]
+
+# queue flags bypass to 65535
+ip
+ [ queue num 65535 bypass ]
+
+# queue flags bypass to 1-65535
+ip
+ [ queue num 1-65535 bypass ]
+
+# queue flags bypass,fanout to 1-65535
+ip
+ [ queue num 1-65535 bypass fanout ]
+
+# queue to 1-65535
+ip
+ [ queue num 1-65535 ]
diff --git a/tests/py/any/tcpopt.t b/tests/py/any/tcpopt.t
index f17a20b5..bcc64eac 100644
--- a/tests/py/any/tcpopt.t
+++ b/tests/py/any/tcpopt.t
@@ -31,6 +31,7 @@ tcp option timestamp length 1;ok
tcp option timestamp tsval 1;ok
tcp option timestamp tsecr 1;ok
tcp option 255 missing;ok
+tcp option 6 exists;ok
tcp option @255,8,8 255;ok
tcp option foobar;fail
diff --git a/tests/py/any/tcpopt.t.json b/tests/py/any/tcpopt.t.json
index 139e97d8..a45b4c8b 100644
--- a/tests/py/any/tcpopt.t.json
+++ b/tests/py/any/tcpopt.t.json
@@ -414,6 +414,23 @@
}
]
+# tcp option 6 exists
+[
+ {
+ "match": {
+ "left": {
+ "tcp option": {
+ "base": 6,
+ "len": 8,
+ "offset": 0
+ }
+ },
+ "op": "==",
+ "right": true
+ }
+ }
+]
+
# tcp option 255 missing
[
{
diff --git a/tests/py/any/tcpopt.t.payload b/tests/py/any/tcpopt.t.payload
index 1005df32..51f3a752 100644
--- a/tests/py/any/tcpopt.t.payload
+++ b/tests/py/any/tcpopt.t.payload
@@ -133,6 +133,11 @@ inet
[ exthdr load tcpopt 1b @ 255 + 0 present => reg 1 ]
[ cmp eq reg 1 0x00000000 ]
+# tcp option 6 exists
+inet
+ [ exthdr load tcpopt 1b @ 6 + 0 present => reg 1 ]
+ [ cmp eq reg 1 0x00000001 ]
+
# tcp option @255,8,8 255
inet
[ exthdr load tcpopt 1b @ 255 + 1 => reg 1 ]
diff --git a/tests/py/bridge/meta.t b/tests/py/bridge/meta.t
index eda7082f..d77ebd89 100644
--- a/tests/py/bridge/meta.t
+++ b/tests/py/bridge/meta.t
@@ -6,3 +6,6 @@ meta obrname "br0";ok
meta ibrname "br0";ok
meta ibrvproto vlan;ok;meta ibrvproto 8021q
meta ibrpvid 100;ok
+
+meta protocol ip udp dport 67;ok
+meta protocol ip6 udp dport 67;ok
diff --git a/tests/py/bridge/meta.t.json b/tests/py/bridge/meta.t.json
index 3122774e..d7dc9d7b 100644
--- a/tests/py/bridge/meta.t.json
+++ b/tests/py/bridge/meta.t.json
@@ -49,3 +49,57 @@
}
}
]
+
+# meta protocol ip udp dport 67
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "op": "==",
+ "right": "ip"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 67
+ }
+ }
+]
+
+# meta protocol ip6 udp dport 67
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "op": "==",
+ "right": "ip6"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 67
+ }
+ }
+]
diff --git a/tests/py/bridge/meta.t.payload b/tests/py/bridge/meta.t.payload
index aa8c994b..0a39842a 100644
--- a/tests/py/bridge/meta.t.payload
+++ b/tests/py/bridge/meta.t.payload
@@ -17,3 +17,21 @@ bridge test-bridge input
bridge test-bridge input
[ meta load bri_iifpvid => reg 1 ]
[ cmp eq reg 1 0x00000064 ]
+
+# meta protocol ip udp dport 67
+bridge test-bridge input
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x00004300 ]
+
+# meta protocol ip6 udp dport 67
+bridge test-bridge input
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x00004300 ]
diff --git a/tests/py/bridge/reject.t b/tests/py/bridge/reject.t
index b242eef4..336b51bb 100644
--- a/tests/py/bridge/reject.t
+++ b/tests/py/bridge/reject.t
@@ -3,40 +3,40 @@
*bridge;test-bridge;input
# The output is specific for bridge family
-reject with icmp type host-unreachable;ok
-reject with icmp type net-unreachable;ok
-reject with icmp type prot-unreachable;ok
-reject with icmp type port-unreachable;ok
-reject with icmp type net-prohibited;ok
-reject with icmp type host-prohibited;ok
-reject with icmp type admin-prohibited;ok
-
-reject with icmpv6 type no-route;ok
-reject with icmpv6 type admin-prohibited;ok
-reject with icmpv6 type addr-unreachable;ok
-reject with icmpv6 type port-unreachable;ok
+reject with icmp host-unreachable;ok
+reject with icmp net-unreachable;ok
+reject with icmp prot-unreachable;ok
+reject with icmp port-unreachable;ok
+reject with icmp net-prohibited;ok
+reject with icmp host-prohibited;ok
+reject with icmp admin-prohibited;ok
+
+reject with icmpv6 no-route;ok
+reject with icmpv6 admin-prohibited;ok
+reject with icmpv6 addr-unreachable;ok
+reject with icmpv6 port-unreachable;ok
mark 12345 ip protocol tcp reject with tcp reset;ok;meta mark 0x00003039 ip protocol 6 reject with tcp reset
reject;ok
-ether type ip reject;ok;reject with icmp type port-unreachable
-ether type ip6 reject;ok;reject with icmpv6 type port-unreachable
+ether type ip reject;ok;reject with icmp port-unreachable
+ether type ip6 reject;ok;reject with icmpv6 port-unreachable
-reject with icmpx type host-unreachable;ok
-reject with icmpx type no-route;ok
-reject with icmpx type admin-prohibited;ok
-reject with icmpx type port-unreachable;ok;reject
+reject with icmpx host-unreachable;ok
+reject with icmpx no-route;ok
+reject with icmpx admin-prohibited;ok
+reject with icmpx port-unreachable;ok;reject
-ether type ipv6 reject with icmp type host-unreachable;fail
-ether type ip6 reject with icmp type host-unreachable;fail
-ether type ip reject with icmpv6 type no-route;fail
+ether type ipv6 reject with icmp host-unreachable;fail
+ether type ip6 reject with icmp host-unreachable;fail
+ether type ip reject with icmpv6 no-route;fail
ether type vlan reject;ok;ether type 8021q reject
ether type arp reject;fail
ether type vlan reject with tcp reset;ok;meta l4proto 6 ether type 8021q reject with tcp reset
ether type arp reject with tcp reset;fail
ip protocol udp reject with tcp reset;fail
-ether type ip reject with icmpx type admin-prohibited;ok
-ether type ip6 reject with icmpx type admin-prohibited;ok
-ether type 8021q reject with icmpx type admin-prohibited;ok
-ether type arp reject with icmpx type admin-prohibited;fail
+ether type ip reject with icmpx admin-prohibited;ok
+ether type ip6 reject with icmpx admin-prohibited;ok
+ether type 8021q reject with icmpx admin-prohibited;ok
+ether type arp reject with icmpx admin-prohibited;fail
diff --git a/tests/py/bridge/reject.t.json b/tests/py/bridge/reject.t.json
index fe21734d..9f9e6c1e 100644
--- a/tests/py/bridge/reject.t.json
+++ b/tests/py/bridge/reject.t.json
@@ -1,4 +1,4 @@
-# reject with icmp type host-unreachable
+# reject with icmp host-unreachable
[
{
"reject": {
@@ -8,7 +8,7 @@
}
]
-# reject with icmp type net-unreachable
+# reject with icmp net-unreachable
[
{
"reject": {
@@ -18,7 +18,7 @@
}
]
-# reject with icmp type prot-unreachable
+# reject with icmp prot-unreachable
[
{
"reject": {
@@ -28,7 +28,7 @@
}
]
-# reject with icmp type port-unreachable
+# reject with icmp port-unreachable
[
{
"reject": {
@@ -38,7 +38,7 @@
}
]
-# reject with icmp type net-prohibited
+# reject with icmp net-prohibited
[
{
"reject": {
@@ -48,7 +48,7 @@
}
]
-# reject with icmp type host-prohibited
+# reject with icmp host-prohibited
[
{
"reject": {
@@ -58,7 +58,7 @@
}
]
-# reject with icmp type admin-prohibited
+# reject with icmp admin-prohibited
[
{
"reject": {
@@ -68,7 +68,7 @@
}
]
-# reject with icmpv6 type no-route
+# reject with icmpv6 no-route
[
{
"reject": {
@@ -78,7 +78,7 @@
}
]
-# reject with icmpv6 type admin-prohibited
+# reject with icmpv6 admin-prohibited
[
{
"reject": {
@@ -88,7 +88,7 @@
}
]
-# reject with icmpv6 type addr-unreachable
+# reject with icmpv6 addr-unreachable
[
{
"reject": {
@@ -98,7 +98,7 @@
}
]
-# reject with icmpv6 type port-unreachable
+# reject with icmpv6 port-unreachable
[
{
"reject": {
@@ -183,7 +183,7 @@
}
]
-# reject with icmpx type host-unreachable
+# reject with icmpx host-unreachable
[
{
"reject": {
@@ -193,7 +193,7 @@
}
]
-# reject with icmpx type no-route
+# reject with icmpx no-route
[
{
"reject": {
@@ -203,7 +203,7 @@
}
]
-# reject with icmpx type admin-prohibited
+# reject with icmpx admin-prohibited
[
{
"reject": {
@@ -213,7 +213,7 @@
}
]
-# reject with icmpx type port-unreachable
+# reject with icmpx port-unreachable
[
{
"reject": {
@@ -223,7 +223,7 @@
}
]
-# ether type ip reject with icmpx type admin-prohibited
+# ether type ip reject with icmpx admin-prohibited
[
{
"match": {
@@ -245,7 +245,7 @@
}
]
-# ether type ip6 reject with icmpx type admin-prohibited
+# ether type ip6 reject with icmpx admin-prohibited
[
{
"match": {
@@ -318,7 +318,7 @@
}
]
-# ether type 8021q reject with icmpx type admin-prohibited
+# ether type 8021q reject with icmpx admin-prohibited
[
{
"match": {
diff --git a/tests/py/bridge/reject.t.payload b/tests/py/bridge/reject.t.payload
index 22569877..bad9adc0 100644
--- a/tests/py/bridge/reject.t.payload
+++ b/tests/py/bridge/reject.t.payload
@@ -1,64 +1,64 @@
-# reject with icmp type host-unreachable
+# reject with icmp host-unreachable
bridge test-bridge input
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 0 code 1 ]
-# reject with icmp type net-unreachable
+# reject with icmp net-unreachable
bridge test-bridge input
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 0 code 0 ]
-# reject with icmp type prot-unreachable
+# reject with icmp prot-unreachable
bridge test-bridge input
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 0 code 2 ]
-# reject with icmp type port-unreachable
+# reject with icmp port-unreachable
bridge test-bridge input
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 0 code 3 ]
-# reject with icmp type net-prohibited
+# reject with icmp net-prohibited
bridge test-bridge input
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 0 code 9 ]
-# reject with icmp type host-prohibited
+# reject with icmp host-prohibited
bridge test-bridge input
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 0 code 10 ]
-# reject with icmp type admin-prohibited
+# reject with icmp admin-prohibited
bridge test-bridge input
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 0 code 13 ]
-# reject with icmpv6 type no-route
+# reject with icmpv6 no-route
bridge test-bridge input
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x0000dd86 ]
[ reject type 0 code 0 ]
-# reject with icmpv6 type admin-prohibited
+# reject with icmpv6 admin-prohibited
bridge test-bridge input
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x0000dd86 ]
[ reject type 0 code 1 ]
-# reject with icmpv6 type addr-unreachable
+# reject with icmpv6 addr-unreachable
bridge test-bridge input
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x0000dd86 ]
[ reject type 0 code 3 ]
-# reject with icmpv6 type port-unreachable
+# reject with icmpv6 port-unreachable
bridge test-bridge input
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x0000dd86 ]
@@ -90,29 +90,29 @@ bridge test-bridge input
[ cmp eq reg 1 0x0000dd86 ]
[ reject type 0 code 4 ]
-# reject with icmpx type host-unreachable
+# reject with icmpx host-unreachable
bridge test-bridge input
[ reject type 2 code 2 ]
-# reject with icmpx type no-route
+# reject with icmpx no-route
bridge test-bridge input
[ reject type 2 code 0 ]
-# reject with icmpx type admin-prohibited
+# reject with icmpx admin-prohibited
bridge test-bridge input
[ reject type 2 code 3 ]
-# reject with icmpx type port-unreachable
+# reject with icmpx port-unreachable
bridge test-bridge input
[ reject type 2 code 1 ]
-# ether type ip reject with icmpx type admin-prohibited
+# ether type ip reject with icmpx admin-prohibited
bridge test-bridge input
[ payload load 2b @ link header + 12 => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 2 code 3 ]
-# ether type ip6 reject with icmpx type admin-prohibited
+# ether type ip6 reject with icmpx admin-prohibited
bridge test-bridge input
[ payload load 2b @ link header + 12 => reg 1 ]
[ cmp eq reg 1 0x0000dd86 ]
@@ -132,7 +132,7 @@ bridge
[ cmp eq reg 1 0x00000081 ]
[ reject type 1 code 0 ]
-# ether type 8021q reject with icmpx type admin-prohibited
+# ether type 8021q reject with icmpx admin-prohibited
bridge
[ payload load 2b @ link header + 12 => reg 1 ]
[ cmp eq reg 1 0x00000081 ]
diff --git a/tests/py/inet/dnat.t b/tests/py/inet/dnat.t
index b460af39..e4e169f2 100644
--- a/tests/py/inet/dnat.t
+++ b/tests/py/inet/dnat.t
@@ -6,6 +6,7 @@ iifname "foo" tcp dport 80 redirect to :8080;ok
iifname "eth0" tcp dport 443 dnat ip to 192.168.3.2;ok
iifname "eth0" tcp dport 443 dnat ip6 to [dead::beef]:4443;ok
+meta l4proto tcp dnat to :80;ok;meta l4proto 6 dnat to :80
dnat ip to ct mark map { 0x00000014 : 1.2.3.4};ok
dnat ip to ct mark . ip daddr map { 0x00000014 . 1.1.1.1 : 1.2.3.4};ok
diff --git a/tests/py/inet/dnat.t.json b/tests/py/inet/dnat.t.json
index 1b8aba62..c341a045 100644
--- a/tests/py/inet/dnat.t.json
+++ b/tests/py/inet/dnat.t.json
@@ -219,3 +219,23 @@
}
]
+# meta l4proto tcp dnat to :80
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "op": "==",
+ "right": 6
+ }
+ },
+ {
+ "dnat": {
+ "port": 80
+ }
+ }
+]
+
diff --git a/tests/py/inet/dnat.t.payload b/tests/py/inet/dnat.t.payload
index ca3ff631..ce1601ab 100644
--- a/tests/py/inet/dnat.t.payload
+++ b/tests/py/inet/dnat.t.payload
@@ -77,3 +77,10 @@ inet
[ immediate reg 2 0x00005000 ]
[ nat dnat ip addr_min reg 1 proto_min reg 2 flags 0x2 ]
+# meta l4proto tcp dnat to :80
+inet
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ immediate reg 1 0x00005000 ]
+ [ nat dnat inet proto_min reg 1 flags 0x2 ]
+
diff --git a/tests/py/inet/meta.t b/tests/py/inet/meta.t
index 3638898b..423cc5f3 100644
--- a/tests/py/inet/meta.t
+++ b/tests/py/inet/meta.t
@@ -12,6 +12,10 @@ meta nfproto ipv4 tcp dport 22;ok
meta nfproto ipv4 ip saddr 1.2.3.4;ok;ip saddr 1.2.3.4
meta nfproto ipv6 meta l4proto tcp;ok;meta nfproto ipv6 meta l4proto 6
meta nfproto ipv4 counter ip saddr 1.2.3.4;ok
+
+meta protocol ip udp dport 67;ok
+meta protocol ip6 udp dport 67;ok
+
meta ipsec exists;ok
meta secpath missing;ok;meta ipsec missing
meta ibrname "br0";fail
diff --git a/tests/py/inet/meta.t.json b/tests/py/inet/meta.t.json
index 5c0e7d2e..723a36f7 100644
--- a/tests/py/inet/meta.t.json
+++ b/tests/py/inet/meta.t.json
@@ -235,3 +235,57 @@
}
}
]
+
+# meta protocol ip udp dport 67
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "op": "==",
+ "right": "ip"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 67
+ }
+ }
+]
+
+# meta protocol ip6 udp dport 67
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "op": "==",
+ "right": "ip6"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 67
+ }
+ }
+]
diff --git a/tests/py/inet/meta.t.payload b/tests/py/inet/meta.t.payload
index 6ccf6d24..fd054549 100644
--- a/tests/py/inet/meta.t.payload
+++ b/tests/py/inet/meta.t.payload
@@ -79,3 +79,21 @@ inet test-inet input
[ ct load mark => reg 1 ]
[ bitwise reg 1 = ( reg 1 >> 0x00000008 ) ]
[ meta set mark with reg 1 ]
+
+# meta protocol ip udp dport 67
+inet test-inet input
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x00004300 ]
+
+# meta protocol ip6 udp dport 67
+inet test-inet input
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x00004300 ]
diff --git a/tests/py/inet/reject.t b/tests/py/inet/reject.t
index a9ecd2ea..1c8aeebe 100644
--- a/tests/py/inet/reject.t
+++ b/tests/py/inet/reject.t
@@ -2,37 +2,38 @@
*inet;test-inet;input
-reject with icmp type host-unreachable;ok
-reject with icmp type net-unreachable;ok
-reject with icmp type prot-unreachable;ok
-reject with icmp type port-unreachable;ok
-reject with icmp type net-prohibited;ok
-reject with icmp type host-prohibited;ok
-reject with icmp type admin-prohibited;ok
-
-reject with icmpv6 type no-route;ok
-reject with icmpv6 type admin-prohibited;ok
-reject with icmpv6 type addr-unreachable;ok
-reject with icmpv6 type port-unreachable;ok
+reject with icmp host-unreachable;ok
+reject with icmp net-unreachable;ok
+reject with icmp prot-unreachable;ok
+reject with icmp port-unreachable;ok
+reject with icmp net-prohibited;ok
+reject with icmp host-prohibited;ok
+reject with icmp admin-prohibited;ok
+
+reject with icmpv6 no-route;ok
+reject with icmpv6 admin-prohibited;ok
+reject with icmpv6 addr-unreachable;ok
+reject with icmpv6 port-unreachable;ok
mark 12345 reject with tcp reset;ok;meta l4proto 6 meta mark 0x00003039 reject with tcp reset
reject;ok
-meta nfproto ipv4 reject;ok;reject with icmp type port-unreachable
-meta nfproto ipv6 reject;ok;reject with icmpv6 type port-unreachable
+meta nfproto ipv4 reject;ok;reject with icmp port-unreachable
+meta nfproto ipv6 reject;ok;reject with icmpv6 port-unreachable
-reject with icmpx type host-unreachable;ok
-reject with icmpx type no-route;ok
-reject with icmpx type admin-prohibited;ok
-reject with icmpx type port-unreachable;ok;reject
+reject with icmpx host-unreachable;ok
+reject with icmpx no-route;ok
+reject with icmpx admin-prohibited;ok
+reject with icmpx port-unreachable;ok;reject
+reject with icmpx 3;ok;reject with icmpx admin-prohibited
-meta nfproto ipv4 reject with icmp type host-unreachable;ok;reject with icmp type host-unreachable
-meta nfproto ipv6 reject with icmpv6 type no-route;ok;reject with icmpv6 type no-route
+meta nfproto ipv4 reject with icmp host-unreachable;ok;reject with icmp host-unreachable
+meta nfproto ipv6 reject with icmpv6 no-route;ok;reject with icmpv6 no-route
-meta nfproto ipv6 reject with icmp type host-unreachable;fail
-meta nfproto ipv4 ip protocol icmp reject with icmpv6 type no-route;fail
-meta nfproto ipv6 ip protocol icmp reject with icmp type host-unreachable;fail
+meta nfproto ipv6 reject with icmp host-unreachable;fail
+meta nfproto ipv4 ip protocol icmp reject with icmpv6 no-route;fail
+meta nfproto ipv6 ip protocol icmp reject with icmp host-unreachable;fail
meta l4proto udp reject with tcp reset;fail
-meta nfproto ipv4 reject with icmpx type admin-prohibited;ok
-meta nfproto ipv6 reject with icmpx type admin-prohibited;ok
+meta nfproto ipv4 reject with icmpx admin-prohibited;ok
+meta nfproto ipv6 reject with icmpx admin-prohibited;ok
diff --git a/tests/py/inet/reject.t.json b/tests/py/inet/reject.t.json
index bfa94f84..76cd1bf5 100644
--- a/tests/py/inet/reject.t.json
+++ b/tests/py/inet/reject.t.json
@@ -1,4 +1,4 @@
-# reject with icmp type host-unreachable
+# reject with icmp host-unreachable
[
{
"reject": {
@@ -8,7 +8,7 @@
}
]
-# reject with icmp type net-unreachable
+# reject with icmp net-unreachable
[
{
"reject": {
@@ -18,7 +18,7 @@
}
]
-# reject with icmp type prot-unreachable
+# reject with icmp prot-unreachable
[
{
"reject": {
@@ -28,7 +28,7 @@
}
]
-# reject with icmp type port-unreachable
+# reject with icmp port-unreachable
[
{
"reject": {
@@ -38,7 +38,7 @@
}
]
-# reject with icmp type net-prohibited
+# reject with icmp net-prohibited
[
{
"reject": {
@@ -48,7 +48,7 @@
}
]
-# reject with icmp type host-prohibited
+# reject with icmp host-prohibited
[
{
"reject": {
@@ -58,7 +58,7 @@
}
]
-# reject with icmp type admin-prohibited
+# reject with icmp admin-prohibited
[
{
"reject": {
@@ -68,7 +68,7 @@
}
]
-# reject with icmpv6 type no-route
+# reject with icmpv6 no-route
[
{
"reject": {
@@ -78,7 +78,7 @@
}
]
-# reject with icmpv6 type admin-prohibited
+# reject with icmpv6 admin-prohibited
[
{
"reject": {
@@ -88,7 +88,7 @@
}
]
-# reject with icmpv6 type addr-unreachable
+# reject with icmpv6 addr-unreachable
[
{
"reject": {
@@ -98,7 +98,7 @@
}
]
-# reject with icmpv6 type port-unreachable
+# reject with icmpv6 port-unreachable
[
{
"reject": {
@@ -165,7 +165,7 @@
}
]
-# reject with icmpx type host-unreachable
+# reject with icmpx host-unreachable
[
{
"reject": {
@@ -175,7 +175,7 @@
}
]
-# reject with icmpx type no-route
+# reject with icmpx no-route
[
{
"reject": {
@@ -185,7 +185,7 @@
}
]
-# reject with icmpx type admin-prohibited
+# reject with icmpx admin-prohibited
[
{
"reject": {
@@ -195,7 +195,7 @@
}
]
-# reject with icmpx type port-unreachable
+# reject with icmpx port-unreachable
[
{
"reject": {
@@ -205,7 +205,17 @@
}
]
-# meta nfproto ipv4 reject with icmp type host-unreachable
+# reject with icmpx 3
+[
+ {
+ "reject": {
+ "expr": "admin-prohibited",
+ "type": "icmpx"
+ }
+ }
+]
+
+# meta nfproto ipv4 reject with icmp host-unreachable
[
{
"match": {
@@ -224,7 +234,7 @@
}
]
-# meta nfproto ipv6 reject with icmpv6 type no-route
+# meta nfproto ipv6 reject with icmpv6 no-route
[
{
"match": {
@@ -243,7 +253,7 @@
}
]
-# meta nfproto ipv4 reject with icmpx type admin-prohibited
+# meta nfproto ipv4 reject with icmpx admin-prohibited
[
{
"match": {
@@ -264,7 +274,7 @@
}
]
-# meta nfproto ipv6 reject with icmpx type admin-prohibited
+# meta nfproto ipv6 reject with icmpx admin-prohibited
[
{
"match": {
diff --git a/tests/py/inet/reject.t.json.output b/tests/py/inet/reject.t.json.output
index 043617a7..496ce557 100644
--- a/tests/py/inet/reject.t.json.output
+++ b/tests/py/inet/reject.t.json.output
@@ -55,7 +55,7 @@
}
]
-# meta nfproto ipv4 reject with icmp type host-unreachable
+# meta nfproto ipv4 reject with icmp host-unreachable
[
{
"reject": {
@@ -65,7 +65,7 @@
}
]
-# meta nfproto ipv6 reject with icmpv6 type no-route
+# meta nfproto ipv6 reject with icmpv6 no-route
[
{
"reject": {
diff --git a/tests/py/inet/reject.t.payload.inet b/tests/py/inet/reject.t.payload.inet
index 3f220282..62078d91 100644
--- a/tests/py/inet/reject.t.payload.inet
+++ b/tests/py/inet/reject.t.payload.inet
@@ -1,64 +1,64 @@
-# reject with icmp type host-unreachable
+# reject with icmp host-unreachable
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x00000002 ]
[ reject type 0 code 1 ]
-# reject with icmp type net-unreachable
+# reject with icmp net-unreachable
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x00000002 ]
[ reject type 0 code 0 ]
-# reject with icmp type prot-unreachable
+# reject with icmp prot-unreachable
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x00000002 ]
[ reject type 0 code 2 ]
-# reject with icmp type port-unreachable
+# reject with icmp port-unreachable
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x00000002 ]
[ reject type 0 code 3 ]
-# reject with icmp type net-prohibited
+# reject with icmp net-prohibited
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x00000002 ]
[ reject type 0 code 9 ]
-# reject with icmp type host-prohibited
+# reject with icmp host-prohibited
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x00000002 ]
[ reject type 0 code 10 ]
-# reject with icmp type admin-prohibited
+# reject with icmp admin-prohibited
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x00000002 ]
[ reject type 0 code 13 ]
-# reject with icmpv6 type no-route
+# reject with icmpv6 no-route
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x0000000a ]
[ reject type 0 code 0 ]
-# reject with icmpv6 type admin-prohibited
+# reject with icmpv6 admin-prohibited
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x0000000a ]
[ reject type 0 code 1 ]
-# reject with icmpv6 type addr-unreachable
+# reject with icmpv6 addr-unreachable
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x0000000a ]
[ reject type 0 code 3 ]
-# reject with icmpv6 type port-unreachable
+# reject with icmpv6 port-unreachable
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x0000000a ]
@@ -88,41 +88,45 @@ inet test-inet input
[ cmp eq reg 1 0x0000000a ]
[ reject type 0 code 4 ]
-# reject with icmpx type host-unreachable
+# reject with icmpx host-unreachable
inet test-inet input
[ reject type 2 code 2 ]
-# reject with icmpx type no-route
+# reject with icmpx no-route
inet test-inet input
[ reject type 2 code 0 ]
-# reject with icmpx type admin-prohibited
+# reject with icmpx admin-prohibited
inet test-inet input
[ reject type 2 code 3 ]
-# reject with icmpx type port-unreachable
+# reject with icmpx port-unreachable
inet test-inet input
[ reject type 2 code 1 ]
-# meta nfproto ipv4 reject with icmp type host-unreachable
+# reject with icmpx 3
+inet test-inet input
+ [ reject type 2 code 3 ]
+
+# meta nfproto ipv4 reject with icmp host-unreachable
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x00000002 ]
[ reject type 0 code 1 ]
-# meta nfproto ipv6 reject with icmpv6 type no-route
+# meta nfproto ipv6 reject with icmpv6 no-route
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x0000000a ]
[ reject type 0 code 0 ]
-# meta nfproto ipv4 reject with icmpx type admin-prohibited
+# meta nfproto ipv4 reject with icmpx admin-prohibited
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x00000002 ]
[ reject type 2 code 3 ]
-# meta nfproto ipv6 reject with icmpx type admin-prohibited
+# meta nfproto ipv6 reject with icmpx admin-prohibited
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x0000000a ]
diff --git a/tests/py/inet/tcp.t b/tests/py/inet/tcp.t
index 532da277..aa07c3ba 100644
--- a/tests/py/inet/tcp.t
+++ b/tests/py/inet/tcp.t
@@ -68,10 +68,25 @@ tcp flags cwr;ok
tcp flags != cwr;ok
tcp flags == syn;ok
tcp flags fin,syn / fin,syn;ok
+tcp flags != syn / fin,syn;ok
+tcp flags & syn != 0;ok;tcp flags syn
+tcp flags & syn == 0;ok;tcp flags ! syn
+tcp flags & (syn | ack) != 0;ok;tcp flags syn,ack
+tcp flags & (syn | ack) == 0;ok;tcp flags ! syn,ack
+# it should be possible to transform this to: tcp flags syn
+tcp flags & syn == syn;ok
+tcp flags & syn != syn;ok
+tcp flags & (fin | syn | rst | ack) syn;ok;tcp flags syn / fin,syn,rst,ack
+tcp flags & (fin | syn | rst | ack) == syn;ok;tcp flags syn / fin,syn,rst,ack
+tcp flags & (fin | syn | rst | ack) != syn;ok;tcp flags != syn / fin,syn,rst,ack
+tcp flags & (fin | syn | rst | ack) == (syn | ack);ok;tcp flags syn,ack / fin,syn,rst,ack
+tcp flags & (fin | syn | rst | ack) != (syn | ack);ok;tcp flags != syn,ack / fin,syn,rst,ack
+tcp flags & (syn | ack) == (syn | ack);ok;tcp flags syn,ack / syn,ack
tcp flags & (fin | syn | rst | psh | ack | urg | ecn | cwr) == fin | syn | rst | psh | ack | urg | ecn | cwr;ok;tcp flags == 0xff
tcp flags { syn, syn | ack };ok
tcp flags & (fin | syn | rst | psh | ack | urg) == { fin, ack, psh | ack, fin | psh | ack };ok
tcp flags ! fin,rst;ok
+tcp flags & (fin | syn | rst | ack) ! syn;fail
tcp window 22222;ok
tcp window 22;ok
diff --git a/tests/py/inet/tcp.t.json b/tests/py/inet/tcp.t.json
index 8c2a376b..8439c2b5 100644
--- a/tests/py/inet/tcp.t.json
+++ b/tests/py/inet/tcp.t.json
@@ -1496,3 +1496,304 @@
}
}
]
+
+# tcp flags != syn / fin,syn
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ [
+ "fin",
+ "syn"
+ ]
+ ]
+ },
+ "op": "!=",
+ "right": "syn"
+ }
+ }
+]
+
+# tcp flags & syn == 0
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ "op": "!",
+ "right": "syn"
+ }
+ }
+]
+
+# tcp flags & syn != 0
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ "op": "in",
+ "right": "syn"
+ }
+ }
+]
+
+# tcp flags & (syn | ack) != 0
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ "op": "in",
+ "right": [
+ "syn",
+ "ack"
+ ]
+ }
+ }
+]
+
+# tcp flags & (syn | ack) == 0
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ "op": "!",
+ "right": [
+ "syn",
+ "ack"
+ ]
+ }
+ }
+]
+
+# tcp flags & syn == syn
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ "syn"
+ ]
+ },
+ "op": "==",
+ "right": "syn"
+ }
+ }
+]
+
+# tcp flags & syn != syn
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ "syn"
+ ]
+ },
+ "op": "!=",
+ "right": "syn"
+ }
+ }
+]
+
+# tcp flags & (fin | syn | rst | ack) syn
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ [
+ "fin",
+ "syn",
+ "rst",
+ "ack"
+ ]
+ ]
+ },
+ "op": "==",
+ "right": "syn"
+ }
+ }
+]
+
+# tcp flags & (fin | syn | rst | ack) == syn
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ [
+ "fin",
+ "syn",
+ "rst",
+ "ack"
+ ]
+ ]
+ },
+ "op": "==",
+ "right": "syn"
+ }
+ }
+]
+
+
+# tcp flags & (fin | syn | rst | ack) != syn
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ [
+ "fin",
+ "syn",
+ "rst",
+ "ack"
+ ]
+ ]
+ },
+ "op": "!=",
+ "right": "syn"
+ }
+ }
+]
+
+# tcp flags & (fin | syn | rst | ack) == (syn | ack)
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ [
+ "fin",
+ "syn",
+ "rst",
+ "ack"
+ ]
+ ]
+ },
+ "op": "==",
+ "right": [
+ "syn",
+ "ack"
+ ]
+ }
+ }
+]
+
+# tcp flags & (fin | syn | rst | ack) != (syn | ack)
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ [
+ "fin",
+ "syn",
+ "rst",
+ "ack"
+ ]
+ ]
+ },
+ "op": "!=",
+ "right": [
+ "syn",
+ "ack"
+ ]
+ }
+ }
+]
+
+# tcp flags & (syn | ack) == (syn | ack)
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ [
+ "syn",
+ "ack"
+ ]
+ ]
+ },
+ "op": "==",
+ "right": [
+ "syn",
+ "ack"
+ ]
+ }
+ }
+]
+
diff --git a/tests/py/inet/tcp.t.payload b/tests/py/inet/tcp.t.payload
index ee61b1a7..1cfe500b 100644
--- a/tests/py/inet/tcp.t.payload
+++ b/tests/py/inet/tcp.t.payload
@@ -362,6 +362,110 @@ inet test-inet input
[ bitwise reg 1 = ( reg 1 & 0x00000003 ) ^ 0x00000000 ]
[ cmp eq reg 1 0x00000003 ]
+# tcp flags != syn / fin,syn
+inet test-inet input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 1b @ transport header + 13 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000003 ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000002 ]
+
+# tcp flags & syn != 0
+inet test-inet input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 1b @ transport header + 13 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000002 ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000000 ]
+
+# tcp flags & syn == 0
+inet test-inet input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 1b @ transport header + 13 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000002 ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000000 ]
+
+# tcp flags & (syn | ack) != 0
+inet test-inet input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 1b @ transport header + 13 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000012 ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000000 ]
+
+# tcp flags & (syn | ack) == 0
+inet test-inet input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 1b @ transport header + 13 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000012 ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000000 ]
+
+# tcp flags & syn == syn
+inet test-inet input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 1b @ transport header + 13 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000002 ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000002 ]
+
+# tcp flags & syn != syn
+inet test-inet input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 1b @ transport header + 13 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000002 ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000002 ]
+
+# tcp flags & (fin | syn | rst | ack) syn
+inet test-inet input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 1b @ transport header + 13 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000017 ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000002 ]
+
+# tcp flags & (fin | syn | rst | ack) == syn
+inet test-inet input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 1b @ transport header + 13 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000017 ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000002 ]
+
+# tcp flags & (fin | syn | rst | ack) != syn
+inet test-inet input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 1b @ transport header + 13 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000017 ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000002 ]
+
+# tcp flags & (fin | syn | rst | ack) == (syn | ack)
+inet test-inet input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 1b @ transport header + 13 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000017 ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000012 ]
+
+# tcp flags & (fin | syn | rst | ack) != (syn | ack)
+inet test-inet input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 1b @ transport header + 13 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000017 ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000012 ]
+
+# tcp flags & (syn | ack) == (syn | ack)
+inet test-inet input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 1b @ transport header + 13 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000012 ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000012 ]
+
# tcp flags & (fin | syn | rst | psh | ack | urg | ecn | cwr) == fin | syn | rst | psh | ack | urg | ecn | cwr
inet test-inet input
[ meta load l4proto => reg 1 ]
diff --git a/tests/py/ip/dnat.t b/tests/py/ip/dnat.t
index 089017c8..c5ca4c40 100644
--- a/tests/py/ip/dnat.t
+++ b/tests/py/ip/dnat.t
@@ -8,6 +8,13 @@ iifname "eth0" tcp dport {80, 90, 23} dnat to 192.168.3.2;ok
iifname "eth0" tcp dport != {80, 90, 23} dnat to 192.168.3.2;ok
iifname "eth0" tcp dport != 23-34 dnat to 192.168.3.2;ok
iifname "eth0" tcp dport 81 dnat to 192.168.3.2:8080;ok
+iifname "eth0" tcp dport 81 dnat to 192.168.3.2:8080-8999;ok
+iifname "eth0" tcp dport 81 dnat to 192.168.3.2-192.168.3.4:8080;ok
+iifname "eth0" tcp dport 81 dnat to 192.168.3.2-192.168.3.4:8080-8999;ok
dnat to ct mark map { 0x00000014 : 1.2.3.4};ok
dnat to ct mark . ip daddr map { 0x00000014 . 1.1.1.1 : 1.2.3.4};ok
+
+dnat ip to ip saddr . tcp dport map { 192.168.1.2 . 80 : 10.141.10.0/24 . 8888 - 8999 };ok
+dnat ip to ip saddr . tcp dport map { 192.168.1.2 . 80 : 10.141.10.0/24 . 80 };ok
+dnat ip to ip saddr . tcp dport map { 192.168.1.2 . 80 : 10.141.10.2 . 8888 - 8999 };ok
diff --git a/tests/py/ip/dnat.t.payload.ip b/tests/py/ip/dnat.t.payload.ip
index dd18dae2..4872545a 100644
--- a/tests/py/ip/dnat.t.payload.ip
+++ b/tests/py/ip/dnat.t.payload.ip
@@ -91,3 +91,79 @@ ip test-ip4 output
[ lookup reg 1 set __map%d dreg 1 ]
[ nat dnat ip addr_min reg 1 ]
+# dnat ip to ip saddr . tcp dport map { 192.168.1.2 . 80 : 10.141.10.0/24 . 8888 - 8999 }
+__map%d test-ip4 b size 1
+__map%d test-ip4 0
+ element 0201a8c0 00005000 : 000a8d0a 0000b822 ff0a8d0a 00002723 0 [end]
+ip
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ payload load 2b @ transport header + 2 => reg 9 ]
+ [ lookup reg 1 set __map%d dreg 1 ]
+ [ nat dnat ip addr_min reg 1 addr_max reg 10 proto_min reg 9 proto_max reg 11 ]
+
+# dnat ip to ip saddr . tcp dport map { 192.168.1.2 . 80 : 10.141.10.0/24 . 80 }
+__map%d test-ip4 b size 1
+__map%d test-ip4 0
+ element 0201a8c0 00005000 : 000a8d0a 00005000 ff0a8d0a 00005000 0 [end]
+ip
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ payload load 2b @ transport header + 2 => reg 9 ]
+ [ lookup reg 1 set __map%d dreg 1 ]
+ [ nat dnat ip addr_min reg 1 addr_max reg 10 proto_min reg 9 proto_max reg 11 ]
+
+# dnat ip to ip saddr . tcp dport map { 192.168.1.2 . 80 : 10.141.10.2 . 8888 - 8999 }
+__map%d test-ip4 b size 1
+__map%d test-ip4 0
+ element 0201a8c0 00005000 : 020a8d0a 0000b822 020a8d0a 00002723 0 [end]
+ip
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ payload load 2b @ transport header + 2 => reg 9 ]
+ [ lookup reg 1 set __map%d dreg 1 ]
+ [ nat dnat ip addr_min reg 1 addr_max reg 10 proto_min reg 9 proto_max reg 11 ]
+
+# iifname "eth0" tcp dport 81 dnat to 192.168.3.2:8080-8999
+ip
+ [ meta load iifname => reg 1 ]
+ [ cmp eq reg 1 0x30687465 0x00000000 0x00000000 0x00000000 ]
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x00005100 ]
+ [ immediate reg 1 0x0203a8c0 ]
+ [ immediate reg 2 0x0000901f ]
+ [ immediate reg 3 0x00002723 ]
+ [ nat dnat ip addr_min reg 1 proto_min reg 2 proto_max reg 3 flags 0x2 ]
+
+# iifname "eth0" tcp dport 81 dnat to 192.168.3.2-192.168.3.4:8080
+ip
+ [ meta load iifname => reg 1 ]
+ [ cmp eq reg 1 0x30687465 0x00000000 0x00000000 0x00000000 ]
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x00005100 ]
+ [ immediate reg 1 0x0203a8c0 ]
+ [ immediate reg 2 0x0403a8c0 ]
+ [ immediate reg 3 0x0000901f ]
+ [ nat dnat ip addr_min reg 1 addr_max reg 2 proto_min reg 3 flags 0x2 ]
+
+# iifname "eth0" tcp dport 81 dnat to 192.168.3.2-192.168.3.4:8080-8999
+ip
+ [ meta load iifname => reg 1 ]
+ [ cmp eq reg 1 0x30687465 0x00000000 0x00000000 0x00000000 ]
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x00005100 ]
+ [ immediate reg 1 0x0203a8c0 ]
+ [ immediate reg 2 0x0403a8c0 ]
+ [ immediate reg 3 0x0000901f ]
+ [ immediate reg 4 0x00002723 ]
+ [ nat dnat ip addr_min reg 1 addr_max reg 2 proto_min reg 3 proto_max reg 4 flags 0x2 ]
+
diff --git a/tests/py/ip/meta.t b/tests/py/ip/meta.t
index f733d22d..5a05923a 100644
--- a/tests/py/ip/meta.t
+++ b/tests/py/ip/meta.t
@@ -8,6 +8,8 @@ meta l4proto ipv6-icmp icmpv6 type nd-router-advert;ok;icmpv6 type nd-router-adv
meta l4proto 58 icmpv6 type nd-router-advert;ok;icmpv6 type nd-router-advert
icmpv6 type nd-router-advert;ok
+meta protocol ip udp dport 67;ok;udp dport 67
+
meta ibrname "br0";fail
meta obrname "br0";fail
diff --git a/tests/py/ip/meta.t.json b/tests/py/ip/meta.t.json
index f83864f6..3df31ce3 100644
--- a/tests/py/ip/meta.t.json
+++ b/tests/py/ip/meta.t.json
@@ -140,3 +140,19 @@
"accept": null
}
]
+
+# meta protocol ip udp dport 67
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 67
+ }
+ }
+]
diff --git a/tests/py/ip/meta.t.payload b/tests/py/ip/meta.t.payload
index 7bc69a29..afde5cc1 100644
--- a/tests/py/ip/meta.t.payload
+++ b/tests/py/ip/meta.t.payload
@@ -44,3 +44,10 @@ ip6 test-ip4 input
[ meta load sdifname => reg 1 ]
[ cmp neq reg 1 0x31667276 0x00000000 0x00000000 0x00000000 ]
[ immediate reg 0 accept ]
+
+# meta protocol ip udp dport 67
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x00004300 ]
diff --git a/tests/py/ip/reject.t b/tests/py/ip/reject.t
index cc5561a0..ad009944 100644
--- a/tests/py/ip/reject.t
+++ b/tests/py/ip/reject.t
@@ -3,14 +3,15 @@
*ip;test-ip4;output
reject;ok
-reject with icmp type host-unreachable;ok
-reject with icmp type net-unreachable;ok
-reject with icmp type prot-unreachable;ok
-reject with icmp type port-unreachable;ok;reject
-reject with icmp type net-prohibited;ok
-reject with icmp type host-prohibited;ok
-reject with icmp type admin-prohibited;ok
+reject with icmp host-unreachable;ok
+reject with icmp net-unreachable;ok
+reject with icmp prot-unreachable;ok
+reject with icmp port-unreachable;ok;reject
+reject with icmp net-prohibited;ok
+reject with icmp host-prohibited;ok
+reject with icmp admin-prohibited;ok
+reject with icmp 3;ok;reject
mark 0x80000000 reject with tcp reset;ok;meta mark 0x80000000 reject with tcp reset
-reject with icmp type no-route;fail
-reject with icmpv6 type no-route;fail
+reject with icmp no-route;fail
+reject with icmpv6 no-route;fail
diff --git a/tests/py/ip/reject.t.json b/tests/py/ip/reject.t.json
index d120b9f1..3e1d28de 100644
--- a/tests/py/ip/reject.t.json
+++ b/tests/py/ip/reject.t.json
@@ -5,7 +5,7 @@
}
]
-# reject with icmp type host-unreachable
+# reject with icmp host-unreachable
[
{
"reject": {
@@ -15,7 +15,7 @@
}
]
-# reject with icmp type net-unreachable
+# reject with icmp net-unreachable
[
{
"reject": {
@@ -25,7 +25,7 @@
}
]
-# reject with icmp type prot-unreachable
+# reject with icmp prot-unreachable
[
{
"reject": {
@@ -35,7 +35,7 @@
}
]
-# reject with icmp type port-unreachable
+# reject with icmp port-unreachable
[
{
"reject": {
@@ -45,7 +45,7 @@
}
]
-# reject with icmp type net-prohibited
+# reject with icmp net-prohibited
[
{
"reject": {
@@ -55,7 +55,7 @@
}
]
-# reject with icmp type host-prohibited
+# reject with icmp host-prohibited
[
{
"reject": {
@@ -65,7 +65,7 @@
}
]
-# reject with icmp type admin-prohibited
+# reject with icmp admin-prohibited
[
{
"reject": {
@@ -75,6 +75,16 @@
}
]
+# reject with icmp 3
+[
+ {
+ "reject": {
+ "expr": "port-unreachable",
+ "type": "icmp"
+ }
+ }
+]
+
# mark 0x80000000 reject with tcp reset
[
{
diff --git a/tests/py/ip/reject.t.payload b/tests/py/ip/reject.t.payload
index 07e4cc8d..5829065a 100644
--- a/tests/py/ip/reject.t.payload
+++ b/tests/py/ip/reject.t.payload
@@ -2,34 +2,38 @@
ip test-ip4 output
[ reject type 0 code 3 ]
-# reject with icmp type host-unreachable
+# reject with icmp host-unreachable
ip test-ip4 output
[ reject type 0 code 1 ]
-# reject with icmp type net-unreachable
+# reject with icmp net-unreachable
ip test-ip4 output
[ reject type 0 code 0 ]
-# reject with icmp type prot-unreachable
+# reject with icmp prot-unreachable
ip test-ip4 output
[ reject type 0 code 2 ]
-# reject with icmp type port-unreachable
+# reject with icmp port-unreachable
ip test-ip4 output
[ reject type 0 code 3 ]
-# reject with icmp type net-prohibited
+# reject with icmp net-prohibited
ip test-ip4 output
[ reject type 0 code 9 ]
-# reject with icmp type host-prohibited
+# reject with icmp host-prohibited
ip test-ip4 output
[ reject type 0 code 10 ]
-# reject with icmp type admin-prohibited
+# reject with icmp admin-prohibited
ip test-ip4 output
[ reject type 0 code 13 ]
+# reject with icmp 3
+ip test-ip4 output
+ [ reject type 0 code 3 ]
+
# mark 0x80000000 reject with tcp reset
ip test-ip4 output
[ meta load l4proto => reg 1 ]
diff --git a/tests/py/ip/snat.t b/tests/py/ip/snat.t
index c6e8a8e6..a8ff8d1a 100644
--- a/tests/py/ip/snat.t
+++ b/tests/py/ip/snat.t
@@ -6,9 +6,12 @@ iifname "eth0" tcp dport 80-90 snat to 192.168.3.2;ok
iifname "eth0" tcp dport != 80-90 snat to 192.168.3.2;ok
iifname "eth0" tcp dport {80, 90, 23} snat to 192.168.3.2;ok
iifname "eth0" tcp dport != {80, 90, 23} snat to 192.168.3.2;ok
+iifname "eth0" tcp dport 80-90 snat to 192.168.3.0-192.168.3.255;ok;iifname "eth0" tcp dport 80-90 snat to 192.168.3.0/24
+iifname "eth0" tcp dport 80-90 snat to 192.168.3.15-192.168.3.240;ok
iifname "eth0" tcp dport != 23-34 snat to 192.168.3.2;ok
-snat ip addr . port to ip saddr map { 10.141.11.4 : 192.168.2.3 . 80 };ok
-snat ip interval to ip saddr map { 10.141.11.4 : 192.168.2.2-192.168.2.4 };ok
+snat ip to ip saddr map { 10.141.11.4 : 192.168.2.3 . 80 };ok
+snat ip to ip saddr map { 10.141.11.4 : 192.168.2.2-192.168.2.4 };ok
+snat ip to ip saddr map { 10.141.12.14 : 192.168.2.0/24 };ok
snat ip prefix to ip saddr map { 10.141.11.0/24 : 192.168.2.0/24 };ok
diff --git a/tests/py/ip/snat.t.json b/tests/py/ip/snat.t.json
index 62c6e61b..0813086c 100644
--- a/tests/py/ip/snat.t.json
+++ b/tests/py/ip/snat.t.json
@@ -166,7 +166,91 @@
}
]
-# snat ip addr . port to ip saddr map { 10.141.11.4 : 192.168.2.3 . 80 }
+# iifname "eth0" tcp dport 80-90 snat to 192.168.3.0-192.168.3.255
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "op": "==",
+ "right": "eth0"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "==",
+ "right": {
+ "range": [
+ 80,
+ 90
+ ]
+ }
+ }
+ },
+ {
+ "snat": {
+ "addr": {
+ "prefix": {
+ "addr": "192.168.3.0",
+ "len": 24
+ }
+ }
+ }
+ }
+]
+
+# iifname "eth0" tcp dport 80-90 snat to 192.168.3.15-192.168.3.240
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "op": "==",
+ "right": "eth0"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "==",
+ "right": {
+ "range": [
+ 80,
+ 90
+ ]
+ }
+ }
+ },
+ {
+ "snat": {
+ "addr": {
+ "range": [
+ "192.168.3.15",
+ "192.168.3.240"
+ ]
+ }
+ }
+ }
+]
+
+# snat ip to ip saddr map { 10.141.11.4 : 192.168.2.3 . 80 }
[
{
"snat": {
diff --git a/tests/py/ip/snat.t.payload b/tests/py/ip/snat.t.payload
index ef4c1ce9..64f47896 100644
--- a/tests/py/ip/snat.t.payload
+++ b/tests/py/ip/snat.t.payload
@@ -60,7 +60,33 @@ ip test-ip4 postrouting
[ immediate reg 1 0x0203a8c0 ]
[ nat snat ip addr_min reg 1 ]
-# snat ip addr . port to ip saddr map { 10.141.11.4 : 192.168.2.3 . 80 }
+# iifname "eth0" tcp dport 80-90 snat to 192.168.3.0-192.168.3.255
+ip
+ [ meta load iifname => reg 1 ]
+ [ cmp eq reg 1 0x30687465 0x00000000 0x00000000 0x00000000 ]
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp gte reg 1 0x00005000 ]
+ [ cmp lte reg 1 0x00005a00 ]
+ [ immediate reg 1 0x0003a8c0 ]
+ [ immediate reg 2 0xff03a8c0 ]
+ [ nat snat ip addr_min reg 1 addr_max reg 2 ]
+
+# iifname "eth0" tcp dport 80-90 snat to 192.168.3.15-192.168.3.240
+ip
+ [ meta load iifname => reg 1 ]
+ [ cmp eq reg 1 0x30687465 0x00000000 0x00000000 0x00000000 ]
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp gte reg 1 0x00005000 ]
+ [ cmp lte reg 1 0x00005a00 ]
+ [ immediate reg 1 0x0f03a8c0 ]
+ [ immediate reg 2 0xf003a8c0 ]
+ [ nat snat ip addr_min reg 1 addr_max reg 2 ]
+
+# snat ip to ip saddr map { 10.141.11.4 : 192.168.2.3 . 80 }
__map%d test-ip4 b size 1
__map%d test-ip4 0
element 040b8d0a : 0302a8c0 00005000 0 [end]
@@ -69,7 +95,7 @@ ip
[ lookup reg 1 set __map%d dreg 1 ]
[ nat snat ip addr_min reg 1 proto_min reg 9 ]
-# snat ip interval to ip saddr map { 10.141.11.4 : 192.168.2.2-192.168.2.4 }
+# snat ip to ip saddr map { 10.141.11.4 : 192.168.2.2-192.168.2.4 }
__map%d test-ip4 b size 1
__map%d test-ip4 0
element 040b8d0a : 0202a8c0 0402a8c0 0 [end]
@@ -87,3 +113,11 @@ ip
[ lookup reg 1 set __map%d dreg 1 ]
[ nat snat ip addr_min reg 1 addr_max reg 9 flags 0x40 ]
+# snat ip to ip saddr map { 10.141.12.14 : 192.168.2.0/24 }
+__map%d test-ip4 b size 1
+__map%d test-ip4 0
+ element 0e0c8d0a : 0002a8c0 ff02a8c0 0 [end]
+ip
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ lookup reg 1 set __map%d dreg 1 ]
+ [ nat snat ip addr_min reg 1 addr_max reg 9 ]
diff --git a/tests/py/ip6/meta.t b/tests/py/ip6/meta.t
index dce97f5b..471e1481 100644
--- a/tests/py/ip6/meta.t
+++ b/tests/py/ip6/meta.t
@@ -9,5 +9,8 @@ meta l4proto icmp icmp type echo-request;ok;icmp type echo-request
meta l4proto 1 icmp type echo-request;ok;icmp type echo-request
icmp type echo-request;ok
+meta protocol ip udp dport 67;ok
+meta protocol ip6 udp dport 67;ok;udp dport 67
+
meta sdif "lo" accept;ok
meta sdifname != "vrf1" accept;ok
diff --git a/tests/py/ip6/meta.t.json b/tests/py/ip6/meta.t.json
index e72350f3..351320d7 100644
--- a/tests/py/ip6/meta.t.json
+++ b/tests/py/ip6/meta.t.json
@@ -140,3 +140,57 @@
"accept": null
}
]
+
+# meta protocol ip udp dport 67
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "op": "==",
+ "right": "ip"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 67
+ }
+ }
+]
+
+# meta protocol ip6 udp dport 67
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "op": "==",
+ "right": "ip6"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 67
+ }
+ }
+]
diff --git a/tests/py/ip6/meta.t.payload b/tests/py/ip6/meta.t.payload
index be04816e..0e3db6ba 100644
--- a/tests/py/ip6/meta.t.payload
+++ b/tests/py/ip6/meta.t.payload
@@ -44,3 +44,19 @@ ip6 test-ip6 input
[ meta load sdifname => reg 1 ]
[ cmp neq reg 1 0x31667276 0x00000000 0x00000000 0x00000000 ]
[ immediate reg 0 accept ]
+
+# meta protocol ip udp dport 67
+ip6 test-ip6 input
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x00004300 ]
+
+# meta protocol ip6 udp dport 67
+ip6 test-ip6 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x00004300 ]
diff --git a/tests/py/ip6/reject.t b/tests/py/ip6/reject.t
index 7fa04eec..bfdd094e 100644
--- a/tests/py/ip6/reject.t
+++ b/tests/py/ip6/reject.t
@@ -3,13 +3,14 @@
*ip6;test-ip6;output
reject;ok
-reject with icmpv6 type no-route;ok
-reject with icmpv6 type admin-prohibited;ok
-reject with icmpv6 type addr-unreachable;ok
-reject with icmpv6 type port-unreachable;ok;reject
-reject with icmpv6 type policy-fail;ok
-reject with icmpv6 type reject-route;ok
+reject with icmpv6 no-route;ok
+reject with icmpv6 admin-prohibited;ok
+reject with icmpv6 addr-unreachable;ok
+reject with icmpv6 port-unreachable;ok;reject
+reject with icmpv6 policy-fail;ok
+reject with icmpv6 reject-route;ok
+reject with icmpv6 3;ok;reject with icmpv6 addr-unreachable
mark 0x80000000 reject with tcp reset;ok;meta mark 0x80000000 reject with tcp reset
-reject with icmpv6 type host-unreachable;fail
-reject with icmp type host-unreachable;fail
+reject with icmpv6 host-unreachable;fail
+reject with icmp host-unreachable;fail
diff --git a/tests/py/ip6/reject.t.json b/tests/py/ip6/reject.t.json
index ae57c333..312a7dab 100644
--- a/tests/py/ip6/reject.t.json
+++ b/tests/py/ip6/reject.t.json
@@ -5,7 +5,7 @@
}
]
-# reject with icmpv6 type no-route
+# reject with icmpv6 no-route
[
{
"reject": {
@@ -15,7 +15,7 @@
}
]
-# reject with icmpv6 type admin-prohibited
+# reject with icmpv6 admin-prohibited
[
{
"reject": {
@@ -25,7 +25,7 @@
}
]
-# reject with icmpv6 type addr-unreachable
+# reject with icmpv6 addr-unreachable
[
{
"reject": {
@@ -35,7 +35,7 @@
}
]
-# reject with icmpv6 type port-unreachable
+# reject with icmpv6 port-unreachable
[
{
"reject": {
@@ -45,7 +45,7 @@
}
]
-# reject with icmpv6 type policy-fail
+# reject with icmpv6 policy-fail
[
{
"reject": {
@@ -55,7 +55,7 @@
}
]
-# reject with icmpv6 type reject-route
+# reject with icmpv6 reject-route
[
{
"reject": {
@@ -65,6 +65,16 @@
}
]
+# reject with icmpv6 3
+[
+ {
+ "reject": {
+ "expr": "addr-unreachable",
+ "type": "icmpv6"
+ }
+ }
+]
+
# mark 0x80000000 reject with tcp reset
[
{
diff --git a/tests/py/ip6/reject.t.payload.ip6 b/tests/py/ip6/reject.t.payload.ip6
index dd4491ae..3d4321b0 100644
--- a/tests/py/ip6/reject.t.payload.ip6
+++ b/tests/py/ip6/reject.t.payload.ip6
@@ -2,30 +2,34 @@
ip6 test-ip6 output
[ reject type 0 code 4 ]
-# reject with icmpv6 type no-route
+# reject with icmpv6 no-route
ip6 test-ip6 output
[ reject type 0 code 0 ]
-# reject with icmpv6 type admin-prohibited
+# reject with icmpv6 admin-prohibited
ip6 test-ip6 output
[ reject type 0 code 1 ]
-# reject with icmpv6 type addr-unreachable
+# reject with icmpv6 addr-unreachable
ip6 test-ip6 output
[ reject type 0 code 3 ]
-# reject with icmpv6 type port-unreachable
+# reject with icmpv6 port-unreachable
ip6 test-ip6 output
[ reject type 0 code 4 ]
-# reject with icmpv6 type policy-fail
+# reject with icmpv6 policy-fail
ip6 test-ip6 output
[ reject type 0 code 5 ]
-# reject with icmpv6 type reject-route
+# reject with icmpv6 reject-route
ip6 test-ip6 output
[ reject type 0 code 6 ]
+# reject with icmpv6 3
+ip6 test-ip6 output
+ [ reject type 0 code 3 ]
+
# mark 0x80000000 reject with tcp reset
ip6 test-ip6 output
[ meta load l4proto => reg 1 ]
diff --git a/tests/py/netdev/reject.t b/tests/py/netdev/reject.t
index af109086..c66e649c 100644
--- a/tests/py/netdev/reject.t
+++ b/tests/py/netdev/reject.t
@@ -2,39 +2,39 @@
*netdev;test-netdev;ingress
-reject with icmp type host-unreachable;ok
-reject with icmp type net-unreachable;ok
-reject with icmp type prot-unreachable;ok
-reject with icmp type port-unreachable;ok
-reject with icmp type net-prohibited;ok
-reject with icmp type host-prohibited;ok
-reject with icmp type admin-prohibited;ok
-
-reject with icmpv6 type no-route;ok
-reject with icmpv6 type admin-prohibited;ok
-reject with icmpv6 type addr-unreachable;ok
-reject with icmpv6 type port-unreachable;ok
-reject with icmpv6 type policy-fail;ok
-reject with icmpv6 type reject-route;ok
+reject with icmp host-unreachable;ok
+reject with icmp net-unreachable;ok
+reject with icmp prot-unreachable;ok
+reject with icmp port-unreachable;ok
+reject with icmp net-prohibited;ok
+reject with icmp host-prohibited;ok
+reject with icmp admin-prohibited;ok
+
+reject with icmpv6 no-route;ok
+reject with icmpv6 admin-prohibited;ok
+reject with icmpv6 addr-unreachable;ok
+reject with icmpv6 port-unreachable;ok
+reject with icmpv6 policy-fail;ok
+reject with icmpv6 reject-route;ok
mark 12345 reject with tcp reset;ok;meta l4proto 6 meta mark 0x00003039 reject with tcp reset
reject;ok
-meta protocol ip reject;ok;reject with icmp type port-unreachable
-meta protocol ip6 reject;ok;reject with icmpv6 type port-unreachable
+meta protocol ip reject;ok;reject with icmp port-unreachable
+meta protocol ip6 reject;ok;reject with icmpv6 port-unreachable
-reject with icmpx type host-unreachable;ok
-reject with icmpx type no-route;ok
-reject with icmpx type admin-prohibited;ok
-reject with icmpx type port-unreachable;ok;reject
+reject with icmpx host-unreachable;ok
+reject with icmpx no-route;ok
+reject with icmpx admin-prohibited;ok
+reject with icmpx port-unreachable;ok;reject
-meta protocol ip reject with icmp type host-unreachable;ok;reject with icmp type host-unreachable
-meta protocol ip6 reject with icmpv6 type no-route;ok;reject with icmpv6 type no-route
+meta protocol ip reject with icmp host-unreachable;ok;reject with icmp host-unreachable
+meta protocol ip6 reject with icmpv6 no-route;ok;reject with icmpv6 no-route
-meta protocol ip6 reject with icmp type host-unreachable;fail
-meta protocol ip ip protocol icmp reject with icmpv6 type no-route;fail
-meta protocol ip6 ip protocol icmp reject with icmp type host-unreachable;fail
+meta protocol ip6 reject with icmp host-unreachable;fail
+meta protocol ip ip protocol icmp reject with icmpv6 no-route;fail
+meta protocol ip6 ip protocol icmp reject with icmp host-unreachable;fail
meta l4proto udp reject with tcp reset;fail
-meta protocol ip reject with icmpx type admin-prohibited;ok
-meta protocol ip6 reject with icmpx type admin-prohibited;ok
+meta protocol ip reject with icmpx admin-prohibited;ok
+meta protocol ip6 reject with icmpx admin-prohibited;ok
diff --git a/tests/py/netdev/reject.t.json b/tests/py/netdev/reject.t.json
index 616a2bc1..9968aaf8 100644
--- a/tests/py/netdev/reject.t.json
+++ b/tests/py/netdev/reject.t.json
@@ -1,4 +1,4 @@
-# reject with icmp type host-unreachable
+# reject with icmp host-unreachable
[
{
"reject": {
@@ -8,7 +8,7 @@
}
]
-# reject with icmp type net-unreachable
+# reject with icmp net-unreachable
[
{
"reject": {
@@ -18,7 +18,7 @@
}
]
-# reject with icmp type prot-unreachable
+# reject with icmp prot-unreachable
[
{
"reject": {
@@ -28,7 +28,7 @@
}
]
-# reject with icmp type port-unreachable
+# reject with icmp port-unreachable
[
{
"reject": {
@@ -38,7 +38,7 @@
}
]
-# reject with icmp type net-prohibited
+# reject with icmp net-prohibited
[
{
"reject": {
@@ -48,7 +48,7 @@
}
]
-# reject with icmp type host-prohibited
+# reject with icmp host-prohibited
[
{
"reject": {
@@ -58,7 +58,7 @@
}
]
-# reject with icmp type admin-prohibited
+# reject with icmp admin-prohibited
[
{
"reject": {
@@ -68,7 +68,7 @@
}
]
-# reject with icmpv6 type no-route
+# reject with icmpv6 no-route
[
{
"reject": {
@@ -78,7 +78,7 @@
}
]
-# reject with icmpv6 type admin-prohibited
+# reject with icmpv6 admin-prohibited
[
{
"reject": {
@@ -88,7 +88,7 @@
}
]
-# reject with icmpv6 type addr-unreachable
+# reject with icmpv6 addr-unreachable
[
{
"reject": {
@@ -98,7 +98,7 @@
}
]
-# reject with icmpv6 type port-unreachable
+# reject with icmpv6 port-unreachable
[
{
"reject": {
@@ -108,7 +108,7 @@
}
]
-# reject with icmpv6 type policy-fail
+# reject with icmpv6 policy-fail
[
{
"reject": {
@@ -118,7 +118,7 @@
}
]
-# reject with icmpv6 type reject-route
+# reject with icmpv6 reject-route
[
{
"reject": {
@@ -189,7 +189,7 @@
}
]
-# reject with icmpx type host-unreachable
+# reject with icmpx host-unreachable
[
{
"reject": {
@@ -199,7 +199,7 @@
}
]
-# reject with icmpx type no-route
+# reject with icmpx no-route
[
{
"reject": {
@@ -209,7 +209,7 @@
}
]
-# reject with icmpx type admin-prohibited
+# reject with icmpx admin-prohibited
[
{
"reject": {
@@ -219,7 +219,7 @@
}
]
-# reject with icmpx type port-unreachable
+# reject with icmpx port-unreachable
[
{
"reject": {
@@ -229,7 +229,7 @@
}
]
-# meta protocol ip reject with icmp type host-unreachable
+# meta protocol ip reject with icmp host-unreachable
[
{
"reject": {
@@ -239,7 +239,7 @@
}
]
-# meta protocol ip6 reject with icmpv6 type no-route
+# meta protocol ip6 reject with icmpv6 no-route
[
{
"reject": {
@@ -249,7 +249,7 @@
}
]
-# meta protocol ip reject with icmpx type admin-prohibited
+# meta protocol ip reject with icmpx admin-prohibited
[
{
"match": {
@@ -270,7 +270,7 @@
}
]
-# meta protocol ip6 reject with icmpx type admin-prohibited
+# meta protocol ip6 reject with icmpx admin-prohibited
[
{
"match": {
diff --git a/tests/py/netdev/reject.t.payload b/tests/py/netdev/reject.t.payload
index 5f76b091..d014adab 100644
--- a/tests/py/netdev/reject.t.payload
+++ b/tests/py/netdev/reject.t.payload
@@ -1,76 +1,76 @@
-# reject with icmp type host-unreachable
+# reject with icmp host-unreachable
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 0 code 1 ]
-# reject with icmp type net-unreachable
+# reject with icmp net-unreachable
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 0 code 0 ]
-# reject with icmp type prot-unreachable
+# reject with icmp prot-unreachable
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 0 code 2 ]
-# reject with icmp type port-unreachable
+# reject with icmp port-unreachable
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 0 code 3 ]
-# reject with icmp type net-prohibited
+# reject with icmp net-prohibited
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 0 code 9 ]
-# reject with icmp type host-prohibited
+# reject with icmp host-prohibited
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 0 code 10 ]
-# reject with icmp type admin-prohibited
+# reject with icmp admin-prohibited
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 0 code 13 ]
-# reject with icmpv6 type no-route
+# reject with icmpv6 no-route
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x0000dd86 ]
[ reject type 0 code 0 ]
-# reject with icmpv6 type admin-prohibited
+# reject with icmpv6 admin-prohibited
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x0000dd86 ]
[ reject type 0 code 1 ]
-# reject with icmpv6 type addr-unreachable
+# reject with icmpv6 addr-unreachable
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x0000dd86 ]
[ reject type 0 code 3 ]
-# reject with icmpv6 type port-unreachable
+# reject with icmpv6 port-unreachable
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x0000dd86 ]
[ reject type 0 code 4 ]
-# reject with icmpv6 type policy-fail
+# reject with icmpv6 policy-fail
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x0000dd86 ]
[ reject type 0 code 5 ]
-# reject with icmpv6 type reject-route
+# reject with icmpv6 reject-route
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x0000dd86 ]
@@ -100,41 +100,41 @@ netdev
[ cmp eq reg 1 0x0000dd86 ]
[ reject type 0 code 4 ]
-# reject with icmpx type host-unreachable
+# reject with icmpx host-unreachable
netdev
[ reject type 2 code 2 ]
-# reject with icmpx type no-route
+# reject with icmpx no-route
netdev
[ reject type 2 code 0 ]
-# reject with icmpx type admin-prohibited
+# reject with icmpx admin-prohibited
netdev
[ reject type 2 code 3 ]
-# reject with icmpx type port-unreachable
+# reject with icmpx port-unreachable
netdev
[ reject type 2 code 1 ]
-# meta protocol ip reject with icmp type host-unreachable
+# meta protocol ip reject with icmp host-unreachable
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 0 code 1 ]
-# meta protocol ip6 reject with icmpv6 type no-route
+# meta protocol ip6 reject with icmpv6 no-route
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x0000dd86 ]
[ reject type 0 code 0 ]
-# meta protocol ip reject with icmpx type admin-prohibited
+# meta protocol ip reject with icmpx admin-prohibited
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ reject type 2 code 3 ]
-# meta protocol ip6 reject with icmpx type admin-prohibited
+# meta protocol ip6 reject with icmpx admin-prohibited
netdev
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x0000dd86 ]
diff --git a/tests/py/nft-test.py b/tests/py/nft-test.py
index 15e74d8b..f8f9341c 100755
--- a/tests/py/nft-test.py
+++ b/tests/py/nft-test.py
@@ -1349,6 +1349,33 @@ def run_test_file(filename, force_all_family_option, specific_file):
return [tests, passed, total_warning, total_error, total_unit_run]
+def spawn_netns():
+ # prefer unshare module
+ try:
+ import unshare
+ unshare.unshare(unshare.CLONE_NEWNET)
+ return True
+ except:
+ pass
+
+ # sledgehammer style:
+ # - call ourselves prefixed by 'unshare -n' if found
+ # - pass extra --no-netns parameter to avoid another recursion
+ try:
+ import shutil
+
+ unshare = shutil.which("unshare")
+ if unshare is None:
+ return False
+
+ sys.argv.append("--no-netns")
+ if debug_option:
+ print("calling: ", [unshare, "-n", sys.executable] + sys.argv)
+ os.execv(unshare, [unshare, "-n", sys.executable] + sys.argv)
+ except:
+ pass
+
+ return False
def main():
parser = argparse.ArgumentParser(description='Run nft tests')
@@ -1376,6 +1403,10 @@ def main():
parser.add_argument('-l', '--library', default=None,
help='path to libntables.so.1, overrides --host')
+ parser.add_argument('-N', '--no-netns', action='store_true',
+ dest='no_netns',
+ help='Do not run in own network namespace')
+
parser.add_argument('-s', '--schema', action='store_true',
dest='enable_schema',
help='verify json input/output against schema')
@@ -1400,15 +1431,12 @@ def main():
print("You need to be root to run this, sorry")
return
+ if not args.no_netns and not spawn_netns():
+ print_warning("cannot run in own namespace, connectivity might break")
+
# Change working directory to repository root
os.chdir(TESTS_PATH + "/../..")
- try:
- import unshare
- unshare.unshare(unshare.CLONE_NEWNET)
- except:
- print_warning("cannot run in own namespace, connectivity might break")
-
check_lib_path = True
if args.library is None:
if args.host:
diff --git a/tests/shell/testcases/maps/0010concat_map_0 b/tests/shell/testcases/maps/0010concat_map_0
new file mode 100755
index 00000000..4848d972
--- /dev/null
+++ b/tests/shell/testcases/maps/0010concat_map_0
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -e
+
+EXPECTED="table inet x {
+ map z {
+ type ipv4_addr . inet_proto . inet_service : ipv4_addr . inet_service
+ elements = {
+ 1.1.1.1 . tcp . 20 : 2.2.2.2 . 30
+ }
+ }
+
+ chain y {
+ type nat hook prerouting priority dstnat;
+ dnat ip addr . port to ip saddr . ip protocol . tcp dport map @z
+ }
+}"
+
+$NFT -f - <<< "$EXPECTED"
diff --git a/tests/shell/testcases/maps/0011vmap_0 b/tests/shell/testcases/maps/0011vmap_0
new file mode 100755
index 00000000..83704d48
--- /dev/null
+++ b/tests/shell/testcases/maps/0011vmap_0
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+set -e
+
+EXPECTED="table inet filter {
+ map portmap {
+ type inet_service : verdict
+ counter
+ }
+
+ chain ssh_input {
+ }
+
+ chain wan_input {
+ tcp dport vmap @portmap
+ }
+
+ chain prerouting {
+ type filter hook prerouting priority -300; policy accept;
+ iif vmap { "lo" : jump wan_input }
+ }
+}"
+
+$NFT -f - <<< "$EXPECTED"
+$NFT 'add element inet filter portmap { 22 : jump ssh_input, * : drop }'
diff --git a/tests/shell/testcases/maps/0012map_0 b/tests/shell/testcases/maps/0012map_0
new file mode 100755
index 00000000..dd93c482
--- /dev/null
+++ b/tests/shell/testcases/maps/0012map_0
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -e
+
+EXPECTED="define interfaces = { eth0, eth1 }
+
+table ip x {
+ map z {
+ type ifname : verdict
+ elements = { \$interfaces : drop, lo : accept }
+ }
+ chain y {
+ iifname vmap { lo : accept, \$interfaces : drop }
+ }
+}"
+
+$NFT -f - <<< "$EXPECTED"
diff --git a/tests/shell/testcases/maps/0013map_0 b/tests/shell/testcases/maps/0013map_0
new file mode 100755
index 00000000..70d7fd3b
--- /dev/null
+++ b/tests/shell/testcases/maps/0013map_0
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+set -e
+
+RULESET="
+flush ruleset
+
+add table ip filter
+add chain ip filter FORWARD { type filter hook forward priority 0; policy drop; }
+add map ip filter forwport { type ipv4_addr . inet_proto . inet_service: verdict; flags interval; counter; }
+add rule ip filter FORWARD iifname enp0s8 ip daddr . ip protocol . th dport vmap @forwport counter
+add element ip filter forwport { 10.133.89.138 . tcp . 8081: accept }"
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/maps/dumps/0010concat_map_0.nft b/tests/shell/testcases/maps/dumps/0010concat_map_0.nft
new file mode 100644
index 00000000..b6bc338c
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0010concat_map_0.nft
@@ -0,0 +1,11 @@
+table inet x {
+ map z {
+ type ipv4_addr . inet_proto . inet_service : ipv4_addr . inet_service
+ elements = { 1.1.1.1 . tcp . 20 : 2.2.2.2 . 30 }
+ }
+
+ chain y {
+ type nat hook prerouting priority dstnat; policy accept;
+ meta nfproto ipv4 dnat ip to ip saddr . ip protocol . tcp dport map @z
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/0011vmap_0.nft b/tests/shell/testcases/maps/dumps/0011vmap_0.nft
new file mode 100644
index 00000000..4a72b5e7
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0011vmap_0.nft
@@ -0,0 +1,19 @@
+table inet filter {
+ map portmap {
+ type inet_service : verdict
+ counter
+ elements = { 22 counter packets 0 bytes 0 : jump ssh_input, * counter packets 0 bytes 0 : drop }
+ }
+
+ chain ssh_input {
+ }
+
+ chain wan_input {
+ tcp dport vmap @portmap
+ }
+
+ chain prerouting {
+ type filter hook prerouting priority raw; policy accept;
+ iif vmap { "lo" : jump wan_input }
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/0012map_0.nft b/tests/shell/testcases/maps/dumps/0012map_0.nft
new file mode 100644
index 00000000..e734fc1c
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0012map_0.nft
@@ -0,0 +1,12 @@
+table ip x {
+ map z {
+ type ifname : verdict
+ elements = { "lo" : accept,
+ "eth0" : drop,
+ "eth1" : drop }
+ }
+
+ chain y {
+ iifname vmap { "lo" : accept, "eth0" : drop, "eth1" : drop }
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/0013map_0.nft b/tests/shell/testcases/maps/dumps/0013map_0.nft
new file mode 100644
index 00000000..1455877d
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0013map_0.nft
@@ -0,0 +1,13 @@
+table ip filter {
+ map forwport {
+ type ipv4_addr . inet_proto . inet_service : verdict
+ flags interval
+ counter
+ elements = { 10.133.89.138 . tcp . 8081 counter packets 0 bytes 0 : accept }
+ }
+
+ chain FORWARD {
+ type filter hook forward priority filter; policy drop;
+ iifname "enp0s8" ip daddr . ip protocol . th dport vmap @forwport counter packets 0 bytes 0
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/nat_addr_port.nft b/tests/shell/testcases/maps/dumps/nat_addr_port.nft
index 89c3bd14..cf6b957f 100644
--- a/tests/shell/testcases/maps/dumps/nat_addr_port.nft
+++ b/tests/shell/testcases/maps/dumps/nat_addr_port.nft
@@ -27,10 +27,10 @@ table ip ipfoo {
dnat to ip daddr map @x
ip saddr 10.1.1.1 dnat to 10.2.3.4
ip saddr 10.1.1.2 tcp dport 42 dnat to 10.2.3.4:4242
- meta l4proto tcp dnat ip addr . port to ip saddr map @y
- dnat ip addr . port to ip saddr . tcp dport map @z
+ meta l4proto tcp dnat ip to ip saddr map @y
+ dnat ip to ip saddr . tcp dport map @z
dnat to numgen inc mod 2 map @t1
- meta l4proto tcp dnat ip addr . port to numgen inc mod 2 map @t2
+ meta l4proto tcp dnat ip to numgen inc mod 2 map @t2
}
}
table ip6 ip6foo {
@@ -60,10 +60,10 @@ table ip6 ip6foo {
dnat to ip6 daddr map @x
ip6 saddr dead::1 dnat to feed::1
ip6 saddr dead::2 tcp dport 42 dnat to [c0::1a]:4242
- meta l4proto tcp dnat ip6 addr . port to ip6 saddr map @y
- dnat ip6 addr . port to ip6 saddr . tcp dport map @z
+ meta l4proto tcp dnat ip6 to ip6 saddr map @y
+ dnat ip6 to ip6 saddr . tcp dport map @z
dnat to numgen inc mod 2 map @t1
- meta l4proto tcp dnat ip6 addr . port to numgen inc mod 2 map @t2
+ meta l4proto tcp dnat ip6 to numgen inc mod 2 map @t2
}
}
table inet inetfoo {
@@ -114,16 +114,16 @@ table inet inetfoo {
dnat ip to ip daddr map @x4
ip saddr 10.1.1.1 dnat ip to 10.2.3.4
ip saddr 10.1.1.2 tcp dport 42 dnat ip to 10.2.3.4:4242
- meta l4proto tcp meta nfproto ipv4 dnat ip addr . port to ip saddr map @y4
- meta nfproto ipv4 dnat ip addr . port to ip saddr . tcp dport map @z4
+ meta l4proto tcp meta nfproto ipv4 dnat ip to ip saddr map @y4
+ meta nfproto ipv4 dnat ip to ip saddr . tcp dport map @z4
dnat ip to numgen inc mod 2 map @t1v4
- meta l4proto tcp dnat ip addr . port to numgen inc mod 2 map @t2v4
+ meta l4proto tcp dnat ip to numgen inc mod 2 map @t2v4
dnat ip6 to ip6 daddr map @x6
ip6 saddr dead::1 dnat ip6 to feed::1
ip6 saddr dead::2 tcp dport 42 dnat ip6 to [c0::1a]:4242
- meta l4proto tcp meta nfproto ipv6 dnat ip6 addr . port to ip6 saddr map @y6
- meta nfproto ipv6 dnat ip6 addr . port to ip6 saddr . tcp dport map @z6
+ meta l4proto tcp meta nfproto ipv6 dnat ip6 to ip6 saddr map @y6
+ meta nfproto ipv6 dnat ip6 to ip6 saddr . tcp dport map @z6
dnat ip6 to numgen inc mod 2 map @t1v6
- meta l4proto tcp dnat ip6 addr . port to numgen inc mod 2 map @t2v6
+ meta l4proto tcp dnat ip6 to numgen inc mod 2 map @t2v6
}
}
diff --git a/tests/shell/testcases/nft-f/0012different_defines_0 b/tests/shell/testcases/nft-f/0012different_defines_0
index 0bdbd1b5..fe228587 100755
--- a/tests/shell/testcases/nft-f/0012different_defines_0
+++ b/tests/shell/testcases/nft-f/0012different_defines_0
@@ -14,6 +14,8 @@ define d_ipv4_2 = 10.0.0.2
define d_ipv6 = fe0::1
define d_ipv6_2 = fe0::2
define d_ports = 100-222
+define d_qnum = 0
+define d_qnumr = 1-42
table inet t {
chain c {
@@ -29,6 +31,11 @@ table inet t {
ip daddr . meta iif vmap { \$d_ipv4 . \$d_iif : accept }
tcp dport \$d_ports
udp dport vmap { \$d_ports : accept }
+ tcp dport 1 tcp sport 1 meta oifname \"foobar\" queue num \$d_qnum bypass
+ tcp dport 1 tcp sport 1 meta oifname \"foobar\" queue num \$d_qnumr
+ tcp dport 1 tcp sport 1 meta oifname \"foobar\" queue flags bypass,fanout num \$d_qnumr
+ tcp dport 1 tcp sport 1 meta oifname \"foobar\" queue to symhash mod 2
+ tcp dport 1 tcp sport 1 meta oifname \"foobar\" queue flags bypass to jhash tcp dport . tcp sport mod 4
}
}"
diff --git a/tests/shell/testcases/nft-f/0028variable_cmdline_0 b/tests/shell/testcases/nft-f/0028variable_cmdline_0
new file mode 100755
index 00000000..a2bbd5da
--- /dev/null
+++ b/tests/shell/testcases/nft-f/0028variable_cmdline_0
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+
+RULESET="table inet filter {
+ set whitelist_v4 { type ipv4_addr; }
+}
+add element inet filter whitelist_v4 \$whitelist_v4
+"
+
+# this is intentional: exercise error path
+$NFT --define whitelist_v4="{ wrong }" -f - <<< "$RULESET"
+$NFT --define whitelist_v4="{ 1.1.1.1, \$wrong }" -f - <<< "$RULESET"
+
+set -e
+
+$NFT --define whitelist_v4="{ 1.1.1.1, 2.2.2.2 }" -f - <<< "$RULESET"
+$NFT --define x={5.5.5.5} --define whitelist_v4="{ 3.3.3.3, 4.4.4.4, \$x }" -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/nft-f/dumps/0012different_defines_0.nft b/tests/shell/testcases/nft-f/dumps/0012different_defines_0.nft
index 28094387..4734b2fd 100644
--- a/tests/shell/testcases/nft-f/dumps/0012different_defines_0.nft
+++ b/tests/shell/testcases/nft-f/dumps/0012different_defines_0.nft
@@ -12,5 +12,10 @@ table inet t {
ip daddr . iif vmap { 10.0.0.0 . "lo" : accept }
tcp dport 100-222
udp dport vmap { 100-222 : accept }
+ tcp sport 1 tcp dport 1 oifname "foobar" queue flags bypass to 0
+ tcp sport 1 tcp dport 1 oifname "foobar" queue to 1-42
+ tcp sport 1 tcp dport 1 oifname "foobar" queue flags bypass,fanout to 1-42
+ tcp sport 1 tcp dport 1 oifname "foobar" queue to symhash mod 2
+ tcp sport 1 tcp dport 1 oifname "foobar" queue flags bypass to jhash tcp dport . tcp sport mod 4
}
}
diff --git a/tests/shell/testcases/nft-f/dumps/0022priority_variable_0.nft b/tests/shell/testcases/nft-f/dumps/0022priority_variable_0.nft
deleted file mode 100644
index 2e944599..00000000
--- a/tests/shell/testcases/nft-f/dumps/0022priority_variable_0.nft
+++ /dev/null
@@ -1,5 +0,0 @@
-table inet global {
- chain prerouting {
- type filter hook prerouting priority filter + 10; policy accept;
- }
-}
diff --git a/tests/shell/testcases/nft-f/dumps/0022variables_0.nft b/tests/shell/testcases/nft-f/dumps/0022variables_0.nft
new file mode 100644
index 00000000..d30f4d53
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0022variables_0.nft
@@ -0,0 +1,14 @@
+table ip x {
+ set y {
+ type ipv4_addr
+ size 65535
+ flags dynamic,timeout
+ }
+
+ chain z {
+ type filter hook input priority filter; policy accept;
+ add @y { ip saddr }
+ update @y { ip saddr timeout 30s }
+ ip saddr @y
+ }
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0028variable_cmdline_0.nft b/tests/shell/testcases/nft-f/dumps/0028variable_cmdline_0.nft
new file mode 100644
index 00000000..aa081122
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0028variable_cmdline_0.nft
@@ -0,0 +1,8 @@
+table inet filter {
+ set whitelist_v4 {
+ type ipv4_addr
+ elements = { 1.1.1.1, 2.2.2.2,
+ 3.3.3.3, 4.4.4.4,
+ 5.5.5.5 }
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dependency_kill b/tests/shell/testcases/optimizations/dependency_kill
new file mode 100755
index 00000000..904eecf8
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dependency_kill
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table bridge foo {
+ chain bar {
+ meta protocol ip udp dport 67
+ meta protocol ip6 udp dport 67
+ ether type ip udp dport 67
+ ether type ip6 udp dport 67
+ }
+}
+table ip foo {
+ chain bar {
+ meta protocol ip udp dport 67
+ meta protocol ip6 udp dport 67
+ ether type ip udp dport 67
+ ether type ip6 udp dport 67
+ }
+}
+table ip6 foo {
+ chain bar {
+ meta protocol ip udp dport 67
+ meta protocol ip6 udp dport 67
+ ether type ip udp dport 67
+ ether type ip6 udp dport 67
+ }
+}
+table netdev foo {
+ chain bar {
+ meta protocol ip udp dport 67
+ meta protocol ip6 udp dport 67
+ ether type ip udp dport 67
+ ether type ip6 udp dport 67
+ }
+}
+table inet foo {
+ chain bar {
+ meta protocol ip udp dport 67
+ meta protocol ip6 udp dport 67
+ ether type ip udp dport 67
+ ether type ip6 udp dport 67
+ meta nfproto ipv4 udp dport 67
+ meta nfproto ipv6 udp dport 67
+ }
+}"
+
+$NFT -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/dumps/dependency_kill.nft b/tests/shell/testcases/optimizations/dumps/dependency_kill.nft
new file mode 100644
index 00000000..1781f7be
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/dependency_kill.nft
@@ -0,0 +1,42 @@
+table bridge foo {
+ chain bar {
+ meta protocol ip udp dport 67
+ meta protocol ip6 udp dport 67
+ ether type ip udp dport 67
+ ether type ip6 udp dport 67
+ }
+}
+table ip foo {
+ chain bar {
+ udp dport 67
+ meta protocol ip6 udp dport 67
+ udp dport 67
+ ether type ip6 udp dport 67
+ }
+}
+table ip6 foo {
+ chain bar {
+ meta protocol ip udp dport 67
+ udp dport 67
+ ether type ip udp dport 67
+ udp dport 67
+ }
+}
+table netdev foo {
+ chain bar {
+ meta protocol ip udp dport 67
+ meta protocol ip6 udp dport 67
+ ether type ip udp dport 67
+ ether type ip6 udp dport 67
+ }
+}
+table inet foo {
+ chain bar {
+ meta protocol ip udp dport 67
+ meta protocol ip6 udp dport 67
+ ether type ip udp dport 67
+ ether type ip6 udp dport 67
+ meta nfproto ipv4 udp dport 67
+ meta nfproto ipv6 udp dport 67
+ }
+}
diff --git a/tests/shell/testcases/sets/0031set_timeout_size_0 b/tests/shell/testcases/sets/0031set_timeout_size_0
index 9edd5f6f..796640d6 100755
--- a/tests/shell/testcases/sets/0031set_timeout_size_0
+++ b/tests/shell/testcases/sets/0031set_timeout_size_0
@@ -3,10 +3,10 @@
RULESET="add table x
add set x y { type ipv4_addr; size 128; timeout 30s; flags dynamic; }
add chain x test
-add rule x test set update ip saddr timeout 1d2h3m4s8ms @y
+add rule x test set update ip saddr timeout 1d2h3m4s10ms @y
add rule x test set update ip daddr timeout 100ms @y"
set -e
$NFT -f - <<< "$RULESET"
-$NFT list chain x test | grep -q 'update @y { ip saddr timeout 1d2h3m4s8ms }'
+$NFT list chain x test | grep -q 'update @y { ip saddr timeout 1d2h3m4s10ms }'
$NFT list chain x test | grep -q 'update @y { ip daddr timeout 100ms }'
diff --git a/tests/shell/testcases/sets/0047nat_0 b/tests/shell/testcases/sets/0047nat_0
index 746a6b6d..cb1d4d68 100755
--- a/tests/shell/testcases/sets/0047nat_0
+++ b/tests/shell/testcases/sets/0047nat_0
@@ -10,7 +10,7 @@ EXPECTED="table ip x {
chain y {
type nat hook postrouting priority srcnat; policy accept;
- snat ip interval to ip saddr map @y
+ snat to ip saddr map @y
}
}
"
diff --git a/tests/shell/testcases/sets/0065_icmp_postprocessing b/tests/shell/testcases/sets/0065_icmp_postprocessing
new file mode 100755
index 00000000..f838c3ef
--- /dev/null
+++ b/tests/shell/testcases/sets/0065_icmp_postprocessing
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip x {
+ chain foo {
+ icmp id 42
+ }
+}"
+
+$NFT -f - <<< $RULESET
+
+$NFT insert rule ip x foo index 0 accept
diff --git a/tests/shell/testcases/sets/0067nat_concat_interval_0 b/tests/shell/testcases/sets/0067nat_concat_interval_0
new file mode 100755
index 00000000..3d1b62d6
--- /dev/null
+++ b/tests/shell/testcases/sets/0067nat_concat_interval_0
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+set -e
+
+EXPECTED="table ip nat {
+ map ipportmap {
+ type ipv4_addr : interval ipv4_addr . inet_service
+ flags interval
+ elements = { 192.168.1.2 : 10.141.10.1-10.141.10.3 . 8888-8999 }
+ }
+ chain prerouting {
+ type nat hook prerouting priority dstnat; policy accept;
+ ip protocol tcp dnat ip to ip saddr map @ipportmap
+ }
+}"
+
+$NFT -f - <<< $EXPECTED
+$NFT add element ip nat ipportmap { 192.168.2.0/24 : 10.141.11.5-10.141.11.20 . 8888-8999 }
+
+EXPECTED="table ip nat {
+ map ipportmap2 {
+ type ipv4_addr . ipv4_addr : interval ipv4_addr . inet_service
+ flags interval
+ elements = { 192.168.1.2 . 192.168.2.2 : 127.0.0.1/8 . 42 - 43 }
+ }
+
+ chain prerouting {
+ type nat hook prerouting priority dstnat; policy accept;
+ ip protocol tcp dnat ip to ip saddr . ip daddr map @ipportmap2
+ }
+}"
+
+$NFT -f - <<< $EXPECTED
diff --git a/tests/shell/testcases/sets/dumps/0047nat_0.nft b/tests/shell/testcases/sets/dumps/0047nat_0.nft
index 70730ef3..e7968054 100644
--- a/tests/shell/testcases/sets/dumps/0047nat_0.nft
+++ b/tests/shell/testcases/sets/dumps/0047nat_0.nft
@@ -8,6 +8,6 @@ table ip x {
chain y {
type nat hook postrouting priority srcnat; policy accept;
- snat ip interval to ip saddr map @y
+ snat ip to ip saddr map @y
}
}
diff --git a/tests/shell/testcases/sets/dumps/0067nat_concat_interval_0.nft b/tests/shell/testcases/sets/dumps/0067nat_concat_interval_0.nft
new file mode 100644
index 00000000..c565d21f
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0067nat_concat_interval_0.nft
@@ -0,0 +1,19 @@
+table ip nat {
+ map ipportmap {
+ type ipv4_addr : interval ipv4_addr . inet_service
+ flags interval
+ elements = { 192.168.1.2 : 10.141.10.1-10.141.10.3 . 8888-8999, 192.168.2.0/24 : 10.141.11.5-10.141.11.20 . 8888-8999 }
+ }
+
+ map ipportmap2 {
+ type ipv4_addr . ipv4_addr : interval ipv4_addr . inet_service
+ flags interval
+ elements = { 192.168.1.2 . 192.168.2.2 : 127.0.0.0/8 . 42-43 }
+ }
+
+ chain prerouting {
+ type nat hook prerouting priority dstnat; policy accept;
+ ip protocol tcp dnat ip to ip saddr map @ipportmap
+ ip protocol tcp dnat ip to ip saddr . ip daddr map @ipportmap2
+ }
+}