* [PATCH nft 1/3] segtree: assert on value expressions
@ 2026-08-24 21:29 Pablo Neira Ayuso
2026-08-24 21:29 ` [PATCH nft 2/3] Revert "segtree: basic support for binary operations in concatenated set ranges" Pablo Neira Ayuso
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Pablo Neira Ayuso @ 2026-08-24 21:29 UTC (permalink / raw)
To: netfilter-devel
Assert value expression before performing math on them instead of
crashing with unsupported expressions.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
src/segtree.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/src/segtree.c b/src/segtree.c
index c8a7a3541ac3..4f758e6c9f99 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -253,6 +253,9 @@ int get_set_decompose(struct set *cache_set, struct set *set)
if (i->key->flags & EXPR_F_INTERVAL_END && left) {
list_del(&left->list);
list_del(&i->list);
+
+ assert(i->key->etype == EXPR_VALUE);
+
mpz_sub_ui(i->key->value, i->key->value, 1);
range = get_set_interval_find(cache_set, left, i);
if (!range) {
@@ -325,6 +328,10 @@ static int expr_value_cmp(const void *p1, const void *p2)
return -1;
key_e2 = expr_value(e2);
+
+ assert(key_e1->etype == EXPR_VALUE);
+ assert(key_e2->etype == EXPR_VALUE);
+
ret = mpz_cmp(key_e1->value, key_e2->value);
if (ret == 0) {
if (e1->key->flags & EXPR_F_INTERVAL_END)
@@ -592,6 +599,10 @@ add_interval(struct expr *set, struct expr *low, struct expr *i, bool closed)
mpz_init(p);
key = expr_value(low);
+
+ assert(expr_value(i)->etype == EXPR_VALUE);
+ assert(key->etype == EXPR_VALUE);
+
mpz_sub(range, expr_value(i)->value, key->value);
if (closed)
mpz_sub_ui(range, range, 1);
--
2.47.3
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH nft 2/3] Revert "segtree: basic support for binary operations in concatenated set ranges"
2026-08-24 21:29 [PATCH nft 1/3] segtree: assert on value expressions Pablo Neira Ayuso
@ 2026-08-24 21:29 ` Pablo Neira Ayuso
2026-08-24 21:29 ` [PATCH nft 3/3] segtree: postpone bitmask to symbol conversion for interval sets Pablo Neira Ayuso
2026-08-27 9:03 ` [PATCH nft 1/3] segtree: assert on value expressions Pablo Neira Ayuso
2 siblings, 0 replies; 4+ messages in thread
From: Pablo Neira Ayuso @ 2026-08-24 21:29 UTC (permalink / raw)
To: netfilter-devel
Handling binary expression from the interval set postprocessing step is
tricky, it is easier to handle set element key as value at that stage.
Revert 1de3568ef07f ("segtree: basic support for binary operations in
concatenated set ranges"), this only works for concatenated set ranges.
A follow up patch postpones the conversion from value to symbol for
TYPE_BITMASK expressions after the interval set postprocessing.
Fixes: 1de3568ef07f ("segtree: basic support for binary operations in concatenated set ranges")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
src/segtree.c | 29 +++--------------------------
1 file changed, 3 insertions(+), 26 deletions(-)
diff --git a/src/segtree.c b/src/segtree.c
index 4f758e6c9f99..5e178fa3c48d 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -369,29 +369,6 @@ 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.
@@ -431,13 +408,13 @@ void concat_range_aggregate(struct expr *set)
r2_next = r2->list.next;
free_r1 = 0;
- if (!concat_expr_cmp(r1, r2)) {
+ assert(r1->etype == EXPR_VALUE && r1->etype == EXPR_VALUE);
+
+ if (!mpz_cmp(r1->value, r2->value)) {
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;
--
2.47.3
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH nft 3/3] segtree: postpone bitmask to symbol conversion for interval sets
2026-08-24 21:29 [PATCH nft 1/3] segtree: assert on value expressions Pablo Neira Ayuso
2026-08-24 21:29 ` [PATCH nft 2/3] Revert "segtree: basic support for binary operations in concatenated set ranges" Pablo Neira Ayuso
@ 2026-08-24 21:29 ` Pablo Neira Ayuso
2026-08-27 9:03 ` [PATCH nft 1/3] segtree: assert on value expressions Pablo Neira Ayuso
2 siblings, 0 replies; 4+ messages in thread
From: Pablo Neira Ayuso @ 2026-08-24 21:29 UTC (permalink / raw)
To: netfilter-devel
netlink_delinearize_setelem() calls bitmask_expr_to_binops() to turn a
bitmask into symbols, ie.
tcp flags { 0x2, 0x12 }
into
tcp flags { syn, syn | ack }
However, this call comes too early for interval set with one single key.
The interval_map_decompose() postprocessing can only deal with value
expressions, otherwise it hits an assertion.
Move this conversion at a later stage of interval_map_decompose() for
interval sets.
Ranges and prefix do not call bitmask_expr_to_binops(), we do not use
symbolic representations for such expressions, instead values are used.
The conversion is still perform for non-interval sets, since there is no
specific function to perform postprocessing for this type of sets.
Add and extend existing tests/shell cases.
Fixes: def2cca8e713 ("set_elem: convert flag value to inclusive-OR binops during delinearize")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
src/netlink.c | 6 +-
src/segtree.c | 15 +
.../dumps/vmap_concat_range_binary.json-nft | 154 +++++++++++
.../maps/dumps/vmap_concat_range_binary.nft | 10 +
.../maps/dumps/vmap_range_binary.json-nft | 257 ++++++++++++++++++
.../maps/dumps/vmap_range_binary.nft | 23 ++
.../testcases/maps/vmap_concat_range_binary | 10 +
tests/shell/testcases/maps/vmap_range_binary | 35 +++
8 files changed, 508 insertions(+), 2 deletions(-)
create mode 100644 tests/shell/testcases/maps/dumps/vmap_range_binary.json-nft
create mode 100644 tests/shell/testcases/maps/dumps/vmap_range_binary.nft
create mode 100755 tests/shell/testcases/maps/vmap_range_binary
diff --git a/src/netlink.c b/src/netlink.c
index c305e5619640..8916370604c0 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1440,7 +1440,8 @@ static struct expr *concat_elem_expr(const struct set *set, struct expr *key,
expr->byteorder == BYTEORDER_HOST_ENDIAN))
mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
- if (expr->dtype->basetype != NULL &&
+ if (!(set->flags & NFT_SET_INTERVAL) &&
+ expr->dtype->basetype != NULL &&
expr->dtype->basetype->type == TYPE_BITMASK)
expr = bitmask_expr_to_binops(expr);
@@ -1591,7 +1592,8 @@ key_end:
key->byteorder == BYTEORDER_HOST_ENDIAN)
mpz_switch_byteorder(key->value, key->len / BITS_PER_BYTE);
- if (key->dtype->basetype != NULL &&
+ if (!(set->flags & NFT_SET_INTERVAL) &&
+ key->dtype->basetype != NULL &&
key->dtype->basetype->type == TYPE_BITMASK)
key = bitmask_expr_to_binops(key);
} else if (flags & NFT_SET_ELEM_CATCHALL) {
diff --git a/src/segtree.c b/src/segtree.c
index 5e178fa3c48d..1dc94688aee1 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -411,6 +411,13 @@ void concat_range_aggregate(struct expr *set)
assert(r1->etype == EXPR_VALUE && r1->etype == EXPR_VALUE);
if (!mpz_cmp(r1->value, r2->value)) {
+ if (r2->dtype->basetype != NULL &&
+ r2->dtype->basetype->type == TYPE_BITMASK) {
+ expr_get(r2);
+ tmp = bitmask_expr_to_binops(r2);
+ list_replace(&r2->list, &tmp->list);
+ expr_free(r2);
+ }
free_r1 = 1;
goto next;
}
@@ -590,6 +597,14 @@ add_interval(struct expr *set, struct expr *low, struct expr *i, bool closed)
if (expr_basetype(low)->type == TYPE_STRING)
mpz_switch_byteorder(key->value,
key->len / BITS_PER_BYTE);
+
+ if (key->dtype->basetype != NULL &&
+ key->dtype->basetype->type == TYPE_BITMASK) {
+ if (low->key->etype == EXPR_MAPPING)
+ low->key->left = bitmask_expr_to_binops(low->key->left);
+ else
+ low->key = bitmask_expr_to_binops(low->key);
+ }
low->key->flags |= EXPR_F_KERNEL;
expr = expr_get(low);
} else if (range_is_prefix(range) && !mpz_cmp_ui(p, 0)) {
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
index 8868f2eb180e..de285354d86a 100644
--- a/tests/shell/testcases/maps/dumps/vmap_concat_range_binary.json-nft
+++ b/tests/shell/testcases/maps/dumps/vmap_concat_range_binary.json-nft
@@ -97,6 +97,65 @@
]
}
},
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "x",
+ "type": {
+ "typeof": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "flags"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ }
+ },
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "concat": [
+ {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ },
+ 80
+ ]
+ },
+ {
+ "concat": [
+ "ack",
+ 90
+ ]
+ },
+ {
+ "concat": [
+ "rst",
+ {
+ "range": [
+ 100,
+ 110
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ },
{
"rule": {
"family": "ip",
@@ -204,6 +263,101 @@
}
]
}
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "z",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "flags"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "right": "@s"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "z",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "flags"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "right": {
+ "set": [
+ {
+ "concat": [
+ {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ },
+ 80
+ ]
+ },
+ {
+ "concat": [
+ "ack",
+ 90
+ ]
+ },
+ {
+ "concat": [
+ "rst",
+ {
+ "range": [
+ 100,
+ 110
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
}
]
}
diff --git a/tests/shell/testcases/maps/dumps/vmap_concat_range_binary.nft b/tests/shell/testcases/maps/dumps/vmap_concat_range_binary.nft
index bc74535b852f..b5d6de32a838 100644
--- a/tests/shell/testcases/maps/dumps/vmap_concat_range_binary.nft
+++ b/tests/shell/testcases/maps/dumps/vmap_concat_range_binary.nft
@@ -7,8 +7,18 @@ table ip x {
rst . 100-110 : drop }
}
+ set s {
+ typeof tcp flags . tcp dport
+ flags interval
+ elements = { syn | ack . 80,
+ ack . 90,
+ rst . 100-110 }
+ }
+
chain z {
tcp flags . tcp dport vmap @y
tcp flags . tcp dport vmap { syn | ack . 80 : accept, ack . 90 : drop, rst . 100-110 : drop }
+ tcp flags . tcp dport @s
+ tcp flags . tcp dport { syn | ack . 80, ack . 90, rst . 100-110 }
}
}
diff --git a/tests/shell/testcases/maps/dumps/vmap_range_binary.json-nft b/tests/shell/testcases/maps/dumps/vmap_range_binary.json-nft
new file mode 100644
index 000000000000..eba4afb013d8
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/vmap_range_binary.json-nft
@@ -0,0 +1,257 @@
+{
+ "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": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "flags"
+ }
+ }
+ },
+ "handle": 0,
+ "map": "verdict",
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ [
+ "rst",
+ {
+ "drop": null
+ }
+ ],
+ [
+ "ack",
+ {
+ "drop": null
+ }
+ ],
+ [
+ {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ },
+ {
+ "accept": null
+ }
+ ],
+ [
+ {
+ "range": [
+ "urg",
+ 255
+ ]
+ },
+ {
+ "drop": null
+ }
+ ]
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "x",
+ "type": {
+ "typeof": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "flags"
+ }
+ }
+ },
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ "rst",
+ "ack",
+ {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ },
+ {
+ "range": [
+ "urg",
+ 255
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "z",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "flags"
+ }
+ },
+ "data": "@y"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "z",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "flags"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "rst",
+ {
+ "drop": null
+ }
+ ],
+ [
+ "ack",
+ {
+ "drop": null
+ }
+ ],
+ [
+ {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ },
+ {
+ "accept": null
+ }
+ ],
+ [
+ {
+ "range": [
+ "urg",
+ 255
+ ]
+ },
+ {
+ "drop": null
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "z",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "flags"
+ }
+ },
+ "right": "@s"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "z",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "flags"
+ }
+ },
+ "right": {
+ "set": [
+ "rst",
+ "ack",
+ {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ },
+ {
+ "range": [
+ "urg",
+ 255
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/vmap_range_binary.nft b/tests/shell/testcases/maps/dumps/vmap_range_binary.nft
new file mode 100644
index 000000000000..fbf599c07ff1
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/vmap_range_binary.nft
@@ -0,0 +1,23 @@
+table ip x {
+ map y {
+ typeof tcp flags : verdict
+ flags interval
+ elements = { rst : drop,
+ ack : drop,
+ syn | ack : accept,
+ 0x20-0xff : drop }
+ }
+
+ set s {
+ typeof tcp flags
+ flags interval
+ elements = { rst, ack, syn | ack, 0x20-0xff }
+ }
+
+ chain z {
+ tcp flags vmap @y
+ tcp flags vmap { rst : drop, ack : drop, syn | ack : accept, 0x20-0xff : drop }
+ tcp flags @s
+ tcp flags { rst, ack, syn | ack, 0x20-0xff }
+ }
+}
diff --git a/tests/shell/testcases/maps/vmap_concat_range_binary b/tests/shell/testcases/maps/vmap_concat_range_binary
index ad5ef4a9177e..bd0e5ed8d500 100755
--- a/tests/shell/testcases/maps/vmap_concat_range_binary
+++ b/tests/shell/testcases/maps/vmap_concat_range_binary
@@ -13,9 +13,19 @@ RULESET="table ip x {
rst . 100-110 : drop }
}
+ set s {
+ typeof tcp flags . tcp dport
+ flags interval
+ elements = { syn | ack . 80,
+ ack . 90,
+ rst . 100-110 }
+ }
+
chain z {
tcp flags . tcp dport vmap @y
tcp flags . tcp dport vmap { syn | ack . 80 : accept, ack . 90 : drop, rst . 100-110 : drop }
+ tcp flags . tcp dport @s
+ tcp flags . tcp dport { syn | ack . 80, ack . 90, rst . 100-110 }
}
}"
diff --git a/tests/shell/testcases/maps/vmap_range_binary b/tests/shell/testcases/maps/vmap_range_binary
new file mode 100755
index 000000000000..428cec646aca
--- /dev/null
+++ b/tests/shell/testcases/maps/vmap_range_binary
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip x {
+ map y {
+ typeof tcp flags : verdict
+ flags interval
+ elements = { syn | ack : accept,
+ ack : drop,
+ rst : drop,
+ 0x20-0xff : drop,
+ }
+ }
+
+ set s {
+ typeof tcp flags
+ flags interval
+ elements = { syn | ack,
+ ack,
+ rst,
+ 0x20-0xff,
+ }
+ }
+
+
+ chain z {
+ tcp flags vmap @y
+ tcp flags vmap { syn | ack : accept, ack : drop, rst : drop, 0x20-0xff : drop }
+ tcp flags @s
+ tcp flags { syn | ack, ack, rst, 0x20-0xff }
+ }
+}"
+
+$NFT -f - <<< $RULESET
--
2.47.3
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH nft 1/3] segtree: assert on value expressions
2026-08-24 21:29 [PATCH nft 1/3] segtree: assert on value expressions Pablo Neira Ayuso
2026-08-24 21:29 ` [PATCH nft 2/3] Revert "segtree: basic support for binary operations in concatenated set ranges" Pablo Neira Ayuso
2026-08-24 21:29 ` [PATCH nft 3/3] segtree: postpone bitmask to symbol conversion for interval sets Pablo Neira Ayuso
@ 2026-08-27 9:03 ` Pablo Neira Ayuso
2 siblings, 0 replies; 4+ messages in thread
From: Pablo Neira Ayuso @ 2026-08-27 9:03 UTC (permalink / raw)
To: netfilter-devel
Hi,
On Mon, Aug 24, 2026 at 11:29:07PM +0200, Pablo Neira Ayuso wrote:
> Assert value expression before performing math on them instead of
> crashing with unsupported expressions.
JFR: I have pushed out this series.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-27 9:03 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-24 21:29 [PATCH nft 1/3] segtree: assert on value expressions Pablo Neira Ayuso
2026-08-24 21:29 ` [PATCH nft 2/3] Revert "segtree: basic support for binary operations in concatenated set ranges" Pablo Neira Ayuso
2026-08-24 21:29 ` [PATCH nft 3/3] segtree: postpone bitmask to symbol conversion for interval sets Pablo Neira Ayuso
2026-08-27 9:03 ` [PATCH nft 1/3] segtree: assert on value expressions Pablo Neira Ayuso
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox