diff --git a/0014-evaluate-allow-to-re-use-existing-metered-set.patch b/0014-evaluate-allow-to-re-use-existing-metered-set.patch new file mode 100644 index 0000000..2d6f92e --- /dev/null +++ b/0014-evaluate-allow-to-re-use-existing-metered-set.patch @@ -0,0 +1,271 @@ +From b3c1312b5815b004614d79eae2ad731c6883ce6f Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Wed, 22 Jan 2025 10:18:04 +0100 +Subject: [PATCH] evaluate: allow to re-use existing metered set + +JIRA: https://issues.redhat.com/browse/RHEL-75507 +Upstream Status: nftables commit 639a111e91341cffdc6d86b847aa654646c799cf + +commit 639a111e91341cffdc6d86b847aa654646c799cf +Author: Florian Westphal +Date: Wed Jan 22 10:18:04 2025 +0100 + + evaluate: allow to re-use existing metered set + + Blamed commit translates old meter syntax (which used to allocate an + anonymous set) to dynamic sets. + + A side effect of this is that re-adding a meter rule after chain was + flushed results in an error, unlike anonymous sets named sets are not + impacted by the flush. + + Refine this: if a set of the same name exists and is compatible, then + re-use it instead of returning an error. + + Also pick up the reproducer kindly provided by the reporter and place it + in the shell test directory. + + Fixes: b8f8ddfff733 ("evaluate: translate meter into dynamic set") + Reported-by: Yi Chen + Signed-off-by: Florian Westphal + +Signed-off-by: Eric Garver +--- + src/evaluate.c | 43 +++++-- + .../sets/dumps/meter_set_reuse.json-nft | 105 ++++++++++++++++++ + .../testcases/sets/dumps/meter_set_reuse.nft | 11 ++ + tests/shell/testcases/sets/meter_set_reuse | 20 ++++ + 4 files changed, 170 insertions(+), 9 deletions(-) + create mode 100644 tests/shell/testcases/sets/dumps/meter_set_reuse.json-nft + create mode 100644 tests/shell/testcases/sets/dumps/meter_set_reuse.nft + create mode 100755 tests/shell/testcases/sets/meter_set_reuse + +diff --git a/src/evaluate.c b/src/evaluate.c +index 593a0140e92a..c9cbaa6ae648 100644 +--- a/src/evaluate.c ++++ b/src/evaluate.c +@@ -3338,7 +3338,7 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt) + + static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt) + { +- struct expr *key, *set, *setref; ++ struct expr *key, *setref; + struct set *existing_set; + struct table *table; + +@@ -3349,7 +3349,9 @@ static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt) + return table_not_found(ctx); + + existing_set = set_cache_find(table, stmt->meter.name); +- if (existing_set) ++ if (existing_set && ++ (!set_is_meter_compat(existing_set->flags) || ++ set_is_map(existing_set->flags))) + return cmd_error(ctx, &stmt->location, + "%s; meter '%s' overlaps an existing %s '%s' in family %s", + strerror(EEXIST), +@@ -3370,17 +3372,40 @@ static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt) + + /* Declare an empty set */ + key = stmt->meter.key; +- set = set_expr_alloc(&key->location, NULL); +- set->set_flags |= NFT_SET_EVAL; +- if (key->timeout) +- set->set_flags |= NFT_SET_TIMEOUT; ++ if (existing_set) { ++ if ((existing_set->flags & NFT_SET_TIMEOUT) && !key->timeout) ++ return expr_error(ctx->msgs, stmt->meter.key, ++ "existing set '%s' has timeout flag", ++ stmt->meter.name); ++ ++ if ((existing_set->flags & NFT_SET_TIMEOUT) == 0 && key->timeout) ++ return expr_error(ctx->msgs, stmt->meter.key, ++ "existing set '%s' lacks timeout flag", ++ stmt->meter.name); ++ ++ if (stmt->meter.size > 0 && existing_set->desc.size != stmt->meter.size) ++ return expr_error(ctx->msgs, stmt->meter.key, ++ "existing set '%s' has size %u, meter has %u", ++ stmt->meter.name, existing_set->desc.size, ++ stmt->meter.size); ++ setref = set_ref_expr_alloc(&key->location, existing_set); ++ } else { ++ struct expr *set; ++ ++ set = set_expr_alloc(&key->location, existing_set); ++ if (key->timeout) ++ set->set_flags |= NFT_SET_TIMEOUT; ++ ++ set->set_flags |= NFT_SET_EVAL; ++ setref = implicit_set_declaration(ctx, stmt->meter.name, ++ expr_get(key), NULL, set, 0); ++ if (setref) ++ setref->set->desc.size = stmt->meter.size; ++ } + +- setref = implicit_set_declaration(ctx, stmt->meter.name, +- expr_get(key), NULL, set, 0); + if (!setref) + return -1; + +- setref->set->desc.size = stmt->meter.size; + stmt->meter.set = setref; + + if (stmt_evaluate(ctx, stmt->meter.stmt) < 0) +diff --git a/tests/shell/testcases/sets/dumps/meter_set_reuse.json-nft b/tests/shell/testcases/sets/dumps/meter_set_reuse.json-nft +new file mode 100644 +index 000000000000..ab4ac06184d0 +--- /dev/null ++++ b/tests/shell/testcases/sets/dumps/meter_set_reuse.json-nft +@@ -0,0 +1,105 @@ ++{ ++ "nftables": [ ++ { ++ "metainfo": { ++ "version": "VERSION", ++ "release_name": "RELEASE_NAME", ++ "json_schema_version": 1 ++ } ++ }, ++ { ++ "table": { ++ "family": "ip", ++ "name": "filter", ++ "handle": 0 ++ } ++ }, ++ { ++ "chain": { ++ "family": "ip", ++ "table": "filter", ++ "name": "input", ++ "handle": 0 ++ } ++ }, ++ { ++ "set": { ++ "family": "ip", ++ "name": "http1", ++ "table": "filter", ++ "type": [ ++ "inet_service", ++ "ipv4_addr" ++ ], ++ "handle": 0, ++ "size": 65535, ++ "flags": [ ++ "dynamic" ++ ] ++ } ++ }, ++ { ++ "rule": { ++ "family": "ip", ++ "table": "filter", ++ "chain": "input", ++ "handle": 0, ++ "expr": [ ++ { ++ "match": { ++ "op": "==", ++ "left": { ++ "payload": { ++ "protocol": "tcp", ++ "field": "dport" ++ } ++ }, ++ "right": 80 ++ } ++ }, ++ { ++ "set": { ++ "op": "add", ++ "elem": { ++ "concat": [ ++ { ++ "payload": { ++ "protocol": "tcp", ++ "field": "dport" ++ } ++ }, ++ { ++ "payload": { ++ "protocol": "ip", ++ "field": "saddr" ++ } ++ } ++ ] ++ }, ++ "set": "@http1", ++ "stmt": [ ++ { ++ "limit": { ++ "rate": 200, ++ "burst": 5, ++ "per": "second", ++ "inv": true ++ } ++ } ++ ] ++ } ++ }, ++ { ++ "counter": { ++ "packets": 0, ++ "bytes": 0 ++ } ++ }, ++ { ++ "drop": null ++ } ++ ] ++ } ++ } ++ ] ++} +diff --git a/tests/shell/testcases/sets/dumps/meter_set_reuse.nft b/tests/shell/testcases/sets/dumps/meter_set_reuse.nft +new file mode 100644 +index 000000000000..f911acaffb85 +--- /dev/null ++++ b/tests/shell/testcases/sets/dumps/meter_set_reuse.nft +@@ -0,0 +1,11 @@ ++table ip filter { ++ set http1 { ++ type inet_service . ipv4_addr ++ size 65535 ++ flags dynamic ++ } ++ ++ chain input { ++ tcp dport 80 add @http1 { tcp dport . ip saddr limit rate over 200/second burst 5 packets } counter packets 0 bytes 0 drop ++ } ++} +diff --git a/tests/shell/testcases/sets/meter_set_reuse b/tests/shell/testcases/sets/meter_set_reuse +new file mode 100755 +index 000000000000..94eccc1a7b82 +--- /dev/null ++++ b/tests/shell/testcases/sets/meter_set_reuse +@@ -0,0 +1,20 @@ ++#!/bin/bash ++ ++set -e ++ ++addrule() ++{ ++ $NFT add rule ip filter input tcp dport 80 meter http1 { tcp dport . ip saddr limit rate over 200/second } counter drop ++} ++ ++$NFT add table filter ++$NFT add chain filter input ++addrule ++ ++$NFT list meters ++ ++# This used to remove the anon set, but not anymore ++$NFT flush chain filter input ++ ++# This re-add should work. ++addrule +-- +2.48.1 + diff --git a/nftables.spec b/nftables.spec index 027e153..31f6f98 100644 --- a/nftables.spec +++ b/nftables.spec @@ -1,6 +1,6 @@ Name: nftables Version: 1.1.1 -Release: 3%{?dist} +Release: 4%{?dist} # Upstream released a 0.100 version, then 0.4. Need Epoch to get back on track. Epoch: 1 Summary: Netfilter Tables userspace utilities @@ -29,6 +29,7 @@ Patch10: 0010-json-collapse-set-element-commands-from-parser.patch Patch11: 0011-json-Support-typeof-in-set-and-map-types.patch Patch12: 0012-tests-py-Fix-for-storing-payload-into-missing-file.patch Patch13: 0013-monitor-Recognize-flowtable-add-del-events.patch +Patch14: 0014-evaluate-allow-to-re-use-existing-metered-set.patch BuildRequires: autoconf BuildRequires: automake @@ -143,6 +144,9 @@ cd py/ %files -n python3-nftables -f %{pyproject_files} %changelog +* Mon Mar 03 2025 Eric Garver [1.1.1-4.el10] +- evaluate: allow to re-use existing metered set [RHEL-75507] + * Fri Nov 15 2024 Phil Sutter [1.1.1-3.el10] - Fix for typo in gating.yaml (Phil Sutter) [RHEL-65346]