Compare commits

...

No commits in common. "c8" and "c9s-stable" have entirely different histories.

161 changed files with 12715 additions and 5825 deletions

1
.fmf/version Normal file
View File

@ -0,0 +1 @@
1

25
.gitignore vendored
View File

@ -1 +1,24 @@
SOURCES/nftables-1.0.4.tar.bz2 /nftables-20140326.tar.bz2
/nftables-20140330.tar.bz2
/nftables-20140426.tar.bz2
/nftables-20140704.tar.bz2
/nftables-20140903.tar.bz2
/nftables-0.4.tar.bz2
/nftables-0.5.tar.bz2
/nftables-0.6.tar.bz2
/nftables-0.7.tar.bz2
/nftables-0.8.tar.bz2
/nftables-0.8.1.tar.bz2
/nftables-0.8.2.tar.bz2
/nftables-0.8.3.tar.bz2
/nftables-0.8.4.tar.bz2
/nftables-0.8.5.tar.bz2
/nftables-0.9.0.tar.bz2
/nftables-0.9.1.tar.bz2
/nftables-0.9.2.tar.bz2
/nftables-0.9.3.tar.bz2
/nftables-0.9.6.tar.bz2
/nftables-0.9.7.tar.bz2
/nftables-0.9.8.tar.bz2
/nftables-1.0.4.tar.bz2
/nftables-1.0.9.tar.xz

View File

@ -1 +0,0 @@
e2e8b324cece1409a311284ff4fe26c3a5554809 SOURCES/nftables-1.0.4.tar.bz2

View File

@ -0,0 +1,337 @@
From 450520649ac5ac6f983b40e15e54863aab9d5bd7 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 14 Jun 2024 18:30:55 +0200
Subject: [PATCH] Add support for table's persist flag
JIRA: https://issues.redhat.com/browse/RHEL-32122
Upstream Status: nftables commit 4955ae1a81b73f9a61b7fbf1a73e11544513548e
Conflicts:
- Adjusted to missing commit ffd6b4790a728
("src: add free_const() and use it instead of xfree()")
commit 4955ae1a81b73f9a61b7fbf1a73e11544513548e
Author: Phil Sutter <phil@nwl.cc>
Date: Fri Dec 15 01:10:39 2023 +0100
Add support for table's persist flag
Bison parser lacked support for passing multiple flags, JSON parser
did not support table flags at all.
Document also 'owner' flag (and describe their relationship in nft.8.
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/libnftables-json.adoc | 11 ++++-
doc/nft.txt | 9 ++++
include/rule.h | 4 +-
src/parser_bison.y | 35 +++++++++------
src/parser_json.c | 49 ++++++++++++++++++++-
src/rule.c | 12 +++++
tests/shell/features/table_flag_persist.nft | 3 ++
tests/shell/testcases/owner/0002-persist | 36 +++++++++++++++
8 files changed, 142 insertions(+), 17 deletions(-)
create mode 100644 tests/shell/features/table_flag_persist.nft
create mode 100755 tests/shell/testcases/owner/0002-persist
diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc
index 3e6e1db..0e424c2 100644
--- a/doc/libnftables-json.adoc
+++ b/doc/libnftables-json.adoc
@@ -202,12 +202,19 @@ Rename a chain. The new name is expected in a dedicated property named
=== TABLE
[verse]
+____
*{ "table": {
"family":* 'STRING'*,
"name":* 'STRING'*,
- "handle":* 'NUMBER'
+ "handle":* 'NUMBER'*,
+ "flags":* 'TABLE_FLAGS'
*}}*
+'TABLE_FLAGS' := 'TABLE_FLAG' | *[* 'TABLE_FLAG_LIST' *]*
+'TABLE_FLAG_LIST' := 'TABLE_FLAG' [*,* 'TABLE_FLAG_LIST' ]
+'TABLE_FLAG' := *"dormant"* | *"owner"* | *"persist"*
+____
+
This object describes a table.
*family*::
@@ -217,6 +224,8 @@ This object describes a table.
*handle*::
The table's handle. In input, it is used only in *delete* command as
alternative to *name*.
+*flags*::
+ The table's flags.
=== CHAIN
[verse]
diff --git a/doc/nft.txt b/doc/nft.txt
index b08e32f..dba1b60 100644
--- a/doc/nft.txt
+++ b/doc/nft.txt
@@ -343,8 +343,17 @@ return an error.
|Flag | Description
|dormant |
table is not evaluated any more (base chains are unregistered).
+|owner |
+table is owned by the creating process.
+|persist |
+table shall outlive the owning process.
|=================
+Creating a table with flag *owner* excludes other processes from manipulating
+it or its contents. By default, it will be removed when the process exits.
+Setting flag *persist* will prevent this and the resulting orphaned table will
+accept a new owner, e.g. a restarting daemon maintaining the table.
+
.*Add, change, delete a table*
---------------------------------------
# start nft in interactive mode
diff --git a/include/rule.h b/include/rule.h
index 6236d29..a8bb11f 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -130,10 +130,12 @@ struct symbol *symbol_get(const struct scope *scope, const char *identifier);
enum table_flags {
TABLE_F_DORMANT = (1 << 0),
TABLE_F_OWNER = (1 << 1),
+ TABLE_F_PERSIST = (1 << 2),
};
-#define TABLE_FLAGS_MAX 2
+#define TABLE_FLAGS_MAX 3
const char *table_flag_name(uint32_t flag);
+unsigned int parse_table_flag(const char *name);
/**
* struct table - nftables table
diff --git a/src/parser_bison.y b/src/parser_bison.y
index c517dc3..5ced6e1 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -720,6 +720,8 @@ int nft_lex(void *, void *, void *);
%type <rule> rule rule_alloc
%destructor { rule_free($$); } rule
+%type <val> table_flags table_flag
+
%type <val> set_flag_list set_flag
%type <val> set_policy_spec
@@ -1874,20 +1876,9 @@ table_block_alloc : /* empty */
}
;
-table_options : FLAGS STRING
+table_options : FLAGS table_flags
{
- if (strcmp($2, "dormant") == 0) {
- $<table>0->flags |= TABLE_F_DORMANT;
- xfree($2);
- } else if (strcmp($2, "owner") == 0) {
- $<table>0->flags |= TABLE_F_OWNER;
- xfree($2);
- } else {
- erec_queue(error(&@2, "unknown table option %s", $2),
- state->msgs);
- xfree($2);
- YYERROR;
- }
+ $<table>0->flags |= $2;
}
| comment_spec
{
@@ -1899,6 +1890,24 @@ table_options : FLAGS STRING
}
;
+table_flags : table_flag
+ | table_flags COMMA table_flag
+ {
+ $$ = $1 | $3;
+ }
+ ;
+table_flag : STRING
+ {
+ $$ = parse_table_flag($1);
+ xfree($1);
+ if ($$ == 0) {
+ erec_queue(error(&@1, "unknown table option %s", $1),
+ state->msgs);
+ YYERROR;
+ }
+ }
+ ;
+
table_block : /* empty */ { $$ = $<table>-1; }
| table_block common_block
| table_block stmt_separator
diff --git a/src/parser_json.c b/src/parser_json.c
index 199241a..9e5b656 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -2941,6 +2941,45 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
return NULL;
}
+static int json_parse_table_flags(struct json_ctx *ctx, json_t *root,
+ enum table_flags *flags)
+{
+ json_t *tmp, *tmp2;
+ size_t index;
+ int flag;
+
+ if (json_unpack(root, "{s:o}", "flags", &tmp))
+ return 0;
+
+ if (json_is_string(tmp)) {
+ flag = parse_table_flag(json_string_value(tmp));
+ if (flag) {
+ *flags = flag;
+ return 0;
+ }
+ json_error(ctx, "Invalid table flag '%s'.",
+ json_string_value(tmp));
+ return 1;
+ }
+ if (!json_is_array(tmp)) {
+ json_error(ctx, "Unexpected table flags value.");
+ return 1;
+ }
+ json_array_foreach(tmp, index, tmp2) {
+ if (json_is_string(tmp2)) {
+ flag = parse_table_flag(json_string_value(tmp2));
+
+ if (flag) {
+ *flags |= flag;
+ continue;
+ }
+ }
+ json_error(ctx, "Invalid table flag at index %zu.", index);
+ return 1;
+ }
+ return 0;
+}
+
static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
enum cmd_ops op, enum cmd_obj obj)
{
@@ -2949,6 +2988,7 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
.table.location = *int_loc,
};
struct table *table = NULL;
+ enum table_flags flags = 0;
if (json_unpack_err(ctx, root, "{s:s}",
"family", &family))
@@ -2959,6 +2999,9 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
return NULL;
json_unpack(root, "{s:s}", "comment", &comment);
+ if (json_parse_table_flags(ctx, root, &flags))
+ return NULL;
+
} else if (op == CMD_DELETE &&
json_unpack(root, "{s:s}", "name", &h.table.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
@@ -2972,10 +3015,12 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
if (h.table.name)
h.table.name = xstrdup(h.table.name);
- if (comment) {
+ if (comment || flags) {
table = table_alloc();
handle_merge(&table->handle, &h);
- table->comment = xstrdup(comment);
+ if (comment)
+ table->comment = xstrdup(comment);
+ table->flags = flags;
}
if (op == CMD_ADD)
diff --git a/src/rule.c b/src/rule.c
index 739b7a5..a0e151d 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -1208,6 +1208,7 @@ struct table *table_lookup_fuzzy(const struct handle *h,
static const char *table_flags_name[TABLE_FLAGS_MAX] = {
"dormant",
"owner",
+ "persist",
};
const char *table_flag_name(uint32_t flag)
@@ -1218,6 +1219,17 @@ const char *table_flag_name(uint32_t flag)
return table_flags_name[flag];
}
+unsigned int parse_table_flag(const char *name)
+{
+ int i;
+
+ for (i = 0; i < TABLE_FLAGS_MAX; i++) {
+ if (!strcmp(name, table_flags_name[i]))
+ return 1 << i;
+ }
+ return 0;
+}
+
static void table_print_flags(const struct table *table, const char **delim,
struct output_ctx *octx)
{
diff --git a/tests/shell/features/table_flag_persist.nft b/tests/shell/features/table_flag_persist.nft
new file mode 100644
index 0000000..0da3e6d
--- /dev/null
+++ b/tests/shell/features/table_flag_persist.nft
@@ -0,0 +1,3 @@
+table t {
+ flags persist;
+}
diff --git a/tests/shell/testcases/owner/0002-persist b/tests/shell/testcases/owner/0002-persist
new file mode 100755
index 0000000..cf4b8f1
--- /dev/null
+++ b/tests/shell/testcases/owner/0002-persist
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_table_flag_owner)
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_table_flag_persist)
+
+die() {
+ echo "$@"
+ exit 1
+}
+
+$NFT -f - <<EOF
+table ip t {
+ flags owner, persist
+}
+EOF
+[[ $? -eq 0 ]] || {
+ die "table add failed"
+}
+
+$NFT list ruleset | grep -q 'table ip t' || {
+ die "table does not persist"
+}
+$NFT list ruleset | grep -q 'flags persist$' || {
+ die "unexpected flags in orphaned table"
+}
+
+$NFT -f - <<EOF
+table ip t {
+ flags owner, persist
+}
+EOF
+[[ $? -eq 0 ]] || {
+ die "retake ownership failed"
+}
+
+exit 0

View File

@ -0,0 +1,65 @@
From 2ef49849b901184c3d97c98c05ffa6418b50af1e Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 2 Jul 2024 16:41:22 +0200
Subject: [PATCH] cache: Always set NFT_CACHE_TERSE for list cmd with --terse
JIRA: https://issues.redhat.com/browse/RHEL-45633
Upstream Status: nftables commit cd4e947032a57a585b1a457ce03f546afc7ba033
commit cd4e947032a57a585b1a457ce03f546afc7ba033
Author: Phil Sutter <phil@nwl.cc>
Date: Thu Feb 8 02:10:48 2024 +0100
cache: Always set NFT_CACHE_TERSE for list cmd with --terse
This fixes at least 'nft -t list table ...' and 'nft -t list set ...'.
Note how --terse handling for 'list sets/maps' remains in place since
setting NFT_CACHE_TERSE does not fully undo NFT_CACHE_SETELEM: setting
both enables fetching of anonymous sets which is pointless for that
command.
Reported-by: anton.khazan@gmail.com
Link: https://bugzilla.netfilter.org/show_bug.cgi?id=1735
Suggested-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/cache.c | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/src/cache.c b/src/cache.c
index 4e89fe1..0ac0f7c 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -230,8 +230,6 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
}
if (filter->list.table && filter->list.set)
flags |= NFT_CACHE_TABLE | NFT_CACHE_SET | NFT_CACHE_SETELEM;
- else if (nft_output_terse(&nft->output))
- flags |= NFT_CACHE_FULL | NFT_CACHE_TERSE;
else
flags |= NFT_CACHE_FULL;
break;
@@ -257,17 +255,15 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
flags |= NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE;
break;
case CMD_OBJ_RULESET:
- if (nft_output_terse(&nft->output))
- flags |= NFT_CACHE_FULL | NFT_CACHE_TERSE;
- else
- flags |= NFT_CACHE_FULL;
- break;
default:
flags |= NFT_CACHE_FULL;
break;
}
flags |= NFT_CACHE_REFRESH;
+ if (nft_output_terse(&nft->output))
+ flags |= NFT_CACHE_TERSE;
+
return flags;
}

View File

@ -0,0 +1,213 @@
From f85059245c5c5da7c0db98c3f35ab28a73e7cf4c Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Wed, 23 Apr 2025 14:25:43 +0200
Subject: [PATCH] json: deal appropriately with multidevice in chain
JIRA: https://issues.redhat.com/browse/RHEL-88181
Upstream Status: nftables commit 4dfb5b2010917da3f9f11c83931069931a7d6fb3
Conflicts: Dropped changes to shell testsuite stored dumps, downstream
misses commit 3d24b16b9ac13 ("tests/shell: add JSON dump
files").
commit 4dfb5b2010917da3f9f11c83931069931a7d6fb3
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Thu Nov 23 10:36:50 2023 +0100
json: deal appropriately with multidevice in chain
Chain device support is broken in JSON: listing does not include devices
and parser only deals with one single device.
Use existing json_parse_flowtable_devs() function, rename it to
json_parse_devs() to parse the device array.
Use the dev_array that contains the device names (as string) for the
listing.
Update incorrect .json-nft files in tests/shell.
Fixes: 3fdc7541fba0 ("src: add multidevice support for netdev chain")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/json.c | 25 ++++++-------
src/parser_json.c | 91 +++++++++++++++++++++++------------------------
2 files changed, 58 insertions(+), 58 deletions(-)
diff --git a/src/json.c b/src/json.c
index 068c423..dad93e5 100644
--- a/src/json.c
+++ b/src/json.c
@@ -257,9 +257,8 @@ static json_t *rule_print_json(struct output_ctx *octx,
static json_t *chain_print_json(const struct chain *chain)
{
- int priority, policy, n = 0;
- struct expr *dev, *expr;
- json_t *root, *tmp;
+ json_t *root, *tmp, *devs = NULL;
+ int priority, policy, i;
root = json_pack("{s:s, s:s, s:s, s:I}",
"family", family2str(chain->handle.family),
@@ -281,17 +280,19 @@ static json_t *chain_print_json(const struct chain *chain)
chain->hook.num),
"prio", priority,
"policy", chain_policy2str(policy));
- if (chain->dev_expr) {
- list_for_each_entry(expr, &chain->dev_expr->expressions, list) {
- dev = expr;
- n++;
- }
- }
- if (n == 1) {
- json_object_set_new(tmp, "dev",
- json_string(dev->identifier));
+ for (i = 0; i < chain->dev_array_len; i++) {
+ const char *dev = chain->dev_array[i];
+ if (!devs)
+ devs = json_string(dev);
+ else if (json_is_string(devs))
+ devs = json_pack("[o, s]", devs, dev);
+ else
+ json_array_append_new(devs, json_string(dev));
}
+ if (devs)
+ json_object_set_new(root, "dev", devs);
+
json_object_update(root, tmp);
json_decref(tmp);
}
diff --git a/src/parser_json.c b/src/parser_json.c
index 9e5b656..d57aa6b 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -3045,14 +3045,49 @@ static struct expr *parse_policy(const char *policy)
sizeof(int) * BITS_PER_BYTE, &policy_num);
}
+static struct expr *json_parse_devs(struct json_ctx *ctx, json_t *root)
+{
+ struct expr *tmp, *expr = compound_expr_alloc(int_loc, EXPR_LIST);
+ const char *dev;
+ json_t *value;
+ size_t index;
+
+ if (!json_unpack(root, "s", &dev)) {
+ tmp = constant_expr_alloc(int_loc, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen(dev) * BITS_PER_BYTE, dev);
+ compound_expr_add(expr, tmp);
+ return expr;
+ }
+ if (!json_is_array(root)) {
+ expr_free(expr);
+ return NULL;
+ }
+
+ json_array_foreach(root, index, value) {
+ if (json_unpack(value, "s", &dev)) {
+ json_error(ctx, "Invalid device at index %zu.",
+ index);
+ expr_free(expr);
+ return NULL;
+ }
+ tmp = constant_expr_alloc(int_loc, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen(dev) * BITS_PER_BYTE, dev);
+ compound_expr_add(expr, tmp);
+ }
+ return expr;
+}
+
static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
enum cmd_ops op, enum cmd_obj obj)
{
struct handle h = {
.table.location = *int_loc,
};
- const char *family = "", *policy = "", *type, *hookstr, *name, *comment = NULL;
+ const char *family = "", *policy = "", *type, *hookstr, *comment = NULL;
struct chain *chain = NULL;
+ json_t *devs = NULL;
int prio;
if (json_unpack_err(ctx, root, "{s:s, s:s}",
@@ -3107,16 +3142,15 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
return NULL;
}
- if (!json_unpack(root, "{s:s}", "dev", &name)) {
- struct expr *dev_expr, *expr;
+ json_unpack(root, "{s:o}", "dev", &devs);
- dev_expr = compound_expr_alloc(int_loc, EXPR_LIST);
- expr = constant_expr_alloc(int_loc, &integer_type,
- BYTEORDER_HOST_ENDIAN,
- strlen(name) * BITS_PER_BYTE,
- name);
- compound_expr_add(dev_expr, expr);
- chain->dev_expr = dev_expr;
+ if (devs) {
+ chain->dev_expr = json_parse_devs(ctx, devs);
+ if (!chain->dev_expr) {
+ json_error(ctx, "Invalid chain dev.");
+ chain_free(chain);
+ return NULL;
+ }
}
if (!json_unpack(root, "{s:s}", "policy", &policy)) {
@@ -3411,41 +3445,6 @@ static struct cmd *json_parse_cmd_add_element(struct json_ctx *ctx,
return cmd_alloc(op, cmd_obj, &h, int_loc, expr);
}
-static struct expr *json_parse_flowtable_devs(struct json_ctx *ctx,
- json_t *root)
-{
- struct expr *tmp, *expr = compound_expr_alloc(int_loc, EXPR_LIST);
- const char *dev;
- json_t *value;
- size_t index;
-
- if (!json_unpack(root, "s", &dev)) {
- tmp = constant_expr_alloc(int_loc, &string_type,
- BYTEORDER_HOST_ENDIAN,
- strlen(dev) * BITS_PER_BYTE, dev);
- compound_expr_add(expr, tmp);
- return expr;
- }
- if (!json_is_array(root)) {
- expr_free(expr);
- return NULL;
- }
-
- json_array_foreach(root, index, value) {
- if (json_unpack(value, "s", &dev)) {
- json_error(ctx, "Invalid flowtable dev at index %zu.",
- index);
- expr_free(expr);
- return NULL;
- }
- tmp = constant_expr_alloc(int_loc, &string_type,
- BYTEORDER_HOST_ENDIAN,
- strlen(dev) * BITS_PER_BYTE, dev);
- compound_expr_add(expr, tmp);
- }
- return expr;
-}
-
static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
json_t *root, enum cmd_ops op,
enum cmd_obj cmd_obj)
@@ -3506,7 +3505,7 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
sizeof(int) * BITS_PER_BYTE, &prio);
if (devs) {
- flowtable->dev_expr = json_parse_flowtable_devs(ctx, devs);
+ flowtable->dev_expr = json_parse_devs(ctx, devs);
if (!flowtable->dev_expr) {
json_error(ctx, "Invalid flowtable dev.");
flowtable_free(flowtable);

View File

@ -0,0 +1,280 @@
From c7225a21a31131edd16f018a4e4947944db88f01 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Wed, 23 Apr 2025 14:26:47 +0200
Subject: [PATCH] parser_json: fix handle memleak from error path
JIRA: https://issues.redhat.com/browse/RHEL-88181
Upstream Status: nftables commit 47e18c0eba51a538e1110322d1a9248b0501d7c8
commit 47e18c0eba51a538e1110322d1a9248b0501d7c8
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Mon Aug 19 21:34:49 2024 +0200
parser_json: fix handle memleak from error path
Based on patch from Sebastian Walz.
Fixes: 586ad210368b ("libnftables: Implement JSON parser")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_json.c | 93 ++++++++++++++++++++++++-----------------------
1 file changed, 47 insertions(+), 46 deletions(-)
diff --git a/src/parser_json.c b/src/parser_json.c
index d57aa6b..2acc248 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -3138,8 +3138,7 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
chain->hook.name = chain_hookname_lookup(hookstr);
if (!chain->hook.name) {
json_error(ctx, "Invalid chain hook '%s'.", hookstr);
- chain_free(chain);
- return NULL;
+ goto err_free_chain;
}
json_unpack(root, "{s:o}", "dev", &devs);
@@ -3148,8 +3147,7 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
chain->dev_expr = json_parse_devs(ctx, devs);
if (!chain->dev_expr) {
json_error(ctx, "Invalid chain dev.");
- chain_free(chain);
- return NULL;
+ goto err_free_chain;
}
}
@@ -3157,8 +3155,7 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
chain->policy = parse_policy(policy);
if (!chain->policy) {
json_error(ctx, "Unknown policy '%s'.", policy);
- chain_free(chain);
- return NULL;
+ goto err_free_chain;
}
}
@@ -3167,6 +3164,11 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
handle_merge(&chain->handle, &h);
return cmd_alloc(op, obj, &h, int_loc, chain);
+
+err_free_chain:
+ chain_free(chain);
+ handle_free(&h);
+ return NULL;
}
static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
@@ -3206,6 +3208,7 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
if (!json_is_array(tmp)) {
json_error(ctx, "Value of property \"expr\" must be an array.");
+ handle_free(&h);
return NULL;
}
@@ -3225,16 +3228,14 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
if (!json_is_object(value)) {
json_error(ctx, "Unexpected expr array element of type %s, expected object.",
json_typename(value));
- rule_free(rule);
- return NULL;
+ goto err_free_rule;
}
stmt = json_parse_stmt(ctx, value);
if (!stmt) {
json_error(ctx, "Parsing expr array at index %zd failed.", index);
- rule_free(rule);
- return NULL;
+ goto err_free_rule;
}
rule_stmt_append(rule, stmt);
@@ -3244,6 +3245,11 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
json_object_del(root, "handle");
return cmd_alloc(op, obj, &h, int_loc, rule);
+
+err_free_rule:
+ rule_free(rule);
+ handle_free(&h);
+ return NULL;
}
static int string_to_nft_object(const char *str)
@@ -3617,8 +3623,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
if (ret < 0 || ret >= (int)sizeof(obj->secmark.ctx)) {
json_error(ctx, "Invalid secmark context '%s', max length is %zu.",
tmp, sizeof(obj->secmark.ctx));
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
}
break;
@@ -3634,8 +3639,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
ret >= (int)sizeof(obj->ct_helper.name)) {
json_error(ctx, "Invalid CT helper type '%s', max length is %zu.",
tmp, sizeof(obj->ct_helper.name));
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
}
if (!json_unpack(root, "{s:s}", "protocol", &tmp)) {
@@ -3645,15 +3649,13 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj->ct_helper.l4proto = IPPROTO_UDP;
} else {
json_error(ctx, "Invalid ct helper protocol '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
}
if (!json_unpack(root, "{s:s}", "l3proto", &tmp) &&
parse_family(tmp, &l3proto)) {
json_error(ctx, "Invalid ct helper l3proto '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
obj->ct_helper.l3proto = l3proto;
break;
@@ -3667,23 +3669,19 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj->ct_timeout.l4proto = IPPROTO_UDP;
} else {
json_error(ctx, "Invalid ct timeout protocol '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
}
if (!json_unpack(root, "{s:s}", "l3proto", &tmp) &&
parse_family(tmp, &l3proto)) {
json_error(ctx, "Invalid ct timeout l3proto '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
obj->ct_timeout.l3proto = l3proto;
init_list_head(&obj->ct_timeout.timeout_list);
- if (json_parse_ct_timeout_policy(ctx, root, obj)) {
- obj_free(obj);
- return NULL;
- }
+ if (json_parse_ct_timeout_policy(ctx, root, obj))
+ goto err_free_obj;
break;
case NFT_OBJECT_CT_EXPECT:
cmd_obj = CMD_OBJ_CT_EXPECT;
@@ -3691,8 +3689,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
if (!json_unpack(root, "{s:s}", "l3proto", &tmp) &&
parse_family(tmp, &l3proto)) {
json_error(ctx, "Invalid ct expectation l3proto '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
obj->ct_expect.l3proto = l3proto;
if (!json_unpack(root, "{s:s}", "protocol", &tmp)) {
@@ -3702,8 +3699,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj->ct_expect.l4proto = IPPROTO_UDP;
} else {
json_error(ctx, "Invalid ct expectation protocol '%s'.", tmp);
- obj_free(obj);
- return NULL;
+ goto err_free_obj;
}
}
if (!json_unpack(root, "{s:i}", "dport", &i))
@@ -3717,10 +3713,9 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj->type = NFT_OBJECT_LIMIT;
if (json_unpack_err(ctx, root, "{s:I, s:s}",
"rate", &obj->limit.rate,
- "per", &tmp)) {
- obj_free(obj);
- return NULL;
- }
+ "per", &tmp))
+ goto err_free_obj;
+
json_unpack(root, "{s:s}", "rate_unit", &rate_unit);
json_unpack(root, "{s:b}", "inv", &inv);
json_unpack(root, "{s:i}", "burst", &obj->limit.burst);
@@ -3741,20 +3736,18 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
case CMD_OBJ_SYNPROXY:
obj->type = NFT_OBJECT_SYNPROXY;
if (json_unpack_err(ctx, root, "{s:i, s:i}",
- "mss", &i, "wscale", &j)) {
- obj_free(obj);
- return NULL;
- }
+ "mss", &i, "wscale", &j))
+ goto err_free_obj;
+
obj->synproxy.mss = i;
obj->synproxy.wscale = j;
obj->synproxy.flags |= NF_SYNPROXY_OPT_MSS;
obj->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE;
if (!json_unpack(root, "{s:o}", "flags", &jflags)) {
flags = json_parse_synproxy_flags(ctx, jflags);
- if (flags < 0) {
- obj_free(obj);
- return NULL;
- }
+ if (flags < 0)
+ goto err_free_obj;
+
obj->synproxy.flags |= flags;
}
break;
@@ -3766,6 +3759,11 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
json_object_del(root, "handle");
return cmd_alloc(op, cmd_obj, &h, int_loc, obj);
+
+err_free_obj:
+ obj_free(obj);
+ handle_free(&h);
+ return NULL;
}
static struct cmd *json_parse_cmd_add(struct json_ctx *ctx,
@@ -3879,8 +3877,7 @@ static struct cmd *json_parse_cmd_replace(struct json_ctx *ctx,
if (!json_is_object(value)) {
json_error(ctx, "Unexpected expr array element of type %s, expected object.",
json_typename(value));
- rule_free(rule);
- return NULL;
+ goto err_free_replace;
}
stmt = json_parse_stmt(ctx, value);
@@ -3888,8 +3885,7 @@ static struct cmd *json_parse_cmd_replace(struct json_ctx *ctx,
if (!stmt) {
json_error(ctx, "Parsing expr array at index %zd failed.",
index);
- rule_free(rule);
- return NULL;
+ goto err_free_replace;
}
rule_stmt_append(rule, stmt);
@@ -3899,6 +3895,11 @@ static struct cmd *json_parse_cmd_replace(struct json_ctx *ctx,
json_object_del(root, "handle");
return cmd_alloc(op, CMD_OBJ_RULE, &h, int_loc, rule);
+
+err_free_replace:
+ rule_free(rule);
+ handle_free(&h);
+ return NULL;
}
static struct cmd *json_parse_cmd_list_multiple(struct json_ctx *ctx,

View File

@ -0,0 +1,56 @@
From 1172955315cf4d14f0ddc53f26ff82447f0dadda Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 16 Oct 2025 16:12:09 +0200
Subject: [PATCH] tests: shell: Fix sets/reset_command_0 for current kernels
JIRA: https://issues.redhat.com/browse/RHEL-114095
Upstream Status: nftables commit 7a6089a400a573b9a4fd92f29c00a6be7b8ef269
commit 7a6089a400a573b9a4fd92f29c00a6be7b8ef269
Author: Phil Sutter <phil@nwl.cc>
Date: Thu Nov 2 16:02:14 2023 +0100
tests: shell: Fix sets/reset_command_0 for current kernels
Since kernel commit 4c90bba60c26 ("netfilter: nf_tables: do not refresh
timeout when resetting element"), element reset won't touch expiry
anymore. Invert the one check to make sure it remains unaltered, drop
the other testing behaviour for per-element timeouts.
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/shell/testcases/sets/reset_command_0 | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/tests/shell/testcases/sets/reset_command_0 b/tests/shell/testcases/sets/reset_command_0
index e663dac..d38ddb3 100755
--- a/tests/shell/testcases/sets/reset_command_0
+++ b/tests/shell/testcases/sets/reset_command_0
@@ -44,10 +44,10 @@ elem='element t s { 1.0.0.1 . udp . 53 }'
grep 'elements = ' | drop_seconds | uniq | wc -l) == 1 ]]
echo OK
-echo -n "counters and expiry are reset: "
+echo -n "counters are reset, expiry left alone: "
NEW=$($NFT "get $elem")
grep -q 'counter packets 0 bytes 0' <<< "$NEW"
-[[ $(expires_minutes <<< "$NEW") -gt 20 ]]
+[[ $(expires_minutes <<< "$NEW") -lt 20 ]]
echo OK
echo -n "get map elem matches reset map elem: "
@@ -80,12 +80,6 @@ OUT=$($NFT reset map t m)
$DIFF -u <(echo "$EXP") <(echo "$OUT")
echo OK
-echo -n "reset command respects per-element timeout: "
-VAL=$($NFT get element t s '{ 2.0.0.2 . tcp . 22 }' | expires_minutes)
-[[ $VAL -lt 15 ]] # custom timeout applies
-[[ $VAL -gt 10 ]] # expires was reset
-echo OK
-
echo -n "remaining elements are reset: "
OUT=$($NFT list ruleset)
grep -q '2.0.0.2 . tcp . 22 counter packets 0 bytes 0' <<< "$OUT"

View File

@ -0,0 +1,165 @@
From 52f4e05d55ef0215dd7df050ff93270f185c07b0 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 16 Oct 2025 16:12:09 +0200
Subject: [PATCH] tests: shell: connect chains to hook point
JIRA: https://issues.redhat.com/browse/RHEL-114095
Upstream Status: nftables commit 1fc78397e9a1fb5e41841b8b4e92a9eb9536c6f1
Conflicts: Dropped changes to .json-nft files missing downstream
commit 1fc78397e9a1fb5e41841b8b4e92a9eb9536c6f1
Author: Florian Westphal <fw@strlen.de>
Date: Wed Jul 10 02:33:37 2024 +0200
tests: shell: connect chains to hook point
These tests should fail because they contain a loop or exceed the jump stack.
But this depends on the kernel validating chains that are not bound to any
basechain/hook point.
Wire up the initial chain to filter type.
Without this tests will start to fail when kernel stops validating
chains that are not reachable by any base chain.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/shell/testcases/chains/0003jump_loop_1 | 3 ++-
tests/shell/testcases/chains/0010endless_jump_loop_1 | 2 +-
tests/shell/testcases/chains/0011endless_jump_loop_1 | 2 +-
tests/shell/testcases/chains/0018check_jump_loop_1 | 2 +-
tests/shell/testcases/chains/dumps/0003jump_loop_1.nft | 1 +
tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.nft | 1 +
tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.nft | 1 +
tests/shell/testcases/chains/dumps/0018check_jump_loop_1.nft | 1 +
tests/shell/testcases/transactions/0023rule_1 | 2 +-
tests/shell/testcases/transactions/anon_chain_loop | 2 +-
10 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/tests/shell/testcases/chains/0003jump_loop_1 b/tests/shell/testcases/chains/0003jump_loop_1
index 80e243f..1a8eaf6 100755
--- a/tests/shell/testcases/chains/0003jump_loop_1
+++ b/tests/shell/testcases/chains/0003jump_loop_1
@@ -5,8 +5,9 @@ set -e
MAX_JUMPS=16
$NFT add table t
+$NFT "add chain t c1 { type filter hook prerouting priority 0; }"
-for i in $(seq 1 $MAX_JUMPS)
+for i in $(seq 2 $MAX_JUMPS)
do
$NFT add chain t c${i}
done
diff --git a/tests/shell/testcases/chains/0010endless_jump_loop_1 b/tests/shell/testcases/chains/0010endless_jump_loop_1
index 5d3ef23..6000e5d 100755
--- a/tests/shell/testcases/chains/0010endless_jump_loop_1
+++ b/tests/shell/testcases/chains/0010endless_jump_loop_1
@@ -3,7 +3,7 @@
set -e
$NFT add table t
-$NFT add chain t c
+$NFT add chain "t c { type filter hook input priority 0; }"
# kernel should return ELOOP
$NFT add rule t c tcp dport vmap {1 : jump c} 2>/dev/null || exit 0
diff --git a/tests/shell/testcases/chains/0011endless_jump_loop_1 b/tests/shell/testcases/chains/0011endless_jump_loop_1
index d75932d..66abf8d 100755
--- a/tests/shell/testcases/chains/0011endless_jump_loop_1
+++ b/tests/shell/testcases/chains/0011endless_jump_loop_1
@@ -3,7 +3,7 @@
set -e
$NFT add table t
-$NFT add chain t c1
+$NFT add chain "t c1 { type filter hook forward priority 0; }"
$NFT add chain t c2
$NFT add map t m {type inet_service : verdict \;}
$NFT add element t m {2 : jump c2}
diff --git a/tests/shell/testcases/chains/0018check_jump_loop_1 b/tests/shell/testcases/chains/0018check_jump_loop_1
index b87520f..1e674d3 100755
--- a/tests/shell/testcases/chains/0018check_jump_loop_1
+++ b/tests/shell/testcases/chains/0018check_jump_loop_1
@@ -3,7 +3,7 @@
set -e
$NFT add table ip filter
-$NFT add chain ip filter ap1
+$NFT add chain ip filter ap1 "{ type filter hook input priority 0; }"
$NFT add chain ip filter ap2
$NFT add rule ip filter ap1 jump ap2
diff --git a/tests/shell/testcases/chains/dumps/0003jump_loop_1.nft b/tests/shell/testcases/chains/dumps/0003jump_loop_1.nft
index 7054cde..8d89bc4 100644
--- a/tests/shell/testcases/chains/dumps/0003jump_loop_1.nft
+++ b/tests/shell/testcases/chains/dumps/0003jump_loop_1.nft
@@ -1,5 +1,6 @@
table ip t {
chain c1 {
+ type filter hook prerouting priority filter; policy accept;
jump c2
}
diff --git a/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.nft b/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.nft
index 1e0d1d6..62fefaf 100644
--- a/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.nft
+++ b/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.nft
@@ -1,4 +1,5 @@
table ip t {
chain c {
+ type filter hook input priority filter; policy accept;
}
}
diff --git a/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.nft b/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.nft
index ca0a737..d35736e 100644
--- a/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.nft
+++ b/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.nft
@@ -5,6 +5,7 @@ table ip t {
}
chain c1 {
+ type filter hook forward priority filter; policy accept;
tcp dport vmap @m
}
diff --git a/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.nft b/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.nft
index 437900b..bdd0ead 100644
--- a/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.nft
+++ b/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.nft
@@ -1,5 +1,6 @@
table ip filter {
chain ap1 {
+ type filter hook input priority filter; policy accept;
jump ap2
}
diff --git a/tests/shell/testcases/transactions/0023rule_1 b/tests/shell/testcases/transactions/0023rule_1
index e58c088..863bcde 100755
--- a/tests/shell/testcases/transactions/0023rule_1
+++ b/tests/shell/testcases/transactions/0023rule_1
@@ -1,7 +1,7 @@
#!/bin/bash
RULESET="add table x
-add chain x y
+add chain x y { type filter hook input priority 0; }
add rule x y jump y"
# kernel must return ELOOP
diff --git a/tests/shell/testcases/transactions/anon_chain_loop b/tests/shell/testcases/transactions/anon_chain_loop
index 2fd6181..3053d16 100755
--- a/tests/shell/testcases/transactions/anon_chain_loop
+++ b/tests/shell/testcases/transactions/anon_chain_loop
@@ -3,7 +3,7 @@
# anon chains with c1 -> c2 recursive jump, expect failure
$NFT -f - <<EOF
table ip t {
- chain c2 { }
+ chain c2 { type filter hook input priority 0; }
chain c1 { }
}

View File

@ -0,0 +1,146 @@
From 5cf419d1ee6579ca404c4e0778713a65fd473a50 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 16 Oct 2025 16:12:50 +0200
Subject: [PATCH] datatype: rt_symbol_table_init() to search for iproute2
configs
JIRA: https://issues.redhat.com/browse/RHEL-114095
Upstream Status: nftables commit d0f70a1739b8e512986fb460a7fa7ff8a9300b68
commit d0f70a1739b8e512986fb460a7fa7ff8a9300b68
Author: Phil Sutter <phil@nwl.cc>
Date: Fri Dec 15 21:59:44 2023 +0100
datatype: rt_symbol_table_init() to search for iproute2 configs
There is an ongoing effort among various distributions to tidy up in
/etc. The idea is to reduce contents to just what the admin manually
inserted to customize the system, anything else shall move out to /usr
(or so). The various files in /etc/iproute2 fall in that category as
they are seldomly modified.
The crux is though that iproute2 project seems not quite sure yet where
the files should go. While v6.6.0 installs them into /usr/lib/iproute2,
current mast^Wmain branch uses /usr/share/iproute2. Assume this is going
to stay as /(usr/)lib does not seem right for such files.
Note that rt_symbol_table_init() is not just used for
iproute2-maintained configs but also for connlabel.conf - so retain the
old behaviour when passed an absolute path.
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/datatype.c | 38 ++++++++++++++++++++++++++++++++++----
src/meta.c | 2 +-
src/rt.c | 2 +-
3 files changed, 36 insertions(+), 6 deletions(-)
diff --git a/src/datatype.c b/src/datatype.c
index 64e4647..9530ae7 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -855,19 +855,47 @@ const struct datatype inet_service_type = {
#define RT_SYM_TAB_INITIAL_SIZE 16
+static FILE *open_iproute2_db(const char *filename, char **path)
+{
+ FILE *ret;
+
+ if (filename[0] == '/')
+ return fopen(filename, "r");
+
+ if (asprintf(path, "/etc/iproute2/%s", filename) == -1)
+ goto fail;
+
+ ret = fopen(*path, "r");
+ if (ret)
+ return ret;
+
+ free(*path);
+ if (asprintf(path, "/usr/share/iproute2/%s", filename) == -1)
+ goto fail;
+
+ ret = fopen(*path, "r");
+ if (ret)
+ return ret;
+
+ free(*path);
+fail:
+ *path = NULL;
+ return NULL;
+}
+
struct symbol_table *rt_symbol_table_init(const char *filename)
{
+ char buf[512], namebuf[512], *p, *path = NULL;
struct symbolic_constant s;
struct symbol_table *tbl;
unsigned int size, nelems, val;
- char buf[512], namebuf[512], *p;
FILE *f;
size = RT_SYM_TAB_INITIAL_SIZE;
tbl = xmalloc(sizeof(*tbl) + size * sizeof(s));
nelems = 0;
- f = fopen(filename, "r");
+ f = open_iproute2_db(filename, &path);
if (f == NULL)
goto out;
@@ -882,7 +910,7 @@ struct symbol_table *rt_symbol_table_init(const char *filename)
sscanf(p, "%u %511s\n", &val, namebuf) != 2 &&
sscanf(p, "%u %511s #", &val, namebuf) != 2) {
fprintf(stderr, "iproute database '%s' corrupted\n",
- filename);
+ path ?: filename);
break;
}
@@ -899,6 +927,8 @@ struct symbol_table *rt_symbol_table_init(const char *filename)
fclose(f);
out:
+ if (path)
+ free(path);
tbl->symbols[nelems] = SYMBOL_LIST_END;
return tbl;
}
@@ -914,7 +944,7 @@ void rt_symbol_table_free(const struct symbol_table *tbl)
void mark_table_init(struct nft_ctx *ctx)
{
- ctx->output.tbl.mark = rt_symbol_table_init("/etc/iproute2/rt_marks");
+ ctx->output.tbl.mark = rt_symbol_table_init("rt_marks");
}
void mark_table_exit(struct nft_ctx *ctx)
diff --git a/src/meta.c b/src/meta.c
index b578d5e..b69dca2 100644
--- a/src/meta.c
+++ b/src/meta.c
@@ -336,7 +336,7 @@ const struct datatype pkttype_type = {
void devgroup_table_init(struct nft_ctx *ctx)
{
- ctx->output.tbl.devgroup = rt_symbol_table_init("/etc/iproute2/group");
+ ctx->output.tbl.devgroup = rt_symbol_table_init("group");
}
void devgroup_table_exit(struct nft_ctx *ctx)
diff --git a/src/rt.c b/src/rt.c
index f5c8055..3ee710d 100644
--- a/src/rt.c
+++ b/src/rt.c
@@ -25,7 +25,7 @@
void realm_table_rt_init(struct nft_ctx *ctx)
{
- ctx->output.tbl.realm = rt_symbol_table_init("/etc/iproute2/rt_realms");
+ ctx->output.tbl.realm = rt_symbol_table_init("rt_realms");
}
void realm_table_rt_exit(struct nft_ctx *ctx)

View File

@ -0,0 +1,226 @@
From a902fc33ebf202de6e8a7fa7cbd8ba840d80605c Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 16 Oct 2025 16:12:50 +0200
Subject: [PATCH] tests: py: remove huge-limit test cases
JIRA: https://issues.redhat.com/browse/RHEL-114095
Upstream Status: nftables commit 1ee84db0360db0da336d5b1ee92807d29fcfbbe1
commit 1ee84db0360db0da336d5b1ee92807d29fcfbbe1
Author: Florian Westphal <fw@strlen.de>
Date: Thu Jan 18 13:24:04 2024 +0100
tests: py: remove huge-limit test cases
These tests will fail once the kernel checks for overflow
in the internal token bucken counter, so drop them.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/any/limit.t | 4 ---
tests/py/any/limit.t.json | 51 --------------------------------
tests/py/any/limit.t.json.output | 28 ------------------
tests/py/any/limit.t.payload | 17 -----------
4 files changed, 100 deletions(-)
diff --git a/tests/py/any/limit.t b/tests/py/any/limit.t
index a04ef42..2a84e3f 100644
--- a/tests/py/any/limit.t
+++ b/tests/py/any/limit.t
@@ -22,7 +22,6 @@ limit rate 2 kbytes/second;ok
limit rate 1025 kbytes/second;ok
limit rate 1023 mbytes/second;ok
limit rate 10230 mbytes/second;ok
-limit rate 1023000 mbytes/second;ok
limit rate 512 kbytes/second burst 5 packets;fail
limit rate 1 bytes / second;ok;limit rate 1 bytes/second
@@ -33,7 +32,6 @@ limit rate 1 gbytes / second;fail
limit rate 1025 bytes/second burst 512 bytes;ok
limit rate 1025 kbytes/second burst 1023 kbytes;ok
limit rate 1025 mbytes/second burst 1025 kbytes;ok
-limit rate 1025000 mbytes/second burst 1023 mbytes;ok
limit rate over 400/minute;ok;limit rate over 400/minute burst 5 packets
limit rate over 20/second;ok;limit rate over 20/second burst 5 packets
@@ -47,9 +45,7 @@ limit rate over 2 kbytes/second;ok
limit rate over 1025 kbytes/second;ok
limit rate over 1023 mbytes/second;ok
limit rate over 10230 mbytes/second;ok
-limit rate over 1023000 mbytes/second;ok
limit rate over 1025 bytes/second burst 512 bytes;ok
limit rate over 1025 kbytes/second burst 1023 kbytes;ok
limit rate over 1025 mbytes/second burst 1025 kbytes;ok
-limit rate over 1025000 mbytes/second burst 1023 mbytes;ok
diff --git a/tests/py/any/limit.t.json b/tests/py/any/limit.t.json
index e001ba0..73160b2 100644
--- a/tests/py/any/limit.t.json
+++ b/tests/py/any/limit.t.json
@@ -114,17 +114,6 @@
}
]
-# limit rate 1023000 mbytes/second
-[
- {
- "limit": {
- "per": "second",
- "rate": 1023000,
- "rate_unit": "mbytes"
- }
- }
-]
-
# limit rate 1 bytes / second
[
{
@@ -203,19 +192,6 @@
}
]
-# limit rate 1025000 mbytes/second burst 1023 mbytes
-[
- {
- "limit": {
- "burst": 1023,
- "burst_unit": "mbytes",
- "per": "second",
- "rate": 1025000,
- "rate_unit": "mbytes"
- }
- }
-]
-
# limit rate over 400/minute
[
{
@@ -343,18 +319,6 @@
}
]
-# limit rate over 1023000 mbytes/second
-[
- {
- "limit": {
- "inv": true,
- "per": "second",
- "rate": 1023000,
- "rate_unit": "mbytes"
- }
- }
-]
-
# limit rate over 1025 bytes/second burst 512 bytes
[
{
@@ -396,18 +360,3 @@
}
}
]
-
-# limit rate over 1025000 mbytes/second burst 1023 mbytes
-[
- {
- "limit": {
- "burst": 1023,
- "burst_unit": "mbytes",
- "inv": true,
- "per": "second",
- "rate": 1025000,
- "rate_unit": "mbytes"
- }
- }
-]
-
diff --git a/tests/py/any/limit.t.json.output b/tests/py/any/limit.t.json.output
index 5a95f5e..2c94d2d 100644
--- a/tests/py/any/limit.t.json.output
+++ b/tests/py/any/limit.t.json.output
@@ -118,19 +118,6 @@
}
]
-# limit rate 1023000 mbytes/second
-[
- {
- "limit": {
- "burst": 0,
- "burst_unit": "bytes",
- "per": "second",
- "rate": 1023000,
- "rate_unit": "mbytes"
- }
- }
-]
-
# limit rate over 400/minute
[
{
@@ -260,18 +247,3 @@
}
}
]
-
-# limit rate over 1023000 mbytes/second
-[
- {
- "limit": {
- "burst": 0,
- "burst_unit": "bytes",
- "inv": true,
- "per": "second",
- "rate": 1023000,
- "rate_unit": "mbytes"
- }
- }
-]
-
diff --git a/tests/py/any/limit.t.payload b/tests/py/any/limit.t.payload
index 0c7ee94..dc6701b 100644
--- a/tests/py/any/limit.t.payload
+++ b/tests/py/any/limit.t.payload
@@ -42,10 +42,6 @@ ip test-ip4 output
ip test-ip4 output
[ limit rate 10726932480/second burst 0 type bytes flags 0x0 ]
-# limit rate 1023000 mbytes/second
-ip test-ip4 output
- [ limit rate 1072693248000/second burst 0 type bytes flags 0x0 ]
-
# limit rate 1 bytes / second
ip
[ limit rate 1/second burst 0 type bytes flags 0x0 ]
@@ -71,10 +67,6 @@ ip test-ip4 output
ip test-ip4 output
[ limit rate 1074790400/second burst 1049600 type bytes flags 0x0 ]
-# limit rate 1025000 mbytes/second burst 1023 mbytes
-ip test-ip4 output
- [ limit rate 1074790400000/second burst 1072693248 type bytes flags 0x0 ]
-
# limit rate over 400/minute
ip test-ip4 output
[ limit rate 400/minute burst 5 type packets flags 0x1 ]
@@ -119,10 +111,6 @@ ip test-ip4 output
ip test-ip4 output
[ limit rate 10726932480/second burst 0 type bytes flags 0x1 ]
-# limit rate over 1023000 mbytes/second
-ip test-ip4 output
- [ limit rate 1072693248000/second burst 0 type bytes flags 0x1 ]
-
# limit rate over 1025 bytes/second burst 512 bytes
ip test-ip4 output
[ limit rate 1025/second burst 512 type bytes flags 0x1 ]
@@ -134,8 +122,3 @@ ip test-ip4 output
# limit rate over 1025 mbytes/second burst 1025 kbytes
ip test-ip4 output
[ limit rate 1074790400/second burst 1049600 type bytes flags 0x1 ]
-
-# limit rate over 1025000 mbytes/second burst 1023 mbytes
-ip test-ip4 output
- [ limit rate 1074790400000/second burst 1072693248 type bytes flags 0x1 ]
-

View File

@ -0,0 +1,63 @@
From 757dee30af84a3443c6eeb9a3cddf8a5e4f8928a Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 16 Oct 2025 16:12:50 +0200
Subject: [PATCH] tests: py: add missing json.output data
JIRA: https://issues.redhat.com/browse/RHEL-114095
Upstream Status: nftables commit f58e72a2f5aa134653e7ee8b5432b23eb0674c3c
commit f58e72a2f5aa134653e7ee8b5432b23eb0674c3c
Author: Florian Westphal <fw@strlen.de>
Date: Mon Feb 26 09:45:43 2024 +0100
tests: py: add missing json.output data
Fixes: bridge/vlan.t: WARNING: line 56: ...
Fixes: 8b9ae77598b4 ("tests: never merge across non-expression statements redux 2")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/bridge/vlan.t.json.output | 31 ++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/tests/py/bridge/vlan.t.json.output b/tests/py/bridge/vlan.t.json.output
index 2f90c8f..eea2d41 100644
--- a/tests/py/bridge/vlan.t.json.output
+++ b/tests/py/bridge/vlan.t.json.output
@@ -202,3 +202,34 @@
}
}
]
+
+# ether saddr 00:11:22:33:44:55 counter ether type 8021q
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ether"
+ }
+ },
+ "op": "==",
+ "right": "00:11:22:33:44:55"
+ }
+ },
+ {
+ "counter": null
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "type",
+ "protocol": "ether"
+ }
+ },
+ "op": "==",
+ "right": "8021q"
+ }
+ }
+]

View File

@ -0,0 +1,64 @@
From c970cef4925493bc7ef0064896d4b2486bb2fa6a Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 16 Oct 2025 16:12:50 +0200
Subject: [PATCH] tests: py: missing json output in never merge across
non-expression statements
JIRA: https://issues.redhat.com/browse/RHEL-114095
Upstream Status: nftables commit 94fd162ea4d25fe6b0b4d58dcb7ff66dc55f3247
commit 94fd162ea4d25fe6b0b4d58dcb7ff66dc55f3247
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Dec 6 19:30:44 2023 +0100
tests: py: missing json output in never merge across non-expression statements
Add missing json output.
Fixes: 99ab1b8feb16 ("rule: never merge across non-expression statements")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/ip/ip.t.json.output | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/tests/py/ip/ip.t.json.output b/tests/py/ip/ip.t.json.output
index b201cda..351ae93 100644
--- a/tests/py/ip/ip.t.json.output
+++ b/tests/py/ip/ip.t.json.output
@@ -230,3 +230,34 @@
}
]
+# ip saddr 1.2.3.4 counter ip daddr 3.4.5.6
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": "1.2.3.4"
+ }
+ },
+ {
+ "counter": null
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": "3.4.5.6"
+ }
+ }
+]
+

View File

@ -0,0 +1,34 @@
From 8dc1e0b04b316e0e860e39a61d012fd9772bf32c Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Wed, 26 Nov 2025 18:35:24 +0100
Subject: [PATCH] Revert doc/ part of "src: add tcp option reset support"
JIRA: https://issues.redhat.com/browse/RHEL-126817
Upstream Status: RHEL-only
Revert documentation added by commit
5d837d270d5a8b3a4d3fdca12d0f0800b8287cdd.
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/statements.txt | 7 -------
1 file changed, 7 deletions(-)
diff --git a/doc/statements.txt b/doc/statements.txt
index 1967280..abe9e6d 100644
--- a/doc/statements.txt
+++ b/doc/statements.txt
@@ -80,13 +80,6 @@ tcp flags syn tcp option maxseg size set 1360
tcp flags syn tcp option maxseg size set rt mtu
---------------
-You can also remove tcp options via reset keyword.
-
-.remove tcp option
----------------
-tcp flags syn reset tcp option sack-perm
----------------
-
LOG STATEMENT
~~~~~~~~~~~~~
[verse]

View File

@ -0,0 +1,39 @@
From f9387056d058e33ffa48288fcdf5c3468d9ab53c Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Wed, 26 Nov 2025 18:35:53 +0100
Subject: [PATCH] Revert doc/ part of "meta: introduce meta broute support"
JIRA: https://issues.redhat.com/browse/RHEL-126817
Upstream Status: RHEL-only
Revert documentation added by commit
97672e54518b28923951c96191edb5b7b5f5f294.
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/statements.txt | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/doc/statements.txt b/doc/statements.txt
index abe9e6d..c29a2ff 100644
--- a/doc/statements.txt
+++ b/doc/statements.txt
@@ -289,7 +289,7 @@ A meta statement sets the value of a meta expression. The existing meta fields
are: priority, mark, pkttype, nftrace. +
[verse]
-*meta* {*mark* | *priority* | *pkttype* | *nftrace* | *broute*} *set* 'value'
+*meta* {*mark* | *priority* | *pkttype* | *nftrace*} *set* 'value'
A meta statement sets meta data associated with a packet. +
@@ -309,9 +309,6 @@ pkt_type
|nftrace |
ruleset packet tracing on/off. Use *monitor trace* command to watch traces|
0, 1
-|broute |
-broute on/off. packets are routed instead of being bridged|
-0, 1
|==========================
LIMIT STATEMENT

View File

@ -0,0 +1,64 @@
From dc0320014e2b147479705c5e2f7df7080d27bc66 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Wed, 26 Nov 2025 18:36:13 +0100
Subject: [PATCH] Revert doc/ part of "exthdr: add boolean DCCP option
matching"
JIRA: https://issues.redhat.com/browse/RHEL-126817
Upstream Status: RHEL-only
Revert documentation added by commit
6ab0fd6c67dbccedb49209b94eb7f740dd32fd2a.
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/libnftables-json.adoc | 11 -----------
doc/payload-expression.txt | 6 ------
2 files changed, 17 deletions(-)
diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc
index 0e424c2..c5e5f23 100644
--- a/doc/libnftables-json.adoc
+++ b/doc/libnftables-json.adoc
@@ -1235,17 +1235,6 @@ If the *field* property is not given, the expression is to be used as an SCTP
chunk existence check in a *match* statement with a boolean on the right hand
side.
-=== DCCP OPTION
-[verse]
-*{ "dccp option": {
- "type":* 'NUMBER'*
-*}}*
-
-Create a reference to a DCCP option (*type*).
-
-The expression is to be used as a DCCP option existence check in a *match*
-statement with a boolean on the right hand side.
-
=== META
[verse]
____
diff --git a/doc/payload-expression.txt b/doc/payload-expression.txt
index c7c267d..8afeb71 100644
--- a/doc/payload-expression.txt
+++ b/doc/payload-expression.txt
@@ -775,7 +775,6 @@ The following syntaxes are valid only in a relational expression with boolean ty
*exthdr* {*hbh* | *frag* | *rt* | *dst* | *mh*}
*tcp option* {*eol* | *nop* | *maxseg* | *window* | *sack-perm* | *sack* | *sack0* | *sack1* | *sack2* | *sack3* | *timestamp*}
*ip option* { lsrr | ra | rr | ssrr }
-*dccp option* 'dccp_option_type'
.IPv6 extension headers
[options="header"]
@@ -878,11 +877,6 @@ ip6 filter input frag more-fragments 1 counter
filter input ip option lsrr exists counter
---------------------------------------
-.finding DCCP option
-------------------
-filter input dccp option 40 exists counter
----------------------------------------
-
CONNTRACK EXPRESSIONS
~~~~~~~~~~~~~~~~~~~~~
Conntrack expressions refer to meta data of the connection tracking entry associated with a packet. +

View File

@ -0,0 +1,42 @@
From 98fb87d77b87476d5a3847e6482c0ab24d48ca44 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Wed, 18 Mar 2026 16:27:12 +0100
Subject: [PATCH] src: netlink: fix crash when ops doesn't support udata
JIRA: https://issues.redhat.com/browse/https://redhat.atlassian.net/browse/RHEL-153461
Upstream Status: nftables commit be737a1986bfee0ddea4bee7863dca0123a2bcbc
commit be737a1986bfee0ddea4bee7863dca0123a2bcbc
Author: Florian Westphal <fw@strlen.de>
Date: Thu May 8 16:29:04 2025 +0200
src: netlink: fix crash when ops doesn't support udata
Whenever a new version adds udata support to an expression, then old
versions of nft will crash when trying to list such a ruleset generated
by a more recent version of nftables.
Fix this by falling back to 'type' format.
Fixes: 6e48df5329ea ('src: add "typeof" build/parse/print support')
Signed-off-by: Florian Westphal <fw@strlen.de>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/netlink.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/netlink.c b/src/netlink.c
index 120a8ba..04bba59 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -895,7 +895,7 @@ static struct expr *set_make_key(const struct nftnl_udata *attr)
etype = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_TYPEOF_EXPR]);
ops = expr_ops_by_type_u32(etype);
- if (!ops)
+ if (!ops || !ops->parse_udata)
return NULL;
expr = ops->parse_udata(ud[NFTNL_UDATA_SET_TYPEOF_DATA]);

View File

@ -0,0 +1,130 @@
From 0a38b9fcf1841e68088f2db39e3c96b3ad3b0369 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Wed, 18 Mar 2026 22:17:46 +0100
Subject: [PATCH] src: fix reset element support for interval set type
JIRA: https://issues.redhat.com/browse/https://redhat.atlassian.net/browse/RHEL-153461
Upstream Status: nftables commit 2b164aec4295d5f4f6d45aa098279494ab44289b
commit 2b164aec4295d5f4f6d45aa098279494ab44289b
Author: Florian Westphal <fw@strlen.de>
Date: Thu Mar 6 14:23:30 2025 +0100
src: fix reset element support for interval set type
Running reset command yields on an interval (rbtree) set yields:
nft reset element inet filter rbtreeset {1.2.3.4}
BUG: unhandled op 8
This is easy to fix, CMD_RESET doesn't add or remove so it should be
treated like CMD_GET.
Unfortunately, this still doesn't work properly:
nft get element inet filter rbset {1.2.3.4}
returns:
... elements = { 1.2.3.4 }
but its expected that "get" and "reset" also return stateful objects
associated with the element. This works for other set types, but for
rbtree, the list of statements gets lost during segtree processing.
After fix, get/reset returns:
elements = { 1.2.3.4 counter packets 10 ...
A follow up patch will add a test case.
Fixes: 83e0f4402fb7 ("Implement 'reset {set,map,element}' commands")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 1 +
src/segtree.c | 36 ++++++++++++++++++++++++++++++------
2 files changed, 31 insertions(+), 6 deletions(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index 2196e92..711990a 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1729,6 +1729,7 @@ static int interval_set_eval(struct eval_ctx *ctx, struct set *set,
ctx->nft->debug_mask);
break;
case CMD_GET:
+ case CMD_RESET:
break;
default:
BUG("unhandled op %d\n", ctx->cmd->op);
diff --git a/src/segtree.c b/src/segtree.c
index 28172b3..c4029f9 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -206,6 +206,27 @@ static struct expr *expr_to_set_elem(struct expr *e)
return __expr_to_set_elem(e, expr);
}
+static void set_compound_expr_add(struct expr *compound, struct expr *expr, struct expr *orig)
+{
+ struct expr *elem;
+
+ switch (expr->etype) {
+ case EXPR_SET_ELEM:
+ list_splice_init(&orig->stmt_list, &expr->stmt_list);
+ compound_expr_add(compound, expr);
+ break;
+ case EXPR_MAPPING:
+ list_splice_init(&orig->left->stmt_list, &expr->left->stmt_list);
+ compound_expr_add(compound, expr);
+ break;
+ default:
+ elem = set_elem_expr_alloc(&orig->location, expr);
+ list_splice_init(&orig->stmt_list, &elem->stmt_list);
+ compound_expr_add(compound, elem);
+ break;
+ }
+}
+
int get_set_decompose(struct set *cache_set, struct set *set)
{
struct expr *i, *next, *range;
@@ -227,20 +248,23 @@ int get_set_decompose(struct set *cache_set, struct set *set)
errno = ENOENT;
return -1;
}
+
+ set_compound_expr_add(new_init, range, left);
+
expr_free(left);
expr_free(i);
- compound_expr_add(new_init, range);
left = NULL;
} else {
if (left) {
range = get_set_interval_find(cache_set,
left, NULL);
+
if (range)
- compound_expr_add(new_init, range);
+ set_compound_expr_add(new_init, range, left);
else
- compound_expr_add(new_init,
- expr_to_set_elem(left));
+ set_compound_expr_add(new_init,
+ expr_to_set_elem(left), left);
}
left = i;
}
@@ -248,9 +272,9 @@ int get_set_decompose(struct set *cache_set, struct set *set)
if (left) {
range = get_set_interval_find(cache_set, left, NULL);
if (range)
- compound_expr_add(new_init, range);
+ set_compound_expr_add(new_init, range, left);
else
- compound_expr_add(new_init, expr_to_set_elem(left));
+ set_compound_expr_add(new_init, expr_to_set_elem(left), left);
}
expr_free(set->init);

View File

@ -0,0 +1,37 @@
From 5bd9f93cc070364eae782196112fe81953d8f872 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:23:48 +0200
Subject: [PATCH] doc: Fix typo in nat statement 'prefix' description
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 1b3c33a7931967fb7afe4efdf68deb7781786489
commit 1b3c33a7931967fb7afe4efdf68deb7781786489
Author: Phil Sutter <phil@nwl.cc>
Date: Thu May 8 16:35:47 2025 +0200
doc: Fix typo in nat statement 'prefix' description
No point in repeating 'to map' here.
Fixes: 19d73ccdd39fa ("doc: add nat examples")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/statements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/statements.txt b/doc/statements.txt
index c29a2ff..c7529ee 100644
--- a/doc/statements.txt
+++ b/doc/statements.txt
@@ -388,7 +388,7 @@ Before kernel 4.18 nat statements require both prerouting and postrouting base c
to be present since otherwise packets on the return path won't be seen by
netfilter and therefore no reverse translation will take place.
-The optional *prefix* keyword allows to map to map *n* source addresses to *n*
+The optional *prefix* keyword allows to map *n* source addresses to *n*
destination addresses. See 'Advanced NAT examples' below.
.NAT statement values

View File

@ -0,0 +1,39 @@
From 8e7d599a1d42add21949ff5c05ede51db9dc4db9 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:23:49 +0200
Subject: [PATCH] json: Print single set flag as non-array
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 6bedb12af1658562f277ca68d74cf1e9e7433a08
Conflicts: Dropped changes to non-existent dump files
commit 6bedb12af1658562f277ca68d74cf1e9e7433a08
Author: Phil Sutter <phil@nwl.cc>
Date: Thu May 8 16:39:24 2025 +0200
json: Print single set flag as non-array
The code obviously intended to do this already but got the array length
check wrong.
Fixes: e70354f53e9f6 ("libnftables: Implement JSON output support")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/json.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/json.c b/src/json.c
index dad93e5..0e6e39d 100644
--- a/src/json.c
+++ b/src/json.c
@@ -178,7 +178,7 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
if (set->flags & NFT_SET_EVAL)
json_array_append_new(tmp, json_pack("s", "dynamic"));
- if (json_array_size(tmp) > 0) {
+ if (json_array_size(tmp) > 1) {
json_object_set_new(root, "flags", tmp);
} else {
if (json_array_size(tmp))

View File

@ -0,0 +1,40 @@
From 8e81e7293c38e19b4d9e2953b51adaab93a38c90 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:24:19 +0200
Subject: [PATCH] netlink: Avoid potential NULL-ptr deref parsing set elem
expressions
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 89645d04106d45d5e23b6d5206777dad1fb3e6bf
commit 89645d04106d45d5e23b6d5206777dad1fb3e6bf
Author: Phil Sutter <phil@nwl.cc>
Date: Fri May 16 20:08:05 2025 +0200
netlink: Avoid potential NULL-ptr deref parsing set elem expressions
Since netlink_parse_set_expr() may return NULL, the following deref must
be guarded.
Fixes: e6d1d0d611958 ("src: add set element multi-statement support")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/netlink.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/netlink.c b/src/netlink.c
index 04bba59..9fa95ea 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -927,7 +927,8 @@ static int set_elem_parse_expressions(struct nftnl_expr *e, void *data)
struct stmt *stmt;
stmt = netlink_parse_set_expr(set, cache, e);
- list_add_tail(&stmt->list, &setelem_parse_ctx->stmt_list);
+ if (stmt)
+ list_add_tail(&stmt->list, &setelem_parse_ctx->stmt_list);
return 0;
}

View File

@ -0,0 +1,41 @@
From 548a3e9dbc1b111e9bdd0be5df0056d12b77836f Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:24:19 +0200
Subject: [PATCH] netlink: Catch unknown types when deserializing objects
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 690f19eadde5cb607ec3d8d471c86d558c7229bd
commit 690f19eadde5cb607ec3d8d471c86d558c7229bd
Author: Phil Sutter <phil@nwl.cc>
Date: Fri May 16 19:41:19 2025 +0200
netlink: Catch unknown types when deserializing objects
Print an error message and discard the object instead of returning it to
the caller. At least when trying to print it, we would hit an assert()
in obj_type_name() anyway.
Fixes: 4756d92e517ae ("src: listing of stateful objects")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/netlink.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/netlink.c b/src/netlink.c
index 9fa95ea..6130e56 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1733,6 +1733,10 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
obj->synproxy.flags =
nftnl_obj_get_u32(nlo, NFTNL_OBJ_SYNPROXY_FLAGS);
break;
+ default:
+ netlink_io_error(ctx, NULL, "Unknown object type %u", type);
+ obj_free(obj);
+ return NULL;
}
obj->type = type;

View File

@ -0,0 +1,99 @@
From 7fe078fda20ce68b53147afcab2ed126ecf739c6 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:24:19 +0200
Subject: [PATCH] json: prevent null deref if chain->policy is not set
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 69b90023c7220fe283ee38686c758e3494e853d9
commit 69b90023c7220fe283ee38686c758e3494e853d9
Author: Florian Westphal <fw@strlen.de>
Date: Mon Jun 2 14:22:33 2025 +0200
json: prevent null deref if chain->policy is not set
The two commits mentioned below resolved null dererence crashes when the
policy resp. priority keyword was missing in the chain/flowtable
specification.
Same issue exists in the json output path, so apply similar fix there
and extend the existing test cases.
Fixes: 5b37479b42b3 ("nftables: don't crash in 'list ruleset' if policy is not set")
Fixes: b40bebbcee36 ("rule: do not crash if to-be-printed flowtable lacks priority")
Signed-off-by: Florian Westphal <fw@strlen.de>
Acked-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/json.c | 19 ++++++++++++++-----
.../nft-j-f/flowtable-no-priority-crash | 6 ++++++
.../shell/testcases/nft-f/0021list_ruleset_0 | 7 ++++++-
3 files changed, 26 insertions(+), 6 deletions(-)
create mode 100644 tests/shell/testcases/bogons/nft-j-f/flowtable-no-priority-crash
diff --git a/src/json.c b/src/json.c
index 0e6e39d..4836e6b 100644
--- a/src/json.c
+++ b/src/json.c
@@ -272,8 +272,14 @@ static json_t *chain_print_json(const struct chain *chain)
if (chain->flags & CHAIN_F_BASECHAIN) {
mpz_export_data(&priority, chain->priority.expr->value,
BYTEORDER_HOST_ENDIAN, sizeof(int));
- mpz_export_data(&policy, chain->policy->value,
- BYTEORDER_HOST_ENDIAN, sizeof(int));
+
+ if (chain->policy) {
+ mpz_export_data(&policy, chain->policy->value,
+ BYTEORDER_HOST_ENDIAN, sizeof(int));
+ } else {
+ policy = NF_ACCEPT;
+ }
+
tmp = json_pack("{s:s, s:s, s:i, s:s}",
"type", chain->type.str,
"hook", hooknum2str(chain->handle.family,
@@ -451,10 +457,13 @@ static json_t *obj_print_json(const struct obj *obj)
static json_t *flowtable_print_json(const struct flowtable *ftable)
{
json_t *root, *devs = NULL;
- int i, priority;
+ int i, priority = 0;
+
+ if (ftable->priority.expr) {
+ mpz_export_data(&priority, ftable->priority.expr->value,
+ BYTEORDER_HOST_ENDIAN, sizeof(int));
+ }
- mpz_export_data(&priority, ftable->priority.expr->value,
- BYTEORDER_HOST_ENDIAN, sizeof(int));
root = json_pack("{s:s, s:s, s:s, s:I, s:s, s:i}",
"family", family2str(ftable->handle.family),
"name", ftable->handle.flowtable.name,
diff --git a/tests/shell/testcases/bogons/nft-j-f/flowtable-no-priority-crash b/tests/shell/testcases/bogons/nft-j-f/flowtable-no-priority-crash
new file mode 100644
index 0000000..f348da9
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-j-f/flowtable-no-priority-crash
@@ -0,0 +1,6 @@
+table ip filter {
+ flowtable ft1 {
+ devices = { lo }
+ }
+}
+list ruleset
diff --git a/tests/shell/testcases/nft-f/0021list_ruleset_0 b/tests/shell/testcases/nft-f/0021list_ruleset_0
index 37729b4..f3c3749 100755
--- a/tests/shell/testcases/nft-f/0021list_ruleset_0
+++ b/tests/shell/testcases/nft-f/0021list_ruleset_0
@@ -12,4 +12,9 @@ RULESET="table filter {
list ruleset
"
-exec $NFT -f - <<< "$RULESET"
+$NFT -f - <<< "$RULESET"
+
+if [ "$NFT_TEST_HAVE_json" != n ]; then
+ $NFT flush ruleset
+ $NFT -j -f - <<< "$RULESET"
+fi

View File

@ -0,0 +1,40 @@
From 8fe856057e51298ebaadbfa59b56923b5d169b22 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:24:19 +0200
Subject: [PATCH] netlink: Fix for potential crash parsing a flowtable
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit d5ef04441eb1de3efc27aa70193fe3d7f0b5c408
commit d5ef04441eb1de3efc27aa70193fe3d7f0b5c408
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Jun 11 13:12:56 2025 +0200
netlink: Fix for potential crash parsing a flowtable
Kernel's flowtable message might not contain the
NFTA_FLOWTABLE_HOOK_DEVS attribute. In that case, nftnl_flowtable_get()
will return NULL for the respective nftnl attribute.
Fixes: db0697ce7f602 ("src: support for flowtable listing")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/netlink.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/netlink.c b/src/netlink.c
index 6130e56..9a01d8a 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1853,7 +1853,7 @@ netlink_delinearize_flowtable(struct netlink_ctx *ctx,
if (nftnl_flowtable_is_set(nlo, NFTNL_FLOWTABLE_FLAGS))
flowtable->flags = nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_FLAGS);
dev_array = nftnl_flowtable_get(nlo, NFTNL_FLOWTABLE_DEVICES);
- while (dev_array[len])
+ while (dev_array && dev_array[len])
len++;
if (len)

View File

@ -0,0 +1,49 @@
From 3130995d533b2c8155001ca592c4d6fd3e0fc6c7 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:24:19 +0200
Subject: [PATCH] netlink: Do not allocate a bogus flowtable priority expr
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 10b9a85b3278e0933bf47226588fede8c9fcbcc8
commit 10b9a85b3278e0933bf47226588fede8c9fcbcc8
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Jun 11 14:15:38 2025 +0200
netlink: Do not allocate a bogus flowtable priority expr
Code accidentally treats missing NFTNL_FLOWTABLE_PRIO attribute as zero
prio value which may not be correct.
Fixes: db0697ce7f602 ("src: support for flowtable listing")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/netlink.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/netlink.c b/src/netlink.c
index 9a01d8a..b1dd31d 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1868,14 +1868,16 @@ netlink_delinearize_flowtable(struct netlink_ctx *ctx,
sizeof(char *), qsort_device_cmp);
}
- priority = nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_PRIO);
- flowtable->priority.expr =
+ if (nftnl_flowtable_is_set(nlo, NFTNL_FLOWTABLE_PRIO)) {
+ priority = nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_PRIO);
+ flowtable->priority.expr =
constant_expr_alloc(&netlink_location,
&integer_type,
BYTEORDER_HOST_ENDIAN,
sizeof(int) *
BITS_PER_BYTE,
&priority);
+ }
flowtable->hook.num =
nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_HOOKNUM);
flowtable->flags =

View File

@ -0,0 +1,124 @@
From ae0e0face33e74339f4f8082d3e6119b2ad57dcb Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:24:19 +0200
Subject: [PATCH] json: Support typeof in set and map types
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit bb6312484af93a83a9ec8716f3887a43566a775a
Conflicts: Dropped changes to non-existent test and dump files
commit bb6312484af93a83a9ec8716f3887a43566a775a
Author: Phil Sutter <phil@nwl.cc>
Date: Sat Sep 28 00:55:34 2024 +0200
json: Support typeof in set and map types
Implement this as a special "type" property value which is an object
with sole property "typeof". The latter's value is the JSON
representation of the expression in set->key, so for concatenated
typeofs it is a concat expression.
All this is a bit clumsy right now but it works and it should be
possible to tear it down a bit for more user-friendliness in a
compatible way by either replacing the concat expression by the array it
contains or even the whole "typeof" object - the parser would just
assume any object (or objects in an array) in the "type" property value
are expressions to extract a type from.
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/libnftables-json.adoc | 7 ++++---
src/json.c | 13 ++++++++++++-
src/parser_json.c | 9 +++++++++
tests/monitor/testcases/map-expr.t | 2 +-
4 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc
index c5e5f23..c3e3bcc 100644
--- a/doc/libnftables-json.adoc
+++ b/doc/libnftables-json.adoc
@@ -339,7 +339,7 @@ ____
"size":* 'NUMBER'
*}}*
-'SET_TYPE' := 'STRING' | *[* 'SET_TYPE_LIST' *]*
+'SET_TYPE' := 'STRING' | *[* 'SET_TYPE_LIST' *]* | *{ "typeof":* 'EXPRESSION' *}*
'SET_TYPE_LIST' := 'STRING' [*,* 'SET_TYPE_LIST' ]
'SET_POLICY' := *"performance"* | *"memory"*
'SET_FLAG_LIST' := 'SET_FLAG' [*,* 'SET_FLAG_LIST' ]
@@ -377,8 +377,9 @@ that they translate a unique key to a value.
Maximum number of elements supported.
==== TYPE
-The set type might be a string, such as *"ipv4_addr"* or an array
-consisting of strings (for concatenated types).
+The set type might be a string, such as *"ipv4_addr"*, an array
+consisting of strings (for concatenated types) or a *typeof* object containing
+an expression to extract the type from.
==== ELEM
A single set element might be given as string, integer or boolean value for
diff --git a/src/json.c b/src/json.c
index 4836e6b..39b1ada 100644
--- a/src/json.c
+++ b/src/json.c
@@ -87,6 +87,17 @@ static json_t *set_dtype_json(const struct expr *key)
return root;
}
+static json_t *set_key_dtype_json(const struct set *set,
+ struct output_ctx *octx)
+{
+ bool use_typeof = set->key_typeof_valid;
+
+ if (!use_typeof)
+ return set_dtype_json(set->key);
+
+ return json_pack("{s:o}", "typeof", expr_print_json(set->key, octx));
+}
+
static json_t *stmt_print_json(const struct stmt *stmt, struct output_ctx *octx)
{
char buf[1024];
@@ -149,7 +160,7 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
"family", family2str(set->handle.family),
"name", set->handle.set.name,
"table", set->handle.table.name,
- "type", set_dtype_json(set->key),
+ "type", set_key_dtype_json(set, octx),
"handle", set->handle.handle.id);
if (set->comment)
diff --git a/src/parser_json.c b/src/parser_json.c
index 2acc248..936c5aa 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -1700,7 +1700,16 @@ static struct expr *json_parse_dtype_expr(struct json_ctx *ctx, json_t *root)
compound_expr_add(expr, i);
}
return expr;
+ } else if (json_is_object(root)) {
+ const char *key;
+ json_t *val;
+
+ if (!json_unpack_stmt(ctx, root, &key, &val) &&
+ !strcmp(key, "typeof")) {
+ return json_parse_expr(ctx, val);
+ }
}
+
json_error(ctx, "Invalid set datatype.");
return NULL;
}
diff --git a/tests/monitor/testcases/map-expr.t b/tests/monitor/testcases/map-expr.t
index 8729c0b..d11ad0e 100644
--- a/tests/monitor/testcases/map-expr.t
+++ b/tests/monitor/testcases/map-expr.t
@@ -3,4 +3,4 @@ I add table ip t
I add map ip t m { typeof meta day . meta hour : verdict; flags interval; counter; }
O -
J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}}
-J {"add": {"map": {"family": "ip", "name": "m", "table": "t", "type": ["day", "hour"], "handle": 0, "map": "verdict", "flags": ["interval"], "stmt": [{"counter": null}]}}}
+J {"add": {"map": {"family": "ip", "name": "m", "table": "t", "type": {"typeof": {"concat": [{"meta": {"key": "day"}}, {"meta": {"key": "hour"}}]}}, "handle": 0, "map": "verdict", "flags": ["interval"], "stmt": [{"counter": null}]}}}

View File

@ -0,0 +1,108 @@
From d5c438d2a4d502460ad83ec2f3224c7faf83ed8f Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:24:52 +0200
Subject: [PATCH] tests: monitor: Fix for single flag array avoidance
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit b5f43dfc8b28989290a16818f00ca73c6cd8ff65
Conflicts: Dropped changes to non-existent test case
commit b5f43dfc8b28989290a16818f00ca73c6cd8ff65
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Jun 11 17:15:22 2025 +0200
tests: monitor: Fix for single flag array avoidance
Missed to update the JSON monitor expected output.
Fixes: 6bedb12af1658 ("json: Print single set flag as non-array")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/monitor/testcases/map-expr.t | 2 +-
tests/monitor/testcases/set-interval.t | 2 +-
tests/monitor/testcases/set-maps.t | 2 +-
tests/monitor/testcases/set-mixed.t | 2 +-
tests/monitor/testcases/set-multiple.t | 4 ++--
tests/monitor/testcases/set-simple.t | 2 +-
6 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/tests/monitor/testcases/map-expr.t b/tests/monitor/testcases/map-expr.t
index d11ad0e..9042004 100644
--- a/tests/monitor/testcases/map-expr.t
+++ b/tests/monitor/testcases/map-expr.t
@@ -3,4 +3,4 @@ I add table ip t
I add map ip t m { typeof meta day . meta hour : verdict; flags interval; counter; }
O -
J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}}
-J {"add": {"map": {"family": "ip", "name": "m", "table": "t", "type": {"typeof": {"concat": [{"meta": {"key": "day"}}, {"meta": {"key": "hour"}}]}}, "handle": 0, "map": "verdict", "flags": ["interval"], "stmt": [{"counter": null}]}}}
+J {"add": {"map": {"family": "ip", "name": "m", "table": "t", "type": {"typeof": {"concat": [{"meta": {"key": "day"}}, {"meta": {"key": "hour"}}]}}, "handle": 0, "map": "verdict", "flags": "interval", "stmt": [{"counter": null}]}}}
diff --git a/tests/monitor/testcases/set-interval.t b/tests/monitor/testcases/set-interval.t
index 5053c59..84cf98c 100644
--- a/tests/monitor/testcases/set-interval.t
+++ b/tests/monitor/testcases/set-interval.t
@@ -10,7 +10,7 @@ I add set ip t s { type inet_service; flags interval; elements = { 20, 30-40 };
O add set ip t s { type inet_service; flags interval; }
O add element ip t s { 20 }
O add element ip t s { 30-40 }
-J {"add": {"set": {"family": "ip", "name": "s", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}}
+J {"add": {"set": {"family": "ip", "name": "s", "table": "t", "type": "inet_service", "handle": 0, "flags": "interval"}}}
J {"add": {"element": {"family": "ip", "table": "t", "name": "s", "elem": {"set": [20]}}}}
J {"add": {"element": {"family": "ip", "table": "t", "name": "s", "elem": {"set": [{"range": [30, 40]}]}}}}
diff --git a/tests/monitor/testcases/set-maps.t b/tests/monitor/testcases/set-maps.t
index acda480..aaf332f 100644
--- a/tests/monitor/testcases/set-maps.t
+++ b/tests/monitor/testcases/set-maps.t
@@ -3,7 +3,7 @@ I add table ip t
I add map ip t portip { type inet_service: ipv4_addr; flags interval; }
O -
J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}}
-J {"add": {"map": {"family": "ip", "name": "portip", "table": "t", "type": "inet_service", "handle": 0, "map": "ipv4_addr", "flags": ["interval"]}}}
+J {"add": {"map": {"family": "ip", "name": "portip", "table": "t", "type": "inet_service", "handle": 0, "map": "ipv4_addr", "flags": "interval"}}}
I add element ip t portip { 80-100: 10.0.0.1 }
O -
diff --git a/tests/monitor/testcases/set-mixed.t b/tests/monitor/testcases/set-mixed.t
index 08c2011..1cf3d38 100644
--- a/tests/monitor/testcases/set-mixed.t
+++ b/tests/monitor/testcases/set-mixed.t
@@ -4,7 +4,7 @@ I add set ip t portrange { type inet_service; flags interval; }
I add set ip t ports { type inet_service; }
O -
J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}}
-J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}}
+J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": "interval"}}}
J {"add": {"set": {"family": "ip", "name": "ports", "table": "t", "type": "inet_service", "handle": 0}}}
# make sure concurrent adds work
diff --git a/tests/monitor/testcases/set-multiple.t b/tests/monitor/testcases/set-multiple.t
index bd7a624..84de98e 100644
--- a/tests/monitor/testcases/set-multiple.t
+++ b/tests/monitor/testcases/set-multiple.t
@@ -4,8 +4,8 @@ I add set ip t portrange { type inet_service; flags interval; }
I add set ip t portrange2 { type inet_service; flags interval; }
O -
J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}}
-J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}}
-J {"add": {"set": {"family": "ip", "name": "portrange2", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}}
+J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": "interval"}}}
+J {"add": {"set": {"family": "ip", "name": "portrange2", "table": "t", "type": "inet_service", "handle": 0, "flags": "interval"}}}
# make sure concurrent adds work
I add element ip t portrange { 1024-65535 }
diff --git a/tests/monitor/testcases/set-simple.t b/tests/monitor/testcases/set-simple.t
index 8ca4f32..6bac08a 100644
--- a/tests/monitor/testcases/set-simple.t
+++ b/tests/monitor/testcases/set-simple.t
@@ -3,7 +3,7 @@ I add table ip t
I add set ip t portrange { type inet_service; flags interval; }
O -
J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}}
-J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}}
+J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": "interval"}}}
# adding some ranges
I add element ip t portrange { 1-10 }

View File

@ -0,0 +1,232 @@
From 1bbd3a42f69d0eaaab66fede7d781013183ef064 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:25:19 +0200
Subject: [PATCH] tests: py: Properly fix JSON equivalents for netdev/reject.t
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit b7a11c691d459d06a801a28ae9a52bc564584a1a
commit b7a11c691d459d06a801a28ae9a52bc564584a1a
Author: Phil Sutter <phil@nwl.cc>
Date: Thu Jun 12 12:59:29 2025 +0200
tests: py: Properly fix JSON equivalents for netdev/reject.t
Revert commit d1a7b9e19fe65 ("tests: py: update netdev reject test
file"), the stored JSON equivalents were correct in that they matched
the standard syntax input.
In fact, we missed a .json.output file recording the expected deviation
in JSON output.
Fixes: d1a7b9e19fe65 ("tests: py: update netdev reject test file")
Fixes: 7ca3368cd7575 ("reject: Unify inet, netdev and bridge delinearization")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/netdev/reject.t.json | 66 +++++++++++++++--------
tests/py/netdev/reject.t.json.output | 81 ++++++++++++++++++++++++++++
2 files changed, 126 insertions(+), 21 deletions(-)
create mode 100644 tests/py/netdev/reject.t.json.output
diff --git a/tests/py/netdev/reject.t.json b/tests/py/netdev/reject.t.json
index 9968aaf..b80db03 100644
--- a/tests/py/netdev/reject.t.json
+++ b/tests/py/netdev/reject.t.json
@@ -130,17 +130,6 @@
# mark 12345 reject with tcp reset
[
- {
- "match": {
- "left": {
- "meta": {
- "key": "l4proto"
- }
- },
- "op": "==",
- "right": 6
- }
- },
{
"match": {
"left": {
@@ -162,30 +151,43 @@
# reject
[
{
- "reject": {
- "expr": "port-unreachable",
- "type": "icmpx"
- }
+ "reject": null
}
]
# meta protocol ip reject
[
{
- "reject": {
- "expr": "port-unreachable",
- "type": "icmp"
+ "match": {
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "op": "==",
+ "right": "ip"
}
+ },
+ {
+ "reject": null
}
]
# meta protocol ip6 reject
[
{
- "reject": {
- "expr": "port-unreachable",
- "type": "icmpv6"
+ "match": {
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "op": "==",
+ "right": "ip6"
}
+ },
+ {
+ "reject": null
}
]
@@ -231,6 +233,17 @@
# meta protocol ip reject with icmp host-unreachable
[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "op": "==",
+ "right": "ip"
+ }
+ },
{
"reject": {
"expr": "host-unreachable",
@@ -241,6 +254,17 @@
# meta protocol ip6 reject with icmpv6 no-route
[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "op": "==",
+ "right": "ip6"
+ }
+ },
{
"reject": {
"expr": "no-route",
diff --git a/tests/py/netdev/reject.t.json.output b/tests/py/netdev/reject.t.json.output
new file mode 100644
index 0000000..cbd7310
--- /dev/null
+++ b/tests/py/netdev/reject.t.json.output
@@ -0,0 +1,81 @@
+# mark 12345 reject with tcp reset
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "op": "==",
+ "right": 6
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "op": "==",
+ "right": 12345
+ }
+ },
+ {
+ "reject": {
+ "type": "tcp reset"
+ }
+ }
+]
+
+# reject
+[
+ {
+ "reject": {
+ "expr": "port-unreachable",
+ "type": "icmpx"
+ }
+ }
+]
+
+# meta protocol ip reject
+[
+ {
+ "reject": {
+ "expr": "port-unreachable",
+ "type": "icmp"
+ }
+ }
+]
+
+# meta protocol ip6 reject
+[
+ {
+ "reject": {
+ "expr": "port-unreachable",
+ "type": "icmpv6"
+ }
+ }
+]
+
+# meta protocol ip reject with icmp host-unreachable
+[
+ {
+ "reject": {
+ "expr": "host-unreachable",
+ "type": "icmp"
+ }
+ }
+]
+
+# meta protocol ip6 reject with icmpv6 no-route
+[
+ {
+ "reject": {
+ "expr": "no-route",
+ "type": "icmpv6"
+ }
+ }
+]
+

View File

@ -0,0 +1,44 @@
From 0f092c2cb0cc45ea48e8d8714ad2b5bf47a0f9ee Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:25:19 +0200
Subject: [PATCH] netlink: Avoid crash upon missing NFTNL_OBJ_CT_TIMEOUT_ARRAY
attribute
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 2a38f458f12bc032dac1b3ba63f95ca5a3c03fbd
commit 2a38f458f12bc032dac1b3ba63f95ca5a3c03fbd
Author: Phil Sutter <phil@nwl.cc>
Date: Thu Jun 12 20:17:22 2025 +0200
netlink: Avoid crash upon missing NFTNL_OBJ_CT_TIMEOUT_ARRAY attribute
If missing, the memcpy call ends up reading from address zero.
Fixes: c7c94802679cd ("src: add ct timeout support")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/netlink.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/netlink.c b/src/netlink.c
index b1dd31d..aafc583 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1697,9 +1697,10 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
init_list_head(&obj->ct_timeout.timeout_list);
obj->ct_timeout.l3proto = nftnl_obj_get_u16(nlo, NFTNL_OBJ_CT_TIMEOUT_L3PROTO);
obj->ct_timeout.l4proto = nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_TIMEOUT_L4PROTO);
- memcpy(obj->ct_timeout.timeout,
- nftnl_obj_get(nlo, NFTNL_OBJ_CT_TIMEOUT_ARRAY),
- NFTNL_CTTIMEOUT_ARRAY_MAX * sizeof(uint32_t));
+ if (nftnl_obj_is_set(nlo, NFTNL_OBJ_CT_TIMEOUT_ARRAY))
+ memcpy(obj->ct_timeout.timeout,
+ nftnl_obj_get(nlo, NFTNL_OBJ_CT_TIMEOUT_ARRAY),
+ NFTNL_CTTIMEOUT_ARRAY_MAX * sizeof(uint32_t));
break;
case NFT_OBJECT_LIMIT:
obj->limit.rate =

View File

@ -0,0 +1,77 @@
From 708d27f80214211252181333ef680b15e3be8dd7 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:25:19 +0200
Subject: [PATCH] src: BASECHAIN flag no longer implies presence of priority
expression
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 44ea1936463728475768861073ca4ba34a5c2f75
commit 44ea1936463728475768861073ca4ba34a5c2f75
Author: Florian Westphal <fw@strlen.de>
Date: Thu Jun 12 20:17:15 2025 +0200
src: BASECHAIN flag no longer implies presence of priority expression
The included bogon will crash nft because print side assumes that BASECHAIN
flag presence also means that priority expression is available.
Make the print side conditional.
Fixes: a66b5ad9540d ("src: allow for updating devices on existing netdev chain")
Suggested-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/rule.c | 16 ++++++++++------
.../bogons/nft-f/null_ingress_type_crash | 6 ++++++
2 files changed, 16 insertions(+), 6 deletions(-)
create mode 100644 tests/shell/testcases/bogons/nft-f/null_ingress_type_crash
diff --git a/src/rule.c b/src/rule.c
index a0e151d..36d590a 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -1029,8 +1029,10 @@ static void chain_print_declaration(const struct chain *chain,
nft_print(octx, "\n\t\tcomment \"%s\"", chain->comment);
nft_print(octx, "\n");
if (chain->flags & CHAIN_F_BASECHAIN) {
- nft_print(octx, "\t\ttype %s hook %s", chain->type.str,
- hooknum2str(chain->handle.family, chain->hook.num));
+ if (chain->type.str)
+ nft_print(octx, "\t\ttype %s hook %s", chain->type.str,
+ hooknum2str(chain->handle.family, chain->hook.num));
+
if (chain->dev_array_len == 1) {
nft_print(octx, " device \"%s\"", chain->dev_array[0]);
} else if (chain->dev_array_len > 1) {
@@ -1042,10 +1044,12 @@ static void chain_print_declaration(const struct chain *chain,
}
nft_print(octx, " }");
}
- nft_print(octx, " priority %s;",
- prio2str(octx, priobuf, sizeof(priobuf),
- chain->handle.family, chain->hook.num,
- chain->priority.expr));
+
+ if (chain->priority.expr)
+ nft_print(octx, " priority %s;",
+ prio2str(octx, priobuf, sizeof(priobuf),
+ chain->handle.family, chain->hook.num,
+ chain->priority.expr));
if (chain->policy) {
mpz_export_data(&policy, chain->policy->value,
BYTEORDER_HOST_ENDIAN, sizeof(int));
diff --git a/tests/shell/testcases/bogons/nft-f/null_ingress_type_crash b/tests/shell/testcases/bogons/nft-f/null_ingress_type_crash
new file mode 100644
index 0000000..2ed88af
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/null_ingress_type_crash
@@ -0,0 +1,6 @@
+table netdev filter1 {
+ chain c {
+ devices = { lo }
+ }
+}
+list ruleset

View File

@ -0,0 +1,73 @@
From 41f3f87fff4e57993aaefde497c572fd15af2f82 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:25:19 +0200
Subject: [PATCH] rule: skip fuzzy lookup if object name is not available
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit de8396358f869d6d7640eae6d6287c2f7fb0d3dc
commit de8396358f869d6d7640eae6d6287c2f7fb0d3dc
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Sun Jun 15 11:33:42 2025 +0200
rule: skip fuzzy lookup if object name is not available
Skip fuzzy lookup for suggestions when handles are used.
Note that 4cf97abfee61 ("rule: Avoid segfault with anonymous chains")
already skips it for chain.
Fixes: 285bb67a11ad ("src: introduce simple hints on incorrect set")
Fixes: 9f7817a4e022 ("src: introduce simple hints on incorrect chain")
Fixes: d7476ddd5f7d ("src: introduce simple hints on incorrect table")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/rule.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/rule.c b/src/rule.c
index 36d590a..6f97f77 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -211,6 +211,9 @@ struct set *set_lookup_fuzzy(const char *set_name,
struct table *table;
struct set *set;
+ if (!set_name)
+ return NULL;
+
string_misspell_init(&st);
list_for_each_entry(table, &cache->table_cache.list, cache.list) {
@@ -1200,6 +1203,9 @@ struct table *table_lookup_fuzzy(const struct handle *h,
struct string_misspell_state st;
struct table *table;
+ if (!h->table.name)
+ return NULL;
+
string_misspell_init(&st);
list_for_each_entry(table, &cache->table_cache.list, cache.list) {
@@ -1680,6 +1686,9 @@ struct obj *obj_lookup_fuzzy(const char *obj_name,
struct table *table;
struct obj *obj;
+ if (!obj_name)
+ return NULL;
+
string_misspell_init(&st);
list_for_each_entry(table, &cache->table_cache.list, cache.list) {
@@ -2157,6 +2166,9 @@ struct flowtable *flowtable_lookup_fuzzy(const char *ft_name,
struct table *table;
struct flowtable *ft;
+ if (!ft_name)
+ return NULL;
+
string_misspell_init(&st);
list_for_each_entry(table, &cache->table_cache.list, cache.list) {

View File

@ -0,0 +1,75 @@
From b6f184c87e7ddf8262426f17ec3bb02295d422bd Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:25:19 +0200
Subject: [PATCH] cache: assert name is non-nul when looking up
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit f15bc7d368b7c1d897fd830f91e7db6929175b27
commit f15bc7d368b7c1d897fd830f91e7db6929175b27
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Sun Jun 15 11:33:49 2025 +0200
cache: assert name is non-nul when looking up
{table,chain,set,obj,flowtable}_cache_find() should not be called when
handles are used
Fixes: 5ec5c706d993 ("cache: add hashtable cache for table")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/cache.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/src/cache.c b/src/cache.c
index 0ac0f7c..2330112 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -487,8 +487,7 @@ struct table *table_cache_find(const struct cache *cache,
struct table *table;
uint32_t hash;
- if (!name)
- return NULL;
+ assert(name);
hash = djb_hash(name) % NFT_CACHE_HSIZE;
list_for_each_entry(table, &cache->ht[hash], cache.hlist) {
@@ -608,6 +607,8 @@ struct chain *chain_cache_find(const struct table *table, const char *name)
struct chain *chain;
uint32_t hash;
+ assert(name);
+
hash = djb_hash(name) % NFT_CACHE_HSIZE;
list_for_each_entry(chain, &table->chain_cache.ht[hash], cache.hlist) {
if (!strcmp(chain->handle.chain.name, name))
@@ -765,6 +766,8 @@ struct set *set_cache_find(const struct table *table, const char *name)
struct set *set;
uint32_t hash;
+ assert(name);
+
hash = djb_hash(name) % NFT_CACHE_HSIZE;
list_for_each_entry(set, &table->set_cache.ht[hash], cache.hlist) {
if (!strcmp(set->handle.set.name, name))
@@ -851,6 +854,8 @@ struct obj *obj_cache_find(const struct table *table, const char *name,
struct obj *obj;
uint32_t hash;
+ assert(name);
+
hash = djb_hash(name) % NFT_CACHE_HSIZE;
list_for_each_entry(obj, &table->obj_cache.ht[hash], cache.hlist) {
if (!strcmp(obj->handle.obj.name, name) &&
@@ -955,6 +960,8 @@ struct flowtable *ft_cache_find(const struct table *table, const char *name)
struct flowtable *ft;
uint32_t hash;
+ assert(name);
+
hash = djb_hash(name) % NFT_CACHE_HSIZE;
list_for_each_entry(ft, &table->ft_cache.ht[hash], cache.hlist) {
if (!strcmp(ft->handle.flowtable.name, name))

View File

@ -0,0 +1,55 @@
From 9f153e8f0425b02169617aefa65eab6e6f594b9f Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:25:19 +0200
Subject: [PATCH] parser_bison: only reset by name is supported by now
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit e8c7ba0aac7ce321e61008fe9b4b8f11c3ba7e1d
commit e8c7ba0aac7ce321e61008fe9b4b8f11c3ba7e1d
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Sun Jun 15 11:34:11 2025 +0200
parser_bison: only reset by name is supported by now
NFT_MSG_GETSET does not support for handle lookup yet, restrict this to
reset by name by now.
Add a bogon test reported by Florian Westphal.
Fixes: 83e0f4402fb7 ("Implement 'reset {set,map,element}' commands")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_bison.y | 4 ++--
tests/shell/testcases/bogons/nft-f/null_set_name_crash | 2 ++
2 files changed, 4 insertions(+), 2 deletions(-)
create mode 100644 tests/shell/testcases/bogons/nft-f/null_set_name_crash
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 5ced6e1..ff6660c 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -1750,11 +1750,11 @@ reset_cmd : COUNTERS ruleset_spec
{
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
}
- | SET set_or_id_spec
+ | SET set_spec
{
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_SET, &$2, &@$, NULL);
}
- | MAP set_or_id_spec
+ | MAP set_spec
{
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_MAP, &$2, &@$, NULL);
}
diff --git a/tests/shell/testcases/bogons/nft-f/null_set_name_crash b/tests/shell/testcases/bogons/nft-f/null_set_name_crash
new file mode 100644
index 0000000..e5d85b2
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/null_set_name_crash
@@ -0,0 +1,2 @@
+table y { }
+reset set y handle 6

View File

@ -0,0 +1,66 @@
From 7e5c81ad7028802c7e61f304ccba8a932cc3fac8 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:25:19 +0200
Subject: [PATCH] parser_bison: allow delete command with map via handle
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 640312b1529c548790117635c91886a6c83e83f2
commit 640312b1529c548790117635c91886a6c83e83f2
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Sun Jun 15 11:36:28 2025 +0200
parser_bison: allow delete command with map via handle
For consistency with sets, allow delete via handle for maps too.
Fixes: f4a34d25f6d5 ("src: list set handle and delete set via set handle")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_bison.y | 2 +-
tests/shell/testcases/cache/0008_delete_by_handle_0 | 4 ++++
tests/shell/testcases/cache/0009_delete_by_handle_incorrect_0 | 1 +
3 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/parser_bison.y b/src/parser_bison.y
index ff6660c..e252113 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -1403,7 +1403,7 @@ delete_cmd : TABLE table_or_id_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL);
}
- | MAP set_spec
+ | MAP set_or_id_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL);
}
diff --git a/tests/shell/testcases/cache/0008_delete_by_handle_0 b/tests/shell/testcases/cache/0008_delete_by_handle_0
index 0db4c69..9eb75e6 100755
--- a/tests/shell/testcases/cache/0008_delete_by_handle_0
+++ b/tests/shell/testcases/cache/0008_delete_by_handle_0
@@ -16,6 +16,10 @@ $NFT add set t s { type ipv4_addr\; }
HANDLE=`$NFT -a list ruleset | grep "set.*handle" | cut -d' ' -f6`
$NFT delete set t handle $HANDLE
+$NFT add map t m { type ipv4_addr : ipv4_addr\; }
+HANDLE=`$NFT -a list ruleset | grep "map.*handle" | cut -d' ' -f6`
+$NFT delete map t handle $HANDLE
+
$NFT add flowtable t f { hook ingress priority 0\; devices = { lo } \; }
HANDLE=`$NFT -a list ruleset | grep "flowtable.*handle" | cut -d' ' -f6`
$NFT delete flowtable t handle $HANDLE
diff --git a/tests/shell/testcases/cache/0009_delete_by_handle_incorrect_0 b/tests/shell/testcases/cache/0009_delete_by_handle_incorrect_0
index f0bb02a..dd390e7 100755
--- a/tests/shell/testcases/cache/0009_delete_by_handle_incorrect_0
+++ b/tests/shell/testcases/cache/0009_delete_by_handle_incorrect_0
@@ -3,6 +3,7 @@
$NFT delete table handle 4000 && exit 1
$NFT delete chain t handle 4000 && exit 1
$NFT delete set t handle 4000 && exit 1
+$NFT delete map t handle 4000 && exit 1
$NFT delete flowtable t handle 4000 && exit 1
$NFT delete counter t handle 4000 && exit 1
exit 0

View File

@ -0,0 +1,172 @@
From 0a64846a869aab6c9bc5cb1adb268a16f53bb9f8 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:25:19 +0200
Subject: [PATCH] json: reject too long interface names
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit bed99830c4c63eae205c28a7ff914737bedb199d
commit bed99830c4c63eae205c28a7ff914737bedb199d
Author: Florian Westphal <fw@strlen.de>
Date: Tue Jun 24 23:46:59 2025 +0200
json: reject too long interface names
Blamed commit added a length check on ifnames to the bison parser.
Unfortunately that wasn't enough, json parser has the same issue.
Bogon results in:
BUG: Interface length 44 exceeds limit
nft: src/mnl.c:742: nft_dev_add: Assertion `0' failed.
After patch, included bogon results in:
Error: Invalid device at index 0. name d2345678999999999999999999999999999999012345 too long
I intentionally did not extend evaluate.c to catch this, past sentiment
was that frontends should not send garbage.
I'll send a followup patch to also catch this from eval stage in case there
are further reports for frontends passing in such long names.
Fixes: fa52bc225806 ("parser: reject zero-length interface names")
Signed-off-by: Florian Westphal <fw@strlen.de>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_json.c | 17 +++-
.../nft-j-f/dev_name_parser_overflow_crash | 90 +++++++++++++++++++
2 files changed, 105 insertions(+), 2 deletions(-)
create mode 100644 tests/shell/testcases/bogons/nft-j-f/dev_name_parser_overflow_crash
diff --git a/src/parser_json.c b/src/parser_json.c
index 936c5aa..eefb8af 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -3062,7 +3062,13 @@ static struct expr *json_parse_devs(struct json_ctx *ctx, json_t *root)
size_t index;
if (!json_unpack(root, "s", &dev)) {
- tmp = constant_expr_alloc(int_loc, &string_type,
+ if (strlen(dev) >= IFNAMSIZ) {
+ json_error(ctx, "Device name %s too long", dev);
+ expr_free(expr);
+ return NULL;
+ }
+
+ tmp = constant_expr_alloc(int_loc, &ifname_type,
BYTEORDER_HOST_ENDIAN,
strlen(dev) * BITS_PER_BYTE, dev);
compound_expr_add(expr, tmp);
@@ -3080,7 +3086,14 @@ static struct expr *json_parse_devs(struct json_ctx *ctx, json_t *root)
expr_free(expr);
return NULL;
}
- tmp = constant_expr_alloc(int_loc, &string_type,
+
+ if (strlen(dev) >= IFNAMSIZ) {
+ json_error(ctx, "Device name %s too long at index %zu", dev, index);
+ expr_free(expr);
+ return NULL;
+ }
+
+ tmp = constant_expr_alloc(int_loc, &ifname_type,
BYTEORDER_HOST_ENDIAN,
strlen(dev) * BITS_PER_BYTE, dev);
compound_expr_add(expr, tmp);
diff --git a/tests/shell/testcases/bogons/nft-j-f/dev_name_parser_overflow_crash b/tests/shell/testcases/bogons/nft-j-f/dev_name_parser_overflow_crash
new file mode 100644
index 0000000..8303c5c
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-j-f/dev_name_parser_overflow_crash
@@ -0,0 +1,90 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "netdev",
+ "name": "filter1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "filter1",
+ "name": "Main_Ingress1",
+ "handle": 0,
+ "dev": "lo",
+ "type": "filter",
+ "hook": "ingress",
+ "prio": -500,
+ "policy": "accept"
+ }
+ },
+ {
+ "table": {
+ "family": "netdev",
+ "name": "filter2",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "filter2",
+ "name": "Main_Ingress2",
+ "handle": 0,
+ "dev": [
+ "d2345678999999999999999999999999999999012345",
+ "lo"
+ ],
+ "type": "filter",
+ "hook": "ingress",
+ "prio": -500,
+ "policy": "accept"
+ }
+ },
+ {
+ "table": {
+ "family": "netdev",
+ "name": "filter3",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "filter3",
+ "name": "Main_Ingress3",
+ "handle": 0,
+ "dev": [
+ "d23456789012345",
+ "lo"
+ ],
+ "type": "filter",
+ "hook": "ingress",
+ "prio": -500,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "filter3",
+ "name": "Main_Egress3",
+ "handle": 0,
+ "dev": "lo",
+ "type": "filter",
+ "hook": "egress",
+ "prio": -500,
+ "policy": "accept"
+ }
+ }
+ ]
+}

View File

@ -0,0 +1,142 @@
From 34c6ee9bfa7992d449be6436cf0e550ab68e3dd1 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:25:19 +0200
Subject: [PATCH] doc: extend description of fib expression
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit be4b61c05a2491aad596aa9243b17b13c937b347
commit be4b61c05a2491aad596aa9243b17b13c937b347
Author: Florian Westphal <fw@strlen.de>
Date: Thu Oct 10 15:37:42 2024 +0200
doc: extend description of fib expression
Describe the input keys and the result types.
Mention which input keys are mandatory and which keys are mutually
exclusive.
Describe which hooks can be used with the various lookup modifiers
and extend the examples with more information on fib expression
capabilities.
Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1663
Signed-off-by: Florian Westphal <fw@strlen.de>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/primary-expression.txt | 77 +++++++++++++++++++++++++++++++-------
1 file changed, 63 insertions(+), 14 deletions(-)
diff --git a/doc/primary-expression.txt b/doc/primary-expression.txt
index e13970c..e85fa58 100644
--- a/doc/primary-expression.txt
+++ b/doc/primary-expression.txt
@@ -304,17 +304,48 @@ table inet x {
FIB EXPRESSIONS
~~~~~~~~~~~~~~~
[verse]
-*fib* {*saddr* | *daddr* | *mark* | *iif* | *oif*} [*.* ...] {*oif* | *oifname* | *type*}
+*fib* 'FIB_TUPLE' 'FIB_RESULT'
+'FIB_TUPLE' := { *saddr* | *daddr*} [ *.* { *iif* | *oif* } *.* *mark* ]
+'FIB_RESULT' := { *oif* | *oifname* | *type* }
-A fib expression queries the fib (forwarding information base) to obtain
-information such as the output interface index a particular address would use.
-The input is a tuple of elements that is used as input to the fib lookup
-functions.
-.fib expression specific types
+A fib expression queries the fib (forwarding information base) to obtain information
+such as the output interface index.
+
+The first arguments to the *fib* expression are the input keys to be passed to the fib lookup function.
+One of *saddr* or *daddr* is mandatory, they are also mutually exclusive.
+
+*mark*, *iif* and *oif* keywords are optional modifiers to influence the search result, see
+the *FIB_TUPLE* keyword table below for a description.
+The *iif* and *oif* tuple keywords are also mutually exclusive.
+
+The last argument to the *fib* expression is the desired result type.
+
+*oif* asks to obtain the interface index that would be used to send packets to the packets source
+(*saddr* key) or destination (*daddr* key). If no routing entry is found, the returned interface
+index is 0.
+
+*oifname* is like *oif*, but it fills the interface name instead. This is useful to check dynamic
+interfaces such as ppp devices. If no entry is found, an empty interface name is returned.
+
+*type* returns the address type such as unicast or multicast. A complete list of supported
+address types can be shown with *nft* *describe* *fib_addrtype*.
+
+.FIB_TUPLE keywords
[options="header"]
|==================
-|Keyword| Description| Type
+|flag| Description
+|daddr| Perform a normal route lookup: search fib for route to the *destination address* of the packet.
+|saddr| Perform a reverse route lookup: search the fib for route to the *source address* of the packet.
+|mark | consider the packet mark (nfmark) when querying the fib.
+|iif | if fib lookups provides a route then check its output interface is identical to the packets *input* interface.
+|oif | if fib lookups provides a route then check its output interface is identical to the packets *output* interface. This flag can only be used with the *type* result.
+|=======================
+
+.FIB_RESULT keywords
+[options="header"]
+|==================
+|Keyword| Description| Result Type
|oif|
Output interface index|
integer (32 bit)
@@ -323,25 +354,43 @@ Output interface name|
string
|type|
Address type |
-fib_addrtype
+fib_addrtype (see *nft* *describe* *fib_addrtype* for a list)
|=======================
-Use *nft* *describe* *fib_addrtype* to get a list of all address types.
+The *oif* and *oifname* result is only valid in the *prerouting*, *input* and *forward* hooks.
+The *type* can be queried from any one of *prerouting*, *input*, *forward* *output* and *postrouting*.
+
+For *type*, the presence of the *iif* keyword in the 'FIB_TUPLE' modifiers restrict the available
+hooks to those where the packet is associated with an incoming interface, i.e. *prerouting*, *input* and *forward*.
+Likewise, the *oif* keyword in the 'FIB_TUPLE' modifier list will limit the available hooks to
+*forward*, *output* and *postrouting*.
.Using fib expressions
----------------------
# drop packets without a reverse path
filter prerouting fib saddr . iif oif missing drop
-In this example, 'saddr . iif' looks up routing information based on the source address and the input interface.
-oif picks the output interface index from the routing information.
+In this example, 'saddr . iif' looks up a route to the *source address* of the packet and restricts matching
+results to the interface that the packet arrived on, then stores the output interface index from the obtained
+fib route result.
+
If no route was found for the source address/input interface combination, the output interface index is zero.
-In case the input interface is specified as part of the input key, the output interface index is always the same as the input interface index or zero.
-If only 'saddr oif' is given, then oif can be any interface index or zero.
+Hence, this rule will drop all packets that do not have a strict reverse path (hypothetical reply packet
+would be sent via the interface the tested packet arrived on).
+
+If only 'saddr oif' is used as the input key, then this rule would only drop packets where the fib cannot
+find a route. In most setups this will never drop packets because the default route is returned.
-# drop packets to address not configured on incoming interface
+# drop packets if the destination ip address is not configured on the incoming interface
filter prerouting fib daddr . iif type != { local, broadcast, multicast } drop
+This queries the fib based on the current packets' destination address and the incoming interface.
+
+If the packet is sent to a unicast address that is configured on a different interface, then the packet
+will be dropped as such an address would be classified as 'unicast' type.
+Without the 'iif' modifier, any address configured on the local machine is 'local', and unicast addresses
+not configured on any interface would return the type 'unicast'.
+
# perform lookup in a specific 'blackhole' table (0xdead, needs ip appropriate ip rule)
filter prerouting meta mark set 0xdead fib daddr . mark type vmap { blackhole : drop, prohibit : jump prohibited, unreachable : drop }
----------------------

View File

@ -0,0 +1,57 @@
From e3bdd843cd1b342082c7b01a888c33d05a91117b Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:25:19 +0200
Subject: [PATCH] json: fix use after free in table_flags_json()
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit b04512cf30de1ba6657facba5ebe2321e17c2727
commit b04512cf30de1ba6657facba5ebe2321e17c2727
Author: Thomas Haller <thaller@redhat.com>
Date: Tue Nov 14 16:29:25 2023 +0100
json: fix use after free in table_flags_json()
Add `$NFT -j list ruleset` to the end of "tests/shell/testcases/transactions/table_onoff".
Then valgrind will find this issue:
$ make -j && ./tests/shell/run-tests.sh tests/shell/testcases/transactions/table_onoff -V
Gives:
==286== Invalid read of size 4
==286== at 0x49B0261: do_dump (dump.c:211)
==286== by 0x49B08B8: do_dump (dump.c:378)
==286== by 0x49B08B8: do_dump (dump.c:378)
==286== by 0x49B04F7: do_dump (dump.c:273)
==286== by 0x49B08B8: do_dump (dump.c:378)
==286== by 0x49B0E84: json_dump_callback (dump.c:465)
==286== by 0x48AF22A: do_command_list_json (json.c:2016)
==286== by 0x48732F1: do_command_list (rule.c:2335)
==286== by 0x48737F5: do_command (rule.c:2605)
==286== by 0x48A867D: nft_netlink (libnftables.c:42)
==286== by 0x48A92B1: nft_run_cmd_from_buffer (libnftables.c:597)
==286== by 0x402CBA: main (main.c:533)
Fixes: e70354f53e9f ("libnftables: Implement JSON output support")
Signed-off-by: Thomas Haller <thaller@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/json.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/json.c b/src/json.c
index 39b1ada..e5bec4f 100644
--- a/src/json.c
+++ b/src/json.c
@@ -517,7 +517,7 @@ static json_t *table_flags_json(const struct table *table)
json_decref(root);
return NULL;
case 1:
- json_unpack(root, "[o]", &tmp);
+ json_unpack(root, "[O]", &tmp);
json_decref(root);
root = tmp;
break;

View File

@ -0,0 +1,48 @@
From 1c481d182bca10f9384f00545a4e2b85acffb76c Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:25:19 +0200
Subject: [PATCH] json: Print single fib flag as non-array
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit dbe5c44f2b891c1d305cc94a7ea9dd963a7b6100
commit dbe5c44f2b891c1d305cc94a7ea9dd963a7b6100
Author: Phil Sutter <phil@nwl.cc>
Date: Thu May 8 16:40:41 2025 +0200
json: Print single fib flag as non-array
Check array size and reduce the array if possible.
The zero array length check is dead code here due to the surrounding 'if
(flags)' block, but it's a common idiom one could replace by a shared
routine later.
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/json.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/json.c b/src/json.c
index e5bec4f..9320336 100644
--- a/src/json.c
+++ b/src/json.c
@@ -935,7 +935,15 @@ json_t *fib_expr_json(const struct expr *expr, struct output_ctx *octx)
}
if (flags)
json_array_append_new(tmp, json_integer(flags));
- json_object_set_new(root, "flags", tmp);
+
+ if (json_array_size(tmp) > 1) {
+ json_object_set_new(root, "flags", tmp);
+ } else {
+ if (json_array_size(tmp))
+ json_object_set(root, "flags",
+ json_array_get(tmp, 0));
+ json_decref(tmp);
+ }
}
return json_pack("{s:o}", "fib", root);
}

View File

@ -0,0 +1,42 @@
From 399c60ed7706c110123aa28f282f45899a2cd56f Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:25:19 +0200
Subject: [PATCH] json: Print single synproxy flags as non-array
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 38f99ee84fe6bd029fbc3d1389f42ab82fa9789c
commit 38f99ee84fe6bd029fbc3d1389f42ab82fa9789c
Author: Phil Sutter <phil@nwl.cc>
Date: Thu May 8 16:44:39 2025 +0200
json: Print single synproxy flags as non-array
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/json.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/json.c b/src/json.c
index 9320336..9b3a639 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1685,10 +1685,14 @@ json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
if (stmt->synproxy.flags & NF_SYNPROXY_OPT_SACK_PERM)
json_array_append_new(flags, json_string("sack-perm"));
- if (json_array_size(flags) > 0)
+ if (json_array_size(flags) > 1) {
json_object_set_new(root, "flags", flags);
- else
+ } else {
+ if (json_array_size(flags))
+ json_object_set(root, "flags",
+ json_array_get(flags, 0));
json_decref(flags);
+ }
if (!json_object_size(root)) {
json_decref(root);

View File

@ -0,0 +1,221 @@
From a3f57e683bbf70bbba05bd495a2f5d8c32eb6aa4 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:25:20 +0200
Subject: [PATCH] json: Introduce json_add_array_new()
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit a740f2036ad0d50b4a91e110611563809dac420a
Conflicts: Context change due to missing commit a0a15e4dd0576
("json: Fix for memleak in __binop_expr_json")
commit a740f2036ad0d50b4a91e110611563809dac420a
Author: Phil Sutter <phil@nwl.cc>
Date: Thu May 8 17:28:02 2025 +0200
json: Introduce json_add_array_new()
Propagate nat_stmt_add_array() to a generic helper for use in all spots
adding an array property which may reduce to a single item or even not
exist at all.
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/json.c | 99 +++++++++++++-----------------------------------------
1 file changed, 24 insertions(+), 75 deletions(-)
diff --git a/src/json.c b/src/json.c
index 9b3a639..c8abc56 100644
--- a/src/json.c
+++ b/src/json.c
@@ -42,6 +42,18 @@
})
#endif
+static void json_add_array_new(json_t *obj, const char *name, json_t *array)
+{
+ if (json_array_size(array) > 1) {
+ json_object_set_new(obj, name, array);
+ } else {
+ if (json_array_size(array))
+ json_object_set(obj, name,
+ json_array_get(array, 0));
+ json_decref(array);
+ }
+}
+
static json_t *expr_print_json(const struct expr *expr, struct output_ctx *octx)
{
const struct expr_ops *ops;
@@ -188,14 +200,7 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
json_array_append_new(tmp, json_pack("s", "timeout"));
if (set->flags & NFT_SET_EVAL)
json_array_append_new(tmp, json_pack("s", "dynamic"));
-
- if (json_array_size(tmp) > 1) {
- json_object_set_new(root, "flags", tmp);
- } else {
- if (json_array_size(tmp))
- json_object_set(root, "flags", json_array_get(tmp, 0));
- json_decref(tmp);
- }
+ json_add_array_new(root, "flags", tmp);
if (set->timeout) {
tmp = json_integer(set->timeout / 1000);
@@ -443,19 +448,16 @@ static json_t *obj_print_json(const struct obj *obj)
json_decref(tmp);
break;
case NFT_OBJECT_SYNPROXY:
- flags = json_array();
tmp = json_pack("{s:i, s:i}",
"mss", obj->synproxy.mss,
"wscale", obj->synproxy.wscale);
+
+ flags = json_array();
if (obj->synproxy.flags & NF_SYNPROXY_OPT_TIMESTAMP)
json_array_append_new(flags, json_string("timestamp"));
if (obj->synproxy.flags & NF_SYNPROXY_OPT_SACK_PERM)
json_array_append_new(flags, json_string("sack-perm"));
-
- if (json_array_size(flags) > 0)
- json_object_set_new(tmp, "flags", flags);
- else
- json_decref(flags);
+ json_add_array_new(tmp, "flags", flags);
json_object_update(root, tmp);
json_decref(tmp);
@@ -512,31 +514,18 @@ static json_t *table_flags_json(const struct table *table)
flags >>= 1;
i++;
}
- switch (json_array_size(root)) {
- case 0:
- json_decref(root);
- return NULL;
- case 1:
- json_unpack(root, "[O]", &tmp);
- json_decref(root);
- root = tmp;
- break;
- }
return root;
}
static json_t *table_print_json(const struct table *table)
{
- json_t *root, *tmp;
+ json_t *root;
root = json_pack("{s:s, s:s, s:I}",
"family", family2str(table->handle.family),
"name", table->handle.table.name,
"handle", table->handle.handle.id);
-
- tmp = table_flags_json(table);
- if (tmp)
- json_object_set_new(root, "flags", tmp);
+ json_add_array_new(root, "flags", table_flags_json(table));
if (table->comment)
json_object_set_new(root, "comment", json_string(table->comment));
@@ -936,14 +925,7 @@ json_t *fib_expr_json(const struct expr *expr, struct output_ctx *octx)
if (flags)
json_array_append_new(tmp, json_integer(flags));
- if (json_array_size(tmp) > 1) {
- json_object_set_new(root, "flags", tmp);
- } else {
- if (json_array_size(tmp))
- json_object_set(root, "flags",
- json_array_get(tmp, 0));
- json_decref(tmp);
- }
+ json_add_array_new(root, "flags", tmp);
}
return json_pack("{s:o}", "fib", root);
}
@@ -1383,14 +1365,7 @@ json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
if (stmt->log.logflags & NF_LOG_MACDECODE)
json_array_append_new(flags, json_string("ether"));
}
- if (json_array_size(flags) > 1) {
- json_object_set_new(root, "flags", flags);
- } else {
- if (json_array_size(flags))
- json_object_set(root, "flags",
- json_array_get(flags, 0));
- json_decref(flags);
- }
+ json_add_array_new(root, "flags", flags);
if (!json_object_size(root)) {
json_decref(root);
@@ -1425,18 +1400,6 @@ static json_t *nat_type_flags_json(uint32_t type_flags)
return array;
}
-static void nat_stmt_add_array(json_t *root, const char *name, json_t *array)
-{
- if (json_array_size(array) > 1) {
- json_object_set_new(root, name, array);
- } else {
- if (json_array_size(array))
- json_object_set(root, name,
- json_array_get(array, 0));
- json_decref(array);
- }
-}
-
json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
json_t *root = json_object();
@@ -1458,12 +1421,12 @@ json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
json_object_set_new(root, "port",
expr_print_json(stmt->nat.proto, octx));
- nat_stmt_add_array(root, "flags", array);
+ json_add_array_new(root, "flags", array);
if (stmt->nat.type_flags) {
array = nat_type_flags_json(stmt->nat.type_flags);
- nat_stmt_add_array(root, "type_flags", array);
+ json_add_array_new(root, "type_flags", array);
}
if (!json_object_size(root)) {
@@ -1615,14 +1578,7 @@ json_t *queue_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
json_array_append_new(flags, json_string("bypass"));
if (stmt->queue.flags & NFT_QUEUE_FLAG_CPU_FANOUT)
json_array_append_new(flags, json_string("fanout"));
- if (json_array_size(flags) > 1) {
- json_object_set_new(root, "flags", flags);
- } else {
- if (json_array_size(flags))
- json_object_set(root, "flags",
- json_array_get(flags, 0));
- json_decref(flags);
- }
+ json_add_array_new(root, "flags", flags);
if (!json_object_size(root)) {
json_decref(root);
@@ -1685,14 +1641,7 @@ json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
if (stmt->synproxy.flags & NF_SYNPROXY_OPT_SACK_PERM)
json_array_append_new(flags, json_string("sack-perm"));
- if (json_array_size(flags) > 1) {
- json_object_set_new(root, "flags", flags);
- } else {
- if (json_array_size(flags))
- json_object_set(root, "flags",
- json_array_get(flags, 0));
- json_decref(flags);
- }
+ json_add_array_new(root, "flags", flags);
if (!json_object_size(root)) {
json_decref(root);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,769 @@
From df090a258ef0753e76112b58ba3165a452fc24be Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:30:19 +0200
Subject: [PATCH] parser_json: Introduce parse_flags_array()
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 3550dd69632f3a883ab6593daeffb514c67dfb8c
commit 3550dd69632f3a883ab6593daeffb514c67dfb8c
Author: Phil Sutter <phil@nwl.cc>
Date: Thu Apr 10 16:42:42 2025 +0200
parser_json: Introduce parse_flags_array()
Various objects support a 'flags' property with value usually being an
array of strings. There is a special case, when merely a single flag is
set: The value may be a string representing this flag.
Introduce a function assisting in parsing this polymorphic value. Have
callers pass a parser callback translating a single flag name into a
corresponding value. Luckily, these single flag parsers are very common
already.
As a side-effect, enable the single flag spec for set flags as well and
update the documentation accordingly.
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/libnftables-json.adoc | 5 +-
src/parser_json.c | 466 +++++++++++---------------------------
2 files changed, 136 insertions(+), 335 deletions(-)
diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc
index c3e3bcc..27c96c3 100644
--- a/doc/libnftables-json.adoc
+++ b/doc/libnftables-json.adoc
@@ -317,7 +317,7 @@ ____
"handle":* 'NUMBER'*,
"type":* 'SET_TYPE'*,
"policy":* 'SET_POLICY'*,
- "flags": [* 'SET_FLAG_LIST' *],
+ "flags":* 'SET_FLAGS'*,
"elem":* 'SET_ELEMENTS'*,
"timeout":* 'NUMBER'*,
"gc-interval":* 'NUMBER'*,
@@ -332,7 +332,7 @@ ____
"type":* 'SET_TYPE'*,
"map":* 'STRING'*,
"policy":* 'SET_POLICY'*,
- "flags": [* 'SET_FLAG_LIST' *],
+ "flags":* 'SET_FLAGS'*,
"elem":* 'SET_ELEMENTS'*,
"timeout":* 'NUMBER'*,
"gc-interval":* 'NUMBER'*,
@@ -342,6 +342,7 @@ ____
'SET_TYPE' := 'STRING' | *[* 'SET_TYPE_LIST' *]* | *{ "typeof":* 'EXPRESSION' *}*
'SET_TYPE_LIST' := 'STRING' [*,* 'SET_TYPE_LIST' ]
'SET_POLICY' := *"performance"* | *"memory"*
+'SET_FLAGS' := 'SET_FLAG' | *[* 'SET_FLAG_LIST' *]*
'SET_FLAG_LIST' := 'SET_FLAG' [*,* 'SET_FLAG_LIST' ]
'SET_FLAG' := *"constant"* | *"interval"* | *"timeout"*
'SET_ELEMENTS' := 'EXPRESSION' | *[* 'EXPRESSION_LIST' *]*
diff --git a/src/parser_json.c b/src/parser_json.c
index eefb8af..0bf81d1 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -193,6 +193,60 @@ static int json_unpack_stmt(struct json_ctx *ctx, json_t *root,
return 1;
}
+/**
+ * parse_flags_array - parse JSON property as an array of flags
+ *
+ * @ctx: JSON parser context
+ * @obj: JSON object to extract property from
+ * @key: name of property containing the flags array
+ * @flag_parser: Callback parsing a single flag, returns 0 on error
+ *
+ * The property value may be a string representing a single flag or an array of
+ * strings representing a number of flags whose values are ORed together.
+ *
+ * @return: Combined flag value, 0 if no such property found or -1 if data is
+ * malformed or flag parsing failed.
+ */
+static int parse_flags_array(struct json_ctx *ctx, json_t *obj, const char *key,
+ unsigned int (*flag_parser)(const char *flag))
+{
+ json_t *value = json_object_get(obj, key), *tmp;
+ size_t index;
+ int ret = 0;
+
+ if (!value)
+ return 0;
+
+ if (json_is_string(value)) {
+ ret = flag_parser(json_string_value(value));
+ return ret ?: -1;
+ }
+
+ if (!json_is_array(value)) {
+ json_error(ctx,
+ "Expecting string or array in '%s' property.", key);
+ return -1;
+ }
+
+ json_array_foreach(value, index, tmp) {
+ int flag = 0;
+
+ if (json_is_string(tmp))
+ flag = flag_parser(json_string_value(tmp));
+
+ if (!flag) {
+ json_error(ctx,
+ "Invalid flag in '%s' property array at index %zu.",
+ key, index);
+ return -1;
+ }
+
+ ret |= flag;
+ }
+
+ return ret;
+}
+
static int parse_family(const char *name, uint32_t *family)
{
unsigned int i;
@@ -1060,7 +1114,7 @@ static struct expr *json_parse_hash_expr(struct json_ctx *ctx,
return hash_expr;
}
-static int fib_flag_parse(const char *name, int *flags)
+static unsigned int fib_flag_parse(const char *name)
{
const char *fib_flags[] = {
"saddr",
@@ -1072,12 +1126,10 @@ static int fib_flag_parse(const char *name, int *flags)
unsigned int i;
for (i = 0; i < array_size(fib_flags); i++) {
- if (!strcmp(name, fib_flags[i])) {
- *flags |= (1 << i);
- return 0;
- }
+ if (!strcmp(name, fib_flags[i]))
+ return 1 << i;
}
- return 1;
+ return 0;
}
static struct expr *json_parse_fib_expr(struct json_ctx *ctx,
@@ -1090,11 +1142,9 @@ static struct expr *json_parse_fib_expr(struct json_ctx *ctx,
[NFT_FIB_RESULT_ADDRTYPE] = "type",
};
enum nft_fib_result resultval = NFT_FIB_RESULT_UNSPEC;
- json_t *flags, *value;
const char *result;
- unsigned int i;
- size_t index;
int flagval = 0;
+ unsigned int i;
if (json_unpack_err(ctx, root, "{s:s}", "result", &result))
return NULL;
@@ -1110,34 +1160,9 @@ static struct expr *json_parse_fib_expr(struct json_ctx *ctx,
return NULL;
}
- if (!json_unpack(root, "{s:o}", "flags", &flags)) {
- const char *flag;
-
- if (json_is_string(flags)) {
- flag = json_string_value(flags);
-
- if (fib_flag_parse(flag, &flagval)) {
- json_error(ctx, "Invalid fib flag '%s'.", flag);
- return NULL;
- }
- } else if (!json_is_array(flags)) {
- json_error(ctx, "Unexpected object type in fib tuple.");
- return NULL;
- }
-
- json_array_foreach(flags, index, value) {
- if (!json_is_string(value)) {
- json_error(ctx, "Unexpected object type in fib flags array at index %zd.", index);
- return NULL;
- }
- flag = json_string_value(value);
-
- if (fib_flag_parse(flag, &flagval)) {
- json_error(ctx, "Invalid fib flag '%s'.", flag);
- return NULL;
- }
- }
- }
+ flagval = parse_flags_array(ctx, root, "flags", fib_flag_parse);
+ if (flagval < 0)
+ return NULL;
/* sanity checks from fib_expr in parser_bison.y */
@@ -2090,8 +2115,7 @@ static struct stmt *json_parse_secmark_stmt(struct json_ctx *ctx,
return stmt;
}
-static int json_parse_nat_flag(struct json_ctx *ctx,
- json_t *root, int *flags)
+static unsigned int json_parse_nat_flag(const char *flag)
{
const struct {
const char *flag;
@@ -2102,51 +2126,16 @@ static int json_parse_nat_flag(struct json_ctx *ctx,
{ "persistent", NF_NAT_RANGE_PERSISTENT },
{ "netmap", NF_NAT_RANGE_NETMAP },
};
- const char *flag;
unsigned int i;
- assert(flags);
-
- if (!json_is_string(root)) {
- json_error(ctx, "Invalid nat flag type %s, expected string.",
- json_typename(root));
- return 1;
- }
- flag = json_string_value(root);
for (i = 0; i < array_size(flag_tbl); i++) {
- if (!strcmp(flag, flag_tbl[i].flag)) {
- *flags |= flag_tbl[i].val;
- return 0;
- }
- }
- json_error(ctx, "Unknown nat flag '%s'.", flag);
- return 1;
-}
-
-static int json_parse_nat_flags(struct json_ctx *ctx, json_t *root)
-{
- int flags = 0;
- json_t *value;
- size_t index;
-
- if (json_is_string(root)) {
- json_parse_nat_flag(ctx, root, &flags);
- return flags;
- } else if (!json_is_array(root)) {
- json_error(ctx, "Invalid nat flags type %s.",
- json_typename(root));
- return -1;
- }
- json_array_foreach(root, index, value) {
- if (json_parse_nat_flag(ctx, value, &flags))
- json_error(ctx, "Parsing nat flag at index %zu failed.",
- index);
+ if (!strcmp(flag, flag_tbl[i].flag))
+ return flag_tbl[i].val;
}
- return flags;
+ return 0;
}
-static int json_parse_nat_type_flag(struct json_ctx *ctx,
- json_t *root, int *flags)
+static unsigned int json_parse_nat_type_flag(const char *flag)
{
const struct {
const char *flag;
@@ -2156,47 +2145,13 @@ static int json_parse_nat_type_flag(struct json_ctx *ctx,
{ "prefix", STMT_NAT_F_PREFIX },
{ "concat", STMT_NAT_F_CONCAT },
};
- const char *flag;
unsigned int i;
- assert(flags);
-
- if (!json_is_string(root)) {
- json_error(ctx, "Invalid nat type flag type %s, expected string.",
- json_typename(root));
- return 1;
- }
- flag = json_string_value(root);
for (i = 0; i < array_size(flag_tbl); i++) {
- if (!strcmp(flag, flag_tbl[i].flag)) {
- *flags |= flag_tbl[i].val;
- return 0;
- }
- }
- json_error(ctx, "Unknown nat type flag '%s'.", flag);
- return 1;
-}
-
-static int json_parse_nat_type_flags(struct json_ctx *ctx, json_t *root)
-{
- int flags = 0;
- json_t *value;
- size_t index;
-
- if (json_is_string(root)) {
- json_parse_nat_type_flag(ctx, root, &flags);
- return flags;
- } else if (!json_is_array(root)) {
- json_error(ctx, "Invalid nat flags type %s.",
- json_typename(root));
- return -1;
- }
- json_array_foreach(root, index, value) {
- if (json_parse_nat_type_flag(ctx, value, &flags))
- json_error(ctx, "Parsing nat type flag at index %zu failed.",
- index);
+ if (!strcmp(flag, flag_tbl[i].flag))
+ return flag_tbl[i].val;
}
- return flags;
+ return 0;
}
static int nat_type_parse(const char *type)
@@ -2219,7 +2174,7 @@ static int nat_type_parse(const char *type)
static struct stmt *json_parse_nat_stmt(struct json_ctx *ctx,
const char *key, json_t *value)
{
- int type, familyval;
+ int type, familyval, flags;
struct stmt *stmt;
json_t *tmp;
@@ -2252,24 +2207,20 @@ static struct stmt *json_parse_nat_stmt(struct json_ctx *ctx,
return NULL;
}
}
- if (!json_unpack(value, "{s:o}", "flags", &tmp)) {
- int flags = json_parse_nat_flags(ctx, tmp);
-
- if (flags < 0) {
- stmt_free(stmt);
- return NULL;
- }
- stmt->nat.flags = flags;
+ flags = parse_flags_array(ctx, value, "flags", json_parse_nat_flag);
+ if (flags < 0) {
+ stmt_free(stmt);
+ return NULL;
}
- if (!json_unpack(value, "{s:o}", "type_flags", &tmp)) {
- int flags = json_parse_nat_type_flags(ctx, tmp);
+ stmt->nat.flags = flags;
- if (flags < 0) {
- stmt_free(stmt);
- return NULL;
- }
- stmt->nat.type_flags = flags;
+ flags = parse_flags_array(ctx, value, "type_flags",
+ json_parse_nat_type_flag);
+ if (flags < 0) {
+ stmt_free(stmt);
+ return NULL;
}
+ stmt->nat.type_flags = flags;
return stmt;
}
@@ -2481,8 +2432,7 @@ static struct stmt *json_parse_map_stmt(struct json_ctx *ctx,
return stmt;
}
-static int json_parse_log_flag(struct json_ctx *ctx,
- json_t *root, int *flags)
+static unsigned int json_parse_log_flag(const char *flag)
{
const struct {
const char *flag;
@@ -2495,47 +2445,13 @@ static int json_parse_log_flag(struct json_ctx *ctx,
{ "ether", NF_LOG_MACDECODE },
{ "all", NF_LOG_MASK },
};
- const char *flag;
unsigned int i;
- assert(flags);
-
- if (!json_is_string(root)) {
- json_error(ctx, "Invalid log flag type %s, expected string.",
- json_typename(root));
- return 1;
- }
- flag = json_string_value(root);
for (i = 0; i < array_size(flag_tbl); i++) {
- if (!strcmp(flag, flag_tbl[i].flag)) {
- *flags |= flag_tbl[i].val;
- return 0;
- }
- }
- json_error(ctx, "Unknown log flag '%s'.", flag);
- return 1;
-}
-
-static int json_parse_log_flags(struct json_ctx *ctx, json_t *root)
-{
- int flags = 0;
- json_t *value;
- size_t index;
-
- if (json_is_string(root)) {
- json_parse_log_flag(ctx, root, &flags);
- return flags;
- } else if (!json_is_array(root)) {
- json_error(ctx, "Invalid log flags type %s.",
- json_typename(root));
- return -1;
- }
- json_array_foreach(root, index, value) {
- if (json_parse_log_flag(ctx, value, &flags))
- json_error(ctx, "Parsing log flag at index %zu failed.",
- index);
+ if (!strcmp(flag, flag_tbl[i].flag))
+ return flag_tbl[i].val;
}
- return flags;
+ return 0;
}
static struct stmt *json_parse_log_stmt(struct json_ctx *ctx,
@@ -2543,8 +2459,7 @@ static struct stmt *json_parse_log_stmt(struct json_ctx *ctx,
{
const char *tmpstr;
struct stmt *stmt;
- json_t *jflags;
- int tmp;
+ int tmp, flags;
stmt = log_stmt_alloc(int_loc);
@@ -2577,20 +2492,17 @@ static struct stmt *json_parse_log_stmt(struct json_ctx *ctx,
stmt->log.level = level;
stmt->log.flags |= STMT_LOG_LEVEL;
}
- if (!json_unpack(value, "{s:o}", "flags", &jflags)) {
- int flags = json_parse_log_flags(ctx, jflags);
-
- if (flags < 0) {
- stmt_free(stmt);
- return NULL;
- }
- stmt->log.logflags = flags;
+ flags = parse_flags_array(ctx, value, "flags", json_parse_log_flag);
+ if (flags < 0) {
+ stmt_free(stmt);
+ return NULL;
}
+ stmt->log.logflags = flags;
+
return stmt;
}
-static int json_parse_synproxy_flag(struct json_ctx *ctx,
- json_t *root, int *flags)
+static unsigned int json_parse_synproxy_flag(const char *flag)
{
const struct {
const char *flag;
@@ -2599,54 +2511,19 @@ static int json_parse_synproxy_flag(struct json_ctx *ctx,
{ "timestamp", NF_SYNPROXY_OPT_TIMESTAMP },
{ "sack-perm", NF_SYNPROXY_OPT_SACK_PERM },
};
- const char *flag;
unsigned int i;
- assert(flags);
-
- if (!json_is_string(root)) {
- json_error(ctx, "Invalid synproxy flag type %s, expected string.",
- json_typename(root));
- return 1;
- }
- flag = json_string_value(root);
for (i = 0; i < array_size(flag_tbl); i++) {
- if (!strcmp(flag, flag_tbl[i].flag)) {
- *flags |= flag_tbl[i].val;
- return 0;
- }
- }
- json_error(ctx, "Unknown synproxy flag '%s'.", flag);
- return 1;
-}
-
-static int json_parse_synproxy_flags(struct json_ctx *ctx, json_t *root)
-{
- int flags = 0;
- json_t *value;
- size_t index;
-
- if (json_is_string(root)) {
- json_parse_synproxy_flag(ctx, root, &flags);
- return flags;
- } else if (!json_is_array(root)) {
- json_error(ctx, "Invalid synproxy flags type %s.",
- json_typename(root));
- return -1;
- }
- json_array_foreach(root, index, value) {
- if (json_parse_synproxy_flag(ctx, value, &flags))
- json_error(ctx, "Parsing synproxy flag at index %zu failed.",
- index);
+ if (!strcmp(flag, flag_tbl[i].flag))
+ return flag_tbl[i].val;
}
- return flags;
+ return 0;
}
static struct stmt *json_parse_synproxy_stmt(struct json_ctx *ctx,
const char *key, json_t *value)
{
struct stmt *stmt = NULL;
- json_t *jflags;
int tmp, flags;
if (json_typeof(value) == JSON_NULL) {
@@ -2676,15 +2553,16 @@ static struct stmt *json_parse_synproxy_stmt(struct json_ctx *ctx,
stmt->synproxy.wscale = tmp;
stmt->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE;
}
- if (!json_unpack(value, "{s:o}", "flags", &jflags)) {
+
+ flags = parse_flags_array(ctx, value, "flags",
+ json_parse_synproxy_flag);
+ if (flags < 0) {
+ stmt_free(stmt);
+ return NULL;
+ }
+ if (flags) {
if (!stmt)
stmt = synproxy_stmt_alloc(int_loc);
- flags = json_parse_synproxy_flags(ctx, jflags);
-
- if (flags < 0) {
- stmt_free(stmt);
- return NULL;
- }
stmt->synproxy.flags |= flags;
}
@@ -2779,14 +2657,12 @@ static struct stmt *json_parse_meter_stmt(struct json_ctx *ctx,
return stmt;
}
-static int queue_flag_parse(const char *name, uint16_t *flags)
+static unsigned int queue_flag_parse(const char *name)
{
if (!strcmp(name, "bypass"))
- *flags |= NFT_QUEUE_FLAG_BYPASS;
+ return NFT_QUEUE_FLAG_BYPASS;
else if (!strcmp(name, "fanout"))
- *flags |= NFT_QUEUE_FLAG_CPU_FANOUT;
- else
- return 1;
+ return NFT_QUEUE_FLAG_CPU_FANOUT;
return 0;
}
@@ -2794,8 +2670,8 @@ static struct stmt *json_parse_queue_stmt(struct json_ctx *ctx,
const char *key, json_t *value)
{
struct expr *qexpr = NULL;
- uint16_t flags = 0;
json_t *tmp;
+ int flags;
if (!json_unpack(value, "{s:o}", "num", &tmp)) {
qexpr = json_parse_stmt_expr(ctx, tmp);
@@ -2804,43 +2680,13 @@ static struct stmt *json_parse_queue_stmt(struct json_ctx *ctx,
return NULL;
}
}
- if (!json_unpack(value, "{s:o}", "flags", &tmp)) {
- const char *flag;
- size_t index;
- json_t *val;
-
- if (json_is_string(tmp)) {
- flag = json_string_value(tmp);
-
- if (queue_flag_parse(flag, &flags)) {
- json_error(ctx, "Invalid queue flag '%s'.",
- flag);
- expr_free(qexpr);
- return NULL;
- }
- } else if (!json_is_array(tmp)) {
- json_error(ctx, "Unexpected object type in queue flags.");
- expr_free(qexpr);
- return NULL;
- }
- json_array_foreach(tmp, index, val) {
- if (!json_is_string(val)) {
- json_error(ctx, "Invalid object in queue flag array at index %zu.",
- index);
- expr_free(qexpr);
- return NULL;
- }
- flag = json_string_value(val);
-
- if (queue_flag_parse(flag, &flags)) {
- json_error(ctx, "Invalid queue flag '%s'.",
- flag);
- expr_free(qexpr);
- return NULL;
- }
- }
+ flags = parse_flags_array(ctx, value, "flags", queue_flag_parse);
+ if (flags < 0) {
+ expr_free(qexpr);
+ return NULL;
}
+
return queue_stmt_alloc(int_loc, qexpr, flags);
}
@@ -2950,45 +2796,6 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
return NULL;
}
-static int json_parse_table_flags(struct json_ctx *ctx, json_t *root,
- enum table_flags *flags)
-{
- json_t *tmp, *tmp2;
- size_t index;
- int flag;
-
- if (json_unpack(root, "{s:o}", "flags", &tmp))
- return 0;
-
- if (json_is_string(tmp)) {
- flag = parse_table_flag(json_string_value(tmp));
- if (flag) {
- *flags = flag;
- return 0;
- }
- json_error(ctx, "Invalid table flag '%s'.",
- json_string_value(tmp));
- return 1;
- }
- if (!json_is_array(tmp)) {
- json_error(ctx, "Unexpected table flags value.");
- return 1;
- }
- json_array_foreach(tmp, index, tmp2) {
- if (json_is_string(tmp2)) {
- flag = parse_table_flag(json_string_value(tmp2));
-
- if (flag) {
- *flags |= flag;
- continue;
- }
- }
- json_error(ctx, "Invalid table flag at index %zu.", index);
- return 1;
- }
- return 0;
-}
-
static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
enum cmd_ops op, enum cmd_obj obj)
{
@@ -2997,7 +2804,7 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
.table.location = *int_loc,
};
struct table *table = NULL;
- enum table_flags flags = 0;
+ int flags = 0;
if (json_unpack_err(ctx, root, "{s:s}",
"family", &family))
@@ -3008,9 +2815,10 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
return NULL;
json_unpack(root, "{s:s}", "comment", &comment);
- if (json_parse_table_flags(ctx, root, &flags))
- return NULL;
+ flags = parse_flags_array(ctx, root, "flags", parse_table_flag);
+ if (flags < 0)
+ return NULL;
} else if (op == CMD_DELETE &&
json_unpack(root, "{s:s}", "name", &h.table.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
@@ -3291,7 +3099,7 @@ static int string_to_nft_object(const char *str)
return 0;
}
-static int string_to_set_flag(const char *str)
+static unsigned int string_to_set_flag(const char *str)
{
const struct {
enum nft_set_flags val;
@@ -3318,6 +3126,7 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
const char *family = "", *policy, *dtype_ext = NULL;
json_t *tmp, *stmt_json;
struct set *set;
+ int flags;
if (json_unpack_err(ctx, root, "{s:s, s:s}",
"family", &family,
@@ -3398,23 +3207,16 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
return NULL;
}
}
- if (!json_unpack(root, "{s:o}", "flags", &tmp)) {
- json_t *value;
- size_t index;
- json_array_foreach(tmp, index, value) {
- int flag;
-
- if (!json_is_string(value) ||
- !(flag = string_to_set_flag(json_string_value(value)))) {
- json_error(ctx, "Invalid set flag at index %zu.", index);
- set_free(set);
- handle_free(&h);
- return NULL;
- }
- set->flags |= flag;
- }
+ flags = parse_flags_array(ctx, root, "flags", string_to_set_flag);
+ if (flags < 0) {
+ json_error(ctx, "Invalid set flags in set '%s'.", h.set.name);
+ set_free(set);
+ handle_free(&h);
+ return NULL;
}
+ set->flags |= flags;
+
if (!json_unpack(root, "{s:o}", "elem", &tmp)) {
set->init = json_parse_set_expr(ctx, "elem", tmp);
if (!set->init) {
@@ -3585,7 +3387,6 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
int inv = 0, flags = 0, i, j;
struct handle h = { 0 };
struct obj *obj;
- json_t *jflags;
if (json_unpack_err(ctx, root, "{s:s, s:s}",
"family", &family,
@@ -3765,13 +3566,12 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj->synproxy.wscale = j;
obj->synproxy.flags |= NF_SYNPROXY_OPT_MSS;
obj->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE;
- if (!json_unpack(root, "{s:o}", "flags", &jflags)) {
- flags = json_parse_synproxy_flags(ctx, jflags);
- if (flags < 0)
- goto err_free_obj;
+ flags = parse_flags_array(ctx, root, "flags",
+ json_parse_synproxy_flag);
+ if (flags < 0)
+ goto err_free_obj;
- obj->synproxy.flags |= flags;
- }
+ obj->synproxy.flags |= flags;
break;
default:
BUG("Invalid CMD '%d'", cmd_obj);

View File

@ -0,0 +1,92 @@
From da4fc3c74125e4f5eb69ee4174713c7edb91f071 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:30:20 +0200
Subject: [PATCH] tests: py: fix json single-flag output for fib & synproxy
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit c2ee1d5978bf6ea10e3b1a74125da5b5e8468f26
commit c2ee1d5978bf6ea10e3b1a74125da5b5e8468f26
Author: Florian Westphal <fw@strlen.de>
Date: Mon Jun 2 14:12:16 2025 +0200
tests: py: fix json single-flag output for fib & synproxy
Blamed commits change output format but did not adjust existing tests:
inet/fib.t: WARNING: line 16: '{"nftables": ..
Fixes: 38f99ee84fe6 ("json: Print single synproxy flags as non-array")
Fixes: dbe5c44f2b89 ("json: Print single fib flag as non-array")
Signed-off-by: Florian Westphal <fw@strlen.de>
Acked-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/inet/fib.t.json.output | 32 ++++++++++++++++++++++++++++
tests/py/inet/synproxy.t.json.output | 17 +++++++++++++++
2 files changed, 49 insertions(+)
create mode 100644 tests/py/inet/synproxy.t.json.output
diff --git a/tests/py/inet/fib.t.json.output b/tests/py/inet/fib.t.json.output
index 52cd46b..e21f1e7 100644
--- a/tests/py/inet/fib.t.json.output
+++ b/tests/py/inet/fib.t.json.output
@@ -37,3 +37,35 @@
}
]
+# fib daddr oif exists
+[
+ {
+ "match": {
+ "left": {
+ "fib": {
+ "flags": "daddr",
+ "result": "oif"
+ }
+ },
+ "op": "==",
+ "right": true
+ }
+ }
+]
+
+# fib daddr oif missing
+[
+ {
+ "match": {
+ "left": {
+ "fib": {
+ "flags": "daddr",
+ "result": "oif"
+ }
+ },
+ "op": "==",
+ "right": false
+ }
+ }
+]
+
diff --git a/tests/py/inet/synproxy.t.json.output b/tests/py/inet/synproxy.t.json.output
new file mode 100644
index 0000000..e32cdfb
--- /dev/null
+++ b/tests/py/inet/synproxy.t.json.output
@@ -0,0 +1,17 @@
+# synproxy timestamp
+[
+ {
+ "synproxy": {
+ "flags": "timestamp"
+ }
+ }
+]
+
+# synproxy sack-perm
+[
+ {
+ "synproxy": {
+ "flags": "sack-perm"
+ }
+ }
+]

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,134 @@
From 3d8fe285a67b3e4dc38a48622834151f0449e314 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:00 +0200
Subject: [PATCH] fib: allow to use it in set statements
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 525b58568dca5ab9998595fc45313eac2764b6b1
commit 525b58568dca5ab9998595fc45313eac2764b6b1
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Jun 24 18:11:10 2025 +0200
fib: allow to use it in set statements
Allow to use fib expression in set statements, eg.
meta mark set ip saddr . fib daddr check map { 1.2.3.4 . exists : 0x00000001 }
Fixes: 4a75ed32132d ("src: add fib expression")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_bison.y | 1 +
src/parser_json.c | 2 +-
tests/py/inet/fib.t | 2 ++
tests/py/inet/fib.t.json | 45 +++++++++++++++++++++++++++++++++++++
tests/py/inet/fib.t.payload | 8 +++++++
5 files changed, 57 insertions(+), 1 deletion(-)
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 174a339..a6a517f 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -3825,6 +3825,7 @@ primary_stmt_expr : symbol_expr { $$ = $1; }
| payload_expr { $$ = $1; }
| keyword_expr { $$ = $1; }
| socket_expr { $$ = $1; }
+ | fib_expr { $$ = $1; }
| osf_expr { $$ = $1; }
| '(' basic_stmt_expr ')' { $$ = $2; }
;
diff --git a/src/parser_json.c b/src/parser_json.c
index 1b12445..8c9d365 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -1569,7 +1569,7 @@ static struct expr *json_parse_expr(struct json_ctx *ctx, json_t *root)
/* below two are hash expr */
{ "jhash", json_parse_hash_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "symhash", json_parse_hash_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
- { "fib", json_parse_fib_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "fib", json_parse_fib_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "|", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "^", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "&", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
diff --git a/tests/py/inet/fib.t b/tests/py/inet/fib.t
index f9c03b3..60b77a4 100644
--- a/tests/py/inet/fib.t
+++ b/tests/py/inet/fib.t
@@ -17,3 +17,5 @@ fib daddr check missing;ok
fib daddr oif exists;ok;fib daddr check exists
fib daddr check vmap { missing : drop, exists : accept };ok
+
+meta mark set fib daddr check . ct mark map { exists . 0x00000000 : 0x00000001 };ok
diff --git a/tests/py/inet/fib.t.json b/tests/py/inet/fib.t.json
index c2e9d45..14a6249 100644
--- a/tests/py/inet/fib.t.json
+++ b/tests/py/inet/fib.t.json
@@ -159,3 +159,48 @@
}
}
]
+
+# meta mark set fib daddr check . ct mark map { exists . 0x00000000 : 0x00000001 }
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ true,
+ 0
+ ]
+ },
+ 1
+ ]
+ ]
+ },
+ "key": {
+ "concat": [
+ {
+ "fib": {
+ "flags": "daddr",
+ "result": "check"
+ }
+ },
+ {
+ "ct": {
+ "key": "mark"
+ }
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+]
diff --git a/tests/py/inet/fib.t.payload b/tests/py/inet/fib.t.payload
index e09a260..02d92b5 100644
--- a/tests/py/inet/fib.t.payload
+++ b/tests/py/inet/fib.t.payload
@@ -36,3 +36,11 @@ ip test-ip prerouting
ip test-ip prerouting
[ fib daddr oif present => reg 1 ]
[ lookup reg 1 set __map%d dreg 0 ]
+
+# meta mark set fib daddr check . ct mark map { exists . 0x00000000 : 0x00000001 }
+ element 00000001 00000000 : 00000001 0 [end]
+ip test-ip prerouting
+ [ fib daddr oif present => reg 1 ]
+ [ ct load mark => reg 9 ]
+ [ lookup reg 1 set __map%d dreg 1 ]
+ [ meta set mark with reg 1 ]

View File

@ -0,0 +1,45 @@
From 6bcaa14dfd285275ae797e963459088e6d5365eb Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:00 +0200
Subject: [PATCH] tests: py: re-enables nft-test.py to load the local
nftables.py
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 0f3965a38aecaf1fda66c2ce5dcf3b755d8191c6
commit 0f3965a38aecaf1fda66c2ce5dcf3b755d8191c6
Author: Zhongqiu Duan <dzq.aishenghu0@gmail.com>
Date: Fri Jul 4 03:12:16 2025 +0000
tests: py: re-enables nft-test.py to load the local nftables.py
This is a needed follow-up of commit ce443afc21455 ("py: move
package source into src directory") from 2023. Since that change,
nft-test.py started using the host's nftables.py instead of the local
one. But since nft-test.py passes the local src/.libs/libnftables.so.1
as parameter when instantiating the Nftables class, we did nevertheless
use the local libnftables.
Fixes: ce443afc21455 ("py: move package source into src directory")
Reviewed-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Zhongqiu Duan <dzq.aishenghu0@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/nft-test.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/py/nft-test.py b/tests/py/nft-test.py
index 9a25503..35bf66e 100755
--- a/tests/py/nft-test.py
+++ b/tests/py/nft-test.py
@@ -23,7 +23,7 @@ import traceback
import tempfile
TESTS_PATH = os.path.dirname(os.path.abspath(__file__))
-sys.path.insert(0, os.path.join(TESTS_PATH, '../../py/'))
+sys.path.insert(0, os.path.join(TESTS_PATH, '../../py/src'))
os.environ['TZ'] = 'UTC-2'
from nftables import Nftables

View File

@ -0,0 +1,126 @@
From d376d61e94ec31d1e6b8b452f12eb895eb0fdbd3 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:00 +0200
Subject: [PATCH] tests/shell: cover long interface name in
"0042chain_variable_0" test
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit f773041121d6d0d112fa9cb003fd791eacd6e43d
commit f773041121d6d0d112fa9cb003fd791eacd6e43d
Author: Thomas Haller <thaller@redhat.com>
Date: Mon Oct 23 19:00:46 2023 +0200
tests/shell: cover long interface name in "0042chain_variable_0" test
IFNAMSIZ is 16. Adjust "0042chain_variable_0" to use an interface name
with the maximum allowed bytes length.
Instead of adding an entirely different test, adjust an existing one to
use another interface name. The aspect for testing for a long interface
name is not special enough, to warrant a separate test. We can cover it
by extending an existing test.
Note that the length check in "parser_bison.y" is wrong. The test checks
still for the wrong behavior and that "d23456789012345x" is accepted.
Signed-off-by: Thomas Haller <thaller@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
.../testcases/chains/0042chain_variable_0 | 36 ++++++++++++++++---
.../chains/dumps/0042chain_variable_0.nft | 4 +--
2 files changed, 34 insertions(+), 6 deletions(-)
diff --git a/tests/shell/testcases/chains/0042chain_variable_0 b/tests/shell/testcases/chains/0042chain_variable_0
index 1ea44e8..739dc05 100755
--- a/tests/shell/testcases/chains/0042chain_variable_0
+++ b/tests/shell/testcases/chains/0042chain_variable_0
@@ -2,7 +2,8 @@
set -e
-ip link add name dummy0 type dummy
+ip link add name d23456789012345 type dummy
+
EXPECTED="define if_main = \"lo\"
@@ -14,22 +15,50 @@ table netdev filter1 {
$NFT -f - <<< $EXPECTED
+
+EXPECTED="define if_main = \"lo\"
+
+table netdev filter2 {
+ chain Main_Ingress2 {
+ type filter hook ingress devices = { \$if_main, d23456789012345x } priority -500; policy accept;
+ }
+}"
+
+rc=0
+$NFT -f - <<< $EXPECTED || rc=$?
+test "$rc" = 0
+cat <<EOF | $DIFF -u <($NFT list ruleset) -
+table netdev filter1 {
+ chain Main_Ingress1 {
+ type filter hook ingress device "lo" priority -500; policy accept;
+ }
+}
+table netdev filter2 {
+ chain Main_Ingress2 {
+ type filter hook ingress devices = { d23456789012345, lo } priority -500; policy accept;
+ }
+}
+EOF
+
+
EXPECTED="define if_main = \"lo\"
table netdev filter2 {
chain Main_Ingress2 {
- type filter hook ingress devices = { \$if_main, dummy0 } priority -500; policy accept;
+ type filter hook ingress devices = { \$if_main, d23456789012345 } priority -500; policy accept;
}
}"
$NFT -f - <<< $EXPECTED
+
if [ "$NFT_TEST_HAVE_netdev_egress" = n ] ; then
echo "Skip parts of the test due to NFT_TEST_HAVE_netdev_egress=n"
exit 77
fi
-EXPECTED="define if_main = { lo, dummy0 }
+
+EXPECTED="define if_main = { lo, d23456789012345 }
define lan_interfaces = { lo }
table netdev filter3 {
@@ -43,4 +72,3 @@ table netdev filter3 {
$NFT -f - <<< $EXPECTED
-
diff --git a/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft b/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft
index 5ec230d..84a908d 100644
--- a/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft
+++ b/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft
@@ -5,12 +5,12 @@ table netdev filter1 {
}
table netdev filter2 {
chain Main_Ingress2 {
- type filter hook ingress devices = { dummy0, lo } priority -500; policy accept;
+ type filter hook ingress devices = { d23456789012345, lo } priority -500; policy accept;
}
}
table netdev filter3 {
chain Main_Ingress3 {
- type filter hook ingress devices = { dummy0, lo } priority -500; policy accept;
+ type filter hook ingress devices = { d23456789012345, lo } priority -500; policy accept;
}
chain Main_Egress3 {

View File

@ -0,0 +1,74 @@
From a6b30991cc1f4ee116c7a5316af46cf1a9ee0c01 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:00 +0200
Subject: [PATCH] tests: shell: adjust add-after-delete flowtable for older
kernels
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 566182cd8259cfc22f9fd949e9ad3c15ed6a7945
commit 566182cd8259cfc22f9fd949e9ad3c15ed6a7945
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Mon Nov 20 13:54:03 2023 +0100
tests: shell: adjust add-after-delete flowtable for older kernels
Remove counter from flowtable, older kernels (<=5.4) do not support this
in testcases/flowtable/0013addafterdelete_0 so this bug is still
covered.
Skip testcases/flowtable/0014addafterdelete_0 if flowtable counter
support is not available.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/shell/testcases/flowtable/0013addafterdelete_0 | 2 --
tests/shell/testcases/flowtable/0014addafterdelete_0 | 2 ++
tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft | 1 -
3 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/tests/shell/testcases/flowtable/0013addafterdelete_0 b/tests/shell/testcases/flowtable/0013addafterdelete_0
index b23ab97..56c9834 100755
--- a/tests/shell/testcases/flowtable/0013addafterdelete_0
+++ b/tests/shell/testcases/flowtable/0013addafterdelete_0
@@ -7,7 +7,6 @@ RULESET='table inet filter {
flowtable f {
hook ingress priority filter - 1
devices = { lo }
- counter
}
}'
@@ -20,7 +19,6 @@ table inet filter {
flowtable f {
hook ingress priority filter - 1
devices = { lo }
- counter
}
}'
diff --git a/tests/shell/testcases/flowtable/0014addafterdelete_0 b/tests/shell/testcases/flowtable/0014addafterdelete_0
index 6a24c4b..1ac6510 100755
--- a/tests/shell/testcases/flowtable/0014addafterdelete_0
+++ b/tests/shell/testcases/flowtable/0014addafterdelete_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_flowtable_counter)
+
set -e
RULESET='table inet filter {
diff --git a/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft b/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft
index 83fdd5d..67db7d0 100644
--- a/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft
+++ b/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft
@@ -2,6 +2,5 @@ table inet filter {
flowtable f {
hook ingress priority filter - 1
devices = { lo }
- counter
}
}

View File

@ -0,0 +1,61 @@
From 3c78f54adfd9d130aeca4c779a8d82b27bb69e60 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:01 +0200
Subject: [PATCH] tests: shell: skip if kernel does not support flowtable
counter
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 279654904d5186430588b475501b2ca0d3ab6517
commit 279654904d5186430588b475501b2ca0d3ab6517
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Nov 14 16:57:22 2023 +0100
tests: shell: skip if kernel does not support flowtable counter
Check if kernel provides flowtable counter supports which is available
since 53c2b2899af7 ("netfilter: flowtable: add counter support").
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/shell/features/flowtable_counter.sh | 16 ++++++++++++++++
.../testcases/flowtable/0012flowtable_variable_0 | 2 ++
2 files changed, 18 insertions(+)
create mode 100755 tests/shell/features/flowtable_counter.sh
diff --git a/tests/shell/features/flowtable_counter.sh b/tests/shell/features/flowtable_counter.sh
new file mode 100755
index 0000000..a4c4c62
--- /dev/null
+++ b/tests/shell/features/flowtable_counter.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# 53c2b2899af7 ("netfilter: flowtable: add counter support")
+# v5.7-rc1~146^2~12^2~16
+
+EXPECTED="table ip filter2 {
+ flowtable main_ft2 {
+ hook ingress priority filter
+ devices = { lo }
+ counter
+ }
+}"
+
+$NFT -f - <<< $EXPECTED
+
+diff -u <($NFT list ruleset) - <<<"$EXPECTED"
diff --git a/tests/shell/testcases/flowtable/0012flowtable_variable_0 b/tests/shell/testcases/flowtable/0012flowtable_variable_0
index 080059d..9c03820 100755
--- a/tests/shell/testcases/flowtable/0012flowtable_variable_0
+++ b/tests/shell/testcases/flowtable/0012flowtable_variable_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_flowtable_counter)
+
set -e
iface_cleanup() {

View File

@ -0,0 +1,263 @@
From f456bdb07b559c8ff46e46701b0d61729549de73 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:01 +0200
Subject: [PATCH] rule: print chain and flowtable devices in quotes
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit eb30f236d91a8d61ece789e28e6540b3a3fa2a6a
commit eb30f236d91a8d61ece789e28e6540b3a3fa2a6a
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Jul 9 00:13:56 2025 +0200
rule: print chain and flowtable devices in quotes
Print devices in quotes, for consistency with:
- the existing chain listing with single device:
type filter hook ingress device "lo" priority filter; policy accept
- the ifname datatype used in sets.
In general, tokens that are user-defined, not coming in the datatype
symbol list, are enclosed in quotes.
Fixes: 3fdc7541fba0 ("src: add multidevice support for netdev chain")
Acked-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/rule.c | 4 ++--
tests/shell/features/flowtable_counter.sh | 2 +-
.../testcases/chains/dumps/0042chain_variable_0.nft | 4 ++--
.../testcases/flowtable/dumps/0001flowtable_0.nft | 2 +-
.../flowtable/dumps/0002create_flowtable_0.nft | 2 +-
.../flowtable/dumps/0003add_after_flush_0.nft | 2 +-
.../flowtable/dumps/0005delete_in_use_1.nft | 2 +-
.../flowtable/dumps/0012flowtable_variable_0.nft | 4 ++--
.../flowtable/dumps/0013addafterdelete_0.nft | 2 +-
.../flowtable/dumps/0014addafterdelete_0.nft | 2 +-
tests/shell/testcases/listing/0020flowtable_0 | 12 ++++++------
.../testcases/listing/dumps/0020flowtable_0.nft | 4 ++--
12 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/src/rule.c b/src/rule.c
index 6f97f77..c026d8d 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -1041,7 +1041,7 @@ static void chain_print_declaration(const struct chain *chain,
} else if (chain->dev_array_len > 1) {
nft_print(octx, " devices = { ");
for (i = 0; i < chain->dev_array_len; i++) {
- nft_print(octx, "%s", chain->dev_array[i]);
+ nft_print(octx, "\"%s\"", chain->dev_array[i]);
if (i + 1 != chain->dev_array_len)
nft_print(octx, ", ");
}
@@ -2123,7 +2123,7 @@ static void flowtable_print_declaration(const struct flowtable *flowtable,
if (flowtable->dev_array_len > 0) {
nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab);
for (i = 0; i < flowtable->dev_array_len; i++) {
- nft_print(octx, "%s", flowtable->dev_array[i]);
+ nft_print(octx, "\"%s\"", flowtable->dev_array[i]);
if (i + 1 != flowtable->dev_array_len)
nft_print(octx, ", ");
}
diff --git a/tests/shell/features/flowtable_counter.sh b/tests/shell/features/flowtable_counter.sh
index a4c4c62..5d47215 100755
--- a/tests/shell/features/flowtable_counter.sh
+++ b/tests/shell/features/flowtable_counter.sh
@@ -6,7 +6,7 @@
EXPECTED="table ip filter2 {
flowtable main_ft2 {
hook ingress priority filter
- devices = { lo }
+ devices = { \"lo\" }
counter
}
}"
diff --git a/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft b/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft
index 84a908d..08a1901 100644
--- a/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft
+++ b/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft
@@ -5,12 +5,12 @@ table netdev filter1 {
}
table netdev filter2 {
chain Main_Ingress2 {
- type filter hook ingress devices = { d23456789012345, lo } priority -500; policy accept;
+ type filter hook ingress devices = { "d23456789012345", "lo" } priority -500; policy accept;
}
}
table netdev filter3 {
chain Main_Ingress3 {
- type filter hook ingress devices = { d23456789012345, lo } priority -500; policy accept;
+ type filter hook ingress devices = { "d23456789012345", "lo" } priority -500; policy accept;
}
chain Main_Egress3 {
diff --git a/tests/shell/testcases/flowtable/dumps/0001flowtable_0.nft b/tests/shell/testcases/flowtable/dumps/0001flowtable_0.nft
index 629bfe8..79fa591 100644
--- a/tests/shell/testcases/flowtable/dumps/0001flowtable_0.nft
+++ b/tests/shell/testcases/flowtable/dumps/0001flowtable_0.nft
@@ -1,7 +1,7 @@
table inet t {
flowtable f {
hook ingress priority filter + 10
- devices = { lo }
+ devices = { "lo" }
}
chain c {
diff --git a/tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.nft b/tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.nft
index aecfb2a..2d0ea90 100644
--- a/tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.nft
+++ b/tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.nft
@@ -1,6 +1,6 @@
table ip t {
flowtable f {
hook ingress priority filter + 10
- devices = { lo }
+ devices = { "lo" }
}
}
diff --git a/tests/shell/testcases/flowtable/dumps/0003add_after_flush_0.nft b/tests/shell/testcases/flowtable/dumps/0003add_after_flush_0.nft
index dd904f4..39de91b 100644
--- a/tests/shell/testcases/flowtable/dumps/0003add_after_flush_0.nft
+++ b/tests/shell/testcases/flowtable/dumps/0003add_after_flush_0.nft
@@ -1,6 +1,6 @@
table ip x {
flowtable y {
hook ingress priority filter
- devices = { lo }
+ devices = { "lo" }
}
}
diff --git a/tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.nft b/tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.nft
index c1d79e7..b01b302 100644
--- a/tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.nft
+++ b/tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.nft
@@ -1,7 +1,7 @@
table ip x {
flowtable y {
hook ingress priority filter
- devices = { lo }
+ devices = { "lo" }
}
chain x {
diff --git a/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft b/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft
index df1c51a..7863822 100644
--- a/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft
+++ b/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft
@@ -1,14 +1,14 @@
table ip filter1 {
flowtable Main_ft1 {
hook ingress priority filter
- devices = { lo }
+ devices = { "lo" }
counter
}
}
table ip filter2 {
flowtable Main_ft2 {
hook ingress priority filter
- devices = { lo }
+ devices = { "lo" }
counter
}
}
diff --git a/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft b/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft
index 67db7d0..585f63b 100644
--- a/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft
+++ b/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft
@@ -1,6 +1,6 @@
table inet filter {
flowtable f {
hook ingress priority filter - 1
- devices = { lo }
+ devices = { "lo" }
}
}
diff --git a/tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.nft b/tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.nft
index 145aa08..12f97a7 100644
--- a/tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.nft
+++ b/tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.nft
@@ -1,7 +1,7 @@
table inet filter {
flowtable f {
hook ingress priority filter - 1
- devices = { lo }
+ devices = { "lo" }
counter
}
diff --git a/tests/shell/testcases/listing/0020flowtable_0 b/tests/shell/testcases/listing/0020flowtable_0
index 6eb82cf..8bd852b 100755
--- a/tests/shell/testcases/listing/0020flowtable_0
+++ b/tests/shell/testcases/listing/0020flowtable_0
@@ -6,11 +6,11 @@ set -e
FLOWTABLES="flowtable f {
hook ingress priority filter
- devices = { lo }
+ devices = { \"lo\" }
}
flowtable f2 {
hook ingress priority filter
- devices = { d0 }
+ devices = { \"d0\" }
}"
RULESET="table inet filter {
@@ -23,23 +23,23 @@ table ip filter {
EXPECTED="table inet filter {
flowtable f {
hook ingress priority filter
- devices = { lo }
+ devices = { \"lo\" }
}
}"
EXPECTED2="table ip filter {
flowtable f2 {
hook ingress priority filter
- devices = { d0 }
+ devices = { \"d0\" }
}
}"
EXPECTED3="table ip filter {
flowtable f {
hook ingress priority filter
- devices = { lo }
+ devices = { \"lo\" }
}
flowtable f2 {
hook ingress priority filter
- devices = { d0 }
+ devices = { \"d0\" }
}
}"
diff --git a/tests/shell/testcases/listing/dumps/0020flowtable_0.nft b/tests/shell/testcases/listing/dumps/0020flowtable_0.nft
index 4a64e53..0962e73 100644
--- a/tests/shell/testcases/listing/dumps/0020flowtable_0.nft
+++ b/tests/shell/testcases/listing/dumps/0020flowtable_0.nft
@@ -1,7 +1,7 @@
table inet filter {
flowtable f {
hook ingress priority filter
- devices = { lo }
+ devices = { "lo" }
}
flowtable f2 {
@@ -11,7 +11,7 @@ table inet filter {
table ip filter {
flowtable f {
hook ingress priority filter
- devices = { lo }
+ devices = { "lo" }
}
flowtable f2 {

View File

@ -0,0 +1,39 @@
From 59239ade08feeb6abe45304632d6349eefabb5bf Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:01 +0200
Subject: [PATCH] evaluate: validate set expression type before accessing flags
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 2022e8bb5cf0e0fa81ab0a5087bd1ab6e20280ee
commit 2022e8bb5cf0e0fa81ab0a5087bd1ab6e20280ee
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Jul 9 00:21:49 2025 +0200
evaluate: validate set expression type before accessing flags
Validate set->init is of EXPR_SET expression type before accessing
set_flags.
Fixes: 81e36530fcac ("src: replace interval segment tree overlap and automerge")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index 711990a..5012dd2 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -4715,7 +4715,8 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
set->flags |= NFT_SET_EXPR;
if (set_is_anonymous(set->flags)) {
- if (set_is_interval(set->init->set_flags) &&
+ if (set->init->etype == EXPR_SET &&
+ set_is_interval(set->init->set_flags) &&
!(set->init->set_flags & NFT_SET_CONCAT) &&
interval_set_eval(ctx, set, set->init) < 0)
return -1;

View File

@ -0,0 +1,39 @@
From 093c9db2a767666a54bfc500ff940302d44775cb Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:01 +0200
Subject: [PATCH] mnl: Call mnl_attr_nest_end() just once
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 8a1de0ec9709b58a80a86a20cfba107b11c85f6e
commit 8a1de0ec9709b58a80a86a20cfba107b11c85f6e
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Jul 16 14:26:08 2025 +0200
mnl: Call mnl_attr_nest_end() just once
Calling the function after each added nested attribute is harmless but
pointless.
Fixes: a66b5ad9540dd ("src: allow for updating devices on existing netdev chain")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/mnl.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/mnl.c b/src/mnl.c
index 0fb36bd..c62b140 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -797,8 +797,8 @@ static void mnl_nft_chain_devs_build(struct nlmsghdr *nlh, struct cmd *cmd)
for (i = 0; i < num_devs; i++) {
cmd_add_loc(cmd, nlh->nlmsg_len, dev_array[i].location);
mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME, dev_array[i].ifname);
- mnl_attr_nest_end(nlh, nest_dev);
}
+ mnl_attr_nest_end(nlh, nest_dev);
}
nft_dev_array_free(dev_array);
}

View File

@ -0,0 +1,78 @@
From 77a991b09586a95f4f8d583fcc798e7e861d36d7 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:01 +0200
Subject: [PATCH] doc: nft.8: Minor NAT STATEMENTS section review
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 9e1cbf667da2b9c30b41ff887de212b2c38b2eb7
commit 9e1cbf667da2b9c30b41ff887de212b2c38b2eb7
Author: Phil Sutter <phil@nwl.cc>
Date: Thu Jul 31 12:40:11 2025 +0200
doc: nft.8: Minor NAT STATEMENTS section review
Synopsis insinuates an IP address argument is mandatory in snat/dnat
statements although specifying ports alone is perfectly fine. Adjust it
accordingly and add a paragraph briefly describing the behaviour.
While at it, update the redirect statement description with more
relevant examples, the current one is wrong: To *only* alter the
destination port, dnat statement must be used, not redirect.
Fixes: 6908a677ba04c ("nft.8: Enhance NAT documentation")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/statements.txt | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/doc/statements.txt b/doc/statements.txt
index c7529ee..3aceb02 100644
--- a/doc/statements.txt
+++ b/doc/statements.txt
@@ -349,11 +349,12 @@ NAT STATEMENTS
~~~~~~~~~~~~~~
[verse]
____
-*snat* [[*ip* | *ip6*] [ *prefix* ] *to*] 'ADDR_SPEC' [*:*'PORT_SPEC'] ['FLAGS']
-*dnat* [[*ip* | *ip6*] [ *prefix* ] *to*] 'ADDR_SPEC' [*:*'PORT_SPEC'] ['FLAGS']
+*snat* [[*ip* | *ip6*] [ *prefix* ] *to*] 'TARGET_SPEC' ['FLAGS']
+*dnat* [[*ip* | *ip6*] [ *prefix* ] *to*] 'TARGET_SPEC' ['FLAGS']
*masquerade* [*to :*'PORT_SPEC'] ['FLAGS']
*redirect* [*to :*'PORT_SPEC'] ['FLAGS']
+'TARGET_SPEC' := 'ADDR_SPEC' | ['ADDR_SPEC'] *:*'PORT_SPEC'
'ADDR_SPEC' := 'address' | 'address' *-* 'address'
'PORT_SPEC' := 'port' | 'port' *-* 'port'
@@ -363,11 +364,11 @@ ____
The nat statements are only valid from nat chain types. +
-The *snat* and *masquerade* statements specify that the source address of the
+The *snat* and *masquerade* statements specify that the source address/port of the
packet should be modified. While *snat* is only valid in the postrouting and
input chains, *masquerade* makes sense only in postrouting. The dnat and
redirect statements are only valid in the prerouting and output chains, they
-specify that the destination address of the packet should be modified. You can
+specify that the destination address/port of the packet should be modified. You can
use non-base chains which are called from base chains of nat chain type too.
All future packets in this connection will also be mangled, and rules should
cease being examined.
@@ -377,8 +378,12 @@ outgoing interface's IP address to translate to. It is particularly useful on
gateways with dynamic (public) IP addresses.
The *redirect* statement is a special form of dnat which always translates the
-destination address to the local host's one. It comes in handy if one only wants
-to alter the destination port of incoming traffic on different interfaces.
+destination address to the local host's one. It comes in handy to intercept
+traffic passing a router and feeding it to a locally running daemon, e.g. when
+building a transparent proxy or application-layer gateway.
+
+For 'TARGET_SPEC', one may specify addresses, ports, or both. If no address or
+no port is specified, the respective packet header field remains unchanged.
When used in the inet family (available with kernel 5.2), the dnat and snat
statements require the use of the ip and ip6 keyword in case an address is

View File

@ -0,0 +1,54 @@
From 123f57f8e49e53e8806e389cfd524f35dcfbc4a4 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:01 +0200
Subject: [PATCH] src: netlink: netlink_delinearize_table() may return NULL
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit a69d552a005ba467d37e225032e35d01d9491241
commit a69d552a005ba467d37e225032e35d01d9491241
Author: Phil Sutter <phil@nwl.cc>
Date: Fri Aug 8 14:21:41 2025 +0200
src: netlink: netlink_delinearize_table() may return NULL
Catch the error condition in callers to avoid crashes.
Fixes: c156232a530b3 ("src: add comment support when adding tables")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/monitor.c | 4 ++++
src/netlink.c | 3 ++-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/monitor.c b/src/monitor.c
index 82762a0..bf59f2d 100644
--- a/src/monitor.c
+++ b/src/monitor.c
@@ -224,6 +224,10 @@ static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type,
nlt = netlink_table_alloc(nlh);
t = netlink_delinearize_table(monh->ctx, nlt);
+ if (!t) {
+ nftnl_table_free(nlt);
+ return MNL_CB_ERROR;
+ }
cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
switch (monh->format) {
diff --git a/src/netlink.c b/src/netlink.c
index aafc583..73123e9 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -754,7 +754,8 @@ static int list_table_cb(struct nftnl_table *nlt, void *arg)
struct table *table;
table = netlink_delinearize_table(ctx, nlt);
- list_add_tail(&table->list, &ctx->list);
+ if (table)
+ list_add_tail(&table->list, &ctx->list);
return 0;
}

View File

@ -0,0 +1,340 @@
From 9206bb36c6c238cb8019f3d021e52b359e416623 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:01 +0200
Subject: [PATCH] tests: py: Drop stale entries since redundant test case
removal
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 7db422da28349edcdeb3c96a410b242856ca74ac
commit 7db422da28349edcdeb3c96a410b242856ca74ac
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Aug 13 14:14:45 2025 +0200
tests: py: Drop stale entries since redundant test case removal
Fixed commit left stale JSON equivalents and payload records in place,
drop them.
Fixes: ec1ea13314fa5 ("tests: remove redundant test cases")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/any/meta.t.json | 38 ----------
tests/py/ip6/dst.t.json | 23 ------
tests/py/ip6/dst.t.payload.inet | 10 ---
tests/py/ip6/dst.t.payload.ip6 | 8 ---
tests/py/ip6/frag.t.json | 121 --------------------------------
tests/py/ip6/ip6.t.json | 40 -----------
6 files changed, 240 deletions(-)
diff --git a/tests/py/any/meta.t.json b/tests/py/any/meta.t.json
index 4734bbf..6bde76b 100644
--- a/tests/py/any/meta.t.json
+++ b/tests/py/any/meta.t.json
@@ -2294,44 +2294,6 @@
}
]
-# meta cgroup {1048577-1048578}
-[
- {
- "match": {
- "left": {
- "meta": { "key": "cgroup" }
- },
- "op": "==",
- "right": {
- "set": [
- {
- "range": [ 1048577, 1048578 ]
- }
- ]
- }
- }
- }
-]
-
-# meta cgroup != { 1048577-1048578}
-[
- {
- "match": {
- "left": {
- "meta": { "key": "cgroup" }
- },
- "op": "!=",
- "right": {
- "set": [
- {
- "range": [ 1048577, 1048578 ]
- }
- ]
- }
- }
- }
-]
-
# meta iif . meta oif { "lo" . "lo" }
[
{
diff --git a/tests/py/ip6/dst.t.json b/tests/py/ip6/dst.t.json
index e947a76..7ba4572 100644
--- a/tests/py/ip6/dst.t.json
+++ b/tests/py/ip6/dst.t.json
@@ -290,26 +290,3 @@
}
}
]
-
-# dst hdrlength != { 33, 55, 67, 88}
-[
- {
- "match": {
- "left": {
- "exthdr": {
- "field": "hdrlength",
- "name": "dst"
- }
- },
- "op": "!=",
- "right": {
- "set": [
- 33,
- 55,
- 67,
- 88
- ]
- }
- }
- }
-]
diff --git a/tests/py/ip6/dst.t.payload.inet b/tests/py/ip6/dst.t.payload.inet
index 90d6bda..b2a79ed 100644
--- a/tests/py/ip6/dst.t.payload.inet
+++ b/tests/py/ip6/dst.t.payload.inet
@@ -119,13 +119,3 @@ ip6 test-ip6 input
[ cmp eq reg 1 0x0000000a ]
[ exthdr load ipv6 1b @ 60 + 1 => reg 1 ]
[ lookup reg 1 set __set%d ]
-
-# dst hdrlength != { 33, 55, 67, 88}
-__set%d test-ip6 3
-__set%d test-ip6 0
- element 00000021 : 0 [end] element 00000037 : 0 [end] element 00000043 : 0 [end] element 00000058 : 0 [end]
-ip6 test-ip6 input
- [ meta load nfproto => reg 1 ]
- [ cmp eq reg 1 0x0000000a ]
- [ exthdr load ipv6 1b @ 60 + 1 => reg 1 ]
- [ lookup reg 1 set __set%d 0x1 ]
diff --git a/tests/py/ip6/dst.t.payload.ip6 b/tests/py/ip6/dst.t.payload.ip6
index 941140d..5058b46 100644
--- a/tests/py/ip6/dst.t.payload.ip6
+++ b/tests/py/ip6/dst.t.payload.ip6
@@ -89,11 +89,3 @@ __set%d test-ip6 0
ip6 test-ip6 input
[ exthdr load ipv6 1b @ 60 + 1 => reg 1 ]
[ lookup reg 1 set __set%d ]
-
-# dst hdrlength != { 33, 55, 67, 88}
-__set%d test-ip6 3
-__set%d test-ip6 0
- element 00000021 : 0 [end] element 00000037 : 0 [end] element 00000043 : 0 [end] element 00000058 : 0 [end]
-ip6 test-ip6 input
- [ exthdr load ipv6 1b @ 60 + 1 => reg 1 ]
- [ lookup reg 1 set __set%d 0x1 ]
diff --git a/tests/py/ip6/frag.t.json b/tests/py/ip6/frag.t.json
index b8c06df..6953e87 100644
--- a/tests/py/ip6/frag.t.json
+++ b/tests/py/ip6/frag.t.json
@@ -230,46 +230,6 @@
}
]
-# frag reserved { 33-55}
-[
- {
- "match": {
- "left": {
- "exthdr": {
- "field": "reserved",
- "name": "frag"
- }
- },
- "op": "==",
- "right": {
- "set": [
- { "range": [ 33, 55 ] }
- ]
- }
- }
- }
-]
-
-# frag reserved != { 33-55}
-[
- {
- "match": {
- "left": {
- "exthdr": {
- "field": "reserved",
- "name": "frag"
- }
- },
- "op": "!=",
- "right": {
- "set": [
- { "range": [ 33, 55 ] }
- ]
- }
- }
- }
-]
-
# frag frag-off 22
[
{
@@ -384,46 +344,6 @@
}
]
-# frag frag-off { 33-55}
-[
- {
- "match": {
- "left": {
- "exthdr": {
- "field": "frag-off",
- "name": "frag"
- }
- },
- "op": "==",
- "right": {
- "set": [
- { "range": [ 33, 55 ] }
- ]
- }
- }
- }
-]
-
-# frag frag-off != { 33-55}
-[
- {
- "match": {
- "left": {
- "exthdr": {
- "field": "frag-off",
- "name": "frag"
- }
- },
- "op": "!=",
- "right": {
- "set": [
- { "range": [ 33, 55 ] }
- ]
- }
- }
- }
-]
-
# frag reserved2 1
[
{
@@ -601,44 +521,3 @@
}
}
]
-
-# frag id { 33-55}
-[
- {
- "match": {
- "left": {
- "exthdr": {
- "field": "id",
- "name": "frag"
- }
- },
- "op": "==",
- "right": {
- "set": [
- { "range": [ 33, 55 ] }
- ]
- }
- }
- }
-]
-
-# frag id != { 33-55}
-[
- {
- "match": {
- "left": {
- "exthdr": {
- "field": "id",
- "name": "frag"
- }
- },
- "op": "!=",
- "right": {
- "set": [
- { "range": [ 33, 55 ] }
- ]
- }
- }
- }
-]
-
diff --git a/tests/py/ip6/ip6.t.json b/tests/py/ip6/ip6.t.json
index cf80217..46d347d 100644
--- a/tests/py/ip6/ip6.t.json
+++ b/tests/py/ip6/ip6.t.json
@@ -471,46 +471,6 @@
}
]
-# ip6 nexthdr { 33-44}
-[
- {
- "match": {
- "left": {
- "payload": {
- "field": "nexthdr",
- "protocol": "ip6"
- }
- },
- "op": "==",
- "right": {
- "set": [
- { "range": [ 33, 44 ] }
- ]
- }
- }
- }
-]
-
-# ip6 nexthdr != { 33-44}
-[
- {
- "match": {
- "left": {
- "payload": {
- "field": "nexthdr",
- "protocol": "ip6"
- }
- },
- "op": "!=",
- "right": {
- "set": [
- { "range": [ 33, 44 ] }
- ]
- }
- }
- }
-]
-
# ip6 nexthdr 33-44
[
{

View File

@ -0,0 +1,79 @@
From a1194b27b2f418e44608462212fbb90a0e2ded5d Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:01 +0200
Subject: [PATCH] tests: py: Drop duplicate test from inet/geneve.t
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit a3b279794c2e26f9921445163e9bde0df5bda651
commit a3b279794c2e26f9921445163e9bde0df5bda651
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Aug 13 14:19:31 2025 +0200
tests: py: Drop duplicate test from inet/geneve.t
The test was duplicate since day 1. The duplicate JSON equivalent was
added later (semi-automated), remove it as well.
Fixes: 2b9143bc7ab81 ("tests: py: add geneve tests")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/inet/geneve.t | 1 -
tests/py/inet/geneve.t.json | 29 -----------------------------
2 files changed, 30 deletions(-)
diff --git a/tests/py/inet/geneve.t b/tests/py/inet/geneve.t
index 101f6df..ad46272 100644
--- a/tests/py/inet/geneve.t
+++ b/tests/py/inet/geneve.t
@@ -17,7 +17,6 @@ udp dport 6081 geneve icmp type echo-reply;ok
udp dport 6081 geneve ether saddr 62:87:4d:d6:19:05;ok
udp dport 6081 geneve vlan id 10;ok
udp dport 6081 geneve ip dscp 0x02;ok
-udp dport 6081 geneve ip dscp 0x02;ok
udp dport 6081 geneve ip saddr . geneve ip daddr { 1.2.3.4 . 4.3.2.1 };ok
udp dport 6081 geneve ip saddr set 1.2.3.4;fail
diff --git a/tests/py/inet/geneve.t.json b/tests/py/inet/geneve.t.json
index a299fcd..1589918 100644
--- a/tests/py/inet/geneve.t.json
+++ b/tests/py/inet/geneve.t.json
@@ -264,35 +264,6 @@
}
]
-# udp dport 6081 geneve ip dscp 0x02
-[
- {
- "match": {
- "left": {
- "payload": {
- "field": "dport",
- "protocol": "udp"
- }
- },
- "op": "==",
- "right": 6081
- }
- },
- {
- "match": {
- "left": {
- "payload": {
- "field": "dscp",
- "protocol": "ip",
- "tunnel": "geneve"
- }
- },
- "op": "==",
- "right": 2
- }
- }
-]
-
# udp dport 6081 geneve ip saddr . geneve ip daddr { 1.2.3.4 . 4.3.2.1 }
[
{

View File

@ -0,0 +1,67 @@
From 564d6cdd405b352b08ecfb0f25ff43557dc94406 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:01 +0200
Subject: [PATCH] tests: py: Drop duplicate test from inet/gre.t
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 7a566cb4e7b98c5f6509d9cb25d0281d5198da28
commit 7a566cb4e7b98c5f6509d9cb25d0281d5198da28
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Aug 13 14:22:07 2025 +0200
tests: py: Drop duplicate test from inet/gre.t
The test was duplicate since day 1. The duplicate JSON equivalent was
added later (semi-automated), remove it as well.
Fixes: c04ef8d104ec6 ("tests: py: add gre tests")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/inet/gre.t | 1 -
tests/py/inet/gre.t.json | 17 -----------------
2 files changed, 18 deletions(-)
diff --git a/tests/py/inet/gre.t b/tests/py/inet/gre.t
index a3e046a..a21e67e 100644
--- a/tests/py/inet/gre.t
+++ b/tests/py/inet/gre.t
@@ -16,7 +16,6 @@ gre icmp type echo-reply;ok
gre ether saddr 62:87:4d:d6:19:05;fail
gre vlan id 10;fail
gre ip dscp 0x02;ok
-gre ip dscp 0x02;ok
gre ip saddr . gre ip daddr { 1.2.3.4 . 4.3.2.1 };ok
gre ip saddr set 1.2.3.4;fail
diff --git a/tests/py/inet/gre.t.json b/tests/py/inet/gre.t.json
index c443176..a354e6b 100644
--- a/tests/py/inet/gre.t.json
+++ b/tests/py/inet/gre.t.json
@@ -121,23 +121,6 @@
}
]
-# gre ip dscp 0x02
-[
- {
- "match": {
- "left": {
- "payload": {
- "field": "dscp",
- "protocol": "ip",
- "tunnel": "gre"
- }
- },
- "op": "==",
- "right": 2
- }
- }
-]
-
# gre ip saddr . gre ip daddr { 1.2.3.4 . 4.3.2.1 }
[
{

View File

@ -0,0 +1,67 @@
From b90837cd1236b7a76fd482ed48c611f8d1317763 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:01 +0200
Subject: [PATCH] tests: py: Drop duplicate test from inet/gretap.t
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit e159957099746a294ce767c16665d77b5ef2d464
commit e159957099746a294ce767c16665d77b5ef2d464
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Aug 13 14:23:30 2025 +0200
tests: py: Drop duplicate test from inet/gretap.t
The test was duplicate since day 1. The duplicate JSON equivalent was
added later (semi-automated), remove it as well.
Fixes: 39a68d9ffd25c ("tests: py: add gretap tests")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/inet/gretap.t | 1 -
tests/py/inet/gretap.t.json | 17 -----------------
2 files changed, 18 deletions(-)
diff --git a/tests/py/inet/gretap.t b/tests/py/inet/gretap.t
index cd7ee21..f88896f 100644
--- a/tests/py/inet/gretap.t
+++ b/tests/py/inet/gretap.t
@@ -15,7 +15,6 @@ gretap icmp type echo-reply;ok
gretap ether saddr 62:87:4d:d6:19:05;ok
gretap vlan id 10;ok
gretap ip dscp 0x02;ok
-gretap ip dscp 0x02;ok
gretap ip saddr . gretap ip daddr { 1.2.3.4 . 4.3.2.1 };ok
gretap ip saddr set 1.2.3.4;fail
diff --git a/tests/py/inet/gretap.t.json b/tests/py/inet/gretap.t.json
index 36fa978..6c16a08 100644
--- a/tests/py/inet/gretap.t.json
+++ b/tests/py/inet/gretap.t.json
@@ -139,23 +139,6 @@
}
]
-# gretap ip dscp 0x02
-[
- {
- "match": {
- "left": {
- "payload": {
- "field": "dscp",
- "protocol": "ip",
- "tunnel": "gretap"
- }
- },
- "op": "==",
- "right": 2
- }
- }
-]
-
# gretap ip saddr . gretap ip daddr { 1.2.3.4 . 4.3.2.1 }
[
{

View File

@ -0,0 +1,69 @@
From e7191aee64f0ac14a9d0fc2856d25832e281c33a Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:01 +0200
Subject: [PATCH] tests: py: Drop stale entry from inet/tcp.t.json
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit f8fe3d56194f06bea9cd82d1d707064b202f5202
commit f8fe3d56194f06bea9cd82d1d707064b202f5202
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Aug 13 14:32:11 2025 +0200
tests: py: Drop stale entry from inet/tcp.t.json
The test was changed but JSON equivalents not updated. Commit
c0b685951fabb ("json: fix parse of flagcmp expression") then added an
equivalent matching the changed test, so just drop the old one.
Fixes: c3d57114f119b ("parser_bison: add shortcut syntax for matching flags without binary operations")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/inet/tcp.t.json | 31 -------------------------------
1 file changed, 31 deletions(-)
diff --git a/tests/py/inet/tcp.t.json b/tests/py/inet/tcp.t.json
index 8439c2b..cc2562c 100644
--- a/tests/py/inet/tcp.t.json
+++ b/tests/py/inet/tcp.t.json
@@ -910,37 +910,6 @@
}
]
-# tcp flags & (syn|fin) == (syn|fin)
-[
- {
- "match": {
- "left": {
- "&": [
- {
- "payload": {
- "field": "flags",
- "protocol": "tcp"
- }
- },
- {
- "|": [
- "syn",
- "fin"
- ]
- }
- ]
- },
- "op": "==",
- "right": {
- "|": [
- "syn",
- "fin"
- ]
- }
- }
- }
-]
-
# tcp flags & (fin | syn | rst | psh | ack | urg | ecn | cwr) == fin | syn | rst | psh | ack | urg | ecn | cwr
[
{

View File

@ -0,0 +1,79 @@
From 1f8f043819ce263f4462ee43c0c8bb89cd238be9 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:02 +0200
Subject: [PATCH] tests: py: Drop duplicate test from inet/vxlan.t
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit bc66b02a6bb86a71d0a42ea7851f9aeea20edf1c
commit bc66b02a6bb86a71d0a42ea7851f9aeea20edf1c
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Aug 13 14:38:22 2025 +0200
tests: py: Drop duplicate test from inet/vxlan.t
The test was duplicate since day 1. The duplicate JSON equivalent was
added later (semi-automated), remove it as well.
Fixes: df81baa4c2bef ("tests: py: add vxlan tests")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/inet/vxlan.t | 1 -
tests/py/inet/vxlan.t.json | 29 -----------------------------
2 files changed, 30 deletions(-)
diff --git a/tests/py/inet/vxlan.t b/tests/py/inet/vxlan.t
index 10cdb7a..b6db0fe 100644
--- a/tests/py/inet/vxlan.t
+++ b/tests/py/inet/vxlan.t
@@ -17,7 +17,6 @@ udp dport 4789 vxlan icmp type echo-reply;ok
udp dport 4789 vxlan ether saddr 62:87:4d:d6:19:05;ok
udp dport 4789 vxlan vlan id 10;ok
udp dport 4789 vxlan ip dscp 0x02;ok
-udp dport 4789 vxlan ip dscp 0x02;ok
udp dport 4789 vxlan ip saddr . vxlan ip daddr { 1.2.3.4 . 4.3.2.1 };ok
udp dport 4789 vxlan ip saddr set 1.2.3.4;fail
diff --git a/tests/py/inet/vxlan.t.json b/tests/py/inet/vxlan.t.json
index 91b3d29..3c147cb 100644
--- a/tests/py/inet/vxlan.t.json
+++ b/tests/py/inet/vxlan.t.json
@@ -264,35 +264,6 @@
}
]
-# udp dport 4789 vxlan ip dscp 0x02
-[
- {
- "match": {
- "left": {
- "payload": {
- "field": "dport",
- "protocol": "udp"
- }
- },
- "op": "==",
- "right": 4789
- }
- },
- {
- "match": {
- "left": {
- "payload": {
- "field": "dscp",
- "protocol": "ip",
- "tunnel": "vxlan"
- }
- },
- "op": "==",
- "right": 2
- }
- }
-]
-
# udp dport 4789 vxlan ip saddr . vxlan ip daddr { 1.2.3.4 . 4.3.2.1 }
[
{

View File

@ -0,0 +1,70 @@
From 4d71def249ec952bc0767061e5455244d7b1c823 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:02 +0200
Subject: [PATCH] tests: py: Drop stale entry from ip/snat.t.json
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 71d90776fc56b14250f2306c4e19b50c23bd3ae3
commit 71d90776fc56b14250f2306c4e19b50c23bd3ae3
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Aug 13 15:03:29 2025 +0200
tests: py: Drop stale entry from ip/snat.t.json
The test syntax was changed, but the respective JSON equivalent remained
in place.
Fixes: 9b169bfc650eb ("src: remove STMT_NAT_F_INTERVAL flags and interval keyword")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/ip/snat.t.json | 33 ---------------------------------
1 file changed, 33 deletions(-)
diff --git a/tests/py/ip/snat.t.json b/tests/py/ip/snat.t.json
index 967560e..60bd093 100644
--- a/tests/py/ip/snat.t.json
+++ b/tests/py/ip/snat.t.json
@@ -283,39 +283,6 @@
}
]
-# snat ip interval to ip saddr map { 10.141.11.4 : 192.168.2.2-192.168.2.4 }
-[
- {
- "snat": {
- "addr": {
- "map": {
- "data": {
- "set": [
- [
- "10.141.11.4",
- {
- "range": [
- "192.168.2.2",
- "192.168.2.4"
- ]
- }
- ]
- ]
- },
- "key": {
- "payload": {
- "field": "saddr",
- "protocol": "ip"
- }
- }
- }
- },
- "family": "ip",
- "type_flags": "interval"
- }
- }
-]
-
# snat ip prefix to ip saddr map { 10.141.11.0/24 : 192.168.2.0/24 }
[
{

View File

@ -0,0 +1,268 @@
From 21df45dace5eafc3bab216e049357f712abf690a Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:02 +0200
Subject: [PATCH] tests: py: Drop stale entries from ip6/{ct,meta}.t.json
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 3be0bbe9a5ba21a3f6e3baf051c3aaadc393c587
commit 3be0bbe9a5ba21a3f6e3baf051c3aaadc393c587
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Aug 13 15:50:54 2025 +0200
tests: py: Drop stale entries from ip6/{ct,meta}.t.json
Looks like these were added by accident, fixed commit did not add these
test cases.
Fixes: 8221d86e616bd ("tests: py: add test-cases for ct and packet mark payload expressions")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/ip6/ct.t.json | 164 ---------------------------------------
tests/py/ip6/meta.t.json | 58 --------------
2 files changed, 222 deletions(-)
diff --git a/tests/py/ip6/ct.t.json b/tests/py/ip6/ct.t.json
index 7d8c88b..a7511b9 100644
--- a/tests/py/ip6/ct.t.json
+++ b/tests/py/ip6/ct.t.json
@@ -1,167 +1,3 @@
-# ct mark set ip6 dscp lshift 2 or 0x10
-[
- {
- "mangle": {
- "key": {
- "ct": {
- "key": "mark"
- }
- },
- "value": {
- "|": [
- {
- "<<": [
- {
- "payload": {
- "field": "dscp",
- "protocol": "ip6"
- }
- },
- 2
- ]
- },
- 16
- ]
- }
- }
- }
-]
-
-# ct mark set ip6 dscp lshift 26 or 0x10
-[
- {
- "mangle": {
- "key": {
- "ct": {
- "key": "mark"
- }
- },
- "value": {
- "|": [
- {
- "<<": [
- {
- "payload": {
- "field": "dscp",
- "protocol": "ip6"
- }
- },
- 26
- ]
- },
- 16
- ]
- }
- }
- }
-]
-
-# ct mark set ip6 dscp << 2 | 0x10
-[
- {
- "mangle": {
- "key": {
- "ct": {
- "key": "mark"
- }
- },
- "value": {
- "|": [
- {
- "<<": [
- {
- "payload": {
- "field": "dscp",
- "protocol": "ip6"
- }
- },
- 2
- ]
- },
- 16
- ]
- }
- }
- }
-]
-
-# ct mark set ip6 dscp << 26 | 0x10
-[
- {
- "mangle": {
- "key": {
- "ct": {
- "key": "mark"
- }
- },
- "value": {
- "|": [
- {
- "<<": [
- {
- "payload": {
- "field": "dscp",
- "protocol": "ip6"
- }
- },
- 26
- ]
- },
- 16
- ]
- }
- }
- }
-]
-
-# ct mark set ip6 dscp | 0x04
-[
- {
- "mangle": {
- "key": {
- "ct": {
- "key": "mark"
- }
- },
- "value": {
- "|": [
- {
- "payload": {
- "field": "dscp",
- "protocol": "ip6"
- }
- },
- 4
- ]
- }
- }
- }
-]
-
-# ct mark set ip6 dscp | 0xff000000
-[
- {
- "mangle": {
- "key": {
- "ct": {
- "key": "mark"
- }
- },
- "value": {
- "|": [
- {
- "payload": {
- "field": "dscp",
- "protocol": "ip6"
- }
- },
- 4278190080
- ]
- }
- }
- }
-]
-
# ct mark set ip6 dscp << 2 | 0x10
[
{
diff --git a/tests/py/ip6/meta.t.json b/tests/py/ip6/meta.t.json
index 1a2394d..87251f0 100644
--- a/tests/py/ip6/meta.t.json
+++ b/tests/py/ip6/meta.t.json
@@ -195,64 +195,6 @@
}
]
-# meta mark set ip6 dscp lshift 2 or 0x10
-[
- {
- "mangle": {
- "key": {
- "meta": {
- "key": "mark"
- }
- },
- "value": {
- "|": [
- {
- "<<": [
- {
- "payload": {
- "field": "dscp",
- "protocol": "ip6"
- }
- },
- 2
- ]
- },
- 16
- ]
- }
- }
- }
-]
-
-# meta mark set ip6 dscp lshift 26 or 0x10
-[
- {
- "mangle": {
- "key": {
- "meta": {
- "key": "mark"
- }
- },
- "value": {
- "|": [
- {
- "<<": [
- {
- "payload": {
- "field": "dscp",
- "protocol": "ip6"
- }
- },
- 26
- ]
- },
- 16
- ]
- }
- }
- }
-]
-
# meta mark set ip6 dscp << 2 | 0x10
[
{

View File

@ -0,0 +1,48 @@
From 0e3d6eca63a637e22844a3461a06196fc0d00b66 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:02 +0200
Subject: [PATCH] tests: py: Drop stale entry from ip/snat.t.payload
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit c3c60ac84a4f64a98f9c2c22d9032d177ae6a212
commit c3c60ac84a4f64a98f9c2c22d9032d177ae6a212
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Aug 13 16:06:46 2025 +0200
tests: py: Drop stale entry from ip/snat.t.payload
This payload actually belongs to ip/dnat.t.payload, fixed commit added
it to the wrong file.
Fixes: 8f3048954d40d ("evaluate: postpone transport protocol match check after nat expression evaluation")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/ip/snat.t.payload | 14 --------------
1 file changed, 14 deletions(-)
diff --git a/tests/py/ip/snat.t.payload b/tests/py/ip/snat.t.payload
index 71a5e2f..9a59b71 100644
--- a/tests/py/ip/snat.t.payload
+++ b/tests/py/ip/snat.t.payload
@@ -138,17 +138,3 @@ ip
[ payload load 2b @ transport header + 2 => reg 9 ]
[ lookup reg 1 set __map%d dreg 1 ]
[ nat snat ip addr_min reg 1 proto_min reg 9 ]
-
-# ip daddr 192.168.0.1 dnat to tcp dport map { 443 : 10.141.10.4 . 8443, 80 : 10.141.10.4 . 8080 }
-__map%d x b size 2
-__map%d x 0
- element 0000bb01 : 040a8d0a 0000fb20 0 [end] element 00005000 : 040a8d0a 0000901f 0 [end]
-ip
- [ payload load 4b @ network header + 16 => reg 1 ]
- [ cmp eq reg 1 0x0100a8c0 ]
- [ meta load l4proto => reg 1 ]
- [ cmp eq reg 1 0x00000006 ]
- [ payload load 2b @ transport header + 2 => reg 1 ]
- [ lookup reg 1 set __map%d dreg 1 ]
- [ nat dnat ip addr_min reg 1 proto_min reg 9 ]
-

View File

@ -0,0 +1,155 @@
From 16de7abf588d74b78d9b8dc738ba2960ab90a3d2 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:02 +0200
Subject: [PATCH] tests: py: Fix tests added for 'icmpv6 taddr' support
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit ef8b8ef36f80a2bfcc9df35cccb61ab490a8d6e7
commit ef8b8ef36f80a2bfcc9df35cccb61ab490a8d6e7
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Aug 13 16:14:08 2025 +0200
tests: py: Fix tests added for 'icmpv6 taddr' support
There was a duplicate test, also stored JSON equivalents should match
input as much as possible. The expected deviation in output (just like
with standard syntax) is stored in the .json.output file instead.
Fixes: 2e86f45d0260a ("icmpv6: Allow matching target address in NS/NA, redirect and MLD")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/ip6/icmpv6.t | 2 --
tests/py/ip6/icmpv6.t.json | 58 -------------------------------
tests/py/ip6/icmpv6.t.json.output | 36 +++++++++++++++++++
3 files changed, 36 insertions(+), 60 deletions(-)
diff --git a/tests/py/ip6/icmpv6.t b/tests/py/ip6/icmpv6.t
index 35dad2b..00e6661 100644
--- a/tests/py/ip6/icmpv6.t
+++ b/tests/py/ip6/icmpv6.t
@@ -91,8 +91,6 @@ icmpv6 type nd-neighbor-solicit icmpv6 taddr 2001:db8::133;ok
icmpv6 type nd-neighbor-advert icmpv6 taddr 2001:db8::133;ok
icmpv6 taddr 2001:db8::133;ok;icmpv6 type { mld-listener-query, mld-listener-report, mld-listener-done, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect} icmpv6 taddr 2001:db8::133
-icmpv6 taddr 2001:db8::133;ok;icmpv6 type { mld-listener-query, mld-listener-report, mld-listener-done, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect} icmpv6 taddr 2001:db8::133
-
icmpv6 type { mld-listener-query, mld-listener-report, mld-listener-done, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect} icmpv6 taddr 2001:db8::133;ok
icmpv6 type { nd-neighbor-solicit, nd-neighbor-advert } icmpv6 taddr 2001:db8::133;ok
icmpv6 daddr 2001:db8::133;ok
diff --git a/tests/py/ip6/icmpv6.t.json b/tests/py/ip6/icmpv6.t.json
index 224a8e8..b6f93b2 100644
--- a/tests/py/ip6/icmpv6.t.json
+++ b/tests/py/ip6/icmpv6.t.json
@@ -1250,64 +1250,6 @@
# icmpv6 taddr 2001:db8::133
[
- {
- "match": {
- "left": {
- "payload": {
- "field": "type",
- "protocol": "icmpv6"
- }
- },
- "op": "==",
- "right": {
- "set": [
- "mld-listener-query",
- "mld-listener-report",
- "mld-listener-done",
- "nd-neighbor-solicit",
- "nd-neighbor-advert",
- "nd-redirect"
- ]
- }
- }
- },
- {
- "match": {
- "left": {
- "payload": {
- "field": "taddr",
- "protocol": "icmpv6"
- }
- },
- "op": "==",
- "right": "2001:db8::133"
- }
- }
-]
-
-# icmpv6 taddr 2001:db8::133
-[
- {
- "match": {
- "left": {
- "payload": {
- "field": "type",
- "protocol": "icmpv6"
- }
- },
- "op": "==",
- "right": {
- "set": [
- "mld-listener-query",
- "mld-listener-report",
- "mld-listener-done",
- "nd-neighbor-solicit",
- "nd-neighbor-advert",
- "nd-redirect"
- ]
- }
- }
- },
{
"match": {
"left": {
diff --git a/tests/py/ip6/icmpv6.t.json.output b/tests/py/ip6/icmpv6.t.json.output
index 7b8f5c1..db25133 100644
--- a/tests/py/ip6/icmpv6.t.json.output
+++ b/tests/py/ip6/icmpv6.t.json.output
@@ -758,3 +758,39 @@
}
]
+# icmpv6 taddr 2001:db8::133
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "type",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ "mld-listener-query",
+ "mld-listener-report",
+ "mld-listener-done",
+ "nd-neighbor-solicit",
+ "nd-neighbor-advert",
+ "nd-redirect"
+ ]
+ }
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "taddr",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": "2001:db8::133"
+ }
+ }
+]

View File

@ -0,0 +1,44 @@
From 3ab9ef7229ebe3cd66da95e3aabf474342f9eab9 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:02 +0200
Subject: [PATCH] segtree: incorrect type when aggregating concatenated set
ranges
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit c44995e31ba60c4823bb81fecd29020452b9e702
commit c44995e31ba60c4823bb81fecd29020452b9e702
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Aug 13 15:19:23 2025 +0200
segtree: incorrect type when aggregating concatenated set ranges
Uncovered by the compound_expr_remove() replacement by type safe function
coming after this patch.
Add expression to the concatenation which is reachable via expr_value().
This bug is subtle, I could not spot any reproducible buggy behaviour
when using the wrong type when running the existing tests.
Fixes: 8ac2f3b2fca3 ("src: Add support for concatenated set ranges")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/segtree.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/segtree.c b/src/segtree.c
index c4029f9..a3a0fb4 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -447,7 +447,7 @@ next:
mpz_clear(range);
r2 = list_entry(r2_next, typeof(*r2), list);
- compound_expr_remove(start, r1);
+ compound_expr_remove(expr_value(start), r1);
if (free_r1)
expr_free(r1);

View File

@ -0,0 +1,154 @@
From 2970dcfbcb714b5e64d114229c9b46cbd9183e9d Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:02 +0200
Subject: [PATCH] src: ensure chain policy evaluation when specified
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 01277922fede9fef8aacf5cc871bfbd55bbd78ef
commit 01277922fede9fef8aacf5cc871bfbd55bbd78ef
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Sun Aug 17 21:01:30 2025 +0200
src: ensure chain policy evaluation when specified
Set on CHAIN_F_BASECHAIN when policy is specified in chain, otherwise
chain priority is not evaluated.
Toggling this flag requires needs three adjustments to work though:
1) chain_evaluate() needs skip evaluation of hook name and priority if
not specified to allow for updating the default chain policy, e.g.
chain ip x y { policy accept; }
2) update netlink bytecode generation for chain to skip NFTA_CHAIN_HOOK
so update path is exercised in the kernel.
3) error reporting needs to check if basechain priority and type is
set on, otherwise skip further hints.
Fixes: acdfae9c3126 ("src: allow to specify the default policy for base chains")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/cmd.c | 3 +++
src/evaluate.c | 27 ++++++++++---------
src/mnl.c | 8 ++++--
src/parser_bison.y | 1 +
.../bogons/nft-f/basechain_bad_policy | 2 ++
.../bogons/nft-f/unexisting_chain_set_policy | 5 ++++
6 files changed, 32 insertions(+), 14 deletions(-)
create mode 100644 tests/shell/testcases/bogons/nft-f/basechain_bad_policy
create mode 100644 tests/shell/testcases/bogons/nft-f/unexisting_chain_set_policy
diff --git a/src/cmd.c b/src/cmd.c
index 68c476c..f59e30c 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -264,6 +264,9 @@ static int nft_cmd_chain_error(struct netlink_ctx *ctx, struct cmd *cmd,
if (!(chain->flags & CHAIN_F_BASECHAIN))
break;
+ if (!chain->priority.expr || !chain->type.str)
+ break;
+
mpz_export_data(&priority, chain->priority.expr->value,
BYTEORDER_HOST_ENDIAN, sizeof(int));
if (priority <= -200 && !strcmp(chain->type.str, "nat"))
diff --git a/src/evaluate.c b/src/evaluate.c
index 5012dd2..922821b 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -5070,18 +5070,21 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
}
if (chain->flags & CHAIN_F_BASECHAIN) {
- chain->hook.num = str2hooknum(chain->handle.family,
- chain->hook.name);
- if (chain->hook.num == NF_INET_NUMHOOKS)
- return __stmt_binary_error(ctx, &chain->hook.loc, NULL,
- "The %s family does not support this hook",
- family2str(chain->handle.family));
-
- if (!evaluate_priority(ctx, &chain->priority,
- chain->handle.family, chain->hook.num))
- return __stmt_binary_error(ctx, &chain->priority.loc, NULL,
- "invalid priority expression %s in this context.",
- expr_name(chain->priority.expr));
+ if (chain->hook.name) {
+ chain->hook.num = str2hooknum(chain->handle.family,
+ chain->hook.name);
+ if (chain->hook.num == NF_INET_NUMHOOKS)
+ return __stmt_binary_error(ctx, &chain->hook.loc, NULL,
+ "The %s family does not support this hook",
+ family2str(chain->handle.family));
+ }
+ if (chain->priority.expr) {
+ if (!evaluate_priority(ctx, &chain->priority,
+ chain->handle.family, chain->hook.num))
+ return __stmt_binary_error(ctx, &chain->priority.loc, NULL,
+ "invalid priority expression %s in this context.",
+ expr_name(chain->priority.expr));
+ }
if (chain->policy) {
expr_set_context(&ctx->ectx, &policy_type,
NFT_NAME_MAXLEN * BITS_PER_BYTE);
diff --git a/src/mnl.c b/src/mnl.c
index c62b140..6e8066a 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -875,7 +875,9 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
mnl_attr_put_strz(nlh, NFTA_CHAIN_TYPE, cmd->chain->type.str);
}
- nest = mnl_attr_nest_start(nlh, NFTA_CHAIN_HOOK);
+ if (cmd->chain->type.str ||
+ (cmd->chain && cmd->chain->dev_expr))
+ nest = mnl_attr_nest_start(nlh, NFTA_CHAIN_HOOK);
if (cmd->chain->type.str) {
mnl_attr_put_u32(nlh, NFTA_HOOK_HOOKNUM, htonl(cmd->chain->hook.num));
@@ -887,7 +889,9 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
if (cmd->chain && cmd->chain->dev_expr)
mnl_nft_chain_devs_build(nlh, cmd);
- mnl_attr_nest_end(nlh, nest);
+ if (cmd->chain->type.str ||
+ (cmd->chain && cmd->chain->dev_expr))
+ mnl_attr_nest_end(nlh, nest);
}
nftnl_chain_free(nlc);
diff --git a/src/parser_bison.y b/src/parser_bison.y
index a6a517f..c3086c8 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -2756,6 +2756,7 @@ policy_spec : POLICY policy_expr close_scope_policy
}
$<chain>0->policy = $2;
$<chain>0->policy->location = @$;
+ $<chain>0->flags |= CHAIN_F_BASECHAIN;
}
;
diff --git a/tests/shell/testcases/bogons/nft-f/basechain_bad_policy b/tests/shell/testcases/bogons/nft-f/basechain_bad_policy
new file mode 100644
index 0000000..998e423
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/basechain_bad_policy
@@ -0,0 +1,2 @@
+define MY_POLICY = deny
+table T { chain C { policy $MY_POLICY; };};
diff --git a/tests/shell/testcases/bogons/nft-f/unexisting_chain_set_policy b/tests/shell/testcases/bogons/nft-f/unexisting_chain_set_policy
new file mode 100644
index 0000000..0889559
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/unexisting_chain_set_policy
@@ -0,0 +1,5 @@
+table ip x {
+ chain y {
+ policy drop;
+ }
+}

View File

@ -0,0 +1,767 @@
From c47e93f5fa3ca3204080a09edbf854a1c9d7cd64 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:02 +0200
Subject: [PATCH] json: Do not reduce single-item arrays on output
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 5e492307c2c93b7eb654691ca87ddc7ad86640d8
Conflicts: Dropped changes to non-existent test and dump files
commit 5e492307c2c93b7eb654691ca87ddc7ad86640d8
Author: Phil Sutter <phil@nwl.cc>
Date: Tue Aug 12 17:31:47 2025 +0200
json: Do not reduce single-item arrays on output
This is a partial revert of commit a740f2036ad0d ("json: Introduce
json_add_array_new()"), keeping the function but eliminating its primary
task which is to replace arrays of size 1 by their only item. While
support for this on input is convenient for users, it means extra casing
in JSON output parsers to cover for it. The minor reduction in output
size does not justify that.
Fixes: a740f2036ad0d ("json: Introduce json_add_array_new()")
Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1806
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/json.c | 8 +-
tests/py/any/log.t.json.output | 15 +++-
tests/py/any/queue.t.json.output | 117 +++++++++++++++++++++++++
tests/py/inet/fib.t.json | 8 +-
tests/py/inet/fib.t.json.output | 87 ++++++++++++++++++-
tests/py/inet/snat.t.json.output | 22 +++++
tests/py/inet/synproxy.t.json | 8 +-
tests/py/inet/synproxy.t.json.output | 8 +-
tests/py/ip/masquerade.t.json.output | 46 ++++++++++
tests/py/ip/redirect.t.json.output | 118 ++++++++++++++++++++++++++
tests/py/ip/snat.t.json.output | 8 +-
tests/py/ip6/masquerade.t.json.output | 46 ++++++++++
tests/py/ip6/redirect.t.json.output | 70 +++++++++++++++
13 files changed, 536 insertions(+), 25 deletions(-)
create mode 100644 tests/py/inet/snat.t.json.output
diff --git a/src/json.c b/src/json.c
index a2523a4..183047a 100644
--- a/src/json.c
+++ b/src/json.c
@@ -53,14 +53,10 @@ static json_t *__nft_json_pack(unsigned int line, const char *fmt, ...)
static void json_add_array_new(json_t *obj, const char *name, json_t *array)
{
- if (json_array_size(array) > 1) {
+ if (json_array_size(array))
json_object_set_new(obj, name, array);
- } else {
- if (json_array_size(array))
- json_object_set(obj, name,
- json_array_get(array, 0));
+ else
json_decref(array);
- }
}
static json_t *expr_print_json(const struct expr *expr, struct output_ctx *octx)
diff --git a/tests/py/any/log.t.json.output b/tests/py/any/log.t.json.output
index 051c448..bec70a3 100644
--- a/tests/py/any/log.t.json.output
+++ b/tests/py/any/log.t.json.output
@@ -9,7 +9,20 @@
[
{
"log": {
- "flags": "all"
+ "flags": [
+ "all"
+ ]
+ }
+ }
+]
+
+# log flags all
+[
+ {
+ "log": {
+ "flags": [
+ "all"
+ ]
}
}
]
diff --git a/tests/py/any/queue.t.json.output b/tests/py/any/queue.t.json.output
index 1104d76..ea37223 100644
--- a/tests/py/any/queue.t.json.output
+++ b/tests/py/any/queue.t.json.output
@@ -7,3 +7,120 @@
}
]
+# queue num 4-5 fanout
+[
+ {
+ "queue": {
+ "flags": [
+ "fanout"
+ ],
+ "num": {
+ "range": [
+ 4,
+ 5
+ ]
+ }
+ }
+ }
+]
+
+# queue num 4-5 bypass
+[
+ {
+ "queue": {
+ "flags": [
+ "bypass"
+ ],
+ "num": {
+ "range": [
+ 4,
+ 5
+ ]
+ }
+ }
+ }
+]
+
+# queue flags bypass to numgen inc mod 65536
+[
+ {
+ "queue": {
+ "flags": [
+ "bypass"
+ ],
+ "num": {
+ "numgen": {
+ "mod": 65536,
+ "mode": "inc",
+ "offset": 0
+ }
+ }
+ }
+ }
+]
+
+# queue flags bypass to 65535
+[
+ {
+ "queue": {
+ "flags": [
+ "bypass"
+ ],
+ "num": 65535
+ }
+ }
+]
+
+# queue flags bypass to 1-65535
+[
+ {
+ "queue": {
+ "flags": [
+ "bypass"
+ ],
+ "num": {
+ "range": [
+ 1,
+ 65535
+ ]
+ }
+ }
+ }
+]
+
+# queue flags bypass to oifname map { "eth0" : 0, "ppp0" : 2, "eth1" : 2 }
+[
+ {
+ "queue": {
+ "flags": [
+ "bypass"
+ ],
+ "num": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ "eth0",
+ 0
+ ],
+ [
+ "ppp0",
+ 2
+ ],
+ [
+ "eth1",
+ 2
+ ]
+ ]
+ },
+ "key": {
+ "meta": {
+ "key": "oifname"
+ }
+ }
+ }
+ }
+ }
+ }
+]
+
diff --git a/tests/py/inet/fib.t.json b/tests/py/inet/fib.t.json
index 14a6249..2bfe4f7 100644
--- a/tests/py/inet/fib.t.json
+++ b/tests/py/inet/fib.t.json
@@ -100,9 +100,7 @@
"match": {
"left": {
"fib": {
- "flags": [
- "daddr"
- ],
+ "flags": "daddr",
"result": "check"
}
},
@@ -118,9 +116,7 @@
"match": {
"left": {
"fib": {
- "flags": [
- "daddr"
- ],
+ "flags": "daddr",
"result": "check"
}
},
diff --git a/tests/py/inet/fib.t.json.output b/tests/py/inet/fib.t.json.output
index e8d0166..d3396dd 100644
--- a/tests/py/inet/fib.t.json.output
+++ b/tests/py/inet/fib.t.json.output
@@ -43,7 +43,9 @@
"match": {
"left": {
"fib": {
- "flags": "daddr",
+ "flags": [
+ "daddr"
+ ],
"result": "check"
}
},
@@ -59,7 +61,9 @@
"match": {
"left": {
"fib": {
- "flags": "daddr",
+ "flags": [
+ "daddr"
+ ],
"result": "check"
}
},
@@ -69,3 +73,82 @@
}
]
+# fib daddr check vmap { missing : drop, exists : accept }
+[
+ {
+ "vmap": {
+ "data": {
+ "set": [
+ [
+ false,
+ {
+ "drop": null
+ }
+ ],
+ [
+ true,
+ {
+ "accept": null
+ }
+ ]
+ ]
+ },
+ "key": {
+ "fib": {
+ "flags": [
+ "daddr"
+ ],
+ "result": "check"
+ }
+ }
+ }
+ }
+]
+
+# meta mark set fib daddr check . ct mark map { exists . 0x00000000 : 0x00000001 }
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ true,
+ 0
+ ]
+ },
+ 1
+ ]
+ ]
+ },
+ "key": {
+ "concat": [
+ {
+ "fib": {
+ "flags": [
+ "daddr"
+ ],
+ "result": "check"
+ }
+ },
+ {
+ "ct": {
+ "key": "mark"
+ }
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+]
+
diff --git a/tests/py/inet/snat.t.json.output b/tests/py/inet/snat.t.json.output
new file mode 100644
index 0000000..5b95886
--- /dev/null
+++ b/tests/py/inet/snat.t.json.output
@@ -0,0 +1,22 @@
+# iifname "foo" masquerade random
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "op": "==",
+ "right": "foo"
+ }
+ },
+ {
+ "masquerade": {
+ "flags": [
+ "random"
+ ]
+ }
+ }
+]
+
diff --git a/tests/py/inet/synproxy.t.json b/tests/py/inet/synproxy.t.json
index 1dd85a6..b3cec09 100644
--- a/tests/py/inet/synproxy.t.json
+++ b/tests/py/inet/synproxy.t.json
@@ -19,9 +19,7 @@
[
{
"synproxy": {
- "flags": [
- "timestamp"
- ]
+ "flags": "timestamp"
}
}
]
@@ -56,9 +54,7 @@
[
{
"synproxy": {
- "flags": [
- "sack-perm"
- ]
+ "flags": "sack-perm"
}
}
]
diff --git a/tests/py/inet/synproxy.t.json.output b/tests/py/inet/synproxy.t.json.output
index e32cdfb..a1d81bf 100644
--- a/tests/py/inet/synproxy.t.json.output
+++ b/tests/py/inet/synproxy.t.json.output
@@ -2,7 +2,9 @@
[
{
"synproxy": {
- "flags": "timestamp"
+ "flags": [
+ "timestamp"
+ ]
}
}
]
@@ -11,7 +13,9 @@
[
{
"synproxy": {
- "flags": "sack-perm"
+ "flags": [
+ "sack-perm"
+ ]
}
}
]
diff --git a/tests/py/ip/masquerade.t.json.output b/tests/py/ip/masquerade.t.json.output
index 58e7e29..8ca5a42 100644
--- a/tests/py/ip/masquerade.t.json.output
+++ b/tests/py/ip/masquerade.t.json.output
@@ -121,3 +121,49 @@
}
]
+# udp dport 53 masquerade random
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 53
+ }
+ },
+ {
+ "masquerade": {
+ "flags": [
+ "random"
+ ]
+ }
+ }
+]
+
+# udp dport 53 masquerade persistent
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 53
+ }
+ },
+ {
+ "masquerade": {
+ "flags": [
+ "persistent"
+ ]
+ }
+ }
+]
+
diff --git a/tests/py/ip/redirect.t.json.output b/tests/py/ip/redirect.t.json.output
index 4646c60..09f1e48 100644
--- a/tests/py/ip/redirect.t.json.output
+++ b/tests/py/ip/redirect.t.json.output
@@ -1,3 +1,49 @@
+# udp dport 53 redirect random
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 53
+ }
+ },
+ {
+ "redirect": {
+ "flags": [
+ "random"
+ ]
+ }
+ }
+]
+
+# udp dport 53 redirect persistent
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 53
+ }
+ },
+ {
+ "redirect": {
+ "flags": [
+ "persistent"
+ ]
+ }
+ }
+]
+
# udp dport 53 redirect random,persistent,fully-random
[
{
@@ -144,3 +190,75 @@
}
]
+# tcp dport 9128 redirect to :993 random
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "==",
+ "right": 9128
+ }
+ },
+ {
+ "redirect": {
+ "flags": [
+ "random"
+ ],
+ "port": 993
+ }
+ }
+]
+
+# tcp dport 9128 redirect to :993 fully-random
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "==",
+ "right": 9128
+ }
+ },
+ {
+ "redirect": {
+ "flags": [
+ "fully-random"
+ ],
+ "port": 993
+ }
+ }
+]
+
+# tcp dport 9128 redirect to :123 persistent
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "==",
+ "right": 9128
+ }
+ },
+ {
+ "redirect": {
+ "flags": [
+ "persistent"
+ ],
+ "port": 123
+ }
+ }
+]
+
diff --git a/tests/py/ip/snat.t.json.output b/tests/py/ip/snat.t.json.output
index 2a99780..19eba25 100644
--- a/tests/py/ip/snat.t.json.output
+++ b/tests/py/ip/snat.t.json.output
@@ -241,8 +241,12 @@
}
},
"family": "ip",
- "flags": "netmap",
- "type_flags": "prefix"
+ "flags": [
+ "netmap"
+ ],
+ "type_flags": [
+ "prefix"
+ ]
}
}
]
diff --git a/tests/py/ip6/masquerade.t.json.output b/tests/py/ip6/masquerade.t.json.output
index 31d0cd9..21ed4f6 100644
--- a/tests/py/ip6/masquerade.t.json.output
+++ b/tests/py/ip6/masquerade.t.json.output
@@ -96,3 +96,49 @@
}
]
+# udp dport 53 masquerade random
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 53
+ }
+ },
+ {
+ "masquerade": {
+ "flags": [
+ "random"
+ ]
+ }
+ }
+]
+
+# udp dport 53 masquerade persistent
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 53
+ }
+ },
+ {
+ "masquerade": {
+ "flags": [
+ "persistent"
+ ]
+ }
+ }
+]
+
diff --git a/tests/py/ip6/redirect.t.json.output b/tests/py/ip6/redirect.t.json.output
index 0174cc7..69c7b03 100644
--- a/tests/py/ip6/redirect.t.json.output
+++ b/tests/py/ip6/redirect.t.json.output
@@ -144,3 +144,73 @@
}
]
+# udp dport 53 redirect random
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 53
+ }
+ },
+ {
+ "redirect": {
+ "flags": [
+ "random"
+ ]
+ }
+ }
+]
+
+# udp dport 53 redirect persistent
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 53
+ }
+ },
+ {
+ "redirect": {
+ "flags": [
+ "persistent"
+ ]
+ }
+ }
+]
+
+# tcp dport 9128 redirect to :993 random
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "==",
+ "right": 9128
+ }
+ },
+ {
+ "redirect": {
+ "flags": [
+ "random"
+ ],
+ "port": 993
+ }
+ }
+]
+

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,55 @@
From 0ba786e77b069dee73f6fbfd910e6404d28193a8 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:50 +0200
Subject: [PATCH] mnl: silence compiler warning
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit cd9f168875aca72caacdcfb552c15b6484a82bd5
commit cd9f168875aca72caacdcfb552c15b6484a82bd5
Author: Florian Westphal <fw@strlen.de>
Date: Wed Aug 20 14:44:43 2025 +0200
mnl: silence compiler warning
gcc 14.3.0 reports this:
src/mnl.c: In function 'mnl_nft_chain_add':
src/mnl.c:916:25: warning: 'nest' may be used uninitialized [-Wmaybe-uninitialized]
916 | mnl_attr_nest_end(nlh, nest);
I guess its because compiler can't know that the conditions cannot change
in-between and assumes nest_end() can be called without nest_start().
Fixes: 01277922fede ("src: ensure chain policy evaluation when specified")
Signed-off-by: Florian Westphal <fw@strlen.de>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/mnl.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/mnl.c b/src/mnl.c
index 6e8066a..6ba2d85 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -868,7 +868,7 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
nftnl_chain_nlmsg_build_payload(nlh, nlc);
if (cmd->chain && cmd->chain->flags & CHAIN_F_BASECHAIN) {
- struct nlattr *nest;
+ struct nlattr *nest = NULL;
if (cmd->chain->type.str) {
cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->chain->type.loc);
@@ -889,8 +889,7 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
if (cmd->chain && cmd->chain->dev_expr)
mnl_nft_chain_devs_build(nlh, cmd);
- if (cmd->chain->type.str ||
- (cmd->chain && cmd->chain->dev_expr))
+ if (nest)
mnl_attr_nest_end(nlh, nest);
}

View File

@ -0,0 +1,89 @@
From 02efc5f458b73e02dd05ef1f6e4736b3a8999ac2 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:50 +0200
Subject: [PATCH] mnl: continue on ENOBUFS errors when processing batch
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 47e9aaf0227daf16f43a7442e1dceae8851817a5
commit 47e9aaf0227daf16f43a7442e1dceae8851817a5
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Aug 26 10:09:13 2025 +0200
mnl: continue on ENOBUFS errors when processing batch
A user reports that:
nft -f ruleset.nft
fails with:
netlink: Error: Could not process rule: No buffer space available
This was triggered by:
table ip6 fule {
set domestic_ip6 {
type ipv6_addr
flags dynamic,interval
elements = $domestic_ip6
}
chain prerouting {
type filter hook prerouting priority 0;
ip6 daddr @domestic_ip6 counter
}
}
where $domestic_ip6 contains a large number of IPv6 addresses.
This set declaration is not supported currently, because dynamic sets
with intervals are not supported, then every IPv6 address that is added
triggers an error, overruning the userspace socket buffer with lots of
NLMSG_ERROR messages (or too big NLMSG_ERROR message to fit into the
socket buffer).
In the particular context of batch processing, ENOBUFS is just an
indication that too many errors have occurred. The kernel cannot store
any more NLMSG_ERROR messages into the userspace socket buffer.
However, there are still NLMSG_ERROR messages in the socket buffer to be
processed that can provide a hint on what is going on.
Instead of breaking on ENOBUFS in batches, continue error processing.
After this patch, the ruleset above displays:
ruleset.nft:2367:7-18: Error: Could not process rule: Operation not supported
set domestic_ip6 {
^^^^^^^^^^^^
ruleset.nft:2367:7-18: Error: Could not process rule: No such file or directory
set domestic_ip6 {
^^^^^^^^^^^^
Fixes: a72315d2bad4 ("src: add rule batching support")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/mnl.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/mnl.c b/src/mnl.c
index 6ba2d85..96a86aa 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -445,8 +445,13 @@ int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
break;
ret = mnl_socket_recvfrom(nl, rcv_buf, sizeof(rcv_buf));
- if (ret == -1)
+ if (ret == -1) {
+ /* Too many errors, not all errors are displayed. */
+ if (errno == ENOBUFS)
+ continue;
+
return -1;
+ }
/* Continue on error, make sure we get all acknowledgments */
ret = mnl_cb_run2(rcv_buf, ret, 0, portid,

View File

@ -0,0 +1,38 @@
From 50c0c4910132783578eee7b2aa767b88f504e366 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:50 +0200
Subject: [PATCH] monitor: Quote device names in chain declarations, too
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit ed1b5b672b2eeb6244bed566227bee2aa7a1e4b4
commit ed1b5b672b2eeb6244bed566227bee2aa7a1e4b4
Author: Phil Sutter <phil@nwl.cc>
Date: Thu Aug 28 16:47:03 2025 +0200
monitor: Quote device names in chain declarations, too
Fixed commit missed the fact that there are two routines printing chain
declarations.
Fixes: eb30f236d91a8 ("rule: print chain and flowtable devices in quotes")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/rule.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/rule.c b/src/rule.c
index c026d8d..3b75736 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -1110,7 +1110,7 @@ void chain_print_plain(const struct chain *chain, struct output_ctx *octx)
nft_print(octx, "devices = { ");
for (i = 0; i < chain->dev_array_len; i++) {
- nft_print(octx, "%s", chain->dev_array[i]);
+ nft_print(octx, "\"%s\"", chain->dev_array[i]);
if (i + 1 != chain->dev_array_len)
nft_print(octx, ", ");
}

View File

@ -0,0 +1,39 @@
From cae7e6924f2857b6e66db1425abc6beff07bb2ac Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:50 +0200
Subject: [PATCH] tests: monitor: Fix regex collecting expected echo output
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 419338d96bdb19c10e241387c54f416c551a47c3
commit 419338d96bdb19c10e241387c54f416c551a47c3
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Sep 3 15:23:12 2025 +0200
tests: monitor: Fix regex collecting expected echo output
No input triggered this bug, but the match would accept "insert" and
"replace" keywords anywhere in the line not just at the beginning as was
intended.
Fixes: b2506e5504fed ("tests: Merge monitor and echo test suites")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/monitor/run-tests.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/monitor/run-tests.sh b/tests/monitor/run-tests.sh
index f1ac790..c6a3322 100755
--- a/tests/monitor/run-tests.sh
+++ b/tests/monitor/run-tests.sh
@@ -53,7 +53,7 @@ echo_output_append() {
grep '^\(add\|replace\|insert\)' $command_file >>$output_file
return
}
- [[ "$*" =~ ^add|replace|insert ]] && echo "$*" >>$output_file
+ [[ "$*" =~ ^(add|replace|insert) ]] && echo "$*" >>$output_file
}
json_output_filter() { # (filename)
# unify handle values

View File

@ -0,0 +1,189 @@
From c6b5059905d5e1ba029027cecd8e62d7b70c1c51 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:31:50 +0200
Subject: [PATCH] monitor: Inform JSON printer when reporting an object delete
event
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 6c04d24d16f1d15f216f2b3c8e64c9062cd77487
Conflicts:
- Context change due to missing commit 73a8adfc2432e
("monitor: Recognize flowtable add/del events")
- Adjusted to missing commit 3a957f8f1ff1e
("tunnel: add tunnel object and statement json support")
commit 6c04d24d16f1d15f216f2b3c8e64c9062cd77487
Author: Phil Sutter <phil@nwl.cc>
Date: Fri Aug 29 01:07:05 2025 +0200
monitor: Inform JSON printer when reporting an object delete event
Since kernel commit a1050dd07168 ("netfilter: nf_tables: Reintroduce
shortened deletion notifications"), type-specific data is no longer
dumped when notifying for a deleted object. JSON output was not aware of
this and tried to print bogus data.
Fixes: 9e88aae28e9f4 ("monitor: Use libnftables JSON output")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
include/json.h | 5 +++--
src/json.c | 16 ++++++++++------
src/monitor.c | 2 +-
tests/monitor/testcases/object.t | 10 +++++-----
4 files changed, 19 insertions(+), 14 deletions(-)
diff --git a/include/json.h b/include/json.h
index 39be892..23ca49e 100644
--- a/include/json.h
+++ b/include/json.h
@@ -112,7 +112,7 @@ void monitor_print_set_json(struct netlink_mon_handler *monh,
void monitor_print_element_json(struct netlink_mon_handler *monh,
const char *cmd, struct set *s);
void monitor_print_obj_json(struct netlink_mon_handler *monh,
- const char *cmd, struct obj *o);
+ const char *cmd, struct obj *o, bool delete);
void monitor_print_rule_json(struct netlink_mon_handler *monh,
const char *cmd, struct rule *r);
@@ -249,7 +249,8 @@ static inline void monitor_print_element_json(struct netlink_mon_handler *monh,
}
static inline void monitor_print_obj_json(struct netlink_mon_handler *monh,
- const char *cmd, struct obj *o)
+ const char *cmd, struct obj *o,
+ bool delete)
{
/* empty */
}
diff --git a/src/json.c b/src/json.c
index 57b7664..fc2e962 100644
--- a/src/json.c
+++ b/src/json.c
@@ -353,7 +353,7 @@ static json_t *timeout_policy_json(uint8_t l4, const uint32_t *timeout)
return root ? : json_null();
}
-static json_t *obj_print_json(const struct obj *obj)
+static json_t *obj_print_json(const struct obj *obj, bool delete)
{
const char *rate_unit = NULL, *burst_unit = NULL;
const char *type = obj_type_name(obj->type);
@@ -366,6 +366,9 @@ static json_t *obj_print_json(const struct obj *obj)
"table", obj->handle.table.name,
"handle", obj->handle.handle.id);
+ if (delete)
+ goto out;
+
if (obj->comment) {
tmp = nft_json_pack("{s:s}", "comment", obj->comment);
json_object_update(root, tmp);
@@ -469,6 +472,7 @@ static json_t *obj_print_json(const struct obj *obj)
break;
}
+out:
return nft_json_pack("{s:o}", type, root);
}
@@ -1699,7 +1703,7 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
json_array_append_new(root, tmp);
list_for_each_entry(obj, &table->obj_cache.list, cache.list) {
- tmp = obj_print_json(obj);
+ tmp = obj_print_json(obj, false);
json_array_append_new(root, tmp);
}
list_for_each_entry(set, &table->set_cache.list, cache.list) {
@@ -1861,7 +1865,7 @@ static json_t *do_list_sets_json(struct netlink_ctx *ctx, struct cmd *cmd)
static json_t *do_list_obj_json(struct netlink_ctx *ctx,
struct cmd *cmd, uint32_t type)
{
- json_t *root = json_array();
+ json_t *root = json_array(), *tmp;
struct table *table;
struct obj *obj;
@@ -1880,7 +1884,7 @@ static json_t *do_list_obj_json(struct netlink_ctx *ctx,
strcmp(cmd->handle.obj.name, obj->handle.obj.name)))
continue;
- json_array_append_new(root, obj_print_json(obj));
+ json_array_append_new(root, obj_print_json(obj, false));
}
}
@@ -2066,9 +2070,9 @@ void monitor_print_element_json(struct netlink_mon_handler *monh,
}
void monitor_print_obj_json(struct netlink_mon_handler *monh,
- const char *cmd, struct obj *o)
+ const char *cmd, struct obj *o, bool delete)
{
- monitor_print_json(monh, cmd, obj_print_json(o));
+ monitor_print_json(monh, cmd, obj_print_json(o, delete));
}
void monitor_print_rule_json(struct netlink_mon_handler *monh,
diff --git a/src/monitor.c b/src/monitor.c
index bf59f2d..87ff2a3 100644
--- a/src/monitor.c
+++ b/src/monitor.c
@@ -529,7 +529,7 @@ static int netlink_events_obj_cb(const struct nlmsghdr *nlh, int type,
nft_mon_print(monh, "\n");
break;
case NFTNL_OUTPUT_JSON:
- monitor_print_obj_json(monh, cmd, obj);
+ monitor_print_obj_json(monh, cmd, obj, type == NFT_MSG_DELOBJ);
if (!nft_output_echo(&monh->ctx->nft->output))
nft_mon_print(monh, "\n");
break;
diff --git a/tests/monitor/testcases/object.t b/tests/monitor/testcases/object.t
index 53a9f8c..b60dc98 100644
--- a/tests/monitor/testcases/object.t
+++ b/tests/monitor/testcases/object.t
@@ -9,7 +9,7 @@ J {"add": {"counter": {"family": "ip", "name": "c", "table": "t", "handle": 0, "
I delete counter ip t c
O -
-J {"delete": {"counter": {"family": "ip", "name": "c", "table": "t", "handle": 0, "packets": 0, "bytes": 0}}}
+J {"delete": {"counter": {"family": "ip", "name": "c", "table": "t", "handle": 0}}}
# FIXME: input/output shouldn't be asynchronous here
I add quota ip t q 25 mbytes
@@ -18,7 +18,7 @@ J {"add": {"quota": {"family": "ip", "name": "q", "table": "t", "handle": 0, "by
I delete quota ip t q
O -
-J {"delete": {"quota": {"family": "ip", "name": "q", "table": "t", "handle": 0, "bytes": 26214400, "used": 0, "inv": false}}}
+J {"delete": {"quota": {"family": "ip", "name": "q", "table": "t", "handle": 0}}}
# FIXME: input/output shouldn't be asynchronous here
I add limit ip t l rate 1/second
@@ -27,7 +27,7 @@ J {"add": {"limit": {"family": "ip", "name": "l", "table": "t", "handle": 0, "ra
I delete limit ip t l
O -
-J {"delete": {"limit": {"family": "ip", "name": "l", "table": "t", "handle": 0, "rate": 1, "per": "second", "burst": 5}}}
+J {"delete": {"limit": {"family": "ip", "name": "l", "table": "t", "handle": 0}}}
I add ct helper ip t cth { type "sip" protocol tcp; l3proto ip; }
O -
@@ -35,7 +35,7 @@ J {"add": {"ct helper": {"family": "ip", "name": "cth", "table": "t", "handle":
I delete ct helper ip t cth
O -
-J {"delete": {"ct helper": {"family": "ip", "name": "cth", "table": "t", "handle": 0, "type": "sip", "protocol": "tcp", "l3proto": "ip"}}}
+J {"delete": {"ct helper": {"family": "ip", "name": "cth", "table": "t", "handle": 0}}}
I add ct timeout ip t ctt { protocol udp; l3proto ip; policy = { unreplied : 15s, replied : 12s }; }
O -
@@ -43,4 +43,4 @@ J {"add": {"ct timeout": {"family": "ip", "name": "ctt", "table": "t", "handle":
I delete ct timeout ip t ctt
O -
-J {"delete": {"ct timeout": {"family": "ip", "name": "ctt", "table": "t", "handle": 0, "protocol": "udp", "l3proto": "ip", "policy": {"unreplied": 15, "replied": 12}}}}
+J {"delete": {"ct timeout": {"family": "ip", "name": "ctt", "table": "t", "handle": 0}}}

View File

@ -0,0 +1,67 @@
From 69e126ef0536e2b792b970edf0b132dc2aa3548f Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:19 +0200
Subject: [PATCH] fib: Fix for existence check on Big Endian
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 98e51e687616a4b54efa3b723917c292e3acc380
commit 98e51e687616a4b54efa3b723917c292e3acc380
Author: Phil Sutter <phil@nwl.cc>
Date: Tue Sep 9 22:27:19 2025 +0200
fib: Fix for existence check on Big Endian
Adjust the expression size to 1B so cmp expression value is correct.
Without this, the rule 'fib saddr . iif check exists' generates
following byte code on BE:
| [ fib saddr . iif oif present => reg 1 ]
| [ cmp eq reg 1 0x00000001 ]
Though with NFTA_FIB_F_PRESENT flag set, nft_fib.ko writes to the first
byte of reg 1 only (using nft_reg_store8()). With this patch in place,
byte code is correct:
| [ fib saddr . iif oif present => reg 1 ]
| [ cmp eq reg 1 0x01000000 ]
Fixes: f686a17eafa0b ("fib: Support existence check")
Cc: Yi Chen <yiche@redhat.com>
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 1 +
src/fib.c | 4 +++-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index 922821b..6046716 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -2574,6 +2574,7 @@ static int expr_evaluate_fib(struct eval_ctx *ctx, struct expr **exprp)
if (expr->flags & EXPR_F_BOOLEAN) {
expr->fib.flags |= NFTA_FIB_F_PRESENT;
datatype_set(expr, &boolean_type);
+ expr->len = BITS_PER_BYTE;
}
return expr_evaluate_primary(ctx, exprp);
}
diff --git a/src/fib.c b/src/fib.c
index 80ff292..bfedcb7 100644
--- a/src/fib.c
+++ b/src/fib.c
@@ -198,8 +198,10 @@ struct expr *fib_expr_alloc(const struct location *loc,
BUG("Unknown result %d\n", result);
}
- if (flags & NFTA_FIB_F_PRESENT)
+ if (flags & NFTA_FIB_F_PRESENT) {
type = &boolean_type;
+ len = BITS_PER_BYTE;
+ }
expr = expr_alloc(loc, EXPR_FIB, type,
BYTEORDER_HOST_ENDIAN, len);

View File

@ -0,0 +1,41 @@
From 84642c28fab20ef3f0bef23919a45f7b7d7d18db Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:19 +0200
Subject: [PATCH] doc: fix tcpdump example
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit b9516b0a4dfb6e16e3e11c3024683a2df1ea09ab
commit b9516b0a4dfb6e16e3e11c3024683a2df1ea09ab
Author: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
Date: Mon Oct 13 19:17:31 2025 +0200
doc: fix tcpdump example
The expression needs to be enclosed in a single string and combined with
a logical AND to have the desired effect.
Fixes: 1188a69604c3 ("src: introduce SYNPROXY matching")
Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
Reviewed-by: Fernando Fernandez Mancera <fmancera@suse.de>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/statements.txt | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/doc/statements.txt b/doc/statements.txt
index 3aceb02..ad928eb 100644
--- a/doc/statements.txt
+++ b/doc/statements.txt
@@ -592,8 +592,7 @@ needed for selective acknowledgement and window scaling).
---------------------------------------
Determine tcp options used by backend, from an external system
- tcpdump -pni eth0 -c 1 'tcp[tcpflags] == (tcp-syn|tcp-ack)'
- port 80 &
+ tcpdump -pni eth0 -c 1 'tcp[tcpflags] == (tcp-syn|tcp-ack) && port 80' &
telnet 192.0.2.42 80
18:57:24.693307 IP 192.0.2.42.80 > 192.0.2.43.48757:
Flags [S.], seq 360414582, ack 788841994, win 14480,

View File

@ -0,0 +1,60 @@
From 32ab7685f938713f927c1ed312ba6089cb2cf65b Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:19 +0200
Subject: [PATCH] src: parser_json: fix format string bugs
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit b30ad0c25b7b4a289dd73e8da6fa101b66fbf0d7
commit b30ad0c25b7b4a289dd73e8da6fa101b66fbf0d7
Author: Florian Westphal <fw@strlen.de>
Date: Thu Oct 23 14:17:00 2025 +0200
src: parser_json: fix format string bugs
After adding fmt attribute annotation:
warning: format not a string literal and no format arguments [-Wformat-security]
131 | erec_queue(error(&loc, err->text), ctx->msgs);
In function 'json_events_cb':
warning: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type '__u32' {aka 'unsigned int'} [-Wformat=]
Fix that up too.
Fixes: 586ad210368b ("libnftables: Implement JSON parser")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_json.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/parser_json.c b/src/parser_json.c
index 8c9d365..cce5ee2 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -127,7 +127,7 @@ static void json_lib_error(struct json_ctx *ctx, json_error_t *err)
.last_column = err->column,
};
- erec_queue(error(&loc, err->text), ctx->msgs);
+ erec_queue(error(&loc, "%s", err->text), ctx->msgs);
}
__attribute__((format(printf, 2, 3)))
@@ -4176,6 +4176,7 @@ int nft_parse_json_filename(struct nft_ctx *nft, const char *filename,
return ret;
}
+__attribute__((format(printf, 2, 3)))
static int json_echo_error(struct netlink_mon_handler *monh,
const char *fmt, ...)
{
@@ -4242,7 +4243,7 @@ int json_events_cb(const struct nlmsghdr *nlh, struct netlink_mon_handler *monh)
json = seqnum_to_json(nlh->nlmsg_seq);
if (!json) {
- json_echo_error(monh, "No JSON command found with seqnum %lu\n",
+ json_echo_error(monh, "No JSON command found with seqnum %u\n",
nlh->nlmsg_seq);
return MNL_CB_OK;
}

View File

@ -0,0 +1,80 @@
From 247ed03d1d9e2c03744d7c47db95be813f17ce54 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:19 +0200
Subject: [PATCH] datatype: clamp boolean value to 0 and 1
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit afb6a8e66a11178cbdbfc152c4aa9dda961b2140
commit afb6a8e66a11178cbdbfc152c4aa9dda961b2140
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Fri Jan 31 12:54:32 2025 +0100
datatype: clamp boolean value to 0 and 1
If user provides a numeric value larger than 0 or 1, match never
happens:
# nft --debug=netlink add rule x y tcp option sack-perm 4
ip x y
[ exthdr load tcpopt 1b @ 4 + 0 present => reg 1 ]
[ cmp eq reg 1 0x00000004 ]
After this update:
# nft --debug=netlink add rule x y tcp option sack-perm 4
ip x y
[ exthdr load tcpopt 1b @ 4 + 0 present => reg 1 ]
[ cmp eq reg 1 0x00000001 ]
This is to address a rare corner case, in case user specifies the
boolean value through the integer base type.
Fixes: 9fd9baba43c8 ("Introduce boolean datatype and boolean expression")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/datatype.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/src/datatype.c b/src/datatype.c
index 9530ae7..93703f8 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -1417,11 +1417,35 @@ static const struct symbol_table boolean_tbl = {
},
};
+static struct error_record *boolean_type_parse(struct parse_ctx *ctx,
+ const struct expr *sym,
+ struct expr **res)
+{
+ struct error_record *erec;
+ int num;
+
+ erec = integer_type_parse(ctx, sym, res);
+ if (erec)
+ return erec;
+
+ if (mpz_cmp_ui((*res)->value, 0))
+ num = 1;
+ else
+ num = 0;
+
+ expr_free(*res);
+
+ *res = constant_expr_alloc(&sym->location, &boolean_type,
+ BYTEORDER_HOST_ENDIAN, 1, &num);
+ return NULL;
+}
+
const struct datatype boolean_type = {
.type = TYPE_BOOLEAN,
.name = "boolean",
.desc = "boolean type",
.size = 1,
+ .parse = boolean_type_parse,
.basetype = &integer_type,
.sym_tbl = &boolean_tbl,
.json = boolean_type_json,

View File

@ -0,0 +1,39 @@
From d48369a36876854039882dc8981f49c7e42574c5 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:19 +0200
Subject: [PATCH] datatype: Fix boolean type on Big Endian
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit aec699af2a006b1dd6580abb95910c402e306fa9
commit aec699af2a006b1dd6580abb95910c402e306fa9
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Oct 8 23:19:08 2025 +0200
datatype: Fix boolean type on Big Endian
Pass a reference to a variable with correct size when creating the
expression, otherwise mpz_import_data() will read only the always zero
upper byte on Big Endian hosts.
Fixes: afb6a8e66a111 ("datatype: clamp boolean value to 0 and 1")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/datatype.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/datatype.c b/src/datatype.c
index 93703f8..bb93ef3 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -1422,7 +1422,7 @@ static struct error_record *boolean_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
struct error_record *erec;
- int num;
+ uint8_t num;
erec = integer_type_parse(ctx, sym, res);
if (erec)

View File

@ -0,0 +1,56 @@
From 6fdf4d2a6a114044a9932d5031d6d461e12505df Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:19 +0200
Subject: [PATCH] optimize: Fix verdict expression comparison
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 695ee5a8b174f86e2e64786530147e56d8d27f19
commit 695ee5a8b174f86e2e64786530147e56d8d27f19
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Oct 22 14:03:37 2025 +0200
optimize: Fix verdict expression comparison
In verdict expression, 'chain' points at a constant expression of
verdict_type, not a symbol expression. Therefore 'chain->identifier'
points eight bytes (on 64bit systems) into the mpz_t 'value' holding the
chain name. This matches the '_mp_d' data pointer, so works by accident.
Fix this by copying what verdict_jump_chain_print() does and export
chain names before comparing.
Fixes: fb298877ece27 ("src: add ruleset optimization infrastructure")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/optimize.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/src/optimize.c b/src/optimize.c
index 27e0ffe..7b4d036 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -302,13 +302,18 @@ static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b,
static bool expr_verdict_eq(const struct expr *expr_a, const struct expr *expr_b)
{
+ char chain_a[NFT_CHAIN_MAXNAMELEN];
+ char chain_b[NFT_CHAIN_MAXNAMELEN];
+
if (expr_a->verdict != expr_b->verdict)
return false;
if (expr_a->chain && expr_b->chain) {
- if (expr_a->chain->etype != expr_b->chain->etype)
+ if (expr_a->chain->etype != EXPR_VALUE ||
+ expr_a->chain->etype != expr_b->chain->etype)
return false;
- if (expr_a->chain->etype == EXPR_VALUE &&
- strcmp(expr_a->chain->identifier, expr_b->chain->identifier))
+ expr_chain_export(expr_a->chain, chain_a);
+ expr_chain_export(expr_b->chain, chain_b);
+ if (strcmp(chain_a, chain_b))
return false;
} else if (expr_a->chain || expr_b->chain) {
return false;

View File

@ -0,0 +1,69 @@
From 6f5224890b16acddb72ce4c02a6ffc3c2afa12cb Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:19 +0200
Subject: [PATCH] tests: py: any/ct.t.json.output: Drop leftover entry
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 1801480314bf2baa6abf9cb15ccdef975b966867
commit 1801480314bf2baa6abf9cb15ccdef975b966867
Author: Phil Sutter <phil@nwl.cc>
Date: Thu Oct 16 16:23:12 2025 +0200
tests: py: any/ct.t.json.output: Drop leftover entry
The rule with single element anonymous set was replaced, drop this
leftover.
Fixes: 27f6a4c68b4fd ("tests: replace single element sets")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/any/ct.t.json.output | 33 ---------------------------------
1 file changed, 33 deletions(-)
diff --git a/tests/py/any/ct.t.json.output b/tests/py/any/ct.t.json.output
index 70ade7e..82634c2 100644
--- a/tests/py/any/ct.t.json.output
+++ b/tests/py/any/ct.t.json.output
@@ -471,39 +471,6 @@
}
]
-# ct state . ct mark { new . 0x12345678}
-[
- {
- "match": {
- "left": {
- "concat": [
- {
- "ct": {
- "key": "state"
- }
- },
- {
- "ct": {
- "key": "mark"
- }
- }
- ]
- },
- "op": "==",
- "right": {
- "set": [
- {
- "concat": [
- "new",
- 305419896
- ]
- }
- ]
- }
- }
- }
-]
-
# ct state . ct mark { new . 0x12345678, new . 0x34127856, established . 0x12785634}
[
{

View File

@ -0,0 +1,121 @@
From 9811fa070a0e2b0f33e719080e9f10cda366ca58 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:19 +0200
Subject: [PATCH] tests: py: inet/osf.t: Fix element ordering in JSON
equivalents
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit b028f8ce616bb5a219a10844357b9a3822d99a8c
commit b028f8ce616bb5a219a10844357b9a3822d99a8c
Author: Phil Sutter <phil@nwl.cc>
Date: Thu Oct 9 02:06:54 2025 +0200
tests: py: inet/osf.t: Fix element ordering in JSON equivalents
The original rules order set elements differently. Stick to that and add
entries to inet/osf.t.json.output to cover for nftables reordering
entries.
Fixes: 92029c1282958 ("src: osf: add json support")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/inet/osf.t.json | 12 ++++----
tests/py/inet/osf.t.json.output | 53 +++++++++++++++++++++++++++++++++
2 files changed, 59 insertions(+), 6 deletions(-)
create mode 100644 tests/py/inet/osf.t.json.output
diff --git a/tests/py/inet/osf.t.json b/tests/py/inet/osf.t.json
index cedb7f6..b1bf747 100644
--- a/tests/py/inet/osf.t.json
+++ b/tests/py/inet/osf.t.json
@@ -73,8 +73,8 @@
"op": "==",
"right": {
"set": [
- "MacOs",
- "Windows"
+ "Windows",
+ "MacOs"
]
}
}
@@ -114,13 +114,13 @@
"map": {
"data": {
"set": [
- [
- "MacOs",
- 2
- ],
[
"Windows",
1
+ ],
+ [
+ "MacOs",
+ 2
]
]
},
diff --git a/tests/py/inet/osf.t.json.output b/tests/py/inet/osf.t.json.output
new file mode 100644
index 0000000..922e395
--- /dev/null
+++ b/tests/py/inet/osf.t.json.output
@@ -0,0 +1,53 @@
+# osf name { "Windows", "MacOs" }
+[
+ {
+ "match": {
+ "left": {
+ "osf": {
+ "key": "name"
+ }
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ "MacOs",
+ "Windows"
+ ]
+ }
+ }
+ }
+]
+
+# ct mark set osf name map { "Windows" : 0x00000001, "MacOs" : 0x00000002 }
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ "MacOs",
+ 2
+ ],
+ [
+ "Windows",
+ 1
+ ]
+ ]
+ },
+ "key": {
+ "osf": {
+ "key": "name"
+ }
+ }
+ }
+ }
+ }
+ }
+]

View File

@ -0,0 +1,69 @@
From 722fc9bde92c202e740ced94023fc2ad5a3ce343 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:19 +0200
Subject: [PATCH] doc: don't suggest to disable GSO
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 35cd3e7cff079c561ba616c792d5fdf13f6bd331
commit 35cd3e7cff079c561ba616c792d5fdf13f6bd331
Author: Ronan Pigott <ronan@rjp.ie>
Date: Sun Oct 6 09:36:03 2024 -0700
doc: don't suggest to disable GSO
The kernel can form aggregate packets whether or not GSO is enabled.
Disabling GSO is not a useful suggestion in this case.
Fixes: 05628cdd677d (doc: describe behaviour of {ip,ip6} length)
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/payload-expression.txt | 26 ++++++++++++--------------
1 file changed, 12 insertions(+), 14 deletions(-)
diff --git a/doc/payload-expression.txt b/doc/payload-expression.txt
index 8afeb71..6733846 100644
--- a/doc/payload-expression.txt
+++ b/doc/payload-expression.txt
@@ -134,13 +134,12 @@ Destination address |
ipv4_addr
|======================
-Careful with matching on *ip length*: If GRO/GSO is enabled, then the Linux
-kernel might aggregate several packets into one big packet that is larger than
-MTU. Moreover, if GRO/GSO maximum size is larger than 65535 (see man ip-link(8),
-specifically gro_ipv6_max_size and gso_ipv6_max_size), then *ip length* might
-be 0 for such jumbo packets. *meta length* allows you to match on the packet
-length including the IP header size. If you want to perform heuristics on the
-*ip length* field, then disable GRO/GSO.
+Careful with matching on *ip length*: The Linux kernel might aggregate several
+packets into one big packet that is larger than MTU. Moreover, if GRO/GSO
+maximum size is larger than 65535 (see man ip-link(8), specifically
+gro_ipv4_max_size and gso_ipv4_max_size), then *ip length* might be 0 for such
+jumbo packets. *meta length* allows you to match on the packet length including
+the IP header size.
ICMP HEADER EXPRESSION
~~~~~~~~~~~~~~~~~~~~~~
@@ -252,13 +251,12 @@ Destination address |
ipv6_addr
|=======================
-Careful with matching on *ip6 length*: If GRO/GSO is enabled, then the Linux
-kernel might aggregate several packets into one big packet that is larger than
-MTU. Moreover, if GRO/GSO maximum size is larger than 65535 (see man ip-link(8),
-specifically gro_ipv6_max_size and gso_ipv6_max_size), then *ip6 length* might
-be 0 for such jumbo packets. *meta length* allows you to match on the packet
-length including the IP header size. If you want to perform heuristics on the
-*ip6 length* field, then disable GRO/GSO.
+Careful with matching on *ip6 length*: The Linux kernel might aggregate several
+packets into one big packet that is larger than MTU. Moreover, if GRO/GSO
+maximum size is larger than 65535 (see man ip-link(8), specifically
+gro_max_size and gso_max_size), then *ip6 length* might be 0 for such
+jumbo packets. *meta length* allows you to match on the packet length including
+the IPv6 header size.
.Using ip6 header expressions
-----------------------------

View File

@ -0,0 +1,48 @@
From 8287f290fe2883de894cd1f4388bed5a6076cc18 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:19 +0200
Subject: [PATCH] doc: libnftables-json: Describe RULESET object
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 454f361434522bbeba32e114a14c336e1ebf20a1
commit 454f361434522bbeba32e114a14c336e1ebf20a1
Author: Phil Sutter <phil@nwl.cc>
Date: Thu Nov 6 12:14:56 2025 +0100
doc: libnftables-json: Describe RULESET object
Document the syntax of this meta-object used by "list" and "flush"
commands only.
Fixes: 872f373dc50f7 ("doc: Add JSON schema documentation")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/libnftables-json.adoc | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc
index 27c96c3..ada53c4 100644
--- a/doc/libnftables-json.adoc
+++ b/doc/libnftables-json.adoc
@@ -200,6 +200,18 @@ Rename a chain. The new name is expected in a dedicated property named
== RULESET ELEMENTS
+=== RULESET
+[verse]
+____
+*{ "ruleset":* 'RULSET_PROPERTIES' *}*
+
+'RULESET_PROPERTIES' := *null* | *{ "family":* 'STRING' *}*
+____
+
+This is a special object for use with *list* and *flush* commands which will
+then operate on either the whole ruleset or the parts of it belonging to the
+given family.
+
=== TABLE
[verse]
____

View File

@ -0,0 +1,41 @@
From e4980d25274d5afc8360b0fd704f5d1b18d22ff6 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:19 +0200
Subject: [PATCH] rule: skip CMD_OBJ_SETELEMS with no elements after set flush
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 8b7a533f8f8b276bfa71dcb306d6857e54015234
commit 8b7a533f8f8b276bfa71dcb306d6857e54015234
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Thu Nov 20 00:41:13 2025 +0100
rule: skip CMD_OBJ_SETELEMS with no elements after set flush
Set declaration + set flush results in a crash because CMD_OBJ_SETELEMS
does not expect no elements. This internal command only shows up if set
contains elements, however, evaluation flushes set content after the set
expansion. Skip this command CMD_OBJ_SETELEMS if set is empty.
Fixes: d3c8051cb767 ("rule: rework CMD_OBJ_SETELEMS logic")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/rule.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/rule.c b/src/rule.c
index 3b75736..0d6186a 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -1461,6 +1461,9 @@ static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
{
struct set *set = cmd->set;
+ if (!set->init)
+ return 0;
+
return __do_add_elements(ctx, cmd, set, set->init, flags);
}

View File

@ -0,0 +1,38 @@
From 00d91f581bd0f8df0796c10aa1bdaaa93097be69 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:19 +0200
Subject: [PATCH] tests: json_echo: Drop rule handle before multi-add
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit ad74cbd63a9782e8285f7198a5055d7947998b7f
commit ad74cbd63a9782e8285f7198a5055d7947998b7f
Author: Phil Sutter <phil@nwl.cc>
Date: Tue Jan 20 22:47:02 2026 +0100
tests: json_echo: Drop rule handle before multi-add
Now that JSON parser respects rule handles in explicit add commands, the
still present rule handle causes an error since the old rule does not
exist anymore.
Fixes: 50b5b71ebeee3 ("parser_json: Rewrite echo support")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/json_echo/run-test.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/tests/json_echo/run-test.py b/tests/json_echo/run-test.py
index a6bdfc6..7217746 100755
--- a/tests/json_echo/run-test.py
+++ b/tests/json_echo/run-test.py
@@ -286,6 +286,7 @@ add_quota["add"]["quota"]["name"] = "q"
do_flush()
print("doing multi add")
+del(add_rule["add"]["rule"]["handle"])
add_multi = [ add_table, add_chain, add_set, add_rule ]
out = do_command(add_multi)

View File

@ -0,0 +1,53 @@
From 62de57c6207d475b952b54a668db811ac0933d89 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:20 +0200
Subject: [PATCH] monitor: fix memleak in setelem cb
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit d2a427c4abfadd2ce51bdab54c40fbf3d990c724
commit d2a427c4abfadd2ce51bdab54c40fbf3d990c724
Author: Florian Westphal <fw@strlen.de>
Date: Wed Jan 21 14:33:21 2026 +0100
monitor: fix memleak in setelem cb
since 4521732ebbf3 ("monitor: missing cache and set handle initialization")
these fields are set via handle_merge(), so don't clobber those
fields in json output case:
==31877==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 16 byte(s) in 2 object(s) allocated from:
#0 0x7f0cb9f29d4b in strdup asan/asan_interceptors.cpp:593
#1 0x7f0cb9b584fd in xstrdup src/utils.c:80
#2 0x7f0cb9b355b3 in handle_merge src/rule.c:127
#3 0x7f0cb9ae12b8 in netlink_events_setelem_cb src/monitor.c:457
Seen when running tests/monitor with asan enabled.
Fixes: 4521732ebbf3 ("monitor: missing cache and set handle initialization")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/monitor.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/src/monitor.c b/src/monitor.c
index 87ff2a3..0b00207 100644
--- a/src/monitor.c
+++ b/src/monitor.c
@@ -476,13 +476,7 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type,
nft_mon_print(monh, "\n");
break;
case NFTNL_OUTPUT_JSON:
- dummyset->handle.family = family;
- dummyset->handle.set.name = setname;
- dummyset->handle.table.name = table;
monitor_print_element_json(monh, cmd, dummyset);
- /* prevent set_free() from trying to free those */
- dummyset->handle.set.name = NULL;
- dummyset->handle.table.name = NULL;
if (!nft_output_echo(&monh->ctx->nft->output))
nft_mon_print(monh, "\n");
break;

View File

@ -0,0 +1,69 @@
From 5cf2ee0c11f8d985b7b060db9ddf769f7e8e8ed0 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:20 +0200
Subject: [PATCH] segtree: Fix range aggregation on Big Endian
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit e8b17865833b8c2b9f31a4dfb98069651572494d
commit e8b17865833b8c2b9f31a4dfb98069651572494d
Author: Phil Sutter <phil@nwl.cc>
Date: Tue Oct 21 18:36:10 2025 +0200
segtree: Fix range aggregation on Big Endian
Interface name wildcards are received as ranges from prefix with zero
padding to prefix with ones padding. E.g. with "abcd*" (in hex):
61626364000000000000000000000000 - 61626364ffffffffffffffffffffffff
The faulty code tries to export the prefix from the lower boundary (r1)
into a buffer, append "*" and allocate a constant expression from the
resulting string. This does not work on Big Endian though:
mpz_export_data() seems to zero-pad data upon export and not necessarily
respect the passed length value. Moreover, this padding appears in the
first bytes of the buffer. The amount of padding seems illogical, too:
While a 6B prefix causes 2B padding and 8B prefix no padding, 10B prefix
causes 4B padding and 12B prefix even 8B padding.
Work around the odd behaviour by exporting the full data into a larger
buffer.
A similar issue is caused by increasing the constant expression's length
to match the upper boundary data length: Data export when printing puts
the padding upfront, so the resulting string starts with NUL-chars.
Since this length adjustment seems not to have any effect in practice,
just drop it.
Fixes: 88b2345a215ef ("segtree: add pretty-print support for wildcard strings in concatenated sets")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/segtree.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/segtree.c b/src/segtree.c
index a3a0fb4..2b4c4af 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -408,16 +408,16 @@ void concat_range_aggregate(struct expr *set)
if (prefix_len >= 0 &&
(prefix_len % BITS_PER_BYTE) == 0 &&
string_type) {
+ unsigned int r1len = div_round_up(r1->len, BITS_PER_BYTE);
unsigned int str_len = prefix_len / BITS_PER_BYTE;
- char data[str_len + 2];
+ char data[r1len + 1] = {};
- mpz_export_data(data, r1->value, BYTEORDER_HOST_ENDIAN, str_len);
+ mpz_export_data(data, r1->value, BYTEORDER_HOST_ENDIAN, r1len);
data[str_len] = '*';
tmp = constant_expr_alloc(&r1->location, r1->dtype,
BYTEORDER_HOST_ENDIAN,
(str_len + 1) * BITS_PER_BYTE, data);
- tmp->len = r2->len;
list_replace(&r2->list, &tmp->list);
r2_next = tmp->list.next;
expr_free(r2);

View File

@ -0,0 +1,244 @@
From 01bd864e3c9daf0121516109af0e7aca1ebfae38 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:20 +0200
Subject: [PATCH] mergesort: Fix sorting of string values
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 340d904a974ed206fcc4d8ca32540762fd8e59e0
Conflicts: Dropped changes to non-existent dump files
commit 340d904a974ed206fcc4d8ca32540762fd8e59e0
Author: Phil Sutter <phil@nwl.cc>
Date: Thu Nov 13 00:03:37 2025 +0100
mergesort: Fix sorting of string values
Sorting order was obviously wrong, e.g. "ppp0" ordered before "eth1".
Moreover, this happened on Little Endian only so sorting order actually
depended on host's byteorder. By reimporting string values as Big
Endian, both issues are fixed: On one hand, GMP-internal byteorder no
longer depends on host's byteorder, on the other comparing strings
really starts with the first character, not the last.
Fixes: 14ee0a979b622 ("src: sort set elements in netlink_get_setelems()")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/mergesort.c | 7 +++
tests/py/any/meta.t.json.output | 54 -------------------
tests/py/any/queue.t.json.output | 4 +-
tests/py/inet/osf.t.json.output | 54 +++++++++++++++++++
.../shell/testcases/maps/dumps/0012map_0.nft | 8 +--
.../sets/dumps/sets_with_ifnames.nft | 2 +-
6 files changed, 68 insertions(+), 61 deletions(-)
diff --git a/src/mergesort.c b/src/mergesort.c
index 4d0e280..4157b5b 100644
--- a/src/mergesort.c
+++ b/src/mergesort.c
@@ -37,6 +37,13 @@ static mpz_srcptr expr_msort_value(const struct expr *expr, mpz_t value)
case EXPR_RANGE:
return expr_msort_value(expr->left, value);
case EXPR_VALUE:
+ if (expr_basetype(expr)->type == TYPE_STRING) {
+ char buf[expr->len];
+
+ mpz_export_data(buf, expr->value, BYTEORDER_HOST_ENDIAN, expr->len);
+ mpz_import_data(value, buf, BYTEORDER_BIG_ENDIAN, expr->len);
+ return value;
+ }
return expr->value;
case EXPR_CONCAT:
concat_expr_msort_value(expr, value);
diff --git a/tests/py/any/meta.t.json.output b/tests/py/any/meta.t.json.output
index 4e9e669..329073e 100644
--- a/tests/py/any/meta.t.json.output
+++ b/tests/py/any/meta.t.json.output
@@ -233,60 +233,6 @@
}
]
-# meta iifname {"dummy0", "lo"}
-[
- {
- "match": {
- "left": {
- "meta": { "key": "iifname" }
- },
- "op": "==",
- "right": {
- "set": [
- "lo",
- "dummy0"
- ]
- }
- }
- }
-]
-
-# meta iifname != {"dummy0", "lo"}
-[
- {
- "match": {
- "left": {
- "meta": { "key": "iifname" }
- },
- "op": "!=",
- "right": {
- "set": [
- "lo",
- "dummy0"
- ]
- }
- }
- }
-]
-
-# meta oifname { "dummy0", "lo"}
-[
- {
- "match": {
- "left": {
- "meta": { "key": "oifname" }
- },
- "op": "==",
- "right": {
- "set": [
- "lo",
- "dummy0"
- ]
- }
- }
- }
-]
-
# meta skuid {"bin", "root", "daemon"} accept
[
{
diff --git a/tests/py/any/queue.t.json.output b/tests/py/any/queue.t.json.output
index ea37223..90670cc 100644
--- a/tests/py/any/queue.t.json.output
+++ b/tests/py/any/queue.t.json.output
@@ -104,11 +104,11 @@
0
],
[
- "ppp0",
+ "eth1",
2
],
[
- "eth1",
+ "ppp0",
2
]
]
diff --git a/tests/py/inet/osf.t.json.output b/tests/py/inet/osf.t.json.output
index 922e395..77ca7e3 100644
--- a/tests/py/inet/osf.t.json.output
+++ b/tests/py/inet/osf.t.json.output
@@ -18,6 +18,26 @@
}
]
+# osf version { "Windows:XP", "MacOs:Sierra" }
+[
+ {
+ "match": {
+ "left": {
+ "osf": {
+ "key": "version"
+ }
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ "MacOs:Sierra",
+ "Windows:XP"
+ ]
+ }
+ }
+ }
+]
+
# ct mark set osf name map { "Windows" : 0x00000001, "MacOs" : 0x00000002 }
[
{
@@ -51,3 +71,37 @@
}
}
]
+
+# ct mark set osf version map { "Windows:XP" : 0x00000003, "MacOs:Sierra" : 0x00000004 }
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ "MacOs:Sierra",
+ 4
+ ],
+ [
+ "Windows:XP",
+ 3
+ ]
+ ]
+ },
+ "key": {
+ "osf": {
+ "key": "version"
+ }
+ }
+ }
+ }
+ }
+ }
+]
diff --git a/tests/shell/testcases/maps/dumps/0012map_0.nft b/tests/shell/testcases/maps/dumps/0012map_0.nft
index 895490c..b199a00 100644
--- a/tests/shell/testcases/maps/dumps/0012map_0.nft
+++ b/tests/shell/testcases/maps/dumps/0012map_0.nft
@@ -1,9 +1,9 @@
table ip x {
map z {
type ifname : verdict
- elements = { "lo" : accept,
- "eth0" : drop,
- "eth1" : drop }
+ elements = { "eth0" : drop,
+ "eth1" : drop,
+ "lo" : accept }
}
map w {
@@ -14,7 +14,7 @@ table ip x {
}
chain y {
- iifname vmap { "lo" : accept, "eth0" : drop, "eth1" : drop }
+ iifname vmap { "eth0" : drop, "eth1" : drop, "lo" : accept }
}
chain k {
diff --git a/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft b/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft
index 77a8baf..8abca03 100644
--- a/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft
+++ b/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft
@@ -39,7 +39,7 @@ table inet testifsets {
chain v4icmp {
iifname @simple counter packets 0 bytes 0
iifname @simple_wild counter packets 0 bytes 0
- iifname { "eth0", "abcdef0" } counter packets 0 bytes 0
+ iifname { "abcdef0", "eth0" } counter packets 0 bytes 0
iifname { "abcdef*", "eth0" } counter packets 0 bytes 0
iifname vmap @map_wild
}

View File

@ -0,0 +1,84 @@
From c0a28182c114262bc1d4f4557091b0f0ab71c656 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:36 +0200
Subject: [PATCH] optimize: expand expression list when merging into
concatenation
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 0d17d28bb06bf2a04862d5cd879a14bcb9a2d2dc
Conflicts: Drop changes to non-existent dump file
commit 0d17d28bb06bf2a04862d5cd879a14bcb9a2d2dc
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Apr 1 18:11:45 2025 +0200
optimize: expand expression list when merging into concatenation
The following rules:
udp dport 137 ct state new,untracked accept
udp dport 138 ct state new,untracked accept
results in:
nft: src/optimize.c:670: __merge_concat: Assertion `0' failed.
The logic to expand to the new,untracked list in the concatenation is
missing.
Fixes: 187c6d01d357 ("optimize: expand implicit set element when merging into concatenation")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/optimize.c | 10 ++++++++++
.../optimizations/dumps/merge_stmts_concat.nft | 1 +
tests/shell/testcases/optimizations/merge_stmts_concat | 2 ++
3 files changed, 13 insertions(+)
diff --git a/src/optimize.c b/src/optimize.c
index 7b4d036..dd96fc7 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -635,6 +635,16 @@ static void __merge_concat(const struct optimize_ctx *ctx, uint32_t i,
clone = expr_clone(stmt_a->expr->right);
compound_expr_add(concat, clone);
break;
+ case EXPR_LIST:
+ list_for_each_entry(expr, &stmt_a->expr->right->expressions, list) {
+ concat_clone = expr_clone(concat);
+ clone = expr_clone(expr);
+ compound_expr_add(concat_clone, clone);
+ list_add_tail(&concat_clone->list, &pending_list);
+ }
+ list_del(&concat->list);
+ expr_free(concat);
+ break;
default:
assert(0);
break;
diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft
index f56cea1..d00ac41 100644
--- a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft
+++ b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft
@@ -2,6 +2,7 @@ table ip x {
chain y {
iifname . ip saddr . ip daddr { "eth1" . 1.1.1.1 . 2.2.2.3, "eth1" . 1.1.1.2 . 2.2.2.4, "eth1" . 1.1.1.2 . 2.2.3.0/24, "eth1" . 1.1.1.2 . 2.2.4.0-2.2.4.10, "eth2" . 1.1.1.3 . 2.2.2.5 } accept
ip protocol . th dport { tcp . 22, udp . 67 }
+ udp dport . ct state { 137 . new, 138 . new, 137 . untracked, 138 . untracked } accept
}
chain c1 {
diff --git a/tests/shell/testcases/optimizations/merge_stmts_concat b/tests/shell/testcases/optimizations/merge_stmts_concat
index 9679d86..1fd1a30 100755
--- a/tests/shell/testcases/optimizations/merge_stmts_concat
+++ b/tests/shell/testcases/optimizations/merge_stmts_concat
@@ -10,6 +10,8 @@ RULESET="table ip x {
meta iifname eth1 ip saddr 1.1.1.2 ip daddr 2.2.4.0-2.2.4.10 accept
meta iifname eth2 ip saddr 1.1.1.3 ip daddr 2.2.2.5 accept
ip protocol . th dport { tcp . 22, udp . 67 }
+ udp dport 137 ct state new,untracked accept
+ udp dport 138 ct state new,untracked accept
}
}"

View File

@ -0,0 +1,228 @@
From b676be66a623fd718cb7bc5875e9193e8c4f0b35 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:32:51 +0200
Subject: [PATCH] mergesort: Align concatenation sort order with Big Endian
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 0d819bcbbf36d324b5344ac29bd82a5017098c4b
Conflicts: Drop changes to non-existent dumps and test cases
commit 0d819bcbbf36d324b5344ac29bd82a5017098c4b
Author: Phil Sutter <phil@nwl.cc>
Date: Thu Nov 13 00:14:43 2025 +0100
mergesort: Align concatenation sort order with Big Endian
By exporting all concat components in a way independent from host
byteorder and importing that blob of data in the same way aligns sort
order between hosts of different Endianness.
Fixes: 741a06ac15d2b ("mergesort: find base value expression type via recursion")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/mergesort.c | 4 ++--
tests/py/any/ct.t.json.output | 20 +++++++++----------
.../testcases/maps/dumps/typeof_integer_0.nft | 4 ++--
.../nft-f/dumps/0012different_defines_0.nft | 2 +-
.../optimizations/dumps/merge_reject.nft | 4 ++--
.../dumps/merge_stmts_concat.nft | 8 ++++----
.../dumps/merge_stmts_concat_vmap.nft | 2 +-
.../sets/dumps/0029named_ifname_dtype_0.nft | 4 ++--
.../dumps/0037_set_with_inet_service_0.nft | 8 ++++----
9 files changed, 28 insertions(+), 28 deletions(-)
diff --git a/src/mergesort.c b/src/mergesort.c
index 4157b5b..5c4cfeb 100644
--- a/src/mergesort.c
+++ b/src/mergesort.c
@@ -20,11 +20,11 @@ static void concat_expr_msort_value(const struct expr *expr, mpz_t value)
list_for_each_entry(i, &expr->expressions, list) {
ilen = div_round_up(i->len, BITS_PER_BYTE);
- mpz_export_data(data + len, i->value, i->byteorder, ilen);
+ mpz_export_data(data + len, i->value, BYTEORDER_BIG_ENDIAN, ilen);
len += ilen;
}
- mpz_import_data(value, data, BYTEORDER_HOST_ENDIAN, len);
+ mpz_import_data(value, data, BYTEORDER_BIG_ENDIAN, len);
}
static mpz_srcptr expr_msort_value(const struct expr *expr, mpz_t value)
diff --git a/tests/py/any/ct.t.json.output b/tests/py/any/ct.t.json.output
index 82634c2..3f79593 100644
--- a/tests/py/any/ct.t.json.output
+++ b/tests/py/any/ct.t.json.output
@@ -494,14 +494,14 @@
"set": [
{
"concat": [
- "new",
- 305419896
+ "established",
+ 309876276
]
},
{
"concat": [
- "established",
- 309876276
+ "new",
+ 305419896
]
},
{
@@ -578,23 +578,23 @@
[
{
"concat": [
- "new",
- 305419896
+ "established",
+ 2271560481
]
},
{
- "drop": null
+ "accept": null
}
],
[
{
"concat": [
- "established",
- 2271560481
+ "new",
+ 305419896
]
},
{
- "accept": null
+ "drop": null
}
]
]
diff --git a/tests/shell/testcases/maps/dumps/typeof_integer_0.nft b/tests/shell/testcases/maps/dumps/typeof_integer_0.nft
index 19c24fe..7bd7daa 100644
--- a/tests/shell/testcases/maps/dumps/typeof_integer_0.nft
+++ b/tests/shell/testcases/maps/dumps/typeof_integer_0.nft
@@ -8,8 +8,8 @@ table inet t {
map m2 {
typeof udp length . @ih,32,32 : verdict
- elements = { 30 . 0x1e : drop,
- 20 . 0x24 : accept }
+ elements = { 20 . 0x24 : accept,
+ 30 . 0x1e : drop }
}
chain c {
diff --git a/tests/shell/testcases/nft-f/dumps/0012different_defines_0.nft b/tests/shell/testcases/nft-f/dumps/0012different_defines_0.nft
index 4734b2f..a6e16e7 100644
--- a/tests/shell/testcases/nft-f/dumps/0012different_defines_0.nft
+++ b/tests/shell/testcases/nft-f/dumps/0012different_defines_0.nft
@@ -8,7 +8,7 @@ table inet t {
ip6 daddr fe0::1 ip6 saddr fe0::2
ip saddr vmap { 10.0.0.0 : drop, 10.0.0.2 : accept }
ip6 daddr vmap { fe0::1 : drop, fe0::2 : accept }
- ip6 saddr . ip6 nexthdr { fe0::2 . tcp, fe0::1 . udp }
+ ip6 saddr . ip6 nexthdr { fe0::1 . udp, fe0::2 . tcp }
ip daddr . iif vmap { 10.0.0.0 . "lo" : accept }
tcp dport 100-222
udp dport vmap { 100-222 : accept }
diff --git a/tests/shell/testcases/optimizations/dumps/merge_reject.nft b/tests/shell/testcases/optimizations/dumps/merge_reject.nft
index c29ad6d..1727d02 100644
--- a/tests/shell/testcases/optimizations/dumps/merge_reject.nft
+++ b/tests/shell/testcases/optimizations/dumps/merge_reject.nft
@@ -1,13 +1,13 @@
table ip x {
chain y {
ip daddr 172.30.33.70 tcp dport 3306 counter packets 0 bytes 0 drop
- meta l4proto . ip daddr . tcp dport { tcp . 172.30.238.117 . 8080, tcp . 172.30.33.71 . 3306, tcp . 172.30.254.251 . 3306 } counter packets 0 bytes 0 reject
+ meta l4proto . ip daddr . tcp dport { tcp . 172.30.33.71 . 3306, tcp . 172.30.238.117 . 8080, tcp . 172.30.254.251 . 3306 } counter packets 0 bytes 0 reject
ip daddr 172.30.254.252 tcp dport 3306 counter packets 0 bytes 0 reject with tcp reset
}
}
table ip6 x {
chain y {
- meta l4proto . ip6 daddr . tcp dport { tcp . aaaa::3 . 8080, tcp . aaaa::2 . 3306, tcp . aaaa::4 . 3306 } counter packets 0 bytes 0 reject
+ meta l4proto . ip6 daddr . tcp dport { tcp . aaaa::2 . 3306, tcp . aaaa::3 . 8080, tcp . aaaa::4 . 3306 } counter packets 0 bytes 0 reject
ip6 daddr aaaa::5 tcp dport 3306 counter packets 0 bytes 0 reject with tcp reset
}
}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft
index d00ac41..6150258 100644
--- a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft
+++ b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft
@@ -2,18 +2,18 @@ table ip x {
chain y {
iifname . ip saddr . ip daddr { "eth1" . 1.1.1.1 . 2.2.2.3, "eth1" . 1.1.1.2 . 2.2.2.4, "eth1" . 1.1.1.2 . 2.2.3.0/24, "eth1" . 1.1.1.2 . 2.2.4.0-2.2.4.10, "eth2" . 1.1.1.3 . 2.2.2.5 } accept
ip protocol . th dport { tcp . 22, udp . 67 }
- udp dport . ct state { 137 . new, 138 . new, 137 . untracked, 138 . untracked } accept
+ udp dport . ct state { 137 . new, 137 . untracked, 138 . new, 138 . untracked } accept
}
chain c1 {
- udp dport . iifname { 51820 . "foo", 514 . "bar", 67 . "bar" } accept
+ udp dport . iifname { 67 . "bar", 514 . "bar", 51820 . "foo" } accept
}
chain c2 {
- udp dport . iifname { 100 . "foo", 51820 . "foo", 514 . "bar", 67 . "bar" } accept
+ udp dport . iifname { 67 . "bar", 100 . "foo", 514 . "bar", 51820 . "foo" } accept
}
chain c3 {
- udp dport . iifname { 100 . "foo", 51820 . "foo", 514 . "bar", 67 . "bar", 100 . "test", 51820 . "test" } accept
+ udp dport . iifname { 67 . "bar", 100 . "foo", 100 . "test", 514 . "bar", 51820 . "foo", 51820 . "test" } accept
}
}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft
index 780aa09..81abb99 100644
--- a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft
+++ b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft
@@ -1,6 +1,6 @@
table ip x {
chain x {
- meta pkttype . udp dport vmap { broadcast . 547 : accept, broadcast . 67 : accept, multicast . 1900 : drop }
+ meta pkttype . udp dport vmap { broadcast . 67 : accept, broadcast . 547 : accept, multicast . 1900 : drop }
}
chain y {
diff --git a/tests/shell/testcases/sets/dumps/0029named_ifname_dtype_0.nft b/tests/shell/testcases/sets/dumps/0029named_ifname_dtype_0.nft
index 55cd4f2..0ea2926 100644
--- a/tests/shell/testcases/sets/dumps/0029named_ifname_dtype_0.nft
+++ b/tests/shell/testcases/sets/dumps/0029named_ifname_dtype_0.nft
@@ -12,8 +12,8 @@ table inet t {
type inet_service . ifname
elements = { 22 . "eth0",
80 . "eth0",
- 81 . "eth0",
- 80 . "eth1" }
+ 80 . "eth1",
+ 81 . "eth0" }
}
set nv {
diff --git a/tests/shell/testcases/sets/dumps/0037_set_with_inet_service_0.nft b/tests/shell/testcases/sets/dumps/0037_set_with_inet_service_0.nft
index 68b1f7b..0e85f7c 100644
--- a/tests/shell/testcases/sets/dumps/0037_set_with_inet_service_0.nft
+++ b/tests/shell/testcases/sets/dumps/0037_set_with_inet_service_0.nft
@@ -1,11 +1,11 @@
table inet filter {
set myset {
type ipv4_addr . inet_proto . inet_service
- elements = { 192.168.0.113 . tcp . 22,
- 192.168.0.12 . tcp . 53,
- 192.168.0.12 . udp . 53,
+ elements = { 192.168.0.12 . tcp . 53,
192.168.0.12 . tcp . 80,
- 192.168.0.13 . tcp . 80 }
+ 192.168.0.12 . udp . 53,
+ 192.168.0.13 . tcp . 80,
+ 192.168.0.113 . tcp . 22 }
}
chain forward {

View File

@ -0,0 +1,41 @@
From d4c926c17ef1383d37d1c101f12b0d4b5712546c Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:33:22 +0200
Subject: [PATCH] json: complete multi-statement set element support
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 14dc90737a57bd1c6dc0ebc3eb30ab3090cc0afb
Conflicts: Drop changes to non-existent test cases
commit 14dc90737a57bd1c6dc0ebc3eb30ab3090cc0afb
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Mon Feb 16 12:26:45 2026 +0100
json: complete multi-statement set element support
Remove artificial limitation on the maximum number of statements per
element in listings.
Moreover, update tests/shell which are currently incorrect.
Fixes: e6d1d0d61195 ("src: add set element multi-statement support")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/json.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/json.c b/src/json.c
index fc2e962..0d3b535 100644
--- a/src/json.c
+++ b/src/json.c
@@ -705,8 +705,6 @@ json_t *set_elem_expr_json(const struct expr *expr, struct output_ctx *octx)
/* XXX: detect and complain about clashes? */
json_object_update_missing(root, tmp);
json_decref(tmp);
- /* TODO: only one statement per element. */
- break;
}
return nft_json_pack("{s:o}", "elem", root);
}

View File

@ -0,0 +1,117 @@
From cdcae37b8fc86c232ccb64e9d6feb98b455abf39 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:33:37 +0200
Subject: [PATCH] cache: Respect family in all list commands
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit f4a5334f503dc9d99bce45020c105b6812dd15f0
commit f4a5334f503dc9d99bce45020c105b6812dd15f0
Author: Phil Sutter <phil@nwl.cc>
Date: Sat Feb 14 14:58:03 2026 +0100
cache: Respect family in all list commands
Some list commands did not set filter->list.family even if one was given
on command line, fix this.
Fixes: b3ed8fd8c9f33 ("cache: missing family in cache filtering")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Tested-by: Eric Garver <eric@garver.life>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/cache.c | 6 ++-
tests/shell/testcases/listing/cache_filters | 46 +++++++++++++++++++++
2 files changed, 51 insertions(+), 1 deletion(-)
create mode 100755 tests/shell/testcases/listing/cache_filters
diff --git a/src/cache.c b/src/cache.c
index 2330112..40b6c01 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -234,10 +234,12 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
flags |= NFT_CACHE_FULL;
break;
case CMD_OBJ_CHAINS:
+ filter->list.family = cmd->handle.family;
flags |= NFT_CACHE_TABLE | NFT_CACHE_CHAIN;
break;
case CMD_OBJ_SETS:
case CMD_OBJ_MAPS:
+ filter->list.family = cmd->handle.family;
flags |= NFT_CACHE_TABLE | NFT_CACHE_SET;
if (!nft_output_terse(&nft->output))
flags |= NFT_CACHE_SETELEM;
@@ -246,15 +248,17 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
if (filter &&
cmd->handle.table.name &&
cmd->handle.flowtable.name) {
- filter->list.family = cmd->handle.family;
filter->list.table = cmd->handle.table.name;
filter->list.ft = cmd->handle.flowtable.name;
}
/* fall through */
case CMD_OBJ_FLOWTABLES:
+ filter->list.family = cmd->handle.family;
flags |= NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE;
break;
case CMD_OBJ_RULESET:
+ filter->list.family = cmd->handle.family;
+ /* fall through */
default:
flags |= NFT_CACHE_FULL;
break;
diff --git a/tests/shell/testcases/listing/cache_filters b/tests/shell/testcases/listing/cache_filters
new file mode 100755
index 0000000..37c8f84
--- /dev/null
+++ b/tests/shell/testcases/listing/cache_filters
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+set -e
+
+fail() {
+ echo "$*"
+ exit 1
+}
+
+$NFT -f - <<EOF
+table ip ip_t {
+ flowtable ip_t_ft {
+ hook ingress priority 0
+ }
+ set ip_t_s {
+ type inet_service
+ elements = { 22, 80, 443 }
+ }
+ chain ip_t_c {
+ tcp dport 22 accept
+ }
+ chain ip_t_c2 {
+ }
+}
+EOF
+
+$NFT --debug=netlink list ruleset | \
+ grep -q 'payload load' || fail "broken list ruleset"
+$NFT --debug=netlink list ruleset ip6 | \
+ grep -q 'payload load' && fail "broken list ruleset family filter"
+
+$NFT --debug=netlink list chains | \
+ grep -q 'ip ip_t ip_t_c' || fail "broken list chains"
+$NFT --debug=netlink list chains ip6 | \
+ grep -q 'ip ip_t ip_t_c' && fail "broken list chains family filter"
+
+$NFT --debug=netlink list sets | \
+ grep -q 'family 2 ip_t_s ip_t' || fail "broken list sets"
+$NFT --debug=netlink list sets ip6 | \
+ grep -q 'family 2 ip_t_s ip_t' && fail "broken list sets family filter"
+
+$NFT --debug=netlink list flowtables | \
+ grep -q 'flow table ip_t ip_t_ft' || fail "broken list flowtables"
+$NFT --debug=netlink list flowtables ip6 | \
+ grep -q 'flow table ip_t ip_t_ft' && fail "broken list flowtables family filter"
+exit 0

View File

@ -0,0 +1,45 @@
From 78bb602d61f6577540807495f5aa72e61c0fb1e4 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:33:37 +0200
Subject: [PATCH] cache: Relax chain_cache_dump filter application
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 18cd6edf86139621dc6bfe0f17a391e46218c027
commit 18cd6edf86139621dc6bfe0f17a391e46218c027
Author: Phil Sutter <phil@nwl.cc>
Date: Fri Mar 6 17:02:17 2026 +0100
cache: Relax chain_cache_dump filter application
While populating chain cache, a filter was only effective if it limited
fetching to both a table and a chain. Make it apply to 'list chains'
command as well which at most specifies a family and table.
Since the code is OK with filter->list fields being NULL, merely check
for filter to be non-NULL (which is the case if nft_cache_update() is
called by nft_cmd_enoent_chain()).
Fixes: 17297d1acbbf ("cache: Filter chain list on kernel side")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Tested-by: Eric Garver <eric@garver.life>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/cache.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/cache.c b/src/cache.c
index 40b6c01..b8c065d 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -559,7 +559,7 @@ chain_cache_dump(struct netlink_ctx *ctx,
const char *chain = NULL;
int family = NFPROTO_UNSPEC;
- if (filter && filter->list.table && filter->list.chain) {
+ if (filter) {
family = filter->list.family;
table = filter->list.table;
chain = filter->list.chain;

View File

@ -0,0 +1,62 @@
From 8f721b98622ac4c68a2ad30b610037016ebc0312 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:33:37 +0200
Subject: [PATCH] cache: Filter for table when listing sets or maps
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit b7225cda2a4388f38293fba281fc001dff70a16e
commit b7225cda2a4388f38293fba281fc001dff70a16e
Author: Phil Sutter <phil@nwl.cc>
Date: Sat Feb 14 15:02:04 2026 +0100
cache: Filter for table when listing sets or maps
Respect an optionally specified table name to filter listed sets or maps
to by populating the filter accordingly.
Fixes: a1a6b0a5c3c4 ("cache: finer grain cache population for list commands")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Tested-by: Eric Garver <eric@garver.life>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/cache.c | 1 +
tests/shell/testcases/listing/cache_filters | 4 ++++
2 files changed, 5 insertions(+)
diff --git a/src/cache.c b/src/cache.c
index b8c065d..d448bb9 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -240,6 +240,7 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
case CMD_OBJ_SETS:
case CMD_OBJ_MAPS:
filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
flags |= NFT_CACHE_TABLE | NFT_CACHE_SET;
if (!nft_output_terse(&nft->output))
flags |= NFT_CACHE_SETELEM;
diff --git a/tests/shell/testcases/listing/cache_filters b/tests/shell/testcases/listing/cache_filters
index 37c8f84..7a89330 100755
--- a/tests/shell/testcases/listing/cache_filters
+++ b/tests/shell/testcases/listing/cache_filters
@@ -22,6 +22,8 @@ table ip ip_t {
chain ip_t_c2 {
}
}
+table ip ip_t2 {
+}
EOF
$NFT --debug=netlink list ruleset | \
@@ -38,6 +40,8 @@ $NFT --debug=netlink list sets | \
grep -q 'family 2 ip_t_s ip_t' || fail "broken list sets"
$NFT --debug=netlink list sets ip6 | \
grep -q 'family 2 ip_t_s ip_t' && fail "broken list sets family filter"
+$NFT --debug=netlink list sets ip ip_t2 | \
+ grep -q 'family 2 ip_t_s ip_t' && fail "broken list sets table filter"
$NFT --debug=netlink list flowtables | \
grep -q 'flow table ip_t ip_t_ft' || fail "broken list flowtables"

View File

@ -0,0 +1,52 @@
From e778b997601e2c23cf918905873e88ba22c8c06a Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:33:37 +0200
Subject: [PATCH] cache: Filter for table when listing flowtables
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit fa5d7fba4bcc9b7c5c9ec37bbe8d3df3f8298745
commit fa5d7fba4bcc9b7c5c9ec37bbe8d3df3f8298745
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Feb 25 21:07:32 2026 +0100
cache: Filter for table when listing flowtables
Respect an optionally specified table name to filter listed flowtables
to by populating the filter accordingly.
Fixes: a1a6b0a5c3c4 ("cache: finer grain cache population for list commands")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Tested-by: Eric Garver <eric@garver.life>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/cache.c | 1 +
tests/shell/testcases/listing/cache_filters | 3 +++
2 files changed, 4 insertions(+)
diff --git a/src/cache.c b/src/cache.c
index d448bb9..dd159ea 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -255,6 +255,7 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
/* fall through */
case CMD_OBJ_FLOWTABLES:
filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
flags |= NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE;
break;
case CMD_OBJ_RULESET:
diff --git a/tests/shell/testcases/listing/cache_filters b/tests/shell/testcases/listing/cache_filters
index 7a89330..e3d0e5e 100755
--- a/tests/shell/testcases/listing/cache_filters
+++ b/tests/shell/testcases/listing/cache_filters
@@ -47,4 +47,7 @@ $NFT --debug=netlink list flowtables | \
grep -q 'flow table ip_t ip_t_ft' || fail "broken list flowtables"
$NFT --debug=netlink list flowtables ip6 | \
grep -q 'flow table ip_t ip_t_ft' && fail "broken list flowtables family filter"
+$NFT --debug=netlink list flowtables ip ip_t2 | \
+ grep -q 'flow table ip_t ip_t_ft' && fail "broken list flowtables table filter"
+
exit 0

View File

@ -0,0 +1,42 @@
From 3c99a1ec75bdc3adcecfbc150a842aace7ab9af3 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:33:37 +0200
Subject: [PATCH] segtree: Fix for variable-sized object may not be initialized
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 96ba00889f9d4932d8bc2b08461f05793a9a2ac4
commit 96ba00889f9d4932d8bc2b08461f05793a9a2ac4
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Mar 18 22:05:34 2026 +0100
segtree: Fix for variable-sized object may not be initialized
Seen with gcc-11.5.0 on an aarch64 machine, build failed. Looking at the
code, r1len (or r1->len, actually) really seems variable. So use
memset() and fix build for that older compiler version at least.
Fixes: e8b17865833b8 ("segtree: Fix range aggregation on Big Endian")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Acked-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/segtree.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/segtree.c b/src/segtree.c
index 2b4c4af..05664d1 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -410,8 +410,9 @@ void concat_range_aggregate(struct expr *set)
string_type) {
unsigned int r1len = div_round_up(r1->len, BITS_PER_BYTE);
unsigned int str_len = prefix_len / BITS_PER_BYTE;
- char data[r1len + 1] = {};
+ char data[r1len + 1];
+ memset(data, 0, r1len + 1);
mpz_export_data(data, r1->value, BYTEORDER_HOST_ENDIAN, r1len);
data[str_len] = '*';

View File

@ -0,0 +1,39 @@
From a0cdde12907855d417322df4acf177bfd9af2236 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:33:38 +0200
Subject: [PATCH] mnl: Fix ordering of hooks in 'list hooks' output
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 2f3b8d37c2e46f2ddf086c50affcd224b1f7f7dd
commit 2f3b8d37c2e46f2ddf086c50affcd224b1f7f7dd
Author: Phil Sutter <phil@nwl.cc>
Date: Fri Mar 20 13:30:52 2026 +0100
mnl: Fix ordering of hooks in 'list hooks' output
Hooks with same family, basehook and priority were inadvertently
inserted into the list in reverse ordering, fix that.
Suggested-by: Florian Westphal <fw@strlen.de>
Fixes: b98fee20bfe23 ("mnl: revisit hook listing")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/mnl.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/mnl.c b/src/mnl.c
index 96a86aa..2b7e1cf 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -2199,7 +2199,7 @@ static void basehook_list_add_tail(struct basehook *b, struct list_head *head)
continue;
if (hook->num != b->num)
continue;
- if (hook->prio < b->prio)
+ if (hook->prio <= b->prio)
continue;
list_add(&b->list, &hook->list);

View File

@ -0,0 +1,54 @@
From 7a7dbc549c9297f93c393eb70c01c24c15cc36be Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:33:38 +0200
Subject: [PATCH] cache: honor -c/--check for reset commands
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 25f2ff13cf6bc84a16febddcf8c3eda2018c7ece
Conflicts: Adjust to missing commit 68c8fb5f7c988
("cache: accumulate flags in batch")
commit 25f2ff13cf6bc84a16febddcf8c3eda2018c7ece
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Mon May 11 21:51:43 2026 +0200
cache: honor -c/--check for reset commands
Currently:
nft -c reset rules ip x
ignores -c/--check.
The reset and list commands use the netlink GET/DUMP nfnetlink API which
provides no check semantics, compared to the NEW/DELETE nfnetlink batch
API which indeed does.
Emulate -c/--check for the reset command by handling this as a list
command, so the state of the objects is just listed, not reset. This
allows to check for presence and dump the content of the objects.
Fixes: dbff26bfba83 ("cache: consolidate reset command")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/cache.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/cache.c b/src/cache.c
index dd159ea..eef5957 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -447,7 +447,10 @@ int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds,
flags = evaluate_cache_get(cmd, flags);
break;
case CMD_RESET:
- flags |= evaluate_cache_reset(cmd, flags, filter);
+ if (nft->check)
+ flags |= evaluate_cache_list(nft, cmd, flags, filter);
+ else
+ flags |= evaluate_cache_reset(cmd, flags, filter);
break;
case CMD_LIST:
flags |= evaluate_cache_list(nft, cmd, flags, filter);

View File

@ -0,0 +1,56 @@
From 0d1a648aa0a32dae21323c4bdcdc8d5184663ba2 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:34:06 +0200
Subject: [PATCH] parser_json: fix crash in json_parse_set_stmt_list
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 26d9cbefb10e6bc3765df7e9e7a4fc3b951a80f3
commit 26d9cbefb10e6bc3765df7e9e7a4fc3b951a80f3
Author: Sebastian Walz (sivizius) <sebastian.walz@secunet.com>
Date: Tue Aug 20 00:09:26 2024 +0200
parser_json: fix crash in json_parse_set_stmt_list
Due to missing `NULL`-check, there will be a segfault for invalid statements.
Fixes: 07958ec53830 ("json: add set statement list support")
Signed-off-by: Sebastian Walz (sivizius) <sebastian.walz@secunet.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_json.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/src/parser_json.c b/src/parser_json.c
index cce5ee2..7b75a88 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -2317,7 +2317,7 @@ static void json_parse_set_stmt_list(struct json_ctx *ctx,
json_t *stmt_json)
{
struct list_head *head;
- struct stmt *tmp;
+ struct stmt *stmt;
json_t *value;
size_t index;
@@ -2329,9 +2329,14 @@ static void json_parse_set_stmt_list(struct json_ctx *ctx,
head = stmt_list;
json_array_foreach(stmt_json, index, value) {
- tmp = json_parse_stmt(ctx, value);
- list_add(&tmp->list, head);
- head = &tmp->list;
+ stmt = json_parse_stmt(ctx, value);
+ if (!stmt) {
+ json_error(ctx, "Parsing set statements array at index %zd failed.", index);
+ stmt_list_free(stmt_list);
+ return;
+ }
+ list_add(&stmt->list, head);
+ head = &stmt->list;
}
}

View File

@ -0,0 +1,41 @@
From f2e3c291c8d1ca5879697abf87f96c5097cdf50b Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 30 Jun 2026 14:34:06 +0200
Subject: [PATCH] parser_json: allow statement stateful statement only in set
elements
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 85b5b051f0e64f106cee078fc6d66d0872770e79
commit 85b5b051f0e64f106cee078fc6d66d0872770e79
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Apr 1 09:49:48 2025 +0200
parser_json: allow statement stateful statement only in set elements
Upfront reject of non stateful statements in set elements.
Fixes: 07958ec53830 ("json: add set statement list support")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_json.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/parser_json.c b/src/parser_json.c
index 7b75a88..cfeef3c 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -2335,6 +2335,11 @@ static void json_parse_set_stmt_list(struct json_ctx *ctx,
stmt_list_free(stmt_list);
return;
}
+ if (!(stmt->flags & STMT_F_STATEFUL)) {
+ stmt_free(stmt);
+ json_error(ctx, "Unsupported set statements array at index %zd failed.", index);
+ stmt_list_free(stmt_list);
+ }
list_add(&stmt->list, head);
head = &stmt->list;
}

Some files were not shown because too many files have changed in this diff Show More