[PATCH nft 2/3] segtree: basic support for binary operations in concatenated set ranges
Pablo Neira Ayuso <[email protected]> Wed, 5 Aug 2026 18:39:19 +0200
| Newsgroups | gmane.comp.security.firewalls.netfilter.devel |
|---|---|
| Message-ID | <[email protected]> |
Use of tcp flags in concatenated set ranges such as (note the 100-110
range):
table ip x {
chain y {
tcp flags . tcp dport vmap { syn | ack . 80 : accept, ack . 90 : drop, rst . 100-110 : drop
}
}
is broken when listing the ruleset:
AddressSanitizer:DEADLYSIGNAL
=================================================================
==218685==ERROR: AddressSanitizer: SEGV on unknown address 0x50b000029798 (pc 0x7f8318b8fac0 bp 0x7ffd080a4310 sp 0x7ffd080a4198 T0)
==218685==The signal is caused by a READ memory access.
#0 0x7f8318b8fac0 in __gmpz_cmp (/lib/x86_64-linux-gnu/libgmp.so.10+0x19ac0) (BuildId: dff5c2156ec812613c5e4431005c576b212dd323)
#1 0x7f83192fbc3f in concat_range_aggregate src/segtree.c:404
#2 0x7f8319240889 in netlink_list_setelems src/netlink.c:1774
#3 0x7f8319115fdd in cache_init_objects src/cache.c:1189
#4 0x7f8319116f10 in nft_cache_init src/cache.c:1266
#5 0x7f831911772b in nft_cache_update src/cache.c:1325
#6 0x7f83191eac34 in nft_evaluate src/libnftables.c:580
#7 0x7f83191eb8b6 in nft_run_cmd_from_buffer src/libnftables.c:638
#8 0x558baf3bc403 in main src/main.c:538
add basic support so it works with tcp flags, add a new assertion to
crash safely to ensure expression type is EXPR_VALUE before access
when trying to compose the range (this code only supports EXPR_VALUE
when building a range expression at this stage) and a new test.
This patch includes a new tests/shell unit file.
Fixes: 8ac2f3b2fca3 ("src: Add support for concatenated set ranges")
Signed-off-by: Pablo Neira Ayuso <[email protected]>
---
src/segtree.c | 27 ++-
.../dumps/vmap_concat_range_binary.json-nft | 209 ++++++++++++++++++
.../maps/dumps/vmap_concat_range_binary.nft | 14 ++
.../testcases/maps/vmap_concat_range_binary | 22 ++
4 files changed, 271 insertions(+), 1 deletion(-)
create mode 100644 tests/shell/testcases/maps/dumps/vmap_concat_range_binary.json-nft
create mode 100755 tests/shell/testcases/maps/dumps/vmap_concat_range_binary.nft
create mode 100755 tests/shell/testcases/maps/vmap_concat_range_binary
diff --git a/src/segtree.c b/src/segtree.c
index a721078cfa59..c8a7a3541ac3 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -362,6 +362,29 @@ static int range_mask_len(const mpz_t start, const mpz_t end, unsigned int len)
return ret;
}
+static int concat_expr_cmp(const struct expr *r1, const struct expr *r2)
+{
+ int ret;
+
+ assert(r1->etype == r2->etype);
+
+ switch (r1->etype) {
+ case EXPR_BINOP:
+ assert(r1->op == r2->op);
+ ret = 0;
+ ret = mpz_cmp(r1->left->value, r2->left->value);
+ ret |= mpz_cmp(r1->right->value, r2->right->value);
+ break;
+ case EXPR_VALUE:
+ ret = mpz_cmp(r1->value, r2->value);
+ break;
+ default:
+ BUG("unexpected expression %s", expr_name(r1->key));
+ }
+
+ return ret;
+}
+
/* Given a set with two elements (start and end), transform them into a
* concatenation of ranges. That is, from a list of start expressions and a list
* of end expressions, form a list of start - end expressions.
@@ -401,11 +424,13 @@ void concat_range_aggregate(struct expr *set)
r2_next = r2->list.next;
free_r1 = 0;
- if (!mpz_cmp(r1->value, r2->value)) {
+ if (!concat_expr_cmp(r1, r2)) {
free_r1 = 1;
goto next;
}
+ assert(r1->etype == EXPR_VALUE && r1->etype == EXPR_VALUE);
+
if (expr_basetype(r1)->type == TYPE_STRING &&
expr_basetype(r2)->type == TYPE_STRING) {
string_type = true;
diff --git a/tests/shell/testcases/maps/dumps/vmap_concat_range_binary.json-nft b/tests/shell/testcases/maps/dumps/vmap_concat_range_binary.json-nft
new file mode 100644
index 000000000000..8868f2eb180e
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/vmap_concat_range_binary.json-nft
@@ -0,0 +1,209 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "z",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": {
+ "typeof": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "flags"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ }
+ },
+ "handle": 0,
+ "map": "verdict",
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ [
+ {
+ "concat": [
+ {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ },
+ 80
+ ]
+ },
+ {
+ "accept": null
+ }
+ ],
+ [
+ {
+ "concat": [
+ "ack",
+ 90
+ ]
+ },
+ {
+ "drop": null
+ }
+ ],
+ [
+ {
+ "concat": [
+ "rst",
+ {
+ "range": [
+ 100,
+ 110
+ ]
+ }
+ ]
+ },
+ {
+ "drop": null
+ }
+ ]
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "z",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "flags"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "data": "@y"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "z",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "flags"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ },
+ 80
+ ]
+ },
+ {
+ "accept": null
+ }
+ ],
+ [
+ {
+ "concat": [
+ "ack",
+ 90
+ ]
+ },
+ {
+ "drop": null
+ }
+ ],
+ [
+ {
+ "concat": [
+ "rst",
+ {
+ "range": [
+ 100,
+ 110
+ ]
+ }
+ ]
+ },
+ {
+ "drop": null
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/vmap_concat_range_binary.nft b/tests/shell/testcases/maps/dumps/vmap_concat_range_binary.nft
new file mode 100755
index 000000000000..bc74535b852f
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/vmap_concat_range_binary.nft
@@ -0,0 +1,14 @@
+table ip x {
+ map y {
+ typeof tcp flags . tcp dport : verdict
+ flags interval
+ elements = { syn | ack . 80 : accept,
+ ack . 90 : drop,
+ rst . 100-110 : drop }
+ }
+
+ chain z {
+ tcp flags . tcp dport vmap @y
+ tcp flags . tcp dport vmap { syn | ack . 80 : accept, ack . 90 : drop, rst . 100-110 : drop }
+ }
+}
diff --git a/tests/shell/testcases/maps/vmap_concat_range_binary b/tests/shell/testcases/maps/vmap_concat_range_binary
new file mode 100755
index 000000000000..ad5ef4a9177e
--- /dev/null
+++ b/tests/shell/testcases/maps/vmap_concat_range_binary
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
+set -e
+
+RULESET="table ip x {
+ map y {
+ typeof tcp flags . tcp dport : verdict
+ flags interval
+ elements = { syn | ack . 80 : accept,
+ ack . 90 : drop,
+ rst . 100-110 : drop }
+ }
+
+ chain z {
+ tcp flags . tcp dport vmap @y
+ tcp flags . tcp dport vmap { syn | ack . 80 : accept, ack . 90 : drop, rst . 100-110 : drop }
+ }
+}"
+
+$NFT -f - <<< $RULESET
--
2.47.3