Blame SOURCES/0014-proto-track-full-stack-of-seen-l2-protocols-not-just.patch

1d03cd
From 72f0ca53bcc6586b921ff70dac9f3a911e4ec170 Mon Sep 17 00:00:00 2001
1d03cd
From: Phil Sutter <psutter@redhat.com>
1d03cd
Date: Thu, 9 Feb 2023 10:27:57 +0100
1d03cd
Subject: [PATCH] proto: track full stack of seen l2 protocols, not just
1d03cd
 cumulative offset
1d03cd
1d03cd
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2094887
1d03cd
Upstream Status: nftables commit 0d9daa0407212
1d03cd
1d03cd
commit 0d9daa0407212c8cc89b3ea8aee031ddf0109b08
1d03cd
Author: Florian Westphal <fw@strlen.de>
1d03cd
Date:   Mon Jul 25 14:32:13 2022 +0200
1d03cd
1d03cd
    proto: track full stack of seen l2 protocols, not just cumulative offset
1d03cd
1d03cd
    For input, a cumulative size counter of all pushed l2 headers is enough,
1d03cd
    because we have the full expression tree available to us.
1d03cd
1d03cd
    For delinearization we need to track all seen l2 headers, else we lose
1d03cd
    information that we might need at a later time.
1d03cd
1d03cd
    Consider:
1d03cd
1d03cd
    rule netdev nt nc set update ether saddr . vlan id
1d03cd
1d03cd
    during delinearization, the vlan proto_desc replaces the ethernet one,
1d03cd
    and by the time we try to split the concatenation apart we will search
1d03cd
    the ether saddr offset vs. the templates for proto_vlan.
1d03cd
1d03cd
    This replaces the offset with an array that stores the protocol
1d03cd
    descriptions seen.
1d03cd
1d03cd
    Then, if the payload offset is larger than our description, search the
1d03cd
    l2 stack and adjust the offset until we're within the expected offset
1d03cd
    boundary.
1d03cd
1d03cd
    Reported-by: Eric Garver <eric@garver.life>
1d03cd
    Signed-off-by: Florian Westphal <fw@strlen.de>
1d03cd
1d03cd
Signed-off-by: Phil Sutter <psutter@redhat.com>
1d03cd
---
1d03cd
 include/proto.h           |  3 +-
1d03cd
 src/evaluate.c            | 15 +++++++--
1d03cd
 src/netlink_delinearize.c |  5 ---
1d03cd
 src/payload.c             | 67 ++++++++++++++++++++++++++++++++-------
1d03cd
 src/proto.c               |  2 --
1d03cd
 5 files changed, 71 insertions(+), 21 deletions(-)
1d03cd
1d03cd
diff --git a/include/proto.h b/include/proto.h
1d03cd
index a04240a..35e760c 100644
1d03cd
--- a/include/proto.h
1d03cd
+++ b/include/proto.h
1d03cd
@@ -193,13 +193,14 @@ struct proto_ctx {
1d03cd
 	struct {
1d03cd
 		struct location			location;
1d03cd
 		const struct proto_desc		*desc;
1d03cd
-		unsigned int			offset;
1d03cd
 		struct {
1d03cd
 			struct location		location;
1d03cd
 			const struct proto_desc	*desc;
1d03cd
 		} protos[PROTO_CTX_NUM_PROTOS];
1d03cd
 		unsigned int			num_protos;
1d03cd
 	} protocol[PROTO_BASE_MAX + 1];
1d03cd
+	const struct proto_desc *stacked_ll[PROTO_CTX_NUM_PROTOS];
1d03cd
+	uint8_t stacked_ll_count;
1d03cd
 };
1d03cd
 
1d03cd
 extern void proto_ctx_init(struct proto_ctx *ctx, unsigned int family,
1d03cd
diff --git a/src/evaluate.c b/src/evaluate.c
1d03cd
index 82bf131..9246064 100644
1d03cd
--- a/src/evaluate.c
1d03cd
+++ b/src/evaluate.c
1d03cd
@@ -678,7 +678,13 @@ static int resolve_protocol_conflict(struct eval_ctx *ctx,
1d03cd
 	    conflict_resolution_gen_dependency(ctx, link, payload, &nstmt) < 0)
1d03cd
 		return 1;
1d03cd
 
1d03cd
-	payload->payload.offset += ctx->pctx.protocol[base].offset;
1d03cd
+	if (base == PROTO_BASE_LL_HDR) {
1d03cd
+		unsigned int i;
1d03cd
+
1d03cd
+		for (i = 0; i < ctx->pctx.stacked_ll_count; i++)
1d03cd
+			payload->payload.offset += ctx->pctx.stacked_ll[i]->length;
1d03cd
+	}
1d03cd
+
1d03cd
 	rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
1d03cd
 
1d03cd
 	return 0;
1d03cd
@@ -727,7 +733,12 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
1d03cd
 	if (desc == payload->payload.desc) {
1d03cd
 		const struct proto_hdr_template *tmpl;
1d03cd
 
1d03cd
-		payload->payload.offset += ctx->pctx.protocol[base].offset;
1d03cd
+		if (desc->base == PROTO_BASE_LL_HDR) {
1d03cd
+			unsigned int i;
1d03cd
+
1d03cd
+			for (i = 0; i < ctx->pctx.stacked_ll_count; i++)
1d03cd
+				payload->payload.offset += ctx->pctx.stacked_ll[i]->length;
1d03cd
+		}
1d03cd
 check_icmp:
1d03cd
 		if (desc != &proto_icmp && desc != &proto_icmp6)
1d03cd
 			return 0;
1d03cd
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
1d03cd
index cba419d..0b5519d 100644
1d03cd
--- a/src/netlink_delinearize.c
1d03cd
+++ b/src/netlink_delinearize.c
1d03cd
@@ -1976,11 +1976,6 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx,
1d03cd
 				      struct expr *expr,
1d03cd
 				      struct expr *payload)
1d03cd
 {
1d03cd
-	enum proto_bases base = payload->payload.base;
1d03cd
-
1d03cd
-	assert(payload->payload.offset >= ctx->pctx.protocol[base].offset);
1d03cd
-	payload->payload.offset -= ctx->pctx.protocol[base].offset;
1d03cd
-
1d03cd
 	switch (expr->op) {
1d03cd
 	case OP_EQ:
1d03cd
 	case OP_NEQ:
1d03cd
diff --git a/src/payload.c b/src/payload.c
1d03cd
index 66418cd..2c0d0ac 100644
1d03cd
--- a/src/payload.c
1d03cd
+++ b/src/payload.c
1d03cd
@@ -116,8 +116,13 @@ static void payload_expr_pctx_update(struct proto_ctx *ctx,
1d03cd
 	if (desc->base == base->base) {
1d03cd
 		assert(base->length > 0);
1d03cd
 
1d03cd
-		if (!left->payload.is_raw)
1d03cd
-			ctx->protocol[base->base].offset += base->length;
1d03cd
+		if (!left->payload.is_raw) {
1d03cd
+			if (desc->base == PROTO_BASE_LL_HDR &&
1d03cd
+			    ctx->stacked_ll_count < PROTO_CTX_NUM_PROTOS) {
1d03cd
+				ctx->stacked_ll[ctx->stacked_ll_count] = base;
1d03cd
+				ctx->stacked_ll_count++;
1d03cd
+			}
1d03cd
+		}
1d03cd
 	}
1d03cd
 	proto_ctx_update(ctx, desc->base, loc, desc);
1d03cd
 }
1d03cd
@@ -869,6 +874,38 @@ void exthdr_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr,
1d03cd
 	}
1d03cd
 }
1d03cd
 
1d03cd
+static const struct proto_desc *get_stacked_desc(const struct proto_ctx *ctx,
1d03cd
+						 const struct proto_desc *top,
1d03cd
+						 const struct expr *e,
1d03cd
+						 unsigned int *skip)
1d03cd
+{
1d03cd
+	unsigned int i, total, payload_offset = e->payload.offset;
1d03cd
+
1d03cd
+	assert(e->etype == EXPR_PAYLOAD);
1d03cd
+
1d03cd
+	if (e->payload.base != PROTO_BASE_LL_HDR ||
1d03cd
+	    payload_offset < top->length) {
1d03cd
+		*skip = 0;
1d03cd
+		return top;
1d03cd
+	}
1d03cd
+
1d03cd
+	for (i = 0, total = 0; i < ctx->stacked_ll_count; i++) {
1d03cd
+		const struct proto_desc *stacked;
1d03cd
+
1d03cd
+		stacked = ctx->stacked_ll[i];
1d03cd
+		if (payload_offset < stacked->length) {
1d03cd
+			*skip = total;
1d03cd
+			return stacked;
1d03cd
+		}
1d03cd
+
1d03cd
+		payload_offset -= stacked->length;
1d03cd
+		total += stacked->length;
1d03cd
+	}
1d03cd
+
1d03cd
+	*skip = total;
1d03cd
+	return top;
1d03cd
+}
1d03cd
+
1d03cd
 /**
1d03cd
  * payload_expr_complete - fill in type information of a raw payload expr
1d03cd
  *
1d03cd
@@ -880,9 +917,10 @@ void exthdr_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr,
1d03cd
  */
1d03cd
 void payload_expr_complete(struct expr *expr, const struct proto_ctx *ctx)
1d03cd
 {
1d03cd
+	unsigned int payload_offset = expr->payload.offset;
1d03cd
 	const struct proto_desc *desc;
1d03cd
 	const struct proto_hdr_template *tmpl;
1d03cd
-	unsigned int i;
1d03cd
+	unsigned int i, total;
1d03cd
 
1d03cd
 	assert(expr->etype == EXPR_PAYLOAD);
1d03cd
 
1d03cd
@@ -891,9 +929,12 @@ void payload_expr_complete(struct expr *expr, const struct proto_ctx *ctx)
1d03cd
 		return;
1d03cd
 	assert(desc->base == expr->payload.base);
1d03cd
 
1d03cd
+	desc = get_stacked_desc(ctx, desc, expr, &total);
1d03cd
+	payload_offset -= total;
1d03cd
+
1d03cd
 	for (i = 0; i < array_size(desc->templates); i++) {
1d03cd
 		tmpl = &desc->templates[i];
1d03cd
-		if (tmpl->offset != expr->payload.offset ||
1d03cd
+		if (tmpl->offset != payload_offset ||
1d03cd
 		    tmpl->len    != expr->len)
1d03cd
 			continue;
1d03cd
 
1d03cd
@@ -950,6 +991,7 @@ bool payload_expr_trim(struct expr *expr, struct expr *mask,
1d03cd
 	unsigned int payload_len = expr->len;
1d03cd
 	const struct proto_desc *desc;
1d03cd
 	unsigned int off, i, len = 0;
1d03cd
+	unsigned int total;
1d03cd
 
1d03cd
 	assert(expr->etype == EXPR_PAYLOAD);
1d03cd
 
1d03cd
@@ -959,10 +1001,8 @@ bool payload_expr_trim(struct expr *expr, struct expr *mask,
1d03cd
 
1d03cd
 	assert(desc->base == expr->payload.base);
1d03cd
 
1d03cd
-	if (ctx->protocol[expr->payload.base].offset) {
1d03cd
-		assert(payload_offset >= ctx->protocol[expr->payload.base].offset);
1d03cd
-		payload_offset -= ctx->protocol[expr->payload.base].offset;
1d03cd
-	}
1d03cd
+	desc = get_stacked_desc(ctx, desc, expr, &total);
1d03cd
+	payload_offset -= total;
1d03cd
 
1d03cd
 	off = round_up(mask->len, BITS_PER_BYTE) - mask_len;
1d03cd
 	payload_offset += off;
1d03cd
@@ -1009,10 +1049,11 @@ bool payload_expr_trim(struct expr *expr, struct expr *mask,
1d03cd
 void payload_expr_expand(struct list_head *list, struct expr *expr,
1d03cd
 			 const struct proto_ctx *ctx)
1d03cd
 {
1d03cd
+	unsigned int payload_offset = expr->payload.offset;
1d03cd
 	const struct proto_hdr_template *tmpl;
1d03cd
 	const struct proto_desc *desc;
1d03cd
+	unsigned int i, total;
1d03cd
 	struct expr *new;
1d03cd
-	unsigned int i;
1d03cd
 
1d03cd
 	assert(expr->etype == EXPR_PAYLOAD);
1d03cd
 
1d03cd
@@ -1021,13 +1062,16 @@ void payload_expr_expand(struct list_head *list, struct expr *expr,
1d03cd
 		goto raw;
1d03cd
 	assert(desc->base == expr->payload.base);
1d03cd
 
1d03cd
+	desc = get_stacked_desc(ctx, desc, expr, &total);
1d03cd
+	payload_offset -= total;
1d03cd
+
1d03cd
 	for (i = 1; i < array_size(desc->templates); i++) {
1d03cd
 		tmpl = &desc->templates[i];
1d03cd
 
1d03cd
 		if (tmpl->len == 0)
1d03cd
 			break;
1d03cd
 
1d03cd
-		if (tmpl->offset != expr->payload.offset)
1d03cd
+		if (tmpl->offset != payload_offset)
1d03cd
 			continue;
1d03cd
 
1d03cd
 		if (tmpl->icmp_dep && ctx->th_dep.icmp.type &&
1d03cd
@@ -1039,6 +1083,7 @@ void payload_expr_expand(struct list_head *list, struct expr *expr,
1d03cd
 			list_add_tail(&new->list, list);
1d03cd
 			expr->len	     -= tmpl->len;
1d03cd
 			expr->payload.offset += tmpl->len;
1d03cd
+			payload_offset       += tmpl->len;
1d03cd
 			if (expr->len == 0)
1d03cd
 				return;
1d03cd
 		} else if (expr->len > 0) {
1d03cd
@@ -1051,7 +1096,7 @@ void payload_expr_expand(struct list_head *list, struct expr *expr,
1d03cd
 	}
1d03cd
 raw:
1d03cd
 	new = payload_expr_alloc(&expr->location, NULL, 0);
1d03cd
-	payload_init_raw(new, expr->payload.base, expr->payload.offset,
1d03cd
+	payload_init_raw(new, expr->payload.base, payload_offset,
1d03cd
 			 expr->len);
1d03cd
 	list_add_tail(&new->list, list);
1d03cd
 }
1d03cd
diff --git a/src/proto.c b/src/proto.c
1d03cd
index a013a00..2663f21 100644
1d03cd
--- a/src/proto.c
1d03cd
+++ b/src/proto.c
1d03cd
@@ -160,8 +160,6 @@ static void proto_ctx_debug(const struct proto_ctx *ctx, enum proto_bases base,
1d03cd
 			 proto_base_names[i],
1d03cd
 			 ctx->protocol[i].desc ? ctx->protocol[i].desc->name :
1d03cd
 						 "none");
1d03cd
-		if (ctx->protocol[i].offset)
1d03cd
-			pr_debug(" (offset: %u)", ctx->protocol[i].offset);
1d03cd
 		if (i == base)
1d03cd
 			pr_debug(" <-");
1d03cd
 		pr_debug("\n");
1d03cd
-- 
1d03cd
2.39.1
1d03cd