Blame SOURCES/0019-src-allow-anon-set-concatenation-with-ether-and-vlan.patch

1d03cd
From 99e943834fc6708fb8bd8f329988979433db19e8 Mon Sep 17 00:00:00 2001
1d03cd
From: Phil Sutter <psutter@redhat.com>
1d03cd
Date: Thu, 9 Feb 2023 10:27:58 +0100
1d03cd
Subject: [PATCH] src: allow anon set concatenation with ether and vlan
1d03cd
1d03cd
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2094887
1d03cd
Upstream Status: nftables commit c1c223f1b5818
1d03cd
1d03cd
commit c1c223f1b58188542222ee2d9a4a8cc133d1dc3b
1d03cd
Author: Florian Westphal <fw@strlen.de>
1d03cd
Date:   Mon Jul 25 21:34:52 2022 +0200
1d03cd
1d03cd
    src: allow anon set concatenation with ether and vlan
1d03cd
1d03cd
    vlan id uses integer type (which has a length of 0).
1d03cd
1d03cd
    Using it was possible, but listing would assert:
1d03cd
    python: mergesort.c:24: concat_expr_msort_value: Assertion `ilen > 0' failed.
1d03cd
1d03cd
    There are two reasons for this.
1d03cd
    First reason is that the udata/typeof information lacks the 'vlan id'
1d03cd
    part, because internally this is 'payload . binop(payload AND mask)'.
1d03cd
1d03cd
    binop lacks an udata store.  It makes little sense to store it,
1d03cd
    'typeof' keyword expects normal match syntax.
1d03cd
1d03cd
    So, when storing udata, store the left hand side of the binary
1d03cd
    operation, i.e. the load of the 2-byte key.
1d03cd
1d03cd
    With that resolved, delinerization could work, but concat_elem_expr()
1d03cd
    would splice 12 bits off the elements value, but it should be 16 (on
1d03cd
    a byte boundary).
1d03cd
1d03cd
    Signed-off-by: Florian Westphal <fw@strlen.de>
1d03cd
1d03cd
Signed-off-by: Phil Sutter <psutter@redhat.com>
1d03cd
---
1d03cd
 src/expression.c                      | 17 +++++++++--
1d03cd
 src/netlink.c                         | 10 +++++--
1d03cd
 tests/py/bridge/vlan.t                |  2 ++
1d03cd
 tests/py/bridge/vlan.t.json           | 41 +++++++++++++++++++++++++++
1d03cd
 tests/py/bridge/vlan.t.payload        | 12 ++++++++
1d03cd
 tests/py/bridge/vlan.t.payload.netdev | 14 +++++++++
1d03cd
 6 files changed, 91 insertions(+), 5 deletions(-)
1d03cd
1d03cd
diff --git a/src/expression.c b/src/expression.c
1d03cd
index deb649e..7390089 100644
1d03cd
--- a/src/expression.c
1d03cd
+++ b/src/expression.c
1d03cd
@@ -879,17 +879,30 @@ static void concat_expr_print(const struct expr *expr, struct output_ctx *octx)
1d03cd
 #define NFTNL_UDATA_SET_KEY_CONCAT_SUB_DATA 1
1d03cd
 #define NFTNL_UDATA_SET_KEY_CONCAT_SUB_MAX  2
1d03cd
 
1d03cd
+static struct expr *expr_build_udata_recurse(struct expr *e)
1d03cd
+{
1d03cd
+	switch (e->etype) {
1d03cd
+	case EXPR_BINOP:
1d03cd
+		return e->left;
1d03cd
+	default:
1d03cd
+		break;
1d03cd
+	}
1d03cd
+
1d03cd
+	return e;
1d03cd
+}
1d03cd
+
1d03cd
 static int concat_expr_build_udata(struct nftnl_udata_buf *udbuf,
1d03cd
 				    const struct expr *concat_expr)
1d03cd
 {
1d03cd
 	struct nftnl_udata *nest;
1d03cd
+	struct expr *expr, *tmp;
1d03cd
 	unsigned int i = 0;
1d03cd
-	struct expr *expr;
1d03cd
 
1d03cd
-	list_for_each_entry(expr, &concat_expr->expressions, list) {
1d03cd
+	list_for_each_entry_safe(expr, tmp, &concat_expr->expressions, list) {
1d03cd
 		struct nftnl_udata *nest_expr;
1d03cd
 		int err;
1d03cd
 
1d03cd
+		expr = expr_build_udata_recurse(expr);
1d03cd
 		if (!expr_ops(expr)->build_udata || i >= NFT_REG32_SIZE)
1d03cd
 			return -1;
1d03cd
 
1d03cd
diff --git a/src/netlink.c b/src/netlink.c
1d03cd
index 89d864e..799cf9b 100644
1d03cd
--- a/src/netlink.c
1d03cd
+++ b/src/netlink.c
1d03cd
@@ -1114,17 +1114,21 @@ static struct expr *concat_elem_expr(struct expr *key,
1d03cd
 				     struct expr *data, int *off)
1d03cd
 {
1d03cd
 	const struct datatype *subtype;
1d03cd
+	unsigned int sub_length;
1d03cd
 	struct expr *expr;
1d03cd
 
1d03cd
 	if (key) {
1d03cd
 		(*off)--;
1d03cd
-		expr = constant_expr_splice(data, key->len);
1d03cd
+		sub_length = round_up(key->len, BITS_PER_BYTE);
1d03cd
+
1d03cd
+		expr = constant_expr_splice(data, sub_length);
1d03cd
 		expr->dtype = datatype_get(key->dtype);
1d03cd
 		expr->byteorder = key->byteorder;
1d03cd
 		expr->len = key->len;
1d03cd
 	} else {
1d03cd
 		subtype = concat_subtype_lookup(dtype->type, --(*off));
1d03cd
-		expr = constant_expr_splice(data, subtype->size);
1d03cd
+		sub_length = round_up(subtype->size, BITS_PER_BYTE);
1d03cd
+		expr = constant_expr_splice(data, sub_length);
1d03cd
 		expr->dtype = subtype;
1d03cd
 		expr->byteorder = subtype->byteorder;
1d03cd
 	}
1d03cd
@@ -1136,7 +1140,7 @@ static struct expr *concat_elem_expr(struct expr *key,
1d03cd
 	    expr->dtype->basetype->type == TYPE_BITMASK)
1d03cd
 		expr = bitmask_expr_to_binops(expr);
1d03cd
 
1d03cd
-	data->len -= netlink_padding_len(expr->len);
1d03cd
+	data->len -= netlink_padding_len(sub_length);
1d03cd
 
1d03cd
 	return expr;
1d03cd
 }
1d03cd
diff --git a/tests/py/bridge/vlan.t b/tests/py/bridge/vlan.t
1d03cd
index 4920601..95bdff4 100644
1d03cd
--- a/tests/py/bridge/vlan.t
1d03cd
+++ b/tests/py/bridge/vlan.t
1d03cd
@@ -50,3 +50,5 @@ vlan id 1 vlan id set 2;ok
1d03cd
 
1d03cd
 ether saddr 00:01:02:03:04:05 vlan id 1;ok
1d03cd
 vlan id 2 ether saddr 0:1:2:3:4:6;ok;ether saddr 00:01:02:03:04:06 vlan id 2
1d03cd
+
1d03cd
+ether saddr . vlan id { 0a:0b:0c:0d:0e:0f . 42, 0a:0b:0c:0d:0e:0f . 4095 };ok
1d03cd
diff --git a/tests/py/bridge/vlan.t.json b/tests/py/bridge/vlan.t.json
1d03cd
index 58d4a40..f77756f 100644
1d03cd
--- a/tests/py/bridge/vlan.t.json
1d03cd
+++ b/tests/py/bridge/vlan.t.json
1d03cd
@@ -817,3 +817,44 @@
1d03cd
         }
1d03cd
     }
1d03cd
 ]
1d03cd
+
1d03cd
+# ether saddr . vlan id { 0a:0b:0c:0d:0e:0f . 42, 0a:0b:0c:0d:0e:0f . 4095 }
1d03cd
+[
1d03cd
+    {
1d03cd
+        "match": {
1d03cd
+            "left": {
1d03cd
+                "concat": [
1d03cd
+                    {
1d03cd
+                        "payload": {
1d03cd
+                            "field": "saddr",
1d03cd
+                            "protocol": "ether"
1d03cd
+                        }
1d03cd
+                    },
1d03cd
+                    {
1d03cd
+                        "payload": {
1d03cd
+                            "field": "id",
1d03cd
+                            "protocol": "vlan"
1d03cd
+                        }
1d03cd
+                    }
1d03cd
+                ]
1d03cd
+            },
1d03cd
+            "op": "==",
1d03cd
+            "right": {
1d03cd
+                "set": [
1d03cd
+                    {
1d03cd
+                        "concat": [
1d03cd
+                            "0a:0b:0c:0d:0e:0f",
1d03cd
+                            42
1d03cd
+                        ]
1d03cd
+                    },
1d03cd
+                    {
1d03cd
+                        "concat": [
1d03cd
+                            "0a:0b:0c:0d:0e:0f",
1d03cd
+                            4095
1d03cd
+                        ]
1d03cd
+                    }
1d03cd
+                ]
1d03cd
+            }
1d03cd
+        }
1d03cd
+    }
1d03cd
+]
1d03cd
diff --git a/tests/py/bridge/vlan.t.payload b/tests/py/bridge/vlan.t.payload
1d03cd
index 713670e..62e4b89 100644
1d03cd
--- a/tests/py/bridge/vlan.t.payload
1d03cd
+++ b/tests/py/bridge/vlan.t.payload
1d03cd
@@ -292,3 +292,15 @@ bridge test-bridge input
1d03cd
   [ payload load 2b @ link header + 14 => reg 1 ]
1d03cd
   [ bitwise reg 1 = ( reg 1 & 0x0000ff0f ) ^ 0x00000000 ]
1d03cd
   [ cmp eq reg 1 0x00000200 ]
1d03cd
+
1d03cd
+# ether saddr . vlan id { 0a:0b:0c:0d:0e:0f . 42, 0a:0b:0c:0d:0e:0f . 4095 }
1d03cd
+__set%d test-bridge 3 size 2
1d03cd
+__set%d test-bridge 0
1d03cd
+	element 0d0c0b0a 00000f0e 00002a00  : 0 [end]	element 0d0c0b0a 00000f0e 0000ff0f  : 0 [end]
1d03cd
+bridge test-bridge input
1d03cd
+  [ payload load 2b @ link header + 12 => reg 1 ]
1d03cd
+  [ cmp eq reg 1 0x00000081 ]
1d03cd
+  [ payload load 6b @ link header + 6 => reg 1 ]
1d03cd
+  [ payload load 2b @ link header + 14 => reg 10 ]
1d03cd
+  [ bitwise reg 10 = ( reg 10 & 0x0000ff0f ) ^ 0x00000000 ]
1d03cd
+  [ lookup reg 1 set __set%d ]
1d03cd
diff --git a/tests/py/bridge/vlan.t.payload.netdev b/tests/py/bridge/vlan.t.payload.netdev
1d03cd
index 98a2a2b..1018d4c 100644
1d03cd
--- a/tests/py/bridge/vlan.t.payload.netdev
1d03cd
+++ b/tests/py/bridge/vlan.t.payload.netdev
1d03cd
@@ -342,3 +342,17 @@ netdev test-netdev ingress
1d03cd
   [ payload load 2b @ link header + 14 => reg 1 ]
1d03cd
   [ bitwise reg 1 = ( reg 1 & 0x0000ff0f ) ^ 0x00000000 ]
1d03cd
   [ cmp eq reg 1 0x00000100 ]
1d03cd
+
1d03cd
+# ether saddr . vlan id { 0a:0b:0c:0d:0e:0f . 42, 0a:0b:0c:0d:0e:0f . 4095 }
1d03cd
+__set%d test-netdev 3 size 2
1d03cd
+__set%d test-netdev 0
1d03cd
+	element 0d0c0b0a 00000f0e 00002a00  : 0 [end]	element 0d0c0b0a 00000f0e 0000ff0f  : 0 [end]
1d03cd
+netdev test-netdev ingress
1d03cd
+  [ meta load iiftype => reg 1 ]
1d03cd
+  [ cmp eq reg 1 0x00000001 ]
1d03cd
+  [ payload load 2b @ link header + 12 => reg 1 ]
1d03cd
+  [ cmp eq reg 1 0x00000081 ]
1d03cd
+  [ payload load 6b @ link header + 6 => reg 1 ]
1d03cd
+  [ payload load 2b @ link header + 14 => reg 10 ]
1d03cd
+  [ bitwise reg 10 = ( reg 10 & 0x0000ff0f ) ^ 0x00000000 ]
1d03cd
+  [ lookup reg 1 set __set%d ]
1d03cd
-- 
1d03cd
2.39.1
1d03cd