nftables-1.1.5-1.el10

* Thu Nov 20 2025 Phil Sutter <psutter@redhat.com> [1.1.5-1.el10]
- doc: libnftables-json: Describe RULESET object (Phil Sutter) [RHEL-121194]
- doc: don't suggest to disable GSO (Phil Sutter) [RHEL-121194]
- build: don't install ancillary files without systemd service file (Phil Sutter) [RHEL-121194]
- tests: shell: fix typo in vmap_timeout test script (Phil Sutter) [RHEL-121194]
- tests: py: inet/osf.t: Fix element ordering in JSON equivalents (Phil Sutter) [RHEL-121194]
- tests: py: any/ct.t.json.output: Drop leftover entry (Phil Sutter) [RHEL-121194]
- tests: py: any/tcpopt.t.json: Fix JSON equivalent (Phil Sutter) [RHEL-121194]
- optimize: Fix verdict expression comparison (Phil Sutter) [RHEL-121194]
- datatype: Fix boolean type on Big Endian (Phil Sutter) [RHEL-121194]
- src: parser_json: fix format string bugs (Phil Sutter) [RHEL-121194]
- doc: fix tcpdump example (Phil Sutter) [RHEL-121194]
- libnftables: do not re-add default include directory in include search path (Phil Sutter) [RHEL-121194]
- monitor: Inform JSON printer when reporting an object delete event (Phil Sutter) [RHEL-121194]
- tests: shell: skip two bitwise tests if multi-register support isn't available (Phil Sutter) [RHEL-121194]
- tests: monitor: Fix regex collecting expected echo output (Phil Sutter) [RHEL-121194]
- monitor: Quote device names in chain declarations, too (Phil Sutter) [RHEL-121194]
- tools: gitignore nftables.service file (Phil Sutter) [RHEL-121194]
- parser_bison: remove leftover utf-8 character in error (Phil Sutter) [RHEL-121194]
- Rebase onto version 1.1.5 (Phil Sutter) [RHEL-121194]
Resolves: RHEL-121194
This commit is contained in:
Phil Sutter 2025-11-20 20:23:40 +01:00
parent c5a54bc4d2
commit ab440e86fa
42 changed files with 1281 additions and 3690 deletions

1
.gitignore vendored
View File

@ -28,3 +28,4 @@
/nftables-1.0.7.tar.xz
/nftables-1.0.9.tar.xz
/nftables-1.1.1.tar.xz
/nftables-1.1.5.tar.xz

View File

@ -1,11 +1,10 @@
From 50afb84d7064806ad7acc8364455062fc0751528 Mon Sep 17 00:00:00 2001
From df4f84d17daf107d9dc21d5b42ae6255b53f0ffd Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 9 Sep 2025 16:49:27 +0200
Subject: [PATCH] table: Embed creating nft version into userdata
JIRA: https://issues.redhat.com/browse/RHEL-108851
Upstream Status: nftables commit 64c07e38f0494093a399a68a31056f5866c4d705
Conflicts: Context change due to missing --with-unitdir option.
commit 64c07e38f0494093a399a68a31056f5866c4d705
Author: Phil Sutter <phil@nwl.cc>
@ -32,7 +31,7 @@ Signed-off-by: Phil Sutter <psutter@redhat.com>
6 files changed, 80 insertions(+), 6 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index ba09e7f..c2a6908 100644
index e292d3b..243071e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -33,6 +33,7 @@ sbin_PROGRAMS =
@ -43,7 +42,7 @@ index ba09e7f..c2a6908 100644
###############################################################################
@@ -105,6 +106,8 @@ noinst_HEADERS = \
@@ -106,6 +107,8 @@ noinst_HEADERS = \
\
$(NULL)
@ -53,12 +52,12 @@ index ba09e7f..c2a6908 100644
AM_CPPFLAGS = \
diff --git a/configure.ac b/configure.ac
index 816e920..bac8319 100644
index 3a751cb..da16a6e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -114,6 +114,30 @@ AC_CHECK_DECLS([getprotobyname_r, getprotobynumber_r, getservbyport_r], [], [],
#include <netdb.h>
]])
@@ -131,6 +131,30 @@ AC_ARG_WITH([unitdir],
)
AC_SUBST([unitdir])
+AC_ARG_WITH([stable-release], [AS_HELP_STRING([--with-stable-release],
+ [Stable release number])],
@ -88,7 +87,7 @@ index 816e920..bac8319 100644
Makefile \
libnftables.pc \
diff --git a/include/rule.h b/include/rule.h
index 238be23..1b52972 100644
index 470ae10..319f9c3 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -170,6 +170,7 @@ struct table {
@ -100,7 +99,7 @@ index 238be23..1b52972 100644
extern struct table *table_alloc(void);
diff --git a/src/mnl.c b/src/mnl.c
index 12a6345..e748ab6 100644
index 892fb8b..25cd872 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -10,6 +10,7 @@
@ -111,7 +110,7 @@ index 12a6345..e748ab6 100644
#include <libmnl/libmnl.h>
#include <libnftnl/common.h>
@@ -1054,24 +1055,32 @@ int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd,
@@ -1082,24 +1083,32 @@ int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd,
if (nlt == NULL)
memory_allocation_error();
@ -151,7 +150,7 @@ index 12a6345..e748ab6 100644
NFT_MSG_NEWTABLE,
cmd->handle.family,
diff --git a/src/netlink.c b/src/netlink.c
index 2ced863..7f9730d 100644
index 94cbcbf..b5da33e 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -10,6 +10,7 @@
@ -162,7 +161,7 @@ index 2ced863..7f9730d 100644
#include <errno.h>
#include <libmnl/libmnl.h>
@@ -728,6 +729,14 @@ static int table_parse_udata_cb(const struct nftnl_udata *attr, void *data)
@@ -799,6 +800,14 @@ static int table_parse_udata_cb(const struct nftnl_udata *attr, void *data)
if (value[len - 1] != '\0')
return -1;
break;
@ -177,7 +176,7 @@ index 2ced863..7f9730d 100644
default:
return 0;
}
@@ -735,6 +744,29 @@ static int table_parse_udata_cb(const struct nftnl_udata *attr, void *data)
@@ -806,6 +815,29 @@ static int table_parse_udata_cb(const struct nftnl_udata *attr, void *data)
return 0;
}
@ -207,7 +206,7 @@ index 2ced863..7f9730d 100644
struct table *netlink_delinearize_table(struct netlink_ctx *ctx,
const struct nftnl_table *nlt)
{
@@ -759,6 +791,7 @@ struct table *netlink_delinearize_table(struct netlink_ctx *ctx,
@@ -830,6 +862,7 @@ struct table *netlink_delinearize_table(struct netlink_ctx *ctx,
}
if (ud[NFTNL_UDATA_TABLE_COMMENT])
table->comment = xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_TABLE_COMMENT]));
@ -216,10 +215,10 @@ index 2ced863..7f9730d 100644
return table;
diff --git a/src/rule.c b/src/rule.c
index 151ed53..e4d6f53 100644
index 0ad948e..398e5bd 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -1274,6 +1274,10 @@ static void table_print(const struct table *table, struct output_ctx *octx)
@@ -1298,6 +1298,10 @@ static void table_print(const struct table *table, struct output_ctx *octx)
fprintf(octx->error_fp,
"# Warning: table %s %s is managed by iptables-nft, do not touch!\n",
family, table->handle.table.name);

View File

@ -1,45 +0,0 @@
From 1ce7bc1ca89494fdbb2fa10b176d33a5944ede01 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 7 Nov 2024 18:38:45 +0100
Subject: [PATCH] tests: shell: fix spurious dump failure in vmap timeout test
JIRA: https://issues.redhat.com/browse/RHEL-65346
Upstream Status: nftables commit 95017b8c8f10ada09c2faa7e6bae71b60f38f259
commit 95017b8c8f10ada09c2faa7e6bae71b60f38f259
Author: Florian Westphal <fw@strlen.de>
Date: Fri Oct 11 02:32:08 2024 +0200
tests: shell: fix spurious dump failure in vmap timeout test
Blamed commit can update the timeout to 6s, but last line waits
for 5 seconds and expects that to be enough to have all elements vanish.
Fix the typo to limit update timeout also to 5 seconds and not 6.
This fixes spurious dump failures like this one:
- elements = { 1.2.3.4 . 22 : jump ssh_input }
+ elements = { 1.2.3.4 . 22 : jump ssh_input,
+ 10.0.95.144 . 38023 timeout 6s expires 545ms : jump other_input }
Fixes: db80037c0279 ("tests: shell: extend vmap test with updates")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/shell/testcases/maps/vmap_timeout | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/shell/testcases/maps/vmap_timeout b/tests/shell/testcases/maps/vmap_timeout
index 3f0563a..6d73f3c 100755
--- a/tests/shell/testcases/maps/vmap_timeout
+++ b/tests/shell/testcases/maps/vmap_timeout
@@ -32,7 +32,7 @@ for i in $(seq 1 100) ; do
timeout=$((timeout+1))
expire=$((RANDOM%timeout))
utimeout=$((RANDOM%5))
- utimeout=$((timeout+1))
+ utimeout=$((utimeout+1))
timeout_str="timeout ${timeout}s"
expire_str=""

View File

@ -1,4 +1,4 @@
From 575c0a20b143f5487a184c2c5c866dd8b14a69f5 Mon Sep 17 00:00:00 2001
From d930a5aa54976ceb1a28bc5002273f7b96fc8684 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 9 Sep 2025 16:50:13 +0200
Subject: [PATCH] Makefile: Fix for 'make CFLAGS=...'
@ -26,10 +26,10 @@ Signed-off-by: Phil Sutter <psutter@redhat.com>
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/Makefile.am b/Makefile.am
index c2a6908..58c6959 100644
index 243071e..4035e65 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -154,6 +154,8 @@ AM_CFLAGS = \
@@ -155,6 +155,8 @@ AM_CFLAGS = \
\
$(GCC_FVISIBILITY_HIDDEN) \
\
@ -39,10 +39,10 @@ index c2a6908..58c6959 100644
AM_YFLAGS = -d -Wno-yacc
diff --git a/configure.ac b/configure.ac
index bac8319..c14aa67 100644
index da16a6e..3517ea0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -136,7 +136,6 @@ AC_CONFIG_COMMANDS([nftversion.h], [
@@ -153,7 +153,6 @@ AC_CONFIG_COMMANDS([nftversion.h], [
# Current date should be fetched exactly once per build,
# so have 'make' call date and pass the value to every 'gcc' call
AC_SUBST([MAKE_STAMP], ["\$(shell date +%s)"])

View File

@ -1,38 +0,0 @@
From 08d33851ff012bb14237127553be80dbb00fa07d Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 7 Nov 2024 18:38:45 +0100
Subject: [PATCH] libnftables-json: fix raw payload expression documentation
JIRA: https://issues.redhat.com/browse/RHEL-65346
Upstream Status: nftables commit 570320ab9a0752c7749a6c9cc85b34a5e7ab91b5
commit 570320ab9a0752c7749a6c9cc85b34a5e7ab91b5
Author: Eric Long <i@hack3r.moe>
Date: Thu Oct 17 23:33:17 2024 +0800
libnftables-json: fix raw payload expression documentation
Raw payload expression accesses payload data in bits, not bytes.
Fixes: 872f373dc50f7 ("doc: Add JSON schema documentation")
Signed-off-by: Eric Long <i@hack3r.moe>
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/libnftables-json.adoc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc
index a8a6165..2f29ac0 100644
--- a/doc/libnftables-json.adoc
+++ b/doc/libnftables-json.adoc
@@ -1182,7 +1182,7 @@ ____
Construct a payload expression, i.e. a reference to a certain part of packet
data. The first form creates a raw payload expression to point at a random
-number (*len*) of bytes at a certain offset (*offset*) from a given reference
+number (*len*) of bits at a certain offset (*offset*) from a given reference
point (*base*). The following *base* values are accepted:
*"ll"*::

View File

@ -1,4 +1,4 @@
From 5dafd1cfd00116d0c6f289a0dc32b8a10d7c4c06 Mon Sep 17 00:00:00 2001
From 14a205946dd436385ff141604794e64754290fe1 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 4 Nov 2025 16:20:14 +0100
Subject: [PATCH] fib: Fix for existence check on Big Endian
@ -38,10 +38,10 @@ Signed-off-by: Phil Sutter <psutter@redhat.com>
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index c9cbaa6..474dc64 100644
index aaeb7b4..9125d5f 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -2795,6 +2795,7 @@ static int expr_evaluate_fib(struct eval_ctx *ctx, struct expr **exprp)
@@ -3001,6 +3001,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);
@ -50,10 +50,10 @@ index c9cbaa6..474dc64 100644
return expr_evaluate_primary(ctx, exprp);
}
diff --git a/src/fib.c b/src/fib.c
index e95271c..0749007 100644
index 5383613..4db7cd2 100644
--- a/src/fib.c
+++ b/src/fib.c
@@ -189,8 +189,10 @@ struct expr *fib_expr_alloc(const struct location *loc,
@@ -198,8 +198,10 @@ struct expr *fib_expr_alloc(const struct location *loc,
BUG("Unknown result %d\n", result);
}

View File

@ -1,339 +0,0 @@
From 005c220f08964958eae2ca6e40a070b5bc9d6f79 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 7 Nov 2024 18:38:45 +0100
Subject: [PATCH] src: collapse set element commands from parser
JIRA: https://issues.redhat.com/browse/RHEL-65346
Upstream Status: nftables commit 20f1c60ac8c88be3bdf3096083b24ada06570a77
commit 20f1c60ac8c88be3bdf3096083b24ada06570a77
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Oct 23 11:43:58 2024 +0200
src: collapse set element commands from parser
498a5f0c219d ("rule: collapse set element commands") does not help to
reduce memory consumption in the case of large sets defined by one
element per line:
add element ip x y { 1.1.1.1 }
add element ip x y { 1.1.1.2 }
...
This patch reduces memory consumption by ~75%, set elements are
collapsed into an existing cmd object wherever possible to reduce the
number of cmd objects.
This patch also adds a special case for variables for sets similar to:
be055af5c58d ("cmd: skip variable set elements when collapsing commands")
This patch requires this small kernel fix:
commit b53c116642502b0c85ecef78bff4f826a7dd4145
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Fri May 20 00:02:06 2022 +0200
netfilter: nf_tables: set element extended ACK reporting support
which is already included in recent -stable kernels:
# cat ruleset.nft
add table ip x
add chain ip x y
add set ip x y { type ipv4_addr; }
create element ip x y { 1.1.1.1 }
create element ip x y { 1.1.1.1 }
# nft -f ruleset.nft
ruleset.nft:5:25-31: Error: Could not process rule: File exists
create element ip x y { 1.1.1.1 }
^^^^^^^
since there is no need to relate commands via sequence number anymore,
this allows also removes the uncollapse step.
Fixes: 498a5f0c219d ("rule: collapse set element commands")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
include/cmd.h | 7 +--
include/expression.h | 1 -
include/list.h | 11 +++++
include/rule.h | 1 -
src/cmd.c | 105 +++++++++++--------------------------------
src/libnftables.c | 7 ---
src/parser_bison.y | 13 ++++++
src/rule.c | 1 -
8 files changed, 54 insertions(+), 92 deletions(-)
diff --git a/include/cmd.h b/include/cmd.h
index 92a4152..0a8779b 100644
--- a/include/cmd.h
+++ b/include/cmd.h
@@ -2,12 +2,13 @@
#define _NFT_CMD_H_
void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc);
+struct mnl_err;
void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd,
struct mnl_err *err);
+bool nft_cmd_collapse_elems(enum cmd_ops op, struct list_head *cmds,
+ struct handle *handle, struct expr *init);
+
void nft_cmd_expand(struct cmd *cmd);
-void nft_cmd_post_expand(struct cmd *cmd);
-bool nft_cmd_collapse(struct list_head *cmds);
-void nft_cmd_uncollapse(struct list_head *cmds);
#endif
diff --git a/include/expression.h b/include/expression.h
index 8982110..da2f693 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -255,7 +255,6 @@ struct expr {
enum expr_types etype:8;
enum ops op:8;
unsigned int len;
- struct cmd *cmd;
union {
struct {
diff --git a/include/list.h b/include/list.h
index 857921e..37fbe3e 100644
--- a/include/list.h
+++ b/include/list.h
@@ -348,6 +348,17 @@ static inline void list_splice_tail_init(struct list_head *list,
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
+/**
+ * list_last_entry - get the last element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_head within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_last_entry(ptr, type, member) \
+ list_entry((ptr)->prev, type, member)
+
/**
* list_next_entry - get the next element in list
* @pos: the type * to cursor
diff --git a/include/rule.h b/include/rule.h
index 5b3e12b..a1628d8 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -718,7 +718,6 @@ struct cmd {
enum cmd_obj obj;
struct handle handle;
uint32_t seqnum;
- struct list_head collapse_list;
union {
void *data;
struct expr *expr;
diff --git a/src/cmd.c b/src/cmd.c
index 9a572b5..e010dcb 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -378,6 +378,32 @@ static void nft_cmd_expand_chain(struct chain *chain, struct list_head *new_cmds
}
}
+bool nft_cmd_collapse_elems(enum cmd_ops op, struct list_head *cmds,
+ struct handle *handle, struct expr *init)
+{
+ struct cmd *last_cmd;
+
+ if (list_empty(cmds))
+ return false;
+
+ if (init->etype == EXPR_VARIABLE)
+ return false;
+
+ last_cmd = list_last_entry(cmds, struct cmd, list);
+ if (last_cmd->op != op ||
+ last_cmd->obj != CMD_OBJ_ELEMENTS ||
+ last_cmd->expr->etype == EXPR_VARIABLE ||
+ last_cmd->handle.family != handle->family ||
+ strcmp(last_cmd->handle.table.name, handle->table.name) ||
+ strcmp(last_cmd->handle.set.name, handle->set.name))
+ return false;
+
+ list_splice_tail_init(&init->expressions, &last_cmd->expr->expressions);
+ last_cmd->expr->size += init->size;
+
+ return true;
+}
+
void nft_cmd_expand(struct cmd *cmd)
{
struct list_head new_cmds;
@@ -459,82 +485,3 @@ void nft_cmd_expand(struct cmd *cmd)
break;
}
}
-
-bool nft_cmd_collapse(struct list_head *cmds)
-{
- struct cmd *cmd, *next, *elems = NULL;
- struct expr *expr, *enext;
- bool collapse = false;
-
- list_for_each_entry_safe(cmd, next, cmds, list) {
- if (cmd->op != CMD_ADD &&
- cmd->op != CMD_CREATE) {
- elems = NULL;
- continue;
- }
-
- if (cmd->obj != CMD_OBJ_ELEMENTS) {
- elems = NULL;
- continue;
- }
-
- if (cmd->expr->etype == EXPR_VARIABLE)
- continue;
-
- if (!elems) {
- elems = cmd;
- continue;
- }
-
- if (cmd->op != elems->op) {
- elems = cmd;
- continue;
- }
-
- if (elems->handle.family != cmd->handle.family ||
- strcmp(elems->handle.table.name, cmd->handle.table.name) ||
- strcmp(elems->handle.set.name, cmd->handle.set.name)) {
- elems = cmd;
- continue;
- }
-
- collapse = true;
- list_for_each_entry_safe(expr, enext, &cmd->expr->expressions, list) {
- expr->cmd = cmd;
- list_move_tail(&expr->list, &elems->expr->expressions);
- }
- elems->expr->size += cmd->expr->size;
- list_move_tail(&cmd->list, &elems->collapse_list);
- }
-
- return collapse;
-}
-
-void nft_cmd_uncollapse(struct list_head *cmds)
-{
- struct cmd *cmd, *cmd_next, *collapse_cmd, *collapse_cmd_next;
- struct expr *expr, *next;
-
- list_for_each_entry_safe(cmd, cmd_next, cmds, list) {
- if (list_empty(&cmd->collapse_list))
- continue;
-
- assert(cmd->obj == CMD_OBJ_ELEMENTS);
-
- list_for_each_entry_safe(expr, next, &cmd->expr->expressions, list) {
- if (!expr->cmd)
- continue;
-
- list_move_tail(&expr->list, &expr->cmd->expr->expressions);
- cmd->expr->size--;
- expr->cmd = NULL;
- }
-
- list_for_each_entry_safe(collapse_cmd, collapse_cmd_next, &cmd->collapse_list, list) {
- if (cmd->elem.set)
- collapse_cmd->elem.set = set_get(cmd->elem.set);
-
- list_add(&collapse_cmd->list, &cmd->list);
- }
- }
-}
diff --git a/src/libnftables.c b/src/libnftables.c
index 2ae2150..2834c99 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -513,7 +513,6 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
{
struct nft_cache_filter *filter;
struct cmd *cmd, *next;
- bool collapsed = false;
unsigned int flags;
int err = 0;
@@ -529,9 +528,6 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
nft_cache_filter_fini(filter);
- if (nft_cmd_collapse(cmds))
- collapsed = true;
-
list_for_each_entry(cmd, cmds, list) {
if (cmd->op != CMD_ADD &&
cmd->op != CMD_CREATE)
@@ -553,9 +549,6 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
}
}
- if (collapsed)
- nft_cmd_uncollapse(cmds);
-
if (err < 0 || nft->state->nerrs)
return -1;
diff --git a/src/parser_bison.y b/src/parser_bison.y
index e2936d1..602fc60 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -35,6 +35,7 @@
#include <libnftnl/udata.h>
#include <rule.h>
+#include <cmd.h>
#include <statement.h>
#include <expression.h>
#include <headers.h>
@@ -1219,6 +1220,12 @@ add_cmd : TABLE table_spec
}
| ELEMENT set_spec set_block_expr
{
+ if (nft_cmd_collapse_elems(CMD_ADD, state->cmds, &$2, $3)) {
+ handle_free(&$2);
+ expr_free($3);
+ $$ = NULL;
+ break;
+ }
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
}
| FLOWTABLE flowtable_spec flowtable_block_alloc
@@ -1336,6 +1343,12 @@ create_cmd : TABLE table_spec
}
| ELEMENT set_spec set_block_expr
{
+ if (nft_cmd_collapse_elems(CMD_CREATE, state->cmds, &$2, $3)) {
+ handle_free(&$2);
+ expr_free($3);
+ $$ = NULL;
+ break;
+ }
$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
}
| FLOWTABLE flowtable_spec flowtable_block_alloc
diff --git a/src/rule.c b/src/rule.c
index 9bc160e..9536e68 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -1332,7 +1332,6 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
cmd->attr = xzalloc_array(NFT_NLATTR_LOC_MAX,
sizeof(struct nlerr_loc));
cmd->attr_array_len = NFT_NLATTR_LOC_MAX;
- init_list_head(&cmd->collapse_list);
return cmd;
}

View File

@ -1,78 +0,0 @@
From c2e328edd47ac3d3ed127b313d35ed05839441db Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 7 Nov 2024 18:38:45 +0100
Subject: [PATCH] mnl: rename to mnl_seqnum_alloc() to mnl_seqnum_inc()
JIRA: https://issues.redhat.com/browse/RHEL-65346
Upstream Status: nftables commit b4ce90d52d564efaced298f6e9c575d6942ecf91
commit b4ce90d52d564efaced298f6e9c575d6942ecf91
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Oct 23 22:15:24 2024 +0200
mnl: rename to mnl_seqnum_alloc() to mnl_seqnum_inc()
rename mnl_seqnum_alloc() to mnl_seqnum_inc().
No functional change is intended.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
include/mnl.h | 2 +-
src/libnftables.c | 6 +++---
src/mnl.c | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/include/mnl.h b/include/mnl.h
index c9502f3..7c465d4 100644
--- a/include/mnl.h
+++ b/include/mnl.h
@@ -8,7 +8,7 @@
struct mnl_socket *nft_mnl_socket_open(void);
-uint32_t mnl_seqnum_alloc(uint32_t *seqnum);
+uint32_t mnl_seqnum_inc(uint32_t *seqnum);
uint32_t mnl_genid_get(struct netlink_ctx *ctx);
struct mnl_err {
diff --git a/src/libnftables.c b/src/libnftables.c
index 2834c99..3550961 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -37,9 +37,9 @@ static int nft_netlink(struct nft_ctx *nft,
if (list_empty(cmds))
goto out;
- batch_seqnum = mnl_batch_begin(ctx.batch, mnl_seqnum_alloc(&seqnum));
+ batch_seqnum = mnl_batch_begin(ctx.batch, mnl_seqnum_inc(&seqnum));
list_for_each_entry(cmd, cmds, list) {
- ctx.seqnum = cmd->seqnum = mnl_seqnum_alloc(&seqnum);
+ ctx.seqnum = cmd->seqnum = mnl_seqnum_inc(&seqnum);
ret = do_command(&ctx, cmd);
if (ret < 0) {
netlink_io_error(&ctx, &cmd->location,
@@ -50,7 +50,7 @@ static int nft_netlink(struct nft_ctx *nft,
num_cmds++;
}
if (!nft->check)
- mnl_batch_end(ctx.batch, mnl_seqnum_alloc(&seqnum));
+ mnl_batch_end(ctx.batch, mnl_seqnum_inc(&seqnum));
if (!mnl_batch_ready(ctx.batch))
goto out;
diff --git a/src/mnl.c b/src/mnl.c
index db53a60..c1691da 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -70,7 +70,7 @@ struct mnl_socket *nft_mnl_socket_open(void)
return nf_sock;
}
-uint32_t mnl_seqnum_alloc(unsigned int *seqnum)
+uint32_t mnl_seqnum_inc(unsigned int *seqnum)
{
return (*seqnum)++;
}

View File

@ -0,0 +1,40 @@
From b049bc68b06669875fe687c0686d65ff11653675 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:10:32 +0100
Subject: [PATCH] parser_bison: remove leftover utf-8 character in error
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
JIRA: https://issues.redhat.com/browse/RHEL-121194
Upstream Status: nftables commit 7f37f3ca55810c2dc0e245ca7df108f160acb359
commit 7f37f3ca55810c2dc0e245ca7df108f160acb359
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Sun Aug 31 23:37:51 2025 +0200
parser_bison: remove leftover utf-8 character in error
replace "" (UTF-8, 0xe280 0x98) with "'" (ASCII 0x27).
Fixes: c92ec3b21979 ("src: remove utf-8 character in printf lines")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_bison.y | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 1e4b3f8..9ac1ca3 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -4292,7 +4292,7 @@ variable_expr : '$' identifier
sym = symbol_lookup_fuzzy(scope, $2);
if (sym) {
erec_queue(error(&@2, "unknown identifier '%s'; "
- "did you mean identifier '%s?",
+ "did you mean identifier '%s'?",
$2, sym->identifier),
state->msgs);
} else {

View File

@ -1,312 +0,0 @@
From ed5989c26e998985a01dcd6c57415d8110c63f64 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 7 Nov 2024 18:38:45 +0100
Subject: [PATCH] mnl: update cmd_add_loc() to take struct nlmsghdr
JIRA: https://issues.redhat.com/browse/RHEL-65346
Upstream Status: nftables commit f7c2b27c9f8356c634f0405347444e03e10e151b
commit f7c2b27c9f8356c634f0405347444e03e10e151b
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Oct 23 23:07:31 2024 +0200
mnl: update cmd_add_loc() to take struct nlmsghdr
To prepare for a fix for very large sets.
No functional change is intended.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
include/cmd.h | 2 +-
src/cmd.c | 4 +--
src/mnl.c | 77 +++++++++++++++++++++++++--------------------------
3 files changed, 41 insertions(+), 42 deletions(-)
diff --git a/include/cmd.h b/include/cmd.h
index 0a8779b..cf7e43b 100644
--- a/include/cmd.h
+++ b/include/cmd.h
@@ -1,7 +1,7 @@
#ifndef _NFT_CMD_H_
#define _NFT_CMD_H_
-void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc);
+void cmd_add_loc(struct cmd *cmd, const struct nlmsghdr *nlh, const struct location *loc);
struct mnl_err;
void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd,
struct mnl_err *err);
diff --git a/src/cmd.c b/src/cmd.c
index e010dcb..78a2aa3 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -17,14 +17,14 @@
#include <errno.h>
#include <cache.h>
-void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc)
+void cmd_add_loc(struct cmd *cmd, const struct nlmsghdr *nlh, const struct location *loc)
{
if (cmd->num_attrs >= cmd->attr_array_len) {
cmd->attr_array_len *= 2;
cmd->attr = xrealloc(cmd->attr, sizeof(struct nlerr_loc) * cmd->attr_array_len);
}
- cmd->attr[cmd->num_attrs].offset = offset;
+ cmd->attr[cmd->num_attrs].offset = nlh->nlmsg_len;
cmd->attr[cmd->num_attrs].location = loc;
cmd->num_attrs++;
}
diff --git a/src/mnl.c b/src/mnl.c
index c1691da..42d1b0d 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -474,7 +474,7 @@ static int mnl_nft_expr_build_cb(struct nftnl_expr *nle, void *data)
eloc = nft_expr_loc_find(nle, ctx->lctx);
if (eloc)
- cmd_add_loc(cmd, nlh->nlmsg_len, eloc->loc);
+ cmd_add_loc(cmd, nlh, eloc->loc);
nest = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM);
nftnl_expr_build_payload(nlh, nle);
@@ -527,9 +527,9 @@ int mnl_nft_rule_add(struct netlink_ctx *ctx, struct cmd *cmd,
cmd->handle.family,
NLM_F_CREATE | flags, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location);
+ cmd_add_loc(cmd, nlh, &h->table.location);
mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, h->table.name);
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->chain.location);
+ cmd_add_loc(cmd, nlh, &h->chain.location);
if (h->chain_id)
mnl_attr_put_u32(nlh, NFTA_RULE_CHAIN_ID, htonl(h->chain_id));
@@ -578,11 +578,11 @@ int mnl_nft_rule_replace(struct netlink_ctx *ctx, struct cmd *cmd)
cmd->handle.family,
NLM_F_REPLACE | flags, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location);
+ cmd_add_loc(cmd, nlh, &h->table.location);
mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, h->table.name);
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->chain.location);
+ cmd_add_loc(cmd, nlh, &h->chain.location);
mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, h->chain.name);
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->handle.location);
+ cmd_add_loc(cmd, nlh, &h->handle.location);
mnl_attr_put_u64(nlh, NFTA_RULE_HANDLE, htobe64(h->handle.id));
mnl_nft_rule_build_ctx_init(&rule_ctx, nlh, cmd, &lctx);
@@ -621,14 +621,14 @@ int mnl_nft_rule_del(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY),
0, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location);
+ cmd_add_loc(cmd, nlh, &h->table.location);
mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, h->table.name);
if (h->chain.name) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->chain.location);
+ cmd_add_loc(cmd, nlh, &h->chain.location);
mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, h->chain.name);
}
if (h->handle.id) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->handle.location);
+ cmd_add_loc(cmd, nlh, &h->handle.location);
mnl_attr_put_u64(nlh, NFTA_RULE_HANDLE, htobe64(h->handle.id));
}
@@ -792,12 +792,12 @@ static void mnl_nft_chain_devs_build(struct nlmsghdr *nlh, struct cmd *cmd)
dev_array = nft_dev_array(dev_expr, &num_devs);
if (num_devs == 1) {
- cmd_add_loc(cmd, nlh->nlmsg_len, dev_array[0].location);
+ cmd_add_loc(cmd, nlh, dev_array[0].location);
mnl_attr_put_strz(nlh, NFTA_HOOK_DEV, dev_array[0].ifname);
} else {
nest_dev = mnl_attr_nest_start(nlh, NFTA_HOOK_DEVS);
for (i = 0; i < num_devs; i++) {
- cmd_add_loc(cmd, nlh->nlmsg_len, dev_array[i].location);
+ cmd_add_loc(cmd, nlh, dev_array[i].location);
mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME, dev_array[i].ifname);
mnl_attr_nest_end(nlh, nest_dev);
}
@@ -842,9 +842,9 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
cmd->handle.family,
NLM_F_CREATE | flags, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_CHAIN_TABLE, cmd->handle.table.name);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.chain.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.chain.location);
if (!cmd->chain || !(cmd->chain->flags & CHAIN_F_BINDING)) {
mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME, cmd->handle.chain.name);
@@ -861,7 +861,7 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
if (cmd->chain && cmd->chain->policy) {
mpz_export_data(&policy, cmd->chain->policy->value,
BYTEORDER_HOST_ENDIAN, sizeof(int));
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->chain->policy->location);
+ cmd_add_loc(cmd, nlh, &cmd->chain->policy->location);
mnl_attr_put_u32(nlh, NFTA_CHAIN_POLICY, htonl(policy));
}
@@ -873,7 +873,7 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
struct nlattr *nest;
if (cmd->chain->type.str) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->chain->type.loc);
+ cmd_add_loc(cmd, nlh, &cmd->chain->type.loc);
mnl_attr_put_strz(nlh, NFTA_CHAIN_TYPE, cmd->chain->type.str);
}
@@ -949,13 +949,13 @@ int mnl_nft_chain_del(struct netlink_ctx *ctx, struct cmd *cmd)
cmd->handle.family,
0, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_CHAIN_TABLE, cmd->handle.table.name);
if (cmd->handle.chain.name) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.chain.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.chain.location);
mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME, cmd->handle.chain.name);
} else if (cmd->handle.handle.id) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.handle.location);
mnl_attr_put_u64(nlh, NFTA_CHAIN_HANDLE,
htobe64(cmd->handle.handle.id));
}
@@ -1077,7 +1077,7 @@ int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd,
cmd->handle.family,
flags, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_TABLE_NAME, cmd->handle.table.name);
nftnl_table_nlmsg_build_payload(nlh, nlt);
nftnl_table_free(nlt);
@@ -1106,10 +1106,10 @@ int mnl_nft_table_del(struct netlink_ctx *ctx, struct cmd *cmd)
cmd->handle.family, 0, ctx->seqnum);
if (cmd->handle.table.name) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_TABLE_NAME, cmd->handle.table.name);
} else if (cmd->handle.handle.id) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.handle.location);
mnl_attr_put_u64(nlh, NFTA_TABLE_HANDLE,
htobe64(cmd->handle.handle.id));
}
@@ -1325,9 +1325,9 @@ int mnl_nft_set_add(struct netlink_ctx *ctx, struct cmd *cmd,
h->family,
NLM_F_CREATE | flags, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location);
+ cmd_add_loc(cmd, nlh, &h->table.location);
mnl_attr_put_strz(nlh, NFTA_SET_TABLE, h->table.name);
- cmd_add_loc(cmd, nlh->nlmsg_len, &h->set.location);
+ cmd_add_loc(cmd, nlh, &h->set.location);
mnl_attr_put_strz(nlh, NFTA_SET_NAME, h->set.name);
nftnl_set_nlmsg_build_payload(nlh, nls);
@@ -1359,13 +1359,13 @@ int mnl_nft_set_del(struct netlink_ctx *ctx, struct cmd *cmd)
h->family,
0, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_SET_TABLE, cmd->handle.table.name);
if (h->set.name) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.set.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.set.location);
mnl_attr_put_strz(nlh, NFTA_SET_NAME, cmd->handle.set.name);
} else if (h->handle.id) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.handle.location);
mnl_attr_put_u64(nlh, NFTA_SET_HANDLE,
htobe64(cmd->handle.handle.id));
}
@@ -1544,9 +1544,9 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
NFT_MSG_NEWOBJ, cmd->handle.family,
NLM_F_CREATE | flags, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_OBJ_TABLE, cmd->handle.table.name);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.obj.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.obj.location);
mnl_attr_put_strz(nlh, NFTA_OBJ_NAME, cmd->handle.obj.name);
nftnl_obj_nlmsg_build_payload(nlh, nlo);
@@ -1577,14 +1577,14 @@ int mnl_nft_obj_del(struct netlink_ctx *ctx, struct cmd *cmd, int type)
msg_type, cmd->handle.family,
0, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_OBJ_TABLE, cmd->handle.table.name);
if (cmd->handle.obj.name) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.obj.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.obj.location);
mnl_attr_put_strz(nlh, NFTA_OBJ_NAME, cmd->handle.obj.name);
} else if (cmd->handle.handle.id) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.handle.location);
mnl_attr_put_u64(nlh, NFTA_OBJ_HANDLE,
htobe64(cmd->handle.handle.id));
}
@@ -1764,7 +1764,7 @@ next:
list_for_each_entry_from(expr, &set->expressions, list) {
nlse = alloc_nftnl_setelem(set, expr);
- cmd_add_loc(cmd, nlh->nlmsg_len, &expr->location);
+ cmd_add_loc(cmd, nlh, &expr->location);
nest2 = mnl_attr_nest_start(nlh, ++i);
nftnl_set_elem_nlmsg_build_payload(nlh, nlse);
mnl_attr_nest_end(nlh, nest2);
@@ -2005,7 +2005,7 @@ static void mnl_nft_ft_devs_build(struct nlmsghdr *nlh, struct cmd *cmd)
dev_array = nft_dev_array(dev_expr, &num_devs);
nest_dev = mnl_attr_nest_start(nlh, NFTA_FLOWTABLE_HOOK_DEVS);
for (i = 0; i < num_devs; i++) {
- cmd_add_loc(cmd, nlh->nlmsg_len, dev_array[i].location);
+ cmd_add_loc(cmd, nlh, dev_array[i].location);
mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME, dev_array[i].ifname);
}
@@ -2037,9 +2037,9 @@ int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
NFT_MSG_NEWFLOWTABLE, cmd->handle.family,
NLM_F_CREATE | flags, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_TABLE, cmd->handle.table.name);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.flowtable.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.flowtable.location);
mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_NAME, cmd->handle.flowtable.name);
nftnl_flowtable_nlmsg_build_payload(nlh, flo);
@@ -2086,16 +2086,15 @@ int mnl_nft_flowtable_del(struct netlink_ctx *ctx, struct cmd *cmd)
msg_type, cmd->handle.family,
0, ctx->seqnum);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_TABLE, cmd->handle.table.name);
if (cmd->handle.flowtable.name) {
- cmd_add_loc(cmd, nlh->nlmsg_len,
- &cmd->handle.flowtable.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.flowtable.location);
mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_NAME,
cmd->handle.flowtable.name);
} else if (cmd->handle.handle.id) {
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location);
+ cmd_add_loc(cmd, nlh, &cmd->handle.handle.location);
mnl_attr_put_u64(nlh, NFTA_FLOWTABLE_HANDLE,
htobe64(cmd->handle.handle.id));
}

View File

@ -0,0 +1,30 @@
From 77d195c0d761978971c17504a2775fb4896ff4e7 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:10:32 +0100
Subject: [PATCH] tools: gitignore nftables.service file
JIRA: https://issues.redhat.com/browse/RHEL-121194
Upstream Status: nftables commit 441ff666cb22921f23b14146a70c1111210364a2
commit 441ff666cb22921f23b14146a70c1111210364a2
Author: Phil Sutter <phil@nwl.cc>
Date: Fri Aug 29 01:51:01 2025 +0200
tools: gitignore nftables.service file
Fixes: c4b17cf830510 ("tools: add a systemd unit for static rulesets")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tools/.gitignore | 1 +
1 file changed, 1 insertion(+)
create mode 100644 tools/.gitignore
diff --git a/tools/.gitignore b/tools/.gitignore
new file mode 100644
index 0000000..2d06c49
--- /dev/null
+++ b/tools/.gitignore
@@ -0,0 +1 @@
+nftables.service

View File

@ -0,0 +1,38 @@
From e514f0bbb4ca1cfdd7f2d719377e0276f759ec31 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:10:32 +0100
Subject: [PATCH] monitor: Quote device names in chain declarations, too
JIRA: https://issues.redhat.com/browse/RHEL-121194
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 398e5bd..94f5eab 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -1131,7 +1131,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

@ -1,58 +0,0 @@
From 66dc95d7a3f7c0e4527f4e960f5c397fd3b82af5 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 7 Nov 2024 18:38:45 +0100
Subject: [PATCH] rule: netlink attribute offset is uint32_t for struct
nlerr_loc
JIRA: https://issues.redhat.com/browse/RHEL-65346
Upstream Status: nftables commit 42b081df747729b0d83b69d2816be4091af56a58
commit 42b081df747729b0d83b69d2816be4091af56a58
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Thu Oct 24 00:08:24 2024 +0200
rule: netlink attribute offset is uint32_t for struct nlerr_loc
The maximum netlink message length (nlh->nlmsg_len) is uint32_t, struct
nlerr_loc stores the offset to the netlink attribute which must be
uint32_t, not uint16_t.
While at it, remove check for zero netlink attribute offset in
nft_cmd_error() which should not ever happen, likely this check was
there to prevent the uint16_t offset overflow.
Fixes: f8aec603aa7e ("src: initial extended netlink error reporting")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
include/rule.h | 2 +-
src/cmd.c | 2 --
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/include/rule.h b/include/rule.h
index a1628d8..3fcfa44 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -695,7 +695,7 @@ void monitor_free(struct monitor *m);
#define NFT_NLATTR_LOC_MAX 32
struct nlerr_loc {
- uint16_t offset;
+ uint32_t offset;
const struct location *location;
};
diff --git a/src/cmd.c b/src/cmd.c
index 78a2aa3..0c7a43e 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -323,8 +323,6 @@ void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd,
uint32_t i;
for (i = 0; i < cmd->num_attrs; i++) {
- if (!cmd->attr[i].offset)
- break;
if (cmd->attr[i].offset == err->offset)
loc = cmd->attr[i].location;
}

View File

@ -1,193 +0,0 @@
From c62c11ee27daf90c74a46353df4936b869624e72 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 7 Nov 2024 18:38:45 +0100
Subject: [PATCH] src: fix extended netlink error reporting with large set
elements
JIRA: https://issues.redhat.com/browse/RHEL-65346
Upstream Status: nftables commit 68d2de3ca6c6eb18f5b32f7b4324a85c9c6c358e
commit 68d2de3ca6c6eb18f5b32f7b4324a85c9c6c358e
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Thu Oct 24 00:24:55 2024 +0200
src: fix extended netlink error reporting with large set elements
Large sets can expand into several netlink messages, use sequence number
and attribute offset to correlate the set element and the location.
When set element command expands into several netlink messages,
increment sequence number for each netlink message. Update struct cmd to
store the range of netlink messages that result from this command.
struct nlerr_loc remains in the same size in x86_64.
# nft -f set-65535.nft
set-65535.nft:65029:22-32: Error: Could not process rule: File exists
create element x y { 1.1.254.253 }
^^^^^^^^^^^
Fixes: f8aec603aa7e ("src: initial extended netlink error reporting")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
include/rule.h | 4 +++-
src/cmd.c | 4 +++-
src/libnftables.c | 12 ++++++++----
src/mnl.c | 9 +++++----
src/parser_json.c | 4 ++--
5 files changed, 21 insertions(+), 12 deletions(-)
diff --git a/include/rule.h b/include/rule.h
index 3fcfa44..48e148e 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -695,6 +695,7 @@ void monitor_free(struct monitor *m);
#define NFT_NLATTR_LOC_MAX 32
struct nlerr_loc {
+ uint32_t seqnum;
uint32_t offset;
const struct location *location;
};
@@ -717,7 +718,8 @@ struct cmd {
enum cmd_ops op;
enum cmd_obj obj;
struct handle handle;
- uint32_t seqnum;
+ uint32_t seqnum_from;
+ uint32_t seqnum_to;
union {
void *data;
struct expr *expr;
diff --git a/src/cmd.c b/src/cmd.c
index 0c7a43e..eb44b98 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -24,6 +24,7 @@ void cmd_add_loc(struct cmd *cmd, const struct nlmsghdr *nlh, const struct locat
cmd->attr = xrealloc(cmd->attr, sizeof(struct nlerr_loc) * cmd->attr_array_len);
}
+ cmd->attr[cmd->num_attrs].seqnum = nlh->nlmsg_seq;
cmd->attr[cmd->num_attrs].offset = nlh->nlmsg_len;
cmd->attr[cmd->num_attrs].location = loc;
cmd->num_attrs++;
@@ -323,7 +324,8 @@ void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd,
uint32_t i;
for (i = 0; i < cmd->num_attrs; i++) {
- if (cmd->attr[i].offset == err->offset)
+ if (cmd->attr[i].seqnum == err->seqnum &&
+ cmd->attr[i].offset == err->offset)
loc = cmd->attr[i].location;
}
diff --git a/src/libnftables.c b/src/libnftables.c
index 3550961..1df22b3 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -39,7 +39,7 @@ static int nft_netlink(struct nft_ctx *nft,
batch_seqnum = mnl_batch_begin(ctx.batch, mnl_seqnum_inc(&seqnum));
list_for_each_entry(cmd, cmds, list) {
- ctx.seqnum = cmd->seqnum = mnl_seqnum_inc(&seqnum);
+ ctx.seqnum = cmd->seqnum_from = mnl_seqnum_inc(&seqnum);
ret = do_command(&ctx, cmd);
if (ret < 0) {
netlink_io_error(&ctx, &cmd->location,
@@ -47,6 +47,8 @@ static int nft_netlink(struct nft_ctx *nft,
strerror(errno));
goto out;
}
+ seqnum = cmd->seqnum_to = ctx.seqnum;
+ mnl_seqnum_inc(&seqnum);
num_cmds++;
}
if (!nft->check)
@@ -80,12 +82,14 @@ static int nft_netlink(struct nft_ctx *nft,
cmd = list_first_entry(cmds, struct cmd, list);
list_for_each_entry_from(cmd, cmds, list) {
- last_seqnum = cmd->seqnum;
- if (err->seqnum == cmd->seqnum ||
+ last_seqnum = cmd->seqnum_to;
+ if ((err->seqnum >= cmd->seqnum_from &&
+ err->seqnum <= cmd->seqnum_to) ||
err->seqnum == batch_seqnum) {
nft_cmd_error(&ctx, cmd, err);
errno = err->err;
- if (err->seqnum == cmd->seqnum) {
+ if (err->seqnum >= cmd->seqnum_from ||
+ err->seqnum <= cmd->seqnum_to) {
mnl_err_list_free(err);
break;
}
diff --git a/src/mnl.c b/src/mnl.c
index 42d1b0d..12a6345 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -1722,7 +1722,7 @@ static void netlink_dump_setelem_done(struct netlink_ctx *ctx)
static int mnl_nft_setelem_batch(const struct nftnl_set *nls, struct cmd *cmd,
struct nftnl_batch *batch,
enum nf_tables_msg_types msg_type,
- unsigned int flags, uint32_t seqnum,
+ unsigned int flags, uint32_t *seqnum,
const struct expr *set,
struct netlink_ctx *ctx)
{
@@ -1741,7 +1741,7 @@ static int mnl_nft_setelem_batch(const struct nftnl_set *nls, struct cmd *cmd,
next:
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(batch), msg_type,
nftnl_set_get_u32(nls, NFTNL_SET_FAMILY),
- flags, seqnum);
+ flags, *seqnum);
if (nftnl_set_is_set(nls, NFTNL_SET_TABLE)) {
mnl_attr_put_strz(nlh, NFTA_SET_ELEM_LIST_TABLE,
@@ -1774,6 +1774,7 @@ next:
if (mnl_nft_attr_nest_overflow(nlh, nest1, nest2)) {
mnl_attr_nest_end(nlh, nest1);
mnl_nft_batch_continue(batch);
+ mnl_seqnum_inc(seqnum);
goto next;
}
}
@@ -1808,7 +1809,7 @@ int mnl_nft_setelem_add(struct netlink_ctx *ctx, struct cmd *cmd,
netlink_dump_set(nls, ctx);
err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, NFT_MSG_NEWSETELEM,
- flags, ctx->seqnum, expr, ctx);
+ flags, &ctx->seqnum, expr, ctx);
nftnl_set_free(nls);
return err;
@@ -1868,7 +1869,7 @@ int mnl_nft_setelem_del(struct netlink_ctx *ctx, struct cmd *cmd,
msg_type = NFT_MSG_DESTROYSETELEM;
err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, msg_type, 0,
- ctx->seqnum, init, ctx);
+ &ctx->seqnum, init, ctx);
nftnl_set_free(nls);
return err;
diff --git a/src/parser_json.c b/src/parser_json.c
index bbe3b1c..37ec34c 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -4269,13 +4269,13 @@ static json_t *seqnum_to_json(const uint32_t seqnum)
cur = json_cmd_assoc_list;
json_cmd_assoc_list = cur->next;
- key = cur->cmd->seqnum % CMD_ASSOC_HSIZE;
+ key = cur->cmd->seqnum_from % CMD_ASSOC_HSIZE;
hlist_add_head(&cur->hnode, &json_cmd_assoc_hash[key]);
}
key = seqnum % CMD_ASSOC_HSIZE;
hlist_for_each_entry(cur, n, &json_cmd_assoc_hash[key], hnode) {
- if (cur->cmd->seqnum == seqnum)
+ if (cur->cmd->seqnum_from == seqnum)
return cur->json;
}

View File

@ -0,0 +1,39 @@
From 4f2bf41778c16cbf49c2af275f8391de14814cb3 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:10:32 +0100
Subject: [PATCH] tests: monitor: Fix regex collecting expected echo output
JIRA: https://issues.redhat.com/browse/RHEL-121194
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 67d3e61..76355e8 100755
--- a/tests/monitor/run-tests.sh
+++ b/tests/monitor/run-tests.sh
@@ -52,7 +52,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

@ -1,62 +0,0 @@
From 42ba69f76beabde5f22a8616469fb296ac72e16e Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 7 Nov 2024 18:38:45 +0100
Subject: [PATCH] tests: monitor: fix up test case breakage
JIRA: https://issues.redhat.com/browse/RHEL-65346
Upstream Status: nftables commit c416416b03d804663c5f7a738a3e1449eeb28157
commit c416416b03d804663c5f7a738a3e1449eeb28157
Author: Florian Westphal <fw@strlen.de>
Date: Tue Oct 29 21:12:19 2024 +0100
tests: monitor: fix up test case breakage
Monitor test fails:
echo: running tests from file set-simple.t
echo output differs!
-add element ip t portrange { 1024-65535 }
add element ip t portrange { 100-200 }
+add element ip t portrange { 1024-65535 }
+# new generation 510 by process 129009 (nft)
I also noticed -j mode did not work correctly, add missing json annotations
in set-concat-interval.t while at it.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/monitor/testcases/set-concat-interval.t | 3 +++
tests/monitor/testcases/set-simple.t | 5 +++--
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/tests/monitor/testcases/set-concat-interval.t b/tests/monitor/testcases/set-concat-interval.t
index 763dc31..75f3828 100644
--- a/tests/monitor/testcases/set-concat-interval.t
+++ b/tests/monitor/testcases/set-concat-interval.t
@@ -10,3 +10,6 @@ I add map ip t s { typeof udp length . @ih,32,32 : verdict; flags interval; elem
O add map ip t s { typeof udp length . @ih,32,32 : verdict; flags interval; }
O add element ip t s { 20-80 . 0x14 : accept }
O add element ip t s { 1-10 . 0xa : drop }
+J {"add": {"map": {"family": "ip", "name": "s", "table": "t", "type": ["integer", "integer"], "handle": 0, "map": "verdict", "flags": ["interval"]}}}
+J {"add": {"element": {"family": "ip", "table": "t", "name": "s", "elem": {"set": [[{"concat": [{"range": [20, 80]}, 20]}, {"accept": null}]]}}}}
+J {"add": {"element": {"family": "ip", "table": "t", "name": "s", "elem": {"set": [[{"concat": [{"range": [1, 10]}, 10]}, {"drop": null}]]}}}}
diff --git a/tests/monitor/testcases/set-simple.t b/tests/monitor/testcases/set-simple.t
index 8ca4f32..6853a0e 100644
--- a/tests/monitor/testcases/set-simple.t
+++ b/tests/monitor/testcases/set-simple.t
@@ -37,9 +37,10 @@ J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem"
# make sure half open before other element works
I add element ip t portrange { 1024-65535 }
I add element ip t portrange { 100-200 }
-O -
-J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}}
+O add element ip t portrange { 100-200 }
+O add element ip t portrange { 1024-65535 }
J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [100, 200]}]}}}}
+J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}}
# make sure deletion of elements works
I delete element ip t portrange { 0-10 }

View File

@ -0,0 +1,50 @@
From 82d38b67556dbebf42def4abefa6dca951fbd79e Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:10:32 +0100
Subject: [PATCH] tests: shell: skip two bitwise tests if multi-register
support isn't available
JIRA: https://issues.redhat.com/browse/RHEL-121194
Upstream Status: nftables commit 083c532a2e179ec350881ff5444f35c58b99937f
commit 083c532a2e179ec350881ff5444f35c58b99937f
Author: Florian Westphal <fw@strlen.de>
Date: Mon Sep 8 11:06:35 2025 +0200
tests: shell: skip two bitwise tests if multi-register support isn't available
These tests fail in case kernel requires bitwise RHS to be a constant
value.
Fixes: 67d2a8d4c86f ("tests: shell: add parser and packetpath test")
Reported-by: Yi Chen <yiche@redhat.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/shell/testcases/bitwise/bitwise_in_sets_and_maps | 1 +
tests/shell/testcases/packetpath/bitwise_with_map | 1 +
2 files changed, 2 insertions(+)
diff --git a/tests/shell/testcases/bitwise/bitwise_in_sets_and_maps b/tests/shell/testcases/bitwise/bitwise_in_sets_and_maps
index 4f5044f..e574f78 100755
--- a/tests/shell/testcases/bitwise/bitwise_in_sets_and_maps
+++ b/tests/shell/testcases/bitwise/bitwise_in_sets_and_maps
@@ -1,5 +1,6 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_bitwise_multireg)
# NFT_TEST_REQUIRES(NFT_TEST_HAVE_bitshift)
set -e
diff --git a/tests/shell/testcases/packetpath/bitwise_with_map b/tests/shell/testcases/packetpath/bitwise_with_map
index 33419e4..127b341 100755
--- a/tests/shell/testcases/packetpath/bitwise_with_map
+++ b/tests/shell/testcases/packetpath/bitwise_with_map
@@ -1,5 +1,6 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_bitwise_multireg)
# NFT_TEST_REQUIRES(NFT_TEST_HAVE_bitshift)
set -e

View File

@ -1,142 +0,0 @@
From 86deb09d9886a9ef9c089a6edc0859419e8b4dfd Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 7 Nov 2024 18:38:45 +0100
Subject: [PATCH] doc: extend description of fib expression
JIRA: https://issues.redhat.com/browse/RHEL-65346
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 782494b..c6a33bb 100644
--- a/doc/primary-expression.txt
+++ b/doc/primary-expression.txt
@@ -310,17 +310,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)
@@ -329,25 +360,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,186 @@
From 010b8bbdeb96a873fc030782394dd5e922554bed Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:10:32 +0100
Subject: [PATCH] monitor: Inform JSON printer when reporting an object delete
event
JIRA: https://issues.redhat.com/browse/RHEL-121194
Upstream Status: nftables commit 6c04d24d16f1d15f216f2b3c8e64c9062cd77487
Conflicts: 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 b61eeaf..8ce517b 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_flowtable_json(struct netlink_mon_handler *monh,
const char *cmd, struct flowtable *ft);
void monitor_print_rule_json(struct netlink_mon_handler *monh,
@@ -250,7 +250,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 a1e8c04..851b157 100644
--- a/src/json.c
+++ b/src/json.c
@@ -373,7 +373,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);
@@ -386,6 +386,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);
@@ -489,6 +492,7 @@ static json_t *obj_print_json(const struct obj *obj)
break;
}
+out:
return nft_json_pack("{s:o}", type, root);
}
@@ -1728,7 +1732,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) {
@@ -1884,7 +1888,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;
@@ -1903,7 +1907,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));
}
}
@@ -2116,9 +2120,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_flowtable_json(struct netlink_mon_handler *monh,
diff --git a/src/monitor.c b/src/monitor.c
index da1ad88..676bf61 100644
--- a/src/monitor.c
+++ b/src/monitor.c
@@ -549,7 +549,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

@ -1,83 +0,0 @@
From 21295af879d5cc6a41bd823e708a97684034ed1e Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 7 Nov 2024 18:38:46 +0100
Subject: [PATCH] json: collapse set element commands from parser
JIRA: https://issues.redhat.com/browse/RHEL-65346
Upstream Status: nftables commit 193faa5475a5df7d9ac0b1a8fe647196de3e5688
commit 193faa5475a5df7d9ac0b1a8fe647196de3e5688
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Thu Oct 31 21:38:02 2024 +0100
json: collapse set element commands from parser
Update json parser to collapse {add,create} element commands to reduce
memory consumption in the case of large sets defined by one element per
command:
{"nftables": [{"add": {"element": {"family": "ip", "table": "x", "name":
"y", "elem": [{"set": ["1.1.0.0"]}]}}},...]}
Add CTX_F_COLLAPSED flag to report that command has been collapsed.
This patch reduces memory consumption by ~32% this case.
Fixes: 20f1c60ac8c8 ("src: collapse set element commands from parser")
Reported-by: Eric Garver <eric@garver.life>
Tested-by: Eric Garver <eric@garver.life>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_json.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/src/parser_json.c b/src/parser_json.c
index 37ec34c..68c0600 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -18,6 +18,7 @@
#include <netlink.h>
#include <parser.h>
#include <rule.h>
+#include <cmd.h>
#include <sctp_chunk.h>
#include <socket.h>
@@ -49,6 +50,7 @@
#define CTX_F_SES (1 << 6) /* set_elem_expr_stmt */
#define CTX_F_MAP (1 << 7) /* LHS of map_expr */
#define CTX_F_CONCAT (1 << 8) /* inside concat_expr */
+#define CTX_F_COLLAPSED (1 << 9)
struct json_ctx {
struct nft_ctx *nft;
@@ -3490,6 +3492,15 @@ static struct cmd *json_parse_cmd_add_element(struct json_ctx *ctx,
handle_free(&h);
return NULL;
}
+
+ if ((op == CMD_CREATE || op == CMD_ADD) &&
+ nft_cmd_collapse_elems(op, ctx->cmds, &h, expr)) {
+ handle_free(&h);
+ expr_free(expr);
+ ctx->flags |= CTX_F_COLLAPSED;
+ return NULL;
+ }
+
return cmd_alloc(op, cmd_obj, &h, int_loc, expr);
}
@@ -4319,6 +4330,11 @@ static int __json_parse(struct json_ctx *ctx)
cmd = json_parse_cmd(ctx, value);
if (!cmd) {
+ if (ctx->flags & CTX_F_COLLAPSED) {
+ ctx->flags &= ~CTX_F_COLLAPSED;
+ continue;
+ }
+
json_error(ctx, "Parsing command array at index %zd failed.", index);
return -1;
}

View File

@ -0,0 +1,41 @@
From a440e595cd5f61948684de3ab0c666769cb9f695 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:10:59 +0100
Subject: [PATCH] libnftables: do not re-add default include directory in
include search path
JIRA: https://issues.redhat.com/browse/RHEL-121194
Upstream Status: nftables commit 3af59817b8d3994d52db0f1aa5dabeebc84dae45
commit 3af59817b8d3994d52db0f1aa5dabeebc84dae45
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Sep 24 23:54:12 2025 +0200
libnftables: do not re-add default include directory in include search path
Otherwise globbing might duplicate included files because
include_path_glob() is called twice.
Fixes: 7eb950a8e8fa ("libnftables: include canonical path to avoid duplicates")
Tested-by: Jeremy Sowden <jeremy@azazel.net>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/libnftables.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/libnftables.c b/src/libnftables.c
index c8293f7..9f6a1bc 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -176,6 +176,9 @@ static bool nft_ctx_find_include_path(struct nft_ctx *ctx, const char *path)
return true;
}
+ if (!strcmp(path, DEFAULT_INCLUDE_PATH))
+ return true;
+
return false;
}

View File

@ -0,0 +1,41 @@
From a67f0be23c3d90dc1148c4ad6b9b9215676acbe5 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:10:59 +0100
Subject: [PATCH] doc: fix tcpdump example
JIRA: https://issues.redhat.com/browse/RHEL-121194
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 6226713..834f95f 100644
--- a/doc/statements.txt
+++ b/doc/statements.txt
@@ -691,8 +691,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

@ -1,526 +0,0 @@
From d66b043a46f4b8e48ab96503613d4ea7483899d4 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 7 Nov 2024 18:38:46 +0100
Subject: [PATCH] json: Support typeof in set and map types
JIRA: https://issues.redhat.com/browse/RHEL-65346
Upstream Status: nftables commit bb6312484af93a83a9ec8716f3887a43566a775a
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 +-
tests/monitor/testcases/set-concat-interval.t | 2 +-
.../maps/dumps/0012map_concat_0.json-nft | 21 +++++--
.../maps/dumps/0017_map_variable_0.json-nft | 18 +++++-
.../maps/dumps/named_limits.json-nft | 55 ++++++++++++++++---
.../dumps/typeof_maps_add_delete.json-nft | 9 ++-
.../maps/dumps/typeof_maps_update_0.json-nft | 9 ++-
.../maps/dumps/vmap_timeout.json-nft | 22 ++++++--
.../packetpath/dumps/set_lookups.json-nft | 42 +++++++++++---
.../sets/dumps/0048set_counters_0.json-nft | 9 ++-
.../testcases/sets/dumps/inner_0.json-nft | 34 ++++++++++--
.../set_element_timeout_updates.json-nft | 9 ++-
15 files changed, 220 insertions(+), 41 deletions(-)
diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc
index 2f29ac0..244eb41 100644
--- a/doc/libnftables-json.adoc
+++ b/doc/libnftables-json.adoc
@@ -341,7 +341,7 @@ ____
"auto-merge":* 'BOOLEAN'
*}}*
-'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' ]
@@ -381,8 +381,9 @@ that they translate a unique key to a value.
Automatic merging of adjacent/overlapping set elements in interval sets.
==== 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 b1531ff..1f609bf 100644
--- a/src/json.c
+++ b/src/json.c
@@ -96,6 +96,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];
@@ -158,7 +169,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 68c0600..02cfcd6 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -1731,7 +1731,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}]}}}
diff --git a/tests/monitor/testcases/set-concat-interval.t b/tests/monitor/testcases/set-concat-interval.t
index 75f3828..3542b82 100644
--- a/tests/monitor/testcases/set-concat-interval.t
+++ b/tests/monitor/testcases/set-concat-interval.t
@@ -10,6 +10,6 @@ I add map ip t s { typeof udp length . @ih,32,32 : verdict; flags interval; elem
O add map ip t s { typeof udp length . @ih,32,32 : verdict; flags interval; }
O add element ip t s { 20-80 . 0x14 : accept }
O add element ip t s { 1-10 . 0xa : drop }
-J {"add": {"map": {"family": "ip", "name": "s", "table": "t", "type": ["integer", "integer"], "handle": 0, "map": "verdict", "flags": ["interval"]}}}
+J {"add": {"map": {"family": "ip", "name": "s", "table": "t", "type": {"typeof": {"concat": [{"payload": {"protocol": "udp", "field": "length"}}, {"payload": {"base": "ih", "offset": 32, "len": 32}}]}}, "handle": 0, "map": "verdict", "flags": ["interval"]}}}
J {"add": {"element": {"family": "ip", "table": "t", "name": "s", "elem": {"set": [[{"concat": [{"range": [20, 80]}, 20]}, {"accept": null}]]}}}}
J {"add": {"element": {"family": "ip", "table": "t", "name": "s", "elem": {"set": [[{"concat": [{"range": [1, 10]}, 10]}, {"drop": null}]]}}}}
diff --git a/tests/shell/testcases/maps/dumps/0012map_concat_0.json-nft b/tests/shell/testcases/maps/dumps/0012map_concat_0.json-nft
index 0005223..88bf498 100644
--- a/tests/shell/testcases/maps/dumps/0012map_concat_0.json-nft
+++ b/tests/shell/testcases/maps/dumps/0012map_concat_0.json-nft
@@ -31,10 +31,23 @@
"family": "ip",
"name": "w",
"table": "x",
- "type": [
- "ipv4_addr",
- "mark"
- ],
+ "type": {
+ "typeof": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "meta": {
+ "key": "mark"
+ }
+ }
+ ]
+ }
+ },
"handle": 0,
"map": "verdict",
"flags": [
diff --git a/tests/shell/testcases/maps/dumps/0017_map_variable_0.json-nft b/tests/shell/testcases/maps/dumps/0017_map_variable_0.json-nft
index 725498c..8eacf61 100644
--- a/tests/shell/testcases/maps/dumps/0017_map_variable_0.json-nft
+++ b/tests/shell/testcases/maps/dumps/0017_map_variable_0.json-nft
@@ -19,7 +19,14 @@
"family": "ip",
"name": "y",
"table": "x",
- "type": "ipv4_addr",
+ "type": {
+ "typeof": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ }
+ },
"handle": 0,
"map": "mark",
"elem": [
@@ -39,7 +46,14 @@
"family": "ip",
"name": "z",
"table": "x",
- "type": "ipv4_addr",
+ "type": {
+ "typeof": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ }
+ },
"handle": 0,
"map": "mark",
"elem": [
diff --git a/tests/shell/testcases/maps/dumps/named_limits.json-nft b/tests/shell/testcases/maps/dumps/named_limits.json-nft
index 7fa1298..3c6845a 100644
--- a/tests/shell/testcases/maps/dumps/named_limits.json-nft
+++ b/tests/shell/testcases/maps/dumps/named_limits.json-nft
@@ -75,7 +75,14 @@
"family": "inet",
"name": "tarpit4",
"table": "filter",
- "type": "ipv4_addr",
+ "type": {
+ "typeof": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ }
+ },
"handle": 0,
"size": 10000,
"flags": [
@@ -90,7 +97,14 @@
"family": "inet",
"name": "tarpit6",
"table": "filter",
- "type": "ipv6_addr",
+ "type": {
+ "typeof": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "saddr"
+ }
+ }
+ },
"handle": 0,
"size": 10000,
"flags": [
@@ -105,11 +119,29 @@
"family": "inet",
"name": "addr4limit",
"table": "filter",
- "type": [
- "inet_proto",
- "ipv4_addr",
- "inet_service"
- ],
+ "type": {
+ "typeof": {
+ "concat": [
+ {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "sport"
+ }
+ }
+ ]
+ }
+ },
"handle": 0,
"map": "limit",
"flags": [
@@ -244,7 +276,14 @@
"family": "inet",
"name": "saddr6limit",
"table": "filter",
- "type": "ipv6_addr",
+ "type": {
+ "typeof": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "saddr"
+ }
+ }
+ },
"handle": 0,
"map": "limit",
"flags": [
diff --git a/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft b/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft
index b3204a2..effe02d 100644
--- a/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft
+++ b/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft
@@ -39,7 +39,14 @@
"family": "ip",
"name": "dynmark",
"table": "dynset",
- "type": "ipv4_addr",
+ "type": {
+ "typeof": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ }
+ },
"handle": 0,
"map": "mark",
"size": 64,
diff --git a/tests/shell/testcases/maps/dumps/typeof_maps_update_0.json-nft b/tests/shell/testcases/maps/dumps/typeof_maps_update_0.json-nft
index 1d50477..7315146 100644
--- a/tests/shell/testcases/maps/dumps/typeof_maps_update_0.json-nft
+++ b/tests/shell/testcases/maps/dumps/typeof_maps_update_0.json-nft
@@ -50,7 +50,14 @@
"family": "ip",
"name": "sticky-set-svc-153CN2XYVUHRQ7UB",
"table": "kube-nfproxy-v4",
- "type": "ipv4_addr",
+ "type": {
+ "typeof": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ }
+ },
"handle": 0,
"map": "mark",
"size": 65535,
diff --git a/tests/shell/testcases/maps/dumps/vmap_timeout.json-nft b/tests/shell/testcases/maps/dumps/vmap_timeout.json-nft
index 1c3aa59..71e9a9e 100644
--- a/tests/shell/testcases/maps/dumps/vmap_timeout.json-nft
+++ b/tests/shell/testcases/maps/dumps/vmap_timeout.json-nft
@@ -87,10 +87,24 @@
"family": "inet",
"name": "portaddrmap",
"table": "filter",
- "type": [
- "ipv4_addr",
- "inet_service"
- ],
+ "type": {
+ "typeof": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "th",
+ "field": "dport"
+ }
+ }
+ ]
+ }
+ },
"handle": 0,
"map": "verdict",
"flags": [
diff --git a/tests/shell/testcases/packetpath/dumps/set_lookups.json-nft b/tests/shell/testcases/packetpath/dumps/set_lookups.json-nft
index 24363f9..bcf6914 100644
--- a/tests/shell/testcases/packetpath/dumps/set_lookups.json-nft
+++ b/tests/shell/testcases/packetpath/dumps/set_lookups.json-nft
@@ -60,10 +60,23 @@
"family": "ip",
"name": "s2",
"table": "t",
- "type": [
- "ipv4_addr",
- "iface_index"
- ],
+ "type": {
+ "typeof": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "meta": {
+ "key": "iif"
+ }
+ }
+ ]
+ }
+ },
"handle": 0,
"elem": [
{
@@ -113,10 +126,23 @@
"family": "ip",
"name": "nomatch",
"table": "t",
- "type": [
- "ipv4_addr",
- "iface_index"
- ],
+ "type": {
+ "typeof": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "meta": {
+ "key": "iif"
+ }
+ }
+ ]
+ }
+ },
"handle": 0,
"elem": [
{
diff --git a/tests/shell/testcases/sets/dumps/0048set_counters_0.json-nft b/tests/shell/testcases/sets/dumps/0048set_counters_0.json-nft
index 62a6a17..4be4112 100644
--- a/tests/shell/testcases/sets/dumps/0048set_counters_0.json-nft
+++ b/tests/shell/testcases/sets/dumps/0048set_counters_0.json-nft
@@ -31,7 +31,14 @@
"family": "ip",
"name": "y",
"table": "x",
- "type": "ipv4_addr",
+ "type": {
+ "typeof": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ }
+ },
"handle": 0,
"elem": [
{
diff --git a/tests/shell/testcases/sets/dumps/inner_0.json-nft b/tests/shell/testcases/sets/dumps/inner_0.json-nft
index 8d84e1c..e5dc198 100644
--- a/tests/shell/testcases/sets/dumps/inner_0.json-nft
+++ b/tests/shell/testcases/sets/dumps/inner_0.json-nft
@@ -27,10 +27,26 @@
"family": "netdev",
"name": "x",
"table": "x",
- "type": [
- "ipv4_addr",
- "ipv4_addr"
- ],
+ "type": {
+ "typeof": {
+ "concat": [
+ {
+ "payload": {
+ "tunnel": "vxlan",
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "payload": {
+ "tunnel": "vxlan",
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ }
+ ]
+ }
+ },
"handle": 0,
"elem": [
{
@@ -47,7 +63,15 @@
"family": "netdev",
"name": "y",
"table": "x",
- "type": "ipv4_addr",
+ "type": {
+ "typeof": {
+ "payload": {
+ "tunnel": "vxlan",
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ }
+ },
"handle": 0,
"size": 65535,
"flags": [
diff --git a/tests/shell/testcases/sets/dumps/set_element_timeout_updates.json-nft b/tests/shell/testcases/sets/dumps/set_element_timeout_updates.json-nft
index aa90829..d92d8d7 100644
--- a/tests/shell/testcases/sets/dumps/set_element_timeout_updates.json-nft
+++ b/tests/shell/testcases/sets/dumps/set_element_timeout_updates.json-nft
@@ -31,7 +31,14 @@
"family": "ip",
"name": "s",
"table": "t",
- "type": "ipv4_addr",
+ "type": {
+ "typeof": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ }
+ },
"handle": 0,
"flags": [
"timeout"

View File

@ -0,0 +1,60 @@
From 8cc3c94a06c23d37827b4c02537d691bf2b713a7 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:10:59 +0100
Subject: [PATCH] src: parser_json: fix format string bugs
JIRA: https://issues.redhat.com/browse/RHEL-121194
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 71e44f1..3a50c86 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -128,7 +128,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)))
@@ -4320,6 +4320,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, ...)
{
@@ -4392,7 +4393,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

@ -1,43 +0,0 @@
From 6c31db6766df3bdeb1ff6039e651a54850b68aa3 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 7 Nov 2024 18:38:46 +0100
Subject: [PATCH] tests: py: Fix for storing payload into missing file
JIRA: https://issues.redhat.com/browse/RHEL-65346
Upstream Status: nftables commit c1c0c54e237c880adaa8172b93d7450e6c617cfc
commit c1c0c54e237c880adaa8172b93d7450e6c617cfc
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Oct 2 19:55:49 2024 +0200
tests: py: Fix for storing payload into missing file
When running a test for which no corresponding *.payload file exists,
the *.payload.got file name was incorrectly constructed due to
'payload_path' variable not being set.
Fixes: 2cfab7a3e10fc ("tests/py: Write dissenting payload into the right file")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/nft-test.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/tests/py/nft-test.py b/tests/py/nft-test.py
index 00799e2..7acdb77 100755
--- a/tests/py/nft-test.py
+++ b/tests/py/nft-test.py
@@ -769,10 +769,9 @@ def rule_add(rule, filename, lineno, force_all_family_option, filename_path):
if rule[1].strip() == "ok":
payload_expected = None
- payload_path = None
+ payload_path = "%s.payload" % filename_path
try:
- payload_log = open("%s.payload" % filename_path)
- payload_path = payload_log.name
+ payload_log = open(payload_path)
payload_expected = payload_find_expected(payload_log, rule[0])
except:
payload_log = None

View File

@ -0,0 +1,39 @@
From 8c0c1f8820891bffd7fd992643eef8d3ffaf9937 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:10:59 +0100
Subject: [PATCH] datatype: Fix boolean type on Big Endian
JIRA: https://issues.redhat.com/browse/RHEL-121194
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 f347010..7104ae8 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -1559,7 +1559,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

@ -1,260 +0,0 @@
From 8cfbb8c3427f232484bacab3116f6925f3976c7b Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 7 Nov 2024 18:38:46 +0100
Subject: [PATCH] monitor: Recognize flowtable add/del events
JIRA: https://issues.redhat.com/browse/RHEL-65346
Upstream Status: nftables commit 73a8adfc2432ec8337288cc90e7c9f4509139846
commit 73a8adfc2432ec8337288cc90e7c9f4509139846
Author: Phil Sutter <phil@nwl.cc>
Date: Wed May 15 16:01:20 2024 +0200
monitor: Recognize flowtable add/del events
These were entirely ignored before, add the necessary code analogous to
e.g. objects.
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
include/json.h | 10 ++++
include/netlink.h | 1 +
include/rule.h | 1 +
src/json.c | 6 +++
src/monitor.c | 61 ++++++++++++++++++++++
src/parser_json.c | 6 +++
src/rule.c | 15 ++++++
tests/monitor/testcases/flowtable-simple.t | 10 ++++
8 files changed, 110 insertions(+)
create mode 100644 tests/monitor/testcases/flowtable-simple.t
diff --git a/include/json.h b/include/json.h
index 39be892..0670b87 100644
--- a/include/json.h
+++ b/include/json.h
@@ -11,6 +11,7 @@ struct nlmsghdr;
struct rule;
struct set;
struct obj;
+struct flowtable;
struct stmt;
struct symbol_table;
struct table;
@@ -113,6 +114,8 @@ 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);
+void monitor_print_flowtable_json(struct netlink_mon_handler *monh,
+ const char *cmd, struct flowtable *ft);
void monitor_print_rule_json(struct netlink_mon_handler *monh,
const char *cmd, struct rule *r);
@@ -254,6 +257,13 @@ static inline void monitor_print_obj_json(struct netlink_mon_handler *monh,
/* empty */
}
+static inline void
+monitor_print_flowtable_json(struct netlink_mon_handler *monh,
+ const char *cmd, struct flowtable *ft)
+{
+ /* empty */
+}
+
static inline void monitor_print_rule_json(struct netlink_mon_handler *monh,
const char *cmd, struct rule *r)
{
diff --git a/include/netlink.h b/include/netlink.h
index cf7ba36..e9667a2 100644
--- a/include/netlink.h
+++ b/include/netlink.h
@@ -97,6 +97,7 @@ extern struct nftnl_table *netlink_table_alloc(const struct nlmsghdr *nlh);
extern struct nftnl_chain *netlink_chain_alloc(const struct nlmsghdr *nlh);
extern struct nftnl_set *netlink_set_alloc(const struct nlmsghdr *nlh);
extern struct nftnl_obj *netlink_obj_alloc(const struct nlmsghdr *nlh);
+extern struct nftnl_flowtable *netlink_flowtable_alloc(const struct nlmsghdr *nlh);
extern struct nftnl_rule *netlink_rule_alloc(const struct nlmsghdr *nlh);
struct nft_data_linearize {
diff --git a/include/rule.h b/include/rule.h
index 48e148e..238be23 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -551,6 +551,7 @@ extern struct flowtable *flowtable_lookup_fuzzy(const char *ft_name,
const struct table **table);
void flowtable_print(const struct flowtable *n, struct output_ctx *octx);
+void flowtable_print_plain(const struct flowtable *ft, struct output_ctx *octx);
/**
* enum cmd_ops - command operations
diff --git a/src/json.c b/src/json.c
index 1f609bf..64a6888 100644
--- a/src/json.c
+++ b/src/json.c
@@ -2108,6 +2108,12 @@ void monitor_print_obj_json(struct netlink_mon_handler *monh,
monitor_print_json(monh, cmd, obj_print_json(o));
}
+void monitor_print_flowtable_json(struct netlink_mon_handler *monh,
+ const char *cmd, struct flowtable *ft)
+{
+ monitor_print_json(monh, cmd, flowtable_print_json(ft));
+}
+
void monitor_print_rule_json(struct netlink_mon_handler *monh,
const char *cmd, struct rule *r)
{
diff --git a/src/monitor.c b/src/monitor.c
index 2fc16d6..a787db8 100644
--- a/src/monitor.c
+++ b/src/monitor.c
@@ -127,6 +127,19 @@ struct nftnl_obj *netlink_obj_alloc(const struct nlmsghdr *nlh)
return nlo;
}
+struct nftnl_flowtable *netlink_flowtable_alloc(const struct nlmsghdr *nlh)
+{
+ struct nftnl_flowtable *nlf;
+
+ nlf = nftnl_flowtable_alloc();
+ if (nlf == NULL)
+ memory_allocation_error();
+ if (nftnl_flowtable_nlmsg_parse(nlh, nlf) < 0)
+ netlink_abi_error();
+
+ return nlf;
+}
+
static uint32_t netlink_msg2nftnl_of(uint32_t type, uint16_t flags)
{
switch (type) {
@@ -542,6 +555,50 @@ static int netlink_events_obj_cb(const struct nlmsghdr *nlh, int type,
return MNL_CB_OK;
}
+static int netlink_events_flowtable_cb(const struct nlmsghdr *nlh, int type,
+ struct netlink_mon_handler *monh)
+{
+ const char *family, *cmd;
+ struct nftnl_flowtable *nlf;
+ struct flowtable *ft;
+
+ nlf = netlink_flowtable_alloc(nlh);
+
+ ft = netlink_delinearize_flowtable(monh->ctx, nlf);
+ if (!ft) {
+ nftnl_flowtable_free(nlf);
+ return MNL_CB_ERROR;
+ }
+ family = family2str(ft->handle.family);
+ cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
+
+ switch (monh->format) {
+ case NFTNL_OUTPUT_DEFAULT:
+ nft_mon_print(monh, "%s ", cmd);
+
+ switch (type) {
+ case NFT_MSG_NEWFLOWTABLE:
+ flowtable_print_plain(ft, &monh->ctx->nft->output);
+ break;
+ case NFT_MSG_DELFLOWTABLE:
+ nft_mon_print(monh, "flowtable %s %s %s", family,
+ ft->handle.table.name,
+ ft->handle.flowtable.name);
+ break;
+ }
+ nft_mon_print(monh, "\n");
+ break;
+ case NFTNL_OUTPUT_JSON:
+ monitor_print_flowtable_json(monh, cmd, ft);
+ if (!nft_output_echo(&monh->ctx->nft->output))
+ nft_mon_print(monh, "\n");
+ break;
+ }
+ flowtable_free(ft);
+ nftnl_flowtable_free(nlf);
+ return MNL_CB_OK;
+}
+
static void rule_map_decompose_cb(struct set *s, void *data)
{
if (!set_is_anonymous(s->flags))
@@ -962,6 +1019,10 @@ static int netlink_events_cb(const struct nlmsghdr *nlh, void *data)
case NFT_MSG_DELOBJ:
ret = netlink_events_obj_cb(nlh, type, monh);
break;
+ case NFT_MSG_NEWFLOWTABLE:
+ case NFT_MSG_DELFLOWTABLE:
+ ret = netlink_events_flowtable_cb(nlh, type, monh);
+ break;
case NFT_MSG_NEWGEN:
ret = netlink_events_newgen_cb(nlh, type, monh);
break;
diff --git a/src/parser_json.c b/src/parser_json.c
index 02cfcd6..bae2c3c 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -4437,6 +4437,7 @@ static int json_echo_error(struct netlink_mon_handler *monh,
static uint64_t handle_from_nlmsg(const struct nlmsghdr *nlh)
{
+ struct nftnl_flowtable *nlf;
struct nftnl_table *nlt;
struct nftnl_chain *nlc;
struct nftnl_rule *nlr;
@@ -4473,6 +4474,11 @@ static uint64_t handle_from_nlmsg(const struct nlmsghdr *nlh)
handle = nftnl_obj_get_u64(nlo, NFTNL_OBJ_HANDLE);
nftnl_obj_free(nlo);
break;
+ case NFT_MSG_NEWFLOWTABLE:
+ nlf = netlink_flowtable_alloc(nlh);
+ handle = nftnl_flowtable_get_u64(nlf, NFTNL_FLOWTABLE_HANDLE);
+ nftnl_flowtable_free(nlf);
+ break;
}
return handle;
}
diff --git a/src/rule.c b/src/rule.c
index 9536e68..151ed53 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -2154,6 +2154,21 @@ void flowtable_print(const struct flowtable *s, struct output_ctx *octx)
do_flowtable_print(s, &opts, octx);
}
+void flowtable_print_plain(const struct flowtable *ft, struct output_ctx *octx)
+{
+ struct print_fmt_options opts = {
+ .tab = "",
+ .nl = " ",
+ .table = ft->handle.table.name,
+ .family = family2str(ft->handle.family),
+ .stmt_separator = "; ",
+ };
+
+ flowtable_print_declaration(ft, &opts, octx);
+ nft_print(octx, "}");
+}
+
+
struct flowtable *flowtable_lookup_fuzzy(const char *ft_name,
const struct nft_cache *cache,
const struct table **t)
diff --git a/tests/monitor/testcases/flowtable-simple.t b/tests/monitor/testcases/flowtable-simple.t
new file mode 100644
index 0000000..df8eccb
--- /dev/null
+++ b/tests/monitor/testcases/flowtable-simple.t
@@ -0,0 +1,10 @@
+# setup first
+I add table ip t
+I add flowtable ip t ft { hook ingress priority 0; devices = { lo }; }
+O -
+J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}}
+J {"add": {"flowtable": {"family": "ip", "name": "ft", "table": "t", "handle": 0, "hook": "ingress", "prio": 0, "dev": "lo"}}}
+
+I delete flowtable ip t ft
+O -
+J {"delete": {"flowtable": {"family": "ip", "name": "ft", "table": "t", "handle": 0, "hook": "ingress", "prio": 0, "dev": "lo"}}}

View File

@ -1,268 +0,0 @@
From 75c95b2f59fb09c6375ca1e10277af9d0641e71d Mon Sep 17 00:00:00 2001
From: Florian Westphal <fw@strlen.de>
Date: Wed, 22 Jan 2025 10:18:04 +0100
Subject: [PATCH] evaluate: allow to re-use existing metered set
JIRA: https://issues.redhat.com/browse/RHEL-75507
Upstream Status: nftables commit 639a111e91341cffdc6d86b847aa654646c799cf
commit 639a111e91341cffdc6d86b847aa654646c799cf
Author: Florian Westphal <fw@strlen.de>
Date: Wed Jan 22 10:18:04 2025 +0100
evaluate: allow to re-use existing metered set
Blamed commit translates old meter syntax (which used to allocate an
anonymous set) to dynamic sets.
A side effect of this is that re-adding a meter rule after chain was
flushed results in an error, unlike anonymous sets named sets are not
impacted by the flush.
Refine this: if a set of the same name exists and is compatible, then
re-use it instead of returning an error.
Also pick up the reproducer kindly provided by the reporter and place it
in the shell test directory.
Fixes: b8f8ddfff733 ("evaluate: translate meter into dynamic set")
Reported-by: Yi Chen <yiche@redhat.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Eric Garver <egarver@redhat.com>
---
src/evaluate.c | 43 +++++--
.../sets/dumps/meter_set_reuse.json-nft | 105 ++++++++++++++++++
.../testcases/sets/dumps/meter_set_reuse.nft | 11 ++
tests/shell/testcases/sets/meter_set_reuse | 20 ++++
4 files changed, 170 insertions(+), 9 deletions(-)
create mode 100644 tests/shell/testcases/sets/dumps/meter_set_reuse.json-nft
create mode 100644 tests/shell/testcases/sets/dumps/meter_set_reuse.nft
create mode 100755 tests/shell/testcases/sets/meter_set_reuse
diff --git a/src/evaluate.c b/src/evaluate.c
index 593a014..c9cbaa6 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -3338,7 +3338,7 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
{
- struct expr *key, *set, *setref;
+ struct expr *key, *setref;
struct set *existing_set;
struct table *table;
@@ -3349,7 +3349,9 @@ static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
return table_not_found(ctx);
existing_set = set_cache_find(table, stmt->meter.name);
- if (existing_set)
+ if (existing_set &&
+ (!set_is_meter_compat(existing_set->flags) ||
+ set_is_map(existing_set->flags)))
return cmd_error(ctx, &stmt->location,
"%s; meter '%s' overlaps an existing %s '%s' in family %s",
strerror(EEXIST),
@@ -3370,17 +3372,40 @@ static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
/* Declare an empty set */
key = stmt->meter.key;
- set = set_expr_alloc(&key->location, NULL);
- set->set_flags |= NFT_SET_EVAL;
- if (key->timeout)
- set->set_flags |= NFT_SET_TIMEOUT;
+ if (existing_set) {
+ if ((existing_set->flags & NFT_SET_TIMEOUT) && !key->timeout)
+ return expr_error(ctx->msgs, stmt->meter.key,
+ "existing set '%s' has timeout flag",
+ stmt->meter.name);
+
+ if ((existing_set->flags & NFT_SET_TIMEOUT) == 0 && key->timeout)
+ return expr_error(ctx->msgs, stmt->meter.key,
+ "existing set '%s' lacks timeout flag",
+ stmt->meter.name);
+
+ if (stmt->meter.size > 0 && existing_set->desc.size != stmt->meter.size)
+ return expr_error(ctx->msgs, stmt->meter.key,
+ "existing set '%s' has size %u, meter has %u",
+ stmt->meter.name, existing_set->desc.size,
+ stmt->meter.size);
+ setref = set_ref_expr_alloc(&key->location, existing_set);
+ } else {
+ struct expr *set;
+
+ set = set_expr_alloc(&key->location, existing_set);
+ if (key->timeout)
+ set->set_flags |= NFT_SET_TIMEOUT;
+
+ set->set_flags |= NFT_SET_EVAL;
+ setref = implicit_set_declaration(ctx, stmt->meter.name,
+ expr_get(key), NULL, set, 0);
+ if (setref)
+ setref->set->desc.size = stmt->meter.size;
+ }
- setref = implicit_set_declaration(ctx, stmt->meter.name,
- expr_get(key), NULL, set, 0);
if (!setref)
return -1;
- setref->set->desc.size = stmt->meter.size;
stmt->meter.set = setref;
if (stmt_evaluate(ctx, stmt->meter.stmt) < 0)
diff --git a/tests/shell/testcases/sets/dumps/meter_set_reuse.json-nft b/tests/shell/testcases/sets/dumps/meter_set_reuse.json-nft
new file mode 100644
index 0000000..ab4ac06
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/meter_set_reuse.json-nft
@@ -0,0 +1,105 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "input",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "http1",
+ "table": "filter",
+ "type": [
+ "inet_service",
+ "ipv4_addr"
+ ],
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 80
+ }
+ },
+ {
+ "set": {
+ "op": "add",
+ "elem": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ }
+ ]
+ },
+ "set": "@http1",
+ "stmt": [
+ {
+ "limit": {
+ "rate": 200,
+ "burst": 5,
+ "per": "second",
+ "inv": true
+ }
+ }
+ ]
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/meter_set_reuse.nft b/tests/shell/testcases/sets/dumps/meter_set_reuse.nft
new file mode 100644
index 0000000..f911aca
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/meter_set_reuse.nft
@@ -0,0 +1,11 @@
+table ip filter {
+ set http1 {
+ type inet_service . ipv4_addr
+ size 65535
+ flags dynamic
+ }
+
+ chain input {
+ tcp dport 80 add @http1 { tcp dport . ip saddr limit rate over 200/second burst 5 packets } counter packets 0 bytes 0 drop
+ }
+}
diff --git a/tests/shell/testcases/sets/meter_set_reuse b/tests/shell/testcases/sets/meter_set_reuse
new file mode 100755
index 0000000..94eccc1
--- /dev/null
+++ b/tests/shell/testcases/sets/meter_set_reuse
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+set -e
+
+addrule()
+{
+ $NFT add rule ip filter input tcp dport 80 meter http1 { tcp dport . ip saddr limit rate over 200/second } counter drop
+}
+
+$NFT add table filter
+$NFT add chain filter input
+addrule
+
+$NFT list meters
+
+# This used to remove the anon set, but not anymore
+$NFT flush chain filter input
+
+# This re-add should work.
+addrule

View File

@ -0,0 +1,56 @@
From 0550a1f430aa42cc4195adb3ac505d515d570a3a Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:10:59 +0100
Subject: [PATCH] optimize: Fix verdict expression comparison
JIRA: https://issues.redhat.com/browse/RHEL-121194
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 40756ce..ffad525 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -341,13 +341,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

@ -1,807 +0,0 @@
From bb46381b2d378729d709480806c9522aaa32deeb Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 15 Jul 2025 22:50:32 +0200
Subject: [PATCH] src: split monitor trace code into new trace.c
JIRA: https://issues.redhat.com/browse/RHEL-102994
Upstream Status: nftables commit 8e03d59b5aa46b960454b4fd30541cee77125f77
commit 8e03d59b5aa46b960454b4fd30541cee77125f77
Author: Florian Westphal <fw@strlen.de>
Date: Mon Jul 7 11:47:13 2025 +0200
src: split monitor trace code into new trace.c
Preparation patch to avoid putting more trace functionality into
netlink.c.
Signed-off-by: Florian Westphal <fw@strlen.de>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
Makefile.am | 1 +
include/netlink.h | 5 -
include/trace.h | 8 ++
src/monitor.c | 2 +-
src/netlink.c | 332 -------------------------------------------
src/trace.c | 353 ++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 363 insertions(+), 338 deletions(-)
create mode 100644 include/trace.h
create mode 100644 src/trace.c
diff --git a/Makefile.am b/Makefile.am
index fb64105..ba09e7f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -220,6 +220,7 @@ src_libnftables_la_SOURCES = \
src/misspell.c \
src/mnl.c \
src/monitor.c \
+ src/trace.c \
src/netlink.c \
src/netlink_delinearize.c \
src/netlink_linearize.c \
diff --git a/include/netlink.h b/include/netlink.h
index e9667a2..609f213 100644
--- a/include/netlink.h
+++ b/include/netlink.h
@@ -227,11 +227,6 @@ struct ruleset_parse {
struct cmd *cmd;
};
-struct nftnl_parse_ctx;
-
-int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type,
- struct netlink_mon_handler *monh);
-
enum nft_data_types dtype_map_to_kernel(const struct datatype *dtype);
void netlink_linearize_init(struct netlink_linearize_ctx *lctx,
diff --git a/include/trace.h b/include/trace.h
new file mode 100644
index 0000000..ebebb47
--- /dev/null
+++ b/include/trace.h
@@ -0,0 +1,8 @@
+#ifndef NFTABLES_TRACE_H
+#define NFTABLES_TRACE_H
+#include <linux/netlink.h>
+
+struct netlink_mon_handler;
+int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type,
+ struct netlink_mon_handler *monh);
+#endif /* NFTABLES_TRACE_H */
diff --git a/src/monitor.c b/src/monitor.c
index a787db8..01325c9 100644
--- a/src/monitor.c
+++ b/src/monitor.c
@@ -16,7 +16,6 @@
#include <inttypes.h>
#include <libnftnl/table.h>
-#include <libnftnl/trace.h>
#include <libnftnl/chain.h>
#include <libnftnl/expr.h>
#include <libnftnl/object.h>
@@ -32,6 +31,7 @@
#include <nftables.h>
#include <netlink.h>
#include <mnl.h>
+#include <trace.h>
#include <expression.h>
#include <statement.h>
#include <gmputil.h>
diff --git a/src/netlink.c b/src/netlink.c
index 25ee341..2ced863 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -18,7 +18,6 @@
#include <inttypes.h>
#include <libnftnl/table.h>
-#include <libnftnl/trace.h>
#include <libnftnl/chain.h>
#include <libnftnl/expr.h>
#include <libnftnl/object.h>
@@ -41,7 +40,6 @@
#include <gmputil.h>
#include <utils.h>
#include <erec.h>
-#include <iface.h>
#define nft_mon_print(monh, ...) nft_print(&monh->ctx->nft->output, __VA_ARGS__)
@@ -1859,333 +1857,3 @@ int netlink_list_flowtables(struct netlink_ctx *ctx, const struct handle *h)
nftnl_flowtable_list_free(flowtable_cache);
return err;
}
-
-static void trace_print_hdr(const struct nftnl_trace *nlt,
- struct output_ctx *octx)
-{
- nft_print(octx, "trace id %08x %s ",
- nftnl_trace_get_u32(nlt, NFTNL_TRACE_ID),
- family2str(nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY)));
- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_TABLE))
- nft_print(octx, "%s ",
- nftnl_trace_get_str(nlt, NFTNL_TRACE_TABLE));
- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_CHAIN))
- nft_print(octx, "%s ",
- nftnl_trace_get_str(nlt, NFTNL_TRACE_CHAIN));
-}
-
-static void trace_print_expr(const struct nftnl_trace *nlt, unsigned int attr,
- struct expr *lhs, struct output_ctx *octx)
-{
- struct expr *rhs, *rel;
- const void *data;
- uint32_t len;
-
- data = nftnl_trace_get_data(nlt, attr, &len);
- rhs = constant_expr_alloc(&netlink_location,
- lhs->dtype, lhs->byteorder,
- len * BITS_PER_BYTE, data);
- rel = relational_expr_alloc(&netlink_location, OP_EQ, lhs, rhs);
-
- expr_print(rel, octx);
- nft_print(octx, " ");
- expr_free(rel);
-}
-
-static void trace_print_verdict(const struct nftnl_trace *nlt,
- struct output_ctx *octx)
-{
- struct expr *chain_expr = NULL;
- const char *chain = NULL;
- unsigned int verdict;
- struct expr *expr;
-
- verdict = nftnl_trace_get_u32(nlt, NFTNL_TRACE_VERDICT);
- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_JUMP_TARGET)) {
- chain = xstrdup(nftnl_trace_get_str(nlt, NFTNL_TRACE_JUMP_TARGET));
- chain_expr = constant_expr_alloc(&netlink_location,
- &string_type,
- BYTEORDER_HOST_ENDIAN,
- strlen(chain) * BITS_PER_BYTE,
- chain);
- }
- expr = verdict_expr_alloc(&netlink_location, verdict, chain_expr);
-
- nft_print(octx, "verdict ");
- expr_print(expr, octx);
- expr_free(expr);
-}
-
-static void trace_print_policy(const struct nftnl_trace *nlt,
- struct output_ctx *octx)
-{
- unsigned int policy;
- struct expr *expr;
-
- policy = nftnl_trace_get_u32(nlt, NFTNL_TRACE_POLICY);
-
- expr = verdict_expr_alloc(&netlink_location, policy, NULL);
-
- nft_print(octx, "policy ");
- expr_print(expr, octx);
- expr_free(expr);
-}
-
-static struct rule *trace_lookup_rule(const struct nftnl_trace *nlt,
- uint64_t rule_handle,
- struct nft_cache *cache)
-{
- struct chain *chain;
- struct table *table;
- struct handle h;
-
- h.family = nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY);
- h.table.name = nftnl_trace_get_str(nlt, NFTNL_TRACE_TABLE);
- h.chain.name = nftnl_trace_get_str(nlt, NFTNL_TRACE_CHAIN);
-
- if (!h.table.name)
- return NULL;
-
- table = table_cache_find(&cache->table_cache, h.table.name, h.family);
- if (!table)
- return NULL;
-
- chain = chain_cache_find(table, h.chain.name);
- if (!chain)
- return NULL;
-
- return rule_lookup(chain, rule_handle);
-}
-
-static void trace_print_rule(const struct nftnl_trace *nlt,
- struct output_ctx *octx, struct nft_cache *cache)
-{
- uint64_t rule_handle;
- struct rule *rule;
-
- rule_handle = nftnl_trace_get_u64(nlt, NFTNL_TRACE_RULE_HANDLE);
- rule = trace_lookup_rule(nlt, rule_handle, cache);
-
- trace_print_hdr(nlt, octx);
-
- if (rule) {
- nft_print(octx, "rule ");
- rule_print(rule, octx);
- } else {
- nft_print(octx, "unknown rule handle %" PRIu64, rule_handle);
- }
-
- nft_print(octx, " (");
- trace_print_verdict(nlt, octx);
- nft_print(octx, ")\n");
-}
-
-static void trace_gen_stmts(struct list_head *stmts,
- struct proto_ctx *ctx, struct payload_dep_ctx *pctx,
- const struct nftnl_trace *nlt, unsigned int attr,
- enum proto_bases base)
-{
- struct list_head unordered = LIST_HEAD_INIT(unordered);
- struct list_head list;
- struct expr *rel, *lhs, *rhs, *tmp, *nexpr;
- struct stmt *stmt;
- const struct proto_desc *desc;
- const void *hdr;
- uint32_t hlen;
- unsigned int n;
-
- if (!nftnl_trace_is_set(nlt, attr))
- return;
- hdr = nftnl_trace_get_data(nlt, attr, &hlen);
-
- lhs = payload_expr_alloc(&netlink_location, NULL, 0);
- payload_init_raw(lhs, base, 0, hlen * BITS_PER_BYTE);
- rhs = constant_expr_alloc(&netlink_location,
- &invalid_type, BYTEORDER_INVALID,
- hlen * BITS_PER_BYTE, hdr);
-
-restart:
- init_list_head(&list);
- payload_expr_expand(&list, lhs, ctx);
- expr_free(lhs);
-
- desc = NULL;
- list_for_each_entry_safe(lhs, nexpr, &list, list) {
- if (desc && desc != ctx->protocol[base].desc) {
- /* Chained protocols */
- lhs->payload.offset = 0;
- if (ctx->protocol[base].desc == NULL)
- break;
- goto restart;
- }
-
- tmp = constant_expr_splice(rhs, lhs->len);
- expr_set_type(tmp, lhs->dtype, lhs->byteorder);
- if (tmp->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(tmp->value, tmp->len / BITS_PER_BYTE);
-
- /* Skip unknown and filtered expressions */
- desc = lhs->payload.desc;
- if (lhs->dtype == &invalid_type ||
- lhs->payload.tmpl == &proto_unknown_template ||
- desc->checksum_key == payload_hdr_field(lhs) ||
- desc->format.filter & (1 << payload_hdr_field(lhs))) {
- expr_free(lhs);
- expr_free(tmp);
- continue;
- }
-
- rel = relational_expr_alloc(&lhs->location, OP_EQ, lhs, tmp);
- stmt = expr_stmt_alloc(&rel->location, rel);
- list_add_tail(&stmt->list, &unordered);
-
- desc = ctx->protocol[base].desc;
- relational_expr_pctx_update(ctx, rel);
- }
-
- expr_free(rhs);
-
- n = 0;
-next:
- list_for_each_entry(stmt, &unordered, list) {
- enum proto_bases b = base;
-
- rel = stmt->expr;
- lhs = rel->left;
-
- /* Move statements to result list in defined order */
- desc = lhs->payload.desc;
- if (desc->format.order[n] &&
- desc->format.order[n] != payload_hdr_field(lhs))
- continue;
-
- list_move_tail(&stmt->list, stmts);
- n++;
-
- if (payload_is_stacked(desc, rel))
- b--;
-
- /* Don't strip 'icmp type' from payload dump. */
- if (pctx->icmp_type == 0)
- payload_dependency_kill(pctx, lhs, ctx->family);
- if (lhs->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(pctx, stmt, b);
-
- goto next;
- }
-}
-
-static void trace_print_packet(const struct nftnl_trace *nlt,
- struct output_ctx *octx)
-{
- struct list_head stmts = LIST_HEAD_INIT(stmts);
- const struct proto_desc *ll_desc;
- struct payload_dep_ctx pctx = {};
- struct proto_ctx ctx;
- uint16_t dev_type;
- uint32_t nfproto;
- struct stmt *stmt, *next;
-
- trace_print_hdr(nlt, octx);
-
- nft_print(octx, "packet: ");
- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_IIF))
- trace_print_expr(nlt, NFTNL_TRACE_IIF,
- meta_expr_alloc(&netlink_location,
- NFT_META_IIF), octx);
- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_OIF))
- trace_print_expr(nlt, NFTNL_TRACE_OIF,
- meta_expr_alloc(&netlink_location,
- NFT_META_OIF), octx);
-
- proto_ctx_init(&ctx, nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY), 0, false);
- ll_desc = ctx.protocol[PROTO_BASE_LL_HDR].desc;
- if ((ll_desc == &proto_inet || ll_desc == &proto_netdev) &&
- nftnl_trace_is_set(nlt, NFTNL_TRACE_NFPROTO)) {
- nfproto = nftnl_trace_get_u32(nlt, NFTNL_TRACE_NFPROTO);
-
- proto_ctx_update(&ctx, PROTO_BASE_LL_HDR, &netlink_location, NULL);
- proto_ctx_update(&ctx, PROTO_BASE_NETWORK_HDR, &netlink_location,
- proto_find_upper(ll_desc, nfproto));
- }
- if (ctx.protocol[PROTO_BASE_LL_HDR].desc == NULL &&
- nftnl_trace_is_set(nlt, NFTNL_TRACE_IIFTYPE)) {
- dev_type = nftnl_trace_get_u16(nlt, NFTNL_TRACE_IIFTYPE);
- proto_ctx_update(&ctx, PROTO_BASE_LL_HDR, &netlink_location,
- proto_dev_desc(dev_type));
- }
-
- trace_gen_stmts(&stmts, &ctx, &pctx, nlt, NFTNL_TRACE_LL_HEADER,
- PROTO_BASE_LL_HDR);
- trace_gen_stmts(&stmts, &ctx, &pctx, nlt, NFTNL_TRACE_NETWORK_HEADER,
- PROTO_BASE_NETWORK_HDR);
- trace_gen_stmts(&stmts, &ctx, &pctx, nlt, NFTNL_TRACE_TRANSPORT_HEADER,
- PROTO_BASE_TRANSPORT_HDR);
-
- list_for_each_entry_safe(stmt, next, &stmts, list) {
- stmt_print(stmt, octx);
- nft_print(octx, " ");
- stmt_free(stmt);
- }
- nft_print(octx, "\n");
-}
-
-int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type,
- struct netlink_mon_handler *monh)
-{
- struct nftnl_trace *nlt;
-
- assert(type == NFT_MSG_TRACE);
-
- nlt = nftnl_trace_alloc();
- if (!nlt)
- memory_allocation_error();
-
- if (nftnl_trace_nlmsg_parse(nlh, nlt) < 0)
- netlink_abi_error();
-
- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_LL_HEADER) ||
- nftnl_trace_is_set(nlt, NFTNL_TRACE_NETWORK_HEADER))
- trace_print_packet(nlt, &monh->ctx->nft->output);
-
- switch (nftnl_trace_get_u32(nlt, NFTNL_TRACE_TYPE)) {
- case NFT_TRACETYPE_RULE:
- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_RULE_HANDLE))
- trace_print_rule(nlt, &monh->ctx->nft->output,
- &monh->ctx->nft->cache);
- break;
- case NFT_TRACETYPE_POLICY:
- trace_print_hdr(nlt, &monh->ctx->nft->output);
-
- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_POLICY)) {
- trace_print_policy(nlt, &monh->ctx->nft->output);
- nft_mon_print(monh, " ");
- }
-
- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_MARK))
- trace_print_expr(nlt, NFTNL_TRACE_MARK,
- meta_expr_alloc(&netlink_location,
- NFT_META_MARK),
- &monh->ctx->nft->output);
- nft_mon_print(monh, "\n");
- break;
- case NFT_TRACETYPE_RETURN:
- trace_print_hdr(nlt, &monh->ctx->nft->output);
-
- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_VERDICT)) {
- trace_print_verdict(nlt, &monh->ctx->nft->output);
- nft_mon_print(monh, " ");
- }
-
- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_MARK))
- trace_print_expr(nlt, NFTNL_TRACE_MARK,
- meta_expr_alloc(&netlink_location,
- NFT_META_MARK),
- &monh->ctx->nft->output);
- nft_mon_print(monh, "\n");
- break;
- }
-
- nftnl_trace_free(nlt);
- return MNL_CB_OK;
-}
diff --git a/src/trace.c b/src/trace.c
new file mode 100644
index 0000000..a7cc8ff
--- /dev/null
+++ b/src/trace.c
@@ -0,0 +1,353 @@
+#include <nft.h>
+#include <trace.h>
+
+#include <libnftnl/trace.h>
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter.h>
+
+#include <nftables.h>
+#include <mnl.h>
+#include <parser.h>
+#include <netlink.h>
+#include <expression.h>
+#include <statement.h>
+#include <utils.h>
+
+#define nft_mon_print(monh, ...) nft_print(&monh->ctx->nft->output, __VA_ARGS__)
+
+static void trace_print_hdr(const struct nftnl_trace *nlt,
+ struct output_ctx *octx)
+{
+ nft_print(octx, "trace id %08x %s ",
+ nftnl_trace_get_u32(nlt, NFTNL_TRACE_ID),
+ family2str(nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY)));
+ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_TABLE))
+ nft_print(octx, "%s ",
+ nftnl_trace_get_str(nlt, NFTNL_TRACE_TABLE));
+ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_CHAIN))
+ nft_print(octx, "%s ",
+ nftnl_trace_get_str(nlt, NFTNL_TRACE_CHAIN));
+}
+
+static void trace_print_expr(const struct nftnl_trace *nlt, unsigned int attr,
+ struct expr *lhs, struct output_ctx *octx)
+{
+ struct expr *rhs, *rel;
+ const void *data;
+ uint32_t len;
+
+ data = nftnl_trace_get_data(nlt, attr, &len);
+ rhs = constant_expr_alloc(&netlink_location,
+ lhs->dtype, lhs->byteorder,
+ len * BITS_PER_BYTE, data);
+ rel = relational_expr_alloc(&netlink_location, OP_EQ, lhs, rhs);
+
+ expr_print(rel, octx);
+ nft_print(octx, " ");
+ expr_free(rel);
+}
+
+static void trace_print_verdict(const struct nftnl_trace *nlt,
+ struct output_ctx *octx)
+{
+ struct expr *chain_expr = NULL;
+ const char *chain = NULL;
+ unsigned int verdict;
+ struct expr *expr;
+
+ verdict = nftnl_trace_get_u32(nlt, NFTNL_TRACE_VERDICT);
+ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_JUMP_TARGET)) {
+ chain = xstrdup(nftnl_trace_get_str(nlt, NFTNL_TRACE_JUMP_TARGET));
+ chain_expr = constant_expr_alloc(&netlink_location,
+ &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen(chain) * BITS_PER_BYTE,
+ chain);
+ }
+ expr = verdict_expr_alloc(&netlink_location, verdict, chain_expr);
+
+ nft_print(octx, "verdict ");
+ expr_print(expr, octx);
+ expr_free(expr);
+}
+
+static void trace_print_policy(const struct nftnl_trace *nlt,
+ struct output_ctx *octx)
+{
+ unsigned int policy;
+ struct expr *expr;
+
+ policy = nftnl_trace_get_u32(nlt, NFTNL_TRACE_POLICY);
+
+ expr = verdict_expr_alloc(&netlink_location, policy, NULL);
+
+ nft_print(octx, "policy ");
+ expr_print(expr, octx);
+ expr_free(expr);
+}
+
+static struct rule *trace_lookup_rule(const struct nftnl_trace *nlt,
+ uint64_t rule_handle,
+ struct nft_cache *cache)
+{
+ struct chain *chain;
+ struct table *table;
+ struct handle h;
+
+ h.family = nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY);
+ h.table.name = nftnl_trace_get_str(nlt, NFTNL_TRACE_TABLE);
+ h.chain.name = nftnl_trace_get_str(nlt, NFTNL_TRACE_CHAIN);
+
+ if (!h.table.name)
+ return NULL;
+
+ table = table_cache_find(&cache->table_cache, h.table.name, h.family);
+ if (!table)
+ return NULL;
+
+ chain = chain_cache_find(table, h.chain.name);
+ if (!chain)
+ return NULL;
+
+ return rule_lookup(chain, rule_handle);
+}
+
+static void trace_print_rule(const struct nftnl_trace *nlt,
+ struct output_ctx *octx, struct nft_cache *cache)
+{
+ uint64_t rule_handle;
+ struct rule *rule;
+
+ rule_handle = nftnl_trace_get_u64(nlt, NFTNL_TRACE_RULE_HANDLE);
+ rule = trace_lookup_rule(nlt, rule_handle, cache);
+
+ trace_print_hdr(nlt, octx);
+
+ if (rule) {
+ nft_print(octx, "rule ");
+ rule_print(rule, octx);
+ } else {
+ nft_print(octx, "unknown rule handle %" PRIu64, rule_handle);
+ }
+
+ nft_print(octx, " (");
+ trace_print_verdict(nlt, octx);
+ nft_print(octx, ")\n");
+}
+
+static void trace_gen_stmts(struct list_head *stmts,
+ struct proto_ctx *ctx, struct payload_dep_ctx *pctx,
+ const struct nftnl_trace *nlt, unsigned int attr,
+ enum proto_bases base)
+{
+ struct list_head unordered = LIST_HEAD_INIT(unordered);
+ struct list_head list;
+ struct expr *rel, *lhs, *rhs, *tmp, *nexpr;
+ struct stmt *stmt;
+ const struct proto_desc *desc;
+ const void *hdr;
+ uint32_t hlen;
+ unsigned int n;
+
+ if (!nftnl_trace_is_set(nlt, attr))
+ return;
+ hdr = nftnl_trace_get_data(nlt, attr, &hlen);
+
+ lhs = payload_expr_alloc(&netlink_location, NULL, 0);
+ payload_init_raw(lhs, base, 0, hlen * BITS_PER_BYTE);
+ rhs = constant_expr_alloc(&netlink_location,
+ &invalid_type, BYTEORDER_INVALID,
+ hlen * BITS_PER_BYTE, hdr);
+
+restart:
+ init_list_head(&list);
+ payload_expr_expand(&list, lhs, ctx);
+ expr_free(lhs);
+
+ desc = NULL;
+ list_for_each_entry_safe(lhs, nexpr, &list, list) {
+ if (desc && desc != ctx->protocol[base].desc) {
+ /* Chained protocols */
+ lhs->payload.offset = 0;
+ if (ctx->protocol[base].desc == NULL)
+ break;
+ goto restart;
+ }
+
+ tmp = constant_expr_splice(rhs, lhs->len);
+ expr_set_type(tmp, lhs->dtype, lhs->byteorder);
+ if (tmp->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(tmp->value, tmp->len / BITS_PER_BYTE);
+
+ /* Skip unknown and filtered expressions */
+ desc = lhs->payload.desc;
+ if (lhs->dtype == &invalid_type ||
+ lhs->payload.tmpl == &proto_unknown_template ||
+ desc->checksum_key == payload_hdr_field(lhs) ||
+ desc->format.filter & (1 << payload_hdr_field(lhs))) {
+ expr_free(lhs);
+ expr_free(tmp);
+ continue;
+ }
+
+ rel = relational_expr_alloc(&lhs->location, OP_EQ, lhs, tmp);
+ stmt = expr_stmt_alloc(&rel->location, rel);
+ list_add_tail(&stmt->list, &unordered);
+
+ desc = ctx->protocol[base].desc;
+ relational_expr_pctx_update(ctx, rel);
+ }
+
+ expr_free(rhs);
+
+ n = 0;
+next:
+ list_for_each_entry(stmt, &unordered, list) {
+ enum proto_bases b = base;
+
+ rel = stmt->expr;
+ lhs = rel->left;
+
+ /* Move statements to result list in defined order */
+ desc = lhs->payload.desc;
+ if (desc->format.order[n] &&
+ desc->format.order[n] != payload_hdr_field(lhs))
+ continue;
+
+ list_move_tail(&stmt->list, stmts);
+ n++;
+
+ if (payload_is_stacked(desc, rel))
+ b--;
+
+ /* Don't strip 'icmp type' from payload dump. */
+ if (pctx->icmp_type == 0)
+ payload_dependency_kill(pctx, lhs, ctx->family);
+ if (lhs->flags & EXPR_F_PROTOCOL)
+ payload_dependency_store(pctx, stmt, b);
+
+ goto next;
+ }
+}
+
+static void trace_print_packet(const struct nftnl_trace *nlt,
+ struct output_ctx *octx)
+{
+ struct list_head stmts = LIST_HEAD_INIT(stmts);
+ const struct proto_desc *ll_desc;
+ struct payload_dep_ctx pctx = {};
+ struct proto_ctx ctx;
+ uint16_t dev_type;
+ uint32_t nfproto;
+ struct stmt *stmt, *next;
+
+ trace_print_hdr(nlt, octx);
+
+ nft_print(octx, "packet: ");
+ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_IIF))
+ trace_print_expr(nlt, NFTNL_TRACE_IIF,
+ meta_expr_alloc(&netlink_location,
+ NFT_META_IIF), octx);
+ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_OIF))
+ trace_print_expr(nlt, NFTNL_TRACE_OIF,
+ meta_expr_alloc(&netlink_location,
+ NFT_META_OIF), octx);
+
+ proto_ctx_init(&ctx, nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY), 0, false);
+ ll_desc = ctx.protocol[PROTO_BASE_LL_HDR].desc;
+ if ((ll_desc == &proto_inet || ll_desc == &proto_netdev) &&
+ nftnl_trace_is_set(nlt, NFTNL_TRACE_NFPROTO)) {
+ nfproto = nftnl_trace_get_u32(nlt, NFTNL_TRACE_NFPROTO);
+
+ proto_ctx_update(&ctx, PROTO_BASE_LL_HDR, &netlink_location, NULL);
+ proto_ctx_update(&ctx, PROTO_BASE_NETWORK_HDR, &netlink_location,
+ proto_find_upper(ll_desc, nfproto));
+ }
+ if (ctx.protocol[PROTO_BASE_LL_HDR].desc == NULL &&
+ nftnl_trace_is_set(nlt, NFTNL_TRACE_IIFTYPE)) {
+ dev_type = nftnl_trace_get_u16(nlt, NFTNL_TRACE_IIFTYPE);
+ proto_ctx_update(&ctx, PROTO_BASE_LL_HDR, &netlink_location,
+ proto_dev_desc(dev_type));
+ }
+
+ trace_gen_stmts(&stmts, &ctx, &pctx, nlt, NFTNL_TRACE_LL_HEADER,
+ PROTO_BASE_LL_HDR);
+ trace_gen_stmts(&stmts, &ctx, &pctx, nlt, NFTNL_TRACE_NETWORK_HEADER,
+ PROTO_BASE_NETWORK_HDR);
+ trace_gen_stmts(&stmts, &ctx, &pctx, nlt, NFTNL_TRACE_TRANSPORT_HEADER,
+ PROTO_BASE_TRANSPORT_HDR);
+
+ list_for_each_entry_safe(stmt, next, &stmts, list) {
+ stmt_print(stmt, octx);
+ nft_print(octx, " ");
+ stmt_free(stmt);
+ }
+ nft_print(octx, "\n");
+}
+
+int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type,
+ struct netlink_mon_handler *monh)
+{
+ struct nftnl_trace *nlt;
+
+ assert(type == NFT_MSG_TRACE);
+
+ nlt = nftnl_trace_alloc();
+ if (!nlt)
+ memory_allocation_error();
+
+ if (nftnl_trace_nlmsg_parse(nlh, nlt) < 0)
+ netlink_abi_error();
+
+ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_LL_HEADER) ||
+ nftnl_trace_is_set(nlt, NFTNL_TRACE_NETWORK_HEADER))
+ trace_print_packet(nlt, &monh->ctx->nft->output);
+
+ switch (nftnl_trace_get_u32(nlt, NFTNL_TRACE_TYPE)) {
+ case NFT_TRACETYPE_RULE:
+ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_RULE_HANDLE))
+ trace_print_rule(nlt, &monh->ctx->nft->output,
+ &monh->ctx->nft->cache);
+ break;
+ case NFT_TRACETYPE_POLICY:
+ trace_print_hdr(nlt, &monh->ctx->nft->output);
+
+ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_POLICY)) {
+ trace_print_policy(nlt, &monh->ctx->nft->output);
+ nft_mon_print(monh, " ");
+ }
+
+ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_MARK))
+ trace_print_expr(nlt, NFTNL_TRACE_MARK,
+ meta_expr_alloc(&netlink_location,
+ NFT_META_MARK),
+ &monh->ctx->nft->output);
+ nft_mon_print(monh, "\n");
+ break;
+ case NFT_TRACETYPE_RETURN:
+ trace_print_hdr(nlt, &monh->ctx->nft->output);
+
+ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_VERDICT)) {
+ trace_print_verdict(nlt, &monh->ctx->nft->output);
+ nft_mon_print(monh, " ");
+ }
+
+ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_MARK))
+ trace_print_expr(nlt, NFTNL_TRACE_MARK,
+ meta_expr_alloc(&netlink_location,
+ NFT_META_MARK),
+ &monh->ctx->nft->output);
+ nft_mon_print(monh, "\n");
+ break;
+ }
+
+ nftnl_trace_free(nlt);
+ return MNL_CB_OK;
+}

View File

@ -0,0 +1,167 @@
From 116111d7182fba61985a5fae750e275da397d1a6 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:10:59 +0100
Subject: [PATCH] tests: py: any/tcpopt.t.json: Fix JSON equivalent
JIRA: https://issues.redhat.com/browse/RHEL-121194
Upstream Status: nftables commit 4282c50e4986cf0414282306dbff5fbad2f3c686
commit 4282c50e4986cf0414282306dbff5fbad2f3c686
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Oct 8 23:46:34 2025 +0200
tests: py: any/tcpopt.t.json: Fix JSON equivalent
Set element ordering differed from the rule in standard syntax.
Fixes: d199cca92f9eb ("expression: expr_build_udata_recurse should recurse")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/any/tcpopt.t.json | 24 +++++-----
tests/py/any/tcpopt.t.json.output | 76 +++++++++++++++++++++++++++++++
2 files changed, 88 insertions(+), 12 deletions(-)
diff --git a/tests/py/any/tcpopt.t.json b/tests/py/any/tcpopt.t.json
index e712a5e..65fa1de 100644
--- a/tests/py/any/tcpopt.t.json
+++ b/tests/py/any/tcpopt.t.json
@@ -622,20 +622,20 @@
},
{
"concat": [
- "remove-addr",
- 300
+ "mp-join",
+ 100
]
},
{
"concat": [
- "mp-fastclose",
- 600
+ "add-addr",
+ 200
]
},
{
"concat": [
- "mp-join",
- 100
+ "remove-addr",
+ 300
]
},
{
@@ -646,20 +646,20 @@
},
{
"concat": [
- "mp-tcprst",
- 700
+ "mp-fail",
+ 500
]
},
{
"concat": [
- "add-addr",
- 200
+ "mp-fastclose",
+ 600
]
},
{
"concat": [
- "mp-fail",
- 500
+ "mp-tcprst",
+ 700
]
}
]
diff --git a/tests/py/any/tcpopt.t.json.output b/tests/py/any/tcpopt.t.json.output
index ad0d25f..ae979e7 100644
--- a/tests/py/any/tcpopt.t.json.output
+++ b/tests/py/any/tcpopt.t.json.output
@@ -30,3 +30,79 @@
}
]
+# tcp option mptcp subtype . tcp dport { mp-capable . 10, mp-join . 100, add-addr . 200, remove-addr . 300, mp-prio . 400, mp-fail . 500, mp-fastclose . 600, mp-tcprst . 700 }
+[
+ {
+ "match": {
+ "left": {
+ "concat": [
+ {
+ "tcp option": {
+ "field": "subtype",
+ "name": "mptcp"
+ }
+ },
+ {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "mp-capable",
+ 10
+ ]
+ },
+ {
+ "concat": [
+ "remove-addr",
+ 300
+ ]
+ },
+ {
+ "concat": [
+ "mp-fastclose",
+ 600
+ ]
+ },
+ {
+ "concat": [
+ "mp-join",
+ 100
+ ]
+ },
+ {
+ "concat": [
+ "mp-prio",
+ 400
+ ]
+ },
+ {
+ "concat": [
+ "mp-tcprst",
+ 700
+ ]
+ },
+ {
+ "concat": [
+ "add-addr",
+ 200
+ ]
+ },
+ {
+ "concat": [
+ "mp-fail",
+ 500
+ ]
+ }
+ ]
+ }
+ }
+ }
+]

View File

@ -1,268 +0,0 @@
From 0d28ee52a20e8441f66dc11b690fb595f63db6a3 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 15 Jul 2025 22:50:32 +0200
Subject: [PATCH] src: add conntrack information to trace monitor mode
JIRA: https://issues.redhat.com/browse/RHEL-102994
Upstream Status: nftables commit cfd768615235bb89650f15498c70d19813502825
commit cfd768615235bb89650f15498c70d19813502825
Author: Florian Westphal <fw@strlen.de>
Date: Mon Jul 7 22:38:13 2025 +0200
src: add conntrack information to trace monitor mode
Upcoming kernel change provides the packets conntrack state in the
trace message data.
This allows to see if packet is seen as original or reply, the conntrack
state (new, establieshed, related) and the status bits which show if e.g.
NAT was applied. Alsoi include conntrack ID so users can use conntrack
tool to query the kernel for more information via ctnetlink.
This improves debugging when e.g. packets do not pick up the expected
NAT mapping, which could e.g. also happen because of expectations
following the NAT binding of the owning conntrack entry.
Example output ("conntrack: " lines are new):
trace id 32 t PRE_RAW packet: iif "enp0s3" ether saddr [..]
trace id 32 t PRE_RAW rule tcp flags syn meta nftrace set 1 (verdict continue)
trace id 32 t PRE_RAW policy accept
trace id 32 t PRE_MANGLE conntrack: ct direction original ct state new ct id 2641368242
trace id 32 t PRE_MANGLE packet: iif "enp0s3" ether saddr [..]
trace id 32 t ct_new_pre rule jump rpfilter (verdict jump rpfilter)
trace id 32 t PRE_MANGLE policy accept
trace id 32 t INPUT conntrack: ct direction original ct state new ct status dnat-done ct id 2641368242
trace id 32 t INPUT packet: iif "enp0s3" [..]
trace id 32 t public_in rule tcp dport 443 accept (verdict accept)
v3: remove clash bit again, kernel won't expose it anymore.
v2: add more status bits: helper, clash, offload, hw-offload.
add flag explanation to documentation.
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/data-types.txt | 30 ++---
include/linux/netfilter/nf_conntrack_common.h | 16 +++
src/ct.c | 7 ++
src/trace.c | 109 ++++++++++++++++++
4 files changed, 147 insertions(+), 15 deletions(-)
diff --git a/doc/data-types.txt b/doc/data-types.txt
index 6c0e2f9..abbb7fd 100644
--- a/doc/data-types.txt
+++ b/doc/data-types.txt
@@ -378,21 +378,21 @@ For each of the types above, keywords are available for convenience:
.conntrack status (ct_status)
[options="header"]
|==================
-|Keyword| Value
-|expected|
-1
-|seen-reply|
-2
-|assured|
-4
-|confirmed|
-8
-|snat|
-16
-|dnat|
-32
-|dying|
-512
+|Keyword| Value | Description
+|expected|1| Expected connection; conntrack helper set it up
+|seen-reply|2| Conntrack has seen packets in both directions
+|assured| 4 |Conntrack entry will not be removed if hash table is full
+|confirmed | 8 | Initial packet processed
+|snat| 16 | Original source address differs from reply destination
+|dnat| 32 | Original destination differs from reply source
+|seq-adjust| 64 | tcp sequence number rewrite due to conntrack helper or synproxy
+|snat-done| 128 | tried to find matching snat/masquerade rule
+|dnat-done| 256 | tried to find matching dnat/redirect rule
+|dying| 512 | Connection about to be deleted
+|fixed-timeout | 1024 | entry expires even if traffic is active
+|helper | 8192 | connection is monitored by conntrack helper
+|offload | 16384 | connection is offloaded to a flow table
+|hw-offload | 32768 | connection is offloaded to hardware
|================
.conntrack event bits (ct_event)
diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h
index 768ff25..22bbb6c 100644
--- a/include/linux/netfilter/nf_conntrack_common.h
+++ b/include/linux/netfilter/nf_conntrack_common.h
@@ -77,6 +77,22 @@ enum ip_conntrack_status {
/* Connection has fixed timeout. */
IPS_FIXED_TIMEOUT_BIT = 10,
IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT),
+
+ /* Conntrack is a fake untracked entry. Obsolete and not used anymore */
+ IPS_UNTRACKED_BIT = 12,
+ IPS_UNTRACKED = (1 << IPS_UNTRACKED_BIT),
+
+ /* Conntrack got a helper explicitly attached (ruleset, ctnetlink). */
+ IPS_HELPER_BIT = 13,
+ IPS_HELPER = (1 << IPS_HELPER_BIT),
+
+ /* Conntrack has been offloaded to flow table. */
+ IPS_OFFLOAD_BIT = 14,
+ IPS_OFFLOAD = (1 << IPS_OFFLOAD_BIT),
+
+ /* Conntrack has been offloaded to hardware. */
+ IPS_HW_OFFLOAD_BIT = 15,
+ IPS_HW_OFFLOAD = (1 << IPS_HW_OFFLOAD_BIT),
};
/* Connection tracking event types */
diff --git a/src/ct.c b/src/ct.c
index 6793464..cd97d82 100644
--- a/src/ct.c
+++ b/src/ct.c
@@ -98,7 +98,14 @@ static const struct symbol_table ct_status_tbl = {
SYMBOL("confirmed", IPS_CONFIRMED),
SYMBOL("snat", IPS_SRC_NAT),
SYMBOL("dnat", IPS_DST_NAT),
+ SYMBOL("seq-adjust", IPS_SEQ_ADJUST),
+ SYMBOL("snat-done", IPS_SRC_NAT_DONE),
+ SYMBOL("dnat-done", IPS_DST_NAT_DONE),
SYMBOL("dying", IPS_DYING),
+ SYMBOL("fixed-timeout", IPS_FIXED_TIMEOUT),
+ SYMBOL("helper", IPS_HELPER_BIT),
+ SYMBOL("offload", IPS_OFFLOAD_BIT),
+ SYMBOL("hw-offload", IPS_HW_OFFLOAD_BIT),
SYMBOL_LIST_END
},
};
diff --git a/src/trace.c b/src/trace.c
index a7cc8ff..b270951 100644
--- a/src/trace.c
+++ b/src/trace.c
@@ -237,6 +237,114 @@ next:
}
}
+static struct expr *trace_alloc_list(const struct datatype *dtype,
+ enum byteorder byteorder,
+ unsigned int len, const void *data)
+{
+ struct expr *list_expr;
+ unsigned int i;
+ mpz_t value;
+ uint32_t v;
+
+ if (len != sizeof(v))
+ return constant_expr_alloc(&netlink_location,
+ dtype, byteorder,
+ len * BITS_PER_BYTE, data);
+
+ list_expr = list_expr_alloc(&netlink_location);
+
+ mpz_init2(value, 32);
+ mpz_import_data(value, data, byteorder, len);
+ v = mpz_get_uint32(value);
+ if (v == 0) {
+ mpz_clear(value);
+ return NULL;
+ }
+
+ for (i = 0; i < 32; i++) {
+ uint32_t bitv = v & (1 << i);
+
+ if (bitv == 0)
+ continue;
+
+ compound_expr_add(list_expr,
+ constant_expr_alloc(&netlink_location,
+ dtype, byteorder,
+ len * BITS_PER_BYTE,
+ &bitv));
+ }
+
+ mpz_clear(value);
+ return list_expr;
+}
+
+static void trace_print_ct_expr(const struct nftnl_trace *nlt, unsigned int attr,
+ enum nft_ct_keys key, struct output_ctx *octx)
+{
+ struct expr *lhs, *rhs, *rel;
+ const void *data;
+ uint32_t len;
+
+ data = nftnl_trace_get_data(nlt, attr, &len);
+ lhs = ct_expr_alloc(&netlink_location, key, -1);
+
+ switch (key) {
+ case NFT_CT_STATUS:
+ rhs = trace_alloc_list(lhs->dtype, lhs->byteorder, len, data);
+ if (!rhs) {
+ expr_free(lhs);
+ return;
+ }
+ rel = binop_expr_alloc(&netlink_location, OP_IMPLICIT, lhs, rhs);
+ break;
+ case NFT_CT_DIRECTION:
+ case NFT_CT_STATE:
+ case NFT_CT_ID:
+ /* fallthrough */
+ default:
+ rhs = constant_expr_alloc(&netlink_location,
+ lhs->dtype, lhs->byteorder,
+ len * BITS_PER_BYTE, data);
+ rel = relational_expr_alloc(&netlink_location, OP_IMPLICIT, lhs, rhs);
+ break;
+ }
+
+ expr_print(rel, octx);
+ nft_print(octx, " ");
+ expr_free(rel);
+}
+
+static void trace_print_ct(const struct nftnl_trace *nlt,
+ struct output_ctx *octx)
+{
+ bool ct = nftnl_trace_is_set(nlt, NFTNL_TRACE_CT_STATE);
+
+ if (!ct)
+ return;
+
+ trace_print_hdr(nlt, octx);
+
+ nft_print(octx, "conntrack: ");
+
+ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_CT_DIRECTION))
+ trace_print_ct_expr(nlt, NFTNL_TRACE_CT_DIRECTION,
+ NFT_CT_DIRECTION, octx);
+
+ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_CT_STATE))
+ trace_print_ct_expr(nlt, NFTNL_TRACE_CT_STATE,
+ NFT_CT_STATE, octx);
+
+ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_CT_STATUS))
+ trace_print_ct_expr(nlt, NFTNL_TRACE_CT_STATUS,
+ NFT_CT_STATUS, octx);
+
+ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_CT_ID))
+ trace_print_ct_expr(nlt, NFTNL_TRACE_CT_ID,
+ NFT_CT_ID, octx);
+
+ nft_print(octx, "\n");
+}
+
static void trace_print_packet(const struct nftnl_trace *nlt,
struct output_ctx *octx)
{
@@ -248,6 +356,7 @@ static void trace_print_packet(const struct nftnl_trace *nlt,
uint32_t nfproto;
struct stmt *stmt, *next;
+ trace_print_ct(nlt, octx);
trace_print_hdr(nlt, octx);
nft_print(octx, "packet: ");

View File

@ -0,0 +1,69 @@
From af563649df51ad00c1d0f3897af04ba1051c808e Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:10:59 +0100
Subject: [PATCH] tests: py: any/ct.t.json.output: Drop leftover entry
JIRA: https://issues.redhat.com/browse/RHEL-121194
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 6b193bf44047e8ffac8520980ef4763caaf38886 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:10:59 +0100
Subject: [PATCH] tests: py: inet/osf.t: Fix element ordering in JSON
equivalents
JIRA: https://issues.redhat.com/browse/RHEL-121194
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

@ -1,38 +0,0 @@
From 5ed024ecfaf596ec0298f8ad75c5695f9889464c Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 9 Sep 2025 16:27:52 +0200
Subject: [PATCH] trace: Fix for memleak in trace_alloc_list() error path
JIRA: https://issues.redhat.com/browse/RHEL-111205
Upstream Status: nftables commit fdbb0ec57b5c891c1de17f367b693ab787ea9c2d
commit fdbb0ec57b5c891c1de17f367b693ab787ea9c2d
Author: Phil Sutter <phil@nwl.cc>
Date: Tue Aug 26 12:57:37 2025 +0200
trace: Fix for memleak in trace_alloc_list() error path
The allocated 'list_expr' may leak.
Fixes: cfd768615235b ("src: add conntrack information to trace monitor mode")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/trace.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/trace.c b/src/trace.c
index b270951..47f4c21 100644
--- a/src/trace.c
+++ b/src/trace.c
@@ -258,6 +258,7 @@ static struct expr *trace_alloc_list(const struct datatype *dtype,
v = mpz_get_uint32(value);
if (v == 0) {
mpz_clear(value);
+ expr_free(list_expr);
return NULL;
}

View File

@ -1,78 +0,0 @@
From 3bb2e6c3d03fa60724ab72b96d1e97fa02d7eed9 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Tue, 9 Sep 2025 16:53:21 +0200
Subject: [PATCH] doc: nft.8: Minor NAT STATEMENTS section review
JIRA: https://issues.redhat.com/browse/RHEL-106743
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 74af1d1..7fe9ed3 100644
--- a/doc/statements.txt
+++ b/doc/statements.txt
@@ -399,11 +399,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'
@@ -413,11 +414,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.
@@ -427,8 +428,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,47 @@
From 2f0cfe33e3e2eba28b61421ea8b592426313b9ed Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:10:59 +0100
Subject: [PATCH] tests: shell: fix typo in vmap_timeout test script
JIRA: https://issues.redhat.com/browse/RHEL-121194
Upstream Status: nftables commit b39ba950325bba7b00da57684c99e9466522bb07
commit b39ba950325bba7b00da57684c99e9466522bb07
Author: Gyorgy Sarvari <skandigraun@gmail.com>
Date: Sun Oct 26 21:41:07 2025 +0100
tests: shell: fix typo in vmap_timeout test script
While executing the test suite from tests/shell folder, the following error
is displayed many times:
tests/shell/testcases/maps/vmap_timeout: line 48: [: : integer expected
Looking at the script, a non-existing variable (expires) is tested instead of
the existing one (expire).
Reproduction:
tests/shell/run-tests.sh -v
Fixes: db80037c0279 ("tests: shell: extend vmap test with updates")
Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/shell/testcases/maps/vmap_timeout | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/shell/testcases/maps/vmap_timeout b/tests/shell/testcases/maps/vmap_timeout
index 8ac7e8e..55d1c1b 100755
--- a/tests/shell/testcases/maps/vmap_timeout
+++ b/tests/shell/testcases/maps/vmap_timeout
@@ -45,7 +45,7 @@ for i in $(seq 1 100) ; do
expire=$((RANDOM%utimeout))
expire_str=""
- if [ "$expires" -gt 0 ]; then
+ if [ "$expire" -gt 0 ]; then
expire_str="expires ${expire}s"
fi

View File

@ -0,0 +1,67 @@
From 50fd26900311b0eb0e35fa421541dba42c64927b Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:10:59 +0100
Subject: [PATCH] build: don't install ancillary files without systemd service
file
JIRA: https://issues.redhat.com/browse/RHEL-121194
Upstream Status: nftables commit 31007975ccf5a389b76e1bc6f5f7215847f394ef
Conflicts: Context change due to missing commit df19bf51d49be
("Makefile: Enable support for 'make check'")
commit 31007975ccf5a389b76e1bc6f5f7215847f394ef
Author: Jeremy Sowden <jeremy@azazel.net>
Date: Wed Sep 17 21:34:54 2025 +0100
build: don't install ancillary files without systemd service file
If the systemd service file is not installed, currently the related man-page
and example nft file are still installed. Instead only install them when the
service file is installed.
Fixes: 107580cfa85c ("build: disable --with-unitdir by default")
Signed-off-by: Jeremy Sowden <jeremy@azazel.net>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
Makefile.am | 5 +++++
configure.ac | 1 +
2 files changed, 6 insertions(+)
diff --git a/Makefile.am b/Makefile.am
index 4035e65..ebf64c4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -418,10 +418,14 @@ EXTRA_DIST += \
tools/nftables.service.in \
$(NULL)
+if BUILD_SERVICE
CLEANFILES += tools/nftables.service
+endif
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libnftables.pc
+
+if BUILD_SERVICE
unit_DATA = tools/nftables.service
man_MANS = tools/nftables.service.8
doc_DATA = files/nftables/main.nft
@@ -429,3 +433,4 @@ doc_DATA = files/nftables/main.nft
tools/nftables.service: tools/nftables.service.in ${top_builddir}/config.status
${AM_V_GEN}${MKDIR_P} tools
${AM_V_at}sed -e 's|@''sbindir''@|${sbindir}|g;s|@''pkgsysconfdir''@|${pkgsysconfdir}|g' <${srcdir}/tools/nftables.service.in >$@
+endif
diff --git a/configure.ac b/configure.ac
index 3517ea0..09d1ee5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -130,6 +130,7 @@ AC_ARG_WITH([unitdir],
[unitdir=""]
)
AC_SUBST([unitdir])
+AM_CONDITIONAL([BUILD_SERVICE], [test "x$unitdir" != x])
AC_ARG_WITH([stable-release], [AS_HELP_STRING([--with-stable-release],
[Stable release number])],

View File

@ -0,0 +1,69 @@
From ea26c89c4428c324f53827e4e1e0b7be9878dad8 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:11:18 +0100
Subject: [PATCH] doc: don't suggest to disable GSO
JIRA: https://issues.redhat.com/browse/RHEL-121194
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 ce0c6a2..8c27fe4 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 6bd71d875043356fb8e6b8ed24e8d2d9c4f7f491 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Thu, 20 Nov 2025 20:11:18 +0100
Subject: [PATCH] doc: libnftables-json: Describe RULESET object
JIRA: https://issues.redhat.com/browse/RHEL-121194
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 643884d..049c325 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

@ -1,6 +1,6 @@
Name: nftables
Version: 1.1.1
Release: 9%{?dist}
Version: 1.1.5
Release: 1%{?dist}
# Upstream released a 0.100 version, then 0.4. Need Epoch to get back on track.
Epoch: 1
Summary: Netfilter Tables userspace utilities
@ -16,27 +16,27 @@ Source5: nat.nft
Source6: nft-test.stderr.expect
Source7: run-tests.stderr.expect
Patch1: 0001-tests-shell-fix-spurious-dump-failure-in-vmap-timeou.patch
Patch2: 0002-libnftables-json-fix-raw-payload-expression-document.patch
Patch3: 0003-src-collapse-set-element-commands-from-parser.patch
Patch4: 0004-mnl-rename-to-mnl_seqnum_alloc-to-mnl_seqnum_inc.patch
Patch5: 0005-mnl-update-cmd_add_loc-to-take-struct-nlmsghdr.patch
Patch6: 0006-rule-netlink-attribute-offset-is-uint32_t-for-struct.patch
Patch7: 0007-src-fix-extended-netlink-error-reporting-with-large-.patch
Patch8: 0008-tests-monitor-fix-up-test-case-breakage.patch
Patch9: 0009-doc-extend-description-of-fib-expression.patch
Patch10: 0010-json-collapse-set-element-commands-from-parser.patch
Patch11: 0011-json-Support-typeof-in-set-and-map-types.patch
Patch12: 0012-tests-py-Fix-for-storing-payload-into-missing-file.patch
Patch13: 0013-monitor-Recognize-flowtable-add-del-events.patch
Patch14: 0014-evaluate-allow-to-re-use-existing-metered-set.patch
Patch15: 0015-src-split-monitor-trace-code-into-new-trace.c.patch
Patch16: 0016-src-add-conntrack-information-to-trace-monitor-mode.patch
Patch17: 0017-trace-Fix-for-memleak-in-trace_alloc_list-error-path.patch
Patch18: 0018-doc-nft.8-Minor-NAT-STATEMENTS-section-review.patch
Patch19: 0019-table-Embed-creating-nft-version-into-userdata.patch
Patch20: 0020-Makefile-Fix-for-make-CFLAGS.patch
Patch21: 0021-fib-Fix-for-existence-check-on-Big-Endian.patch
Patch1: 0001-table-Embed-creating-nft-version-into-userdata.patch
Patch2: 0002-Makefile-Fix-for-make-CFLAGS.patch
Patch3: 0003-fib-Fix-for-existence-check-on-Big-Endian.patch
Patch4: 0004-parser_bison-remove-leftover-utf-8-character-in-erro.patch
Patch5: 0005-tools-gitignore-nftables.service-file.patch
Patch6: 0006-monitor-Quote-device-names-in-chain-declarations-too.patch
Patch7: 0007-tests-monitor-Fix-regex-collecting-expected-echo-out.patch
Patch8: 0008-tests-shell-skip-two-bitwise-tests-if-multi-register.patch
Patch9: 0009-monitor-Inform-JSON-printer-when-reporting-an-object.patch
Patch10: 0010-libnftables-do-not-re-add-default-include-directory-.patch
Patch11: 0011-doc-fix-tcpdump-example.patch
Patch12: 0012-src-parser_json-fix-format-string-bugs.patch
Patch13: 0013-datatype-Fix-boolean-type-on-Big-Endian.patch
Patch14: 0014-optimize-Fix-verdict-expression-comparison.patch
Patch15: 0015-tests-py-any-tcpopt.t.json-Fix-JSON-equivalent.patch
Patch16: 0016-tests-py-any-ct.t.json.output-Drop-leftover-entry.patch
Patch17: 0017-tests-py-inet-osf.t-Fix-element-ordering-in-JSON-equ.patch
Patch18: 0018-tests-shell-fix-typo-in-vmap_timeout-test-script.patch
Patch19: 0019-build-don-t-install-ancillary-files-without-systemd-.patch
Patch20: 0020-doc-don-t-suggest-to-disable-GSO.patch
Patch21: 0021-doc-libnftables-json-Describe-RULESET-object.patch
BuildRequires: autoconf
BuildRequires: automake
@ -47,7 +47,7 @@ BuildRequires: flex
BuildRequires: bison
BuildRequires: pkgconfig(libmnl) >= 1.0.4
BuildRequires: gmp-devel
BuildRequires: libnftnl-devel >= 1.2.8-4
BuildRequires: libnftnl-devel >= 1.3.0-1
BuildRequires: systemd
BuildRequires: asciidoc
BuildRequires: pkgconfig(xtables) >= 1.6.1
@ -151,6 +151,27 @@ cd py/
%files -n python3-nftables -f %{pyproject_files}
%changelog
* Thu Nov 20 2025 Phil Sutter <psutter@redhat.com> [1.1.5-1.el10]
- doc: libnftables-json: Describe RULESET object (Phil Sutter) [RHEL-121194]
- doc: don't suggest to disable GSO (Phil Sutter) [RHEL-121194]
- build: don't install ancillary files without systemd service file (Phil Sutter) [RHEL-121194]
- tests: shell: fix typo in vmap_timeout test script (Phil Sutter) [RHEL-121194]
- tests: py: inet/osf.t: Fix element ordering in JSON equivalents (Phil Sutter) [RHEL-121194]
- tests: py: any/ct.t.json.output: Drop leftover entry (Phil Sutter) [RHEL-121194]
- tests: py: any/tcpopt.t.json: Fix JSON equivalent (Phil Sutter) [RHEL-121194]
- optimize: Fix verdict expression comparison (Phil Sutter) [RHEL-121194]
- datatype: Fix boolean type on Big Endian (Phil Sutter) [RHEL-121194]
- src: parser_json: fix format string bugs (Phil Sutter) [RHEL-121194]
- doc: fix tcpdump example (Phil Sutter) [RHEL-121194]
- libnftables: do not re-add default include directory in include search path (Phil Sutter) [RHEL-121194]
- monitor: Inform JSON printer when reporting an object delete event (Phil Sutter) [RHEL-121194]
- tests: shell: skip two bitwise tests if multi-register support isn't available (Phil Sutter) [RHEL-121194]
- tests: monitor: Fix regex collecting expected echo output (Phil Sutter) [RHEL-121194]
- monitor: Quote device names in chain declarations, too (Phil Sutter) [RHEL-121194]
- tools: gitignore nftables.service file (Phil Sutter) [RHEL-121194]
- parser_bison: remove leftover utf-8 character in error (Phil Sutter) [RHEL-121194]
- Rebase onto version 1.1.5 (Phil Sutter) [RHEL-121194]
* Tue Nov 04 2025 Phil Sutter <psutter@redhat.com> [1.1.1-9.el10]
- fib: Fix for existence check on Big Endian (Phil Sutter) [RHEL-113851]

View File

@ -1 +1 @@
SHA512 (nftables-1.1.1.tar.xz) = 676413d4adadffb15d52c1f8f6432636cab83a7bcda1a18d9f0e6b58819a2c027a49922588c02bd9ad386de930eaa697bfe74c0938b595bf1ee485bfa7cf2e50
SHA512 (nftables-1.1.5.tar.xz) = 01fbbea43fd01250b0176a200dfdb6b84d3d51156cc2350acb25a5e66960e1908c3d17a0363baddb32897ea8bea0569b67500a94f708c8587b0e29402f51cbb6