* [nft PATCH 0/2] parser_json: fix JSON delete of ct stateful objects
@ 2026-08-11 13:43 Gergely Palotas
2026-08-11 13:43 ` [nft PATCH 1/2] parser_json: fix CMD_OBJ/NFT_OBJECT mismatch in delete path Gergely Palotas
2026-08-11 13:43 ` [nft PATCH 2/2] tests: shell: add JSON delete test for ct stateful objects Gergely Palotas
0 siblings, 2 replies; 4+ messages in thread
From: Gergely Palotas @ 2026-08-11 13:43 UTC (permalink / raw)
To: netfilter-devel; +Cc: Gergely Palotas
JSON delete commands for ct timeout, ct expectation and tunnel stateful
objects have been broken since their introduction. The dispatch tables
passed NFT_OBJECT_* kernel constants where enum cmd_obj values were
expected, causing the delete path to send the wrong netlink message type
to the kernel (e.g. "delete chain" instead of "delete ct timeout"),
which returned EINVAL.
Patch 1 fixes the mismatch by using CMD_OBJ_* constants consistently
throughout the affected dispatch tables and the function itself.
Patch 2 adds a regression test covering JSON add and delete for all
three affected object types.
Gergely Palotas (2):
parser_json: fix CMD_OBJ/NFT_OBJECT mismatch in delete path
tests: shell: add JSON delete test for ct stateful objects
src/parser_json.c | 34 ++---
.../json/0009json_delete_ct_objects_0 | 140 ++++++++++++++++++
2 files changed, 155 insertions(+), 19 deletions(-)
create mode 100755 tests/shell/testcases/json/0009json_delete_ct_objects_0
--
2.47.3
^ permalink raw reply [flat|nested] 4+ messages in thread
* [nft PATCH 1/2] parser_json: fix CMD_OBJ/NFT_OBJECT mismatch in delete path
2026-08-11 13:43 [nft PATCH 0/2] parser_json: fix JSON delete of ct stateful objects Gergely Palotas
@ 2026-08-11 13:43 ` Gergely Palotas
2026-08-20 11:48 ` Pablo Neira Ayuso
2026-08-11 13:43 ` [nft PATCH 2/2] tests: shell: add JSON delete test for ct stateful objects Gergely Palotas
1 sibling, 1 reply; 4+ messages in thread
From: Gergely Palotas @ 2026-08-11 13:43 UTC (permalink / raw)
To: netfilter-devel; +Cc: Gergely Palotas
The dispatch tables in json_parse_cmd_add() and json_parse_cmd_list()
pass NFT_OBJECT_CT_TIMEOUT, NFT_OBJECT_CT_EXPECT and NFT_OBJECT_TUNNEL
(kernel constants from <linux/netfilter/nf_tables.h>) as the cmd_obj
argument to json_parse_cmd_add_object(), which declares its parameter
as enum cmd_obj.
On the delete/list/destroy early-return path, cmd_obj is forwarded
directly to cmd_alloc(), which expects enum cmd_obj values. For ct
timeout, NFT_OBJECT_CT_TIMEOUT=7 aliases to CMD_OBJ_CHAIN=7, so a
JSON delete of a ct timeout is sent to the kernel as a delete chain
netlink message, returning EINVAL. The same mismatch affects ct
expectation and tunnel objects.
The add path was unaffected because the switch/case blocks inside the
function also used NFT_OBJECT_* constants and contained explicit
cmd_obj = CMD_OBJ_* assignments before falling through to cmd_alloc().
Those assignments are never reached on the delete path due to the
early return.
Fix this by using CMD_OBJ_* constants consistently in the dispatch
tables, matching the declared type of the parameter. Update the
switch cases and the CT_HELPER identity checks in the function
prologue to use CMD_OBJ_* as well, and drop the now-unnecessary
cmd_obj reassignments inside each case.
Signed-off-by: Gergely Palotas <palotasgergely@gmail.com>
---
src/parser_json.c | 34 +++++++++++++++-------------------
1 file changed, 15 insertions(+), 19 deletions(-)
diff --git a/src/parser_json.c b/src/parser_json.c
index e0144b9b..6f40f8d8 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -3792,11 +3792,11 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
"table", &h.table.name))
return NULL;
if ((op != CMD_DELETE ||
- cmd_obj == NFT_OBJECT_CT_HELPER) &&
+ cmd_obj == CMD_OBJ_CT_HELPER) &&
json_unpack_err(ctx, root, "{s:s}", "name", &h.obj.name)) {
return NULL;
} else if ((op == CMD_DELETE || op == CMD_DESTROY) &&
- cmd_obj != NFT_OBJECT_CT_HELPER &&
+ cmd_obj != CMD_OBJ_CT_HELPER &&
json_unpack(root, "{s:s}", "name", &h.obj.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
json_error(ctx, "Either name or handle required to delete an object.");
@@ -3812,7 +3812,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
h.obj.name = xstrdup(h.obj.name);
if (op == CMD_DELETE || op == CMD_LIST || op == CMD_DESTROY) {
- if (cmd_obj == NFT_OBJECT_CT_HELPER)
+ if (cmd_obj == CMD_OBJ_CT_HELPER)
return cmd_alloc_obj_ct(op, NFT_OBJECT_CT_HELPER,
&h, int_loc, obj_alloc(int_loc));
return cmd_alloc(op, cmd_obj, &h, int_loc, NULL);
@@ -3849,8 +3849,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
}
}
break;
- case NFT_OBJECT_CT_HELPER:
- cmd_obj = CMD_OBJ_CT_HELPER;
+ case CMD_OBJ_CT_HELPER:
obj->type = NFT_OBJECT_CT_HELPER;
if (!json_unpack(root, "{s:s}", "type", &tmp)) {
int ret;
@@ -3881,8 +3880,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
}
obj->ct_helper.l3proto = l3proto;
break;
- case NFT_OBJECT_CT_TIMEOUT:
- cmd_obj = CMD_OBJ_CT_TIMEOUT;
+ case CMD_OBJ_CT_TIMEOUT:
init_list_head(&obj->ct_timeout.timeout_list);
obj->type = NFT_OBJECT_CT_TIMEOUT;
if (!json_unpack(root, "{s:s}", "protocol", &tmp)) {
@@ -3905,8 +3903,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
if (json_parse_ct_timeout_policy(ctx, root, obj))
goto err_free_obj;
break;
- case NFT_OBJECT_CT_EXPECT:
- cmd_obj = CMD_OBJ_CT_EXPECT;
+ case CMD_OBJ_CT_EXPECT:
obj->type = NFT_OBJECT_CT_EXPECT;
if (!json_unpack(root, "{s:s}", "l3proto", &tmp) &&
parse_family(tmp, &l3proto)) {
@@ -3972,8 +3969,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj->synproxy.flags |= flags;
break;
- case NFT_OBJECT_TUNNEL:
- cmd_obj = CMD_OBJ_TUNNEL;
+ case CMD_OBJ_TUNNEL:
obj->type = NFT_OBJECT_TUNNEL;
if (json_parse_tunnel(ctx, root, obj))
goto err_free_obj;
@@ -4011,10 +4007,10 @@ static struct cmd *json_parse_cmd_add(struct json_ctx *ctx,
{ "flowtable", CMD_OBJ_FLOWTABLE, json_parse_cmd_add_flowtable },
{ "counter", CMD_OBJ_COUNTER, json_parse_cmd_add_object },
{ "quota", CMD_OBJ_QUOTA, json_parse_cmd_add_object },
- { "ct helper", NFT_OBJECT_CT_HELPER, json_parse_cmd_add_object },
- { "ct timeout", NFT_OBJECT_CT_TIMEOUT, json_parse_cmd_add_object },
- { "ct expectation", NFT_OBJECT_CT_EXPECT, json_parse_cmd_add_object },
- { "tunnel", NFT_OBJECT_TUNNEL, json_parse_cmd_add_object },
+ { "ct helper", CMD_OBJ_CT_HELPER, json_parse_cmd_add_object },
+ { "ct timeout", CMD_OBJ_CT_TIMEOUT, json_parse_cmd_add_object },
+ { "ct expectation", CMD_OBJ_CT_EXPECT, json_parse_cmd_add_object },
+ { "tunnel", CMD_OBJ_TUNNEL, json_parse_cmd_add_object },
{ "limit", CMD_OBJ_LIMIT, json_parse_cmd_add_object },
{ "secmark", CMD_OBJ_SECMARK, json_parse_cmd_add_object },
{ "synproxy", CMD_OBJ_SYNPROXY, json_parse_cmd_add_object }
@@ -4186,11 +4182,11 @@ static struct cmd *json_parse_cmd_list(struct json_ctx *ctx,
{ "counters", CMD_OBJ_COUNTERS, json_parse_cmd_list_multiple },
{ "quota", CMD_OBJ_QUOTA, json_parse_cmd_add_object },
{ "quotas", CMD_OBJ_QUOTAS, json_parse_cmd_list_multiple },
- { "ct helper", NFT_OBJECT_CT_HELPER, json_parse_cmd_add_object },
+ { "ct helper", CMD_OBJ_CT_HELPER, json_parse_cmd_add_object },
{ "ct helpers", CMD_OBJ_CT_HELPERS, json_parse_cmd_list_multiple },
- { "ct timeout", NFT_OBJECT_CT_TIMEOUT, json_parse_cmd_add_object },
- { "ct expectation", NFT_OBJECT_CT_EXPECT, json_parse_cmd_add_object },
- { "tunnel", NFT_OBJECT_TUNNEL, json_parse_cmd_add_object },
+ { "ct timeout", CMD_OBJ_CT_TIMEOUT, json_parse_cmd_add_object },
+ { "ct expectation", CMD_OBJ_CT_EXPECT, json_parse_cmd_add_object },
+ { "tunnel", CMD_OBJ_TUNNEL, json_parse_cmd_add_object },
{ "tunnels", CMD_OBJ_TUNNELS, json_parse_cmd_list_multiple },
{ "limit", CMD_OBJ_LIMIT, json_parse_cmd_add_object },
{ "limits", CMD_OBJ_LIMIT, json_parse_cmd_list_multiple },
--
2.47.3
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [nft PATCH 2/2] tests: shell: add JSON delete test for ct stateful objects
2026-08-11 13:43 [nft PATCH 0/2] parser_json: fix JSON delete of ct stateful objects Gergely Palotas
2026-08-11 13:43 ` [nft PATCH 1/2] parser_json: fix CMD_OBJ/NFT_OBJECT mismatch in delete path Gergely Palotas
@ 2026-08-11 13:43 ` Gergely Palotas
1 sibling, 0 replies; 4+ messages in thread
From: Gergely Palotas @ 2026-08-11 13:43 UTC (permalink / raw)
To: netfilter-devel; +Cc: Gergely Palotas
Verify that JSON delete commands work correctly for ct timeout, ct
expectation and tunnel stateful objects. These were broken before the
previous fix due to a CMD_OBJ/NFT_OBJECT mismatch in the delete path
of json_parse_cmd_add_object().
Signed-off-by: Gergely Palotas <palotasgergely@gmail.com>
---
.../json/0009json_delete_ct_objects_0 | 140 ++++++++++++++++++
1 file changed, 140 insertions(+)
create mode 100755 tests/shell/testcases/json/0009json_delete_ct_objects_0
diff --git a/tests/shell/testcases/json/0009json_delete_ct_objects_0 b/tests/shell/testcases/json/0009json_delete_ct_objects_0
new file mode 100755
index 00000000..78a68be4
--- /dev/null
+++ b/tests/shell/testcases/json/0009json_delete_ct_objects_0
@@ -0,0 +1,140 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_json)
+#
+# Regression test for JSON delete of ct timeout, ct expectation and tunnel
+# objects. Prior to the fix, json_parse_cmd_add_object() passed the raw
+# NFT_OBJECT_* kernel constant to cmd_alloc() on the delete/list/destroy
+# early-return path instead of the correct CMD_OBJ_* userspace enum value.
+# For ct timeout NFT_OBJECT_CT_TIMEOUT=7 aliased to CMD_OBJ_CHAIN=7, causing
+# the kernel to return EINVAL.
+
+set -e
+
+$NFT flush ruleset
+
+$NFT -j -f - << 'EOF'
+{"nftables": [{"add": {"table": {"family": "inet", "name": "t"}}}]}
+EOF
+
+# ===== ct timeout =====
+
+if [ "$NFT_TEST_HAVE_cttimeout" != n ]; then
+
+ echo "Test 1: JSON add ct timeout"
+ $NFT -j -f - << 'EOF'
+{"nftables": [{"add": {"ct timeout": {"family": "inet", "table": "t", "name": "ctto", "protocol": "udp", "policy": {"unreplied": 30, "replied": 60}}}}]}
+EOF
+
+ if ! $NFT list ct timeout inet t ctto > /dev/null 2>&1; then
+ echo "Test 1 failed: ct timeout not created"
+ exit 1
+ fi
+
+ echo "Test 2: JSON delete ct timeout by name"
+ $NFT -j -f - << 'EOF'
+{"nftables": [{"delete": {"ct timeout": {"family": "inet", "table": "t", "name": "ctto"}}}]}
+EOF
+
+ if $NFT list ct timeout inet t ctto > /dev/null 2>&1; then
+ echo "Test 2 failed: ct timeout not deleted"
+ exit 1
+ fi
+
+ echo "Test 3: JSON add + delete ct timeout by handle"
+ $NFT -j -f - << 'EOF'
+{"nftables": [{"add": {"ct timeout": {"family": "inet", "table": "t", "name": "ctto2", "protocol": "tcp", "policy": {"established": 120}}}}]}
+EOF
+
+ HANDLE=$($NFT -a list ct timeout inet t ctto2 | sed -n 's/.*# handle \([0-9]\+\).*/\1/p')
+ if [ -z "$HANDLE" ]; then
+ echo "Test 3 failed: could not get ct timeout handle"
+ exit 1
+ fi
+
+ $NFT -j -f - << EOF
+{"nftables": [{"delete": {"ct timeout": {"family": "inet", "table": "t", "handle": $HANDLE}}}]}
+EOF
+
+ if $NFT list ct timeout inet t ctto2 > /dev/null 2>&1; then
+ echo "Test 3 failed: ct timeout not deleted by handle"
+ exit 1
+ fi
+
+fi
+
+# ===== ct expectation =====
+
+if [ "$NFT_TEST_HAVE_ctexpect" != n ]; then
+
+ $NFT -j -f - << 'EOF'
+{"nftables": [{"add": {"table": {"family": "ip", "name": "t"}}}]}
+EOF
+
+ echo "Test 4: JSON add ct expectation"
+ $NFT -j -f - << 'EOF'
+{"nftables": [{"add": {"ct expectation": {"family": "ip", "table": "t", "name": "ctex", "protocol": "tcp", "dport": 2121, "timeout": 300000, "size": 12}}}]}
+EOF
+
+ if ! $NFT list ct expectation ip t ctex > /dev/null 2>&1; then
+ echo "Test 4 failed: ct expectation not created"
+ exit 1
+ fi
+
+ echo "Test 5: JSON delete ct expectation by name"
+ $NFT -j -f - << 'EOF'
+{"nftables": [{"delete": {"ct expectation": {"family": "ip", "table": "t", "name": "ctex"}}}]}
+EOF
+
+ if $NFT list ct expectation ip t ctex > /dev/null 2>&1; then
+ echo "Test 5 failed: ct expectation not deleted"
+ exit 1
+ fi
+
+ $NFT -j -f - << 'EOF'
+{"nftables": [{"delete": {"table": {"family": "ip", "name": "t"}}}]}
+EOF
+
+fi
+
+# ===== tunnel =====
+#
+# Tunnel objects must be created and deleted in the same batch as the table
+# when using JSON (kernel lookup limitation with separate add commands).
+
+if [ "$NFT_TEST_HAVE_tunnel" != n ]; then
+
+ echo "Test 6: JSON add+delete tunnel object in batch"
+ $NFT -j -f - << 'EOF'
+{"nftables": [
+ {"add": {"table": {"family": "netdev", "name": "t"}}},
+ {"add": {"tunnel": {"family": "netdev", "table": "t", "name": "tun0", "id": 20, "src-ipv4": "192.168.2.20", "dst-ipv4": "192.168.2.21", "sport": 0, "dport": 4789, "tos": 0, "ttl": 255}}}
+]}
+EOF
+
+ if ! $NFT list tunnel netdev t tun0 > /dev/null 2>&1; then
+ echo "Test 6 failed: tunnel not created"
+ exit 1
+ fi
+
+ echo "Test 7: JSON delete tunnel by name"
+ $NFT -j -f - << 'EOF'
+{"nftables": [{"delete": {"tunnel": {"family": "netdev", "table": "t", "name": "tun0"}}}]}
+EOF
+
+ if $NFT list tunnel netdev t tun0 > /dev/null 2>&1; then
+ echo "Test 7 failed: tunnel not deleted"
+ exit 1
+ fi
+
+ $NFT -j -f - << 'EOF'
+{"nftables": [{"delete": {"table": {"family": "netdev", "name": "t"}}}]}
+EOF
+
+fi
+
+$NFT -j -f - << 'EOF'
+{"nftables": [{"delete": {"table": {"family": "inet", "name": "t"}}}]}
+EOF
+
+echo "All tests passed!"
--
2.47.3
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [nft PATCH 1/2] parser_json: fix CMD_OBJ/NFT_OBJECT mismatch in delete path
2026-08-11 13:43 ` [nft PATCH 1/2] parser_json: fix CMD_OBJ/NFT_OBJECT mismatch in delete path Gergely Palotas
@ 2026-08-20 11:48 ` Pablo Neira Ayuso
0 siblings, 0 replies; 4+ messages in thread
From: Pablo Neira Ayuso @ 2026-08-20 11:48 UTC (permalink / raw)
To: Gergely Palotas; +Cc: netfilter-devel
On Tue, Aug 11, 2026 at 03:43:25PM +0200, Gergely Palotas wrote:
> The dispatch tables in json_parse_cmd_add() and json_parse_cmd_list()
> pass NFT_OBJECT_CT_TIMEOUT, NFT_OBJECT_CT_EXPECT and NFT_OBJECT_TUNNEL
> (kernel constants from <linux/netfilter/nf_tables.h>) as the cmd_obj
> argument to json_parse_cmd_add_object(), which declares its parameter
> as enum cmd_obj.
>
> On the delete/list/destroy early-return path, cmd_obj is forwarded
> directly to cmd_alloc(), which expects enum cmd_obj values. For ct
> timeout, NFT_OBJECT_CT_TIMEOUT=7 aliases to CMD_OBJ_CHAIN=7, so a
> JSON delete of a ct timeout is sent to the kernel as a delete chain
> netlink message, returning EINVAL. The same mismatch affects ct
> expectation and tunnel objects.
>
> The add path was unaffected because the switch/case blocks inside the
> function also used NFT_OBJECT_* constants and contained explicit
> cmd_obj = CMD_OBJ_* assignments before falling through to cmd_alloc().
> Those assignments are never reached on the delete path due to the
> early return.
>
> Fix this by using CMD_OBJ_* constants consistently in the dispatch
> tables, matching the declared type of the parameter. Update the
> switch cases and the CT_HELPER identity checks in the function
> prologue to use CMD_OBJ_* as well, and drop the now-unnecessary
> cmd_obj reassignments inside each case.
cmd_alloc_obj_ct() expects NFT_OBJECT_CT_*
switch (type) {
case NFT_OBJECT_CT_HELPER:
cmd_obj = CMD_OBJ_CT_HELPER;
break;
case NFT_OBJECT_CT_TIMEOUT:
cmd_obj = CMD_OBJ_CT_TIMEOUT;
break;
case NFT_OBJECT_CT_EXPECT:
cmd_obj = CMD_OBJ_CT_EXPECT;
break;
default:
BUG("missing type mapping");
}
Maybe this needs to be updated so this looks consistent?
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-20 11:48 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-11 13:43 [nft PATCH 0/2] parser_json: fix JSON delete of ct stateful objects Gergely Palotas
2026-08-11 13:43 ` [nft PATCH 1/2] parser_json: fix CMD_OBJ/NFT_OBJECT mismatch in delete path Gergely Palotas
2026-08-20 11:48 ` Pablo Neira Ayuso
2026-08-11 13:43 ` [nft PATCH 2/2] tests: shell: add JSON delete test for ct stateful objects Gergely Palotas
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.