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

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