All of lore.kernel.org
 help / color / mirror / Atom feed
* [nft PATCH v2 0/3] Support wildcard netdev hooks
@ 2025-07-15 15:15 Phil Sutter
  2025-07-15 15:15 ` [nft PATCH v2 1/3] mnl: Support simple wildcards in " Phil Sutter
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Phil Sutter @ 2025-07-15 15:15 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

This is the remaining needed code change to support wildcard hook specs.
Patch 3 also adds shell test cases to cover the functionality. The
flowtable variant is skipped if 'nft list hooks' does not provide
flowtable information as this requires NFNL_HOOK_TYPE_NFT_FLOWTABLE in
kernel.

Phil Sutter (3):
  mnl: Support simple wildcards in netdev hooks
  parser_bison: Accept ASTERISK_STRING in flowtable_expr_member
  tests: shell: Test ifname-based hooks

 src/mnl.c                                     | 19 +++++---
 src/parser_bison.y                            | 11 +----
 .../features/list_hooks_flowtable_info.sh     |  7 +++
 .../netdev_chain_name_based_hook_0.json-nft   | 34 ++++++++++++++
 .../dumps/netdev_chain_name_based_hook_0.nft  |  5 +++
 .../chains/netdev_chain_name_based_hook_0     | 44 ++++++++++++++++++
 .../testcases/flowtable/0016name_based_hook_0 | 45 +++++++++++++++++++
 .../dumps/0016name_based_hook_0.json-nft      | 32 +++++++++++++
 .../flowtable/dumps/0016name_based_hook_0.nft |  6 +++
 9 files changed, 188 insertions(+), 15 deletions(-)
 create mode 100755 tests/shell/features/list_hooks_flowtable_info.sh
 create mode 100644 tests/shell/testcases/chains/dumps/netdev_chain_name_based_hook_0.json-nft
 create mode 100644 tests/shell/testcases/chains/dumps/netdev_chain_name_based_hook_0.nft
 create mode 100755 tests/shell/testcases/chains/netdev_chain_name_based_hook_0
 create mode 100755 tests/shell/testcases/flowtable/0016name_based_hook_0
 create mode 100644 tests/shell/testcases/flowtable/dumps/0016name_based_hook_0.json-nft
 create mode 100644 tests/shell/testcases/flowtable/dumps/0016name_based_hook_0.nft

-- 
2.49.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [nft PATCH v2 1/3] mnl: Support simple wildcards in netdev hooks
  2025-07-15 15:15 [nft PATCH v2 0/3] Support wildcard netdev hooks Phil Sutter
@ 2025-07-15 15:15 ` Phil Sutter
  2025-07-16  9:47   ` Florian Westphal
  2025-07-15 15:15 ` [nft PATCH v2 2/3] parser_bison: Accept ASTERISK_STRING in flowtable_expr_member Phil Sutter
  2025-07-15 15:15 ` [nft PATCH v2 3/3] tests: shell: Test ifname-based hooks Phil Sutter
  2 siblings, 1 reply; 6+ messages in thread
From: Phil Sutter @ 2025-07-15 15:15 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

When building NFTA_FLOWTABLE_HOOK_DEVS, NFTA_HOOK_DEV or NFTA_HOOK_DEVS
attributes, detect trailing asterisks in interface names and reduce
attribute length accordingly. Kernel will use strncmp(), effectively
performing a prefix search this way.

Deserialization (i.e., appending asterisk to interface names which don't
include a trailing nul-char) happens in libnftnl.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 src/mnl.c | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/src/mnl.c b/src/mnl.c
index 89bc742af3c5b..5abfd4b677cbd 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -799,18 +799,24 @@ static void mnl_nft_chain_devs_build(struct nlmsghdr *nlh, struct cmd *cmd)
 {
 	const struct expr *dev_expr = cmd->chain->dev_expr;
 	const struct nft_dev *dev_array;
+	int i, len, num_devs = 0;
 	struct nlattr *nest_dev;
-	int i, num_devs = 0;
 
 	dev_array = nft_dev_array(dev_expr, &num_devs);
 	if (num_devs == 1) {
 		cmd_add_loc(cmd, nlh, dev_array[0].location);
-		mnl_attr_put_strz(nlh, NFTA_HOOK_DEV, dev_array[0].ifname);
+		len = strlen(dev_array[0].ifname) + 1;
+		if (dev_array[0].ifname[len - 2] == '*')
+			len -= 2;
+		mnl_attr_put(nlh, NFTA_HOOK_DEV, len, 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, dev_array[i].location);
-			mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME, dev_array[i].ifname);
+			len = strlen(dev_array[i].ifname) + 1;
+			if (dev_array[i].ifname[len - 2] == '*')
+				len -= 2;
+			mnl_attr_put(nlh, NFTA_DEVICE_NAME, len, dev_array[i].ifname);
 			mnl_attr_nest_end(nlh, nest_dev);
 		}
 	}
@@ -2084,14 +2090,17 @@ static void mnl_nft_ft_devs_build(struct nlmsghdr *nlh, struct cmd *cmd)
 {
 	const struct expr *dev_expr = cmd->flowtable->dev_expr;
 	const struct nft_dev *dev_array;
+	int i, len, num_devs = 0;
 	struct nlattr *nest_dev;
-	int i, num_devs= 0;
 
 	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, dev_array[i].location);
-		mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME, dev_array[i].ifname);
+		len = strlen(dev_array[i].ifname) + 1;
+		if (dev_array[i].ifname[len - 2] == '*')
+			len -= 2;
+		mnl_attr_put(nlh, NFTA_DEVICE_NAME, len, dev_array[i].ifname);
 	}
 
 	mnl_attr_nest_end(nlh, nest_dev);
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [nft PATCH v2 2/3] parser_bison: Accept ASTERISK_STRING in flowtable_expr_member
  2025-07-15 15:15 [nft PATCH v2 0/3] Support wildcard netdev hooks Phil Sutter
  2025-07-15 15:15 ` [nft PATCH v2 1/3] mnl: Support simple wildcards in " Phil Sutter
@ 2025-07-15 15:15 ` Phil Sutter
  2025-07-15 15:15 ` [nft PATCH v2 3/3] tests: shell: Test ifname-based hooks Phil Sutter
  2 siblings, 0 replies; 6+ messages in thread
From: Phil Sutter @ 2025-07-15 15:15 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

All clauses are identical, so instead of adding a third one for
ASTERISK_STRING, use a single one for 'string' (which combines all three
variants).

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 src/parser_bison.y | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/src/parser_bison.y b/src/parser_bison.y
index 5b84331f220d3..c31fd05ec09cd 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -2472,16 +2472,7 @@ flowtable_list_expr	:	flowtable_expr_member
 			|	flowtable_list_expr	COMMA	opt_newline
 			;
 
-flowtable_expr_member	:	QUOTED_STRING
-			{
-				struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $1);
-
-				if (!expr)
-					YYERROR;
-
-				$$ = expr;
-			}
-			|	STRING
+flowtable_expr_member	:	string
 			{
 				struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $1);
 
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [nft PATCH v2 3/3] tests: shell: Test ifname-based hooks
  2025-07-15 15:15 [nft PATCH v2 0/3] Support wildcard netdev hooks Phil Sutter
  2025-07-15 15:15 ` [nft PATCH v2 1/3] mnl: Support simple wildcards in " Phil Sutter
  2025-07-15 15:15 ` [nft PATCH v2 2/3] parser_bison: Accept ASTERISK_STRING in flowtable_expr_member Phil Sutter
@ 2025-07-15 15:15 ` Phil Sutter
  2 siblings, 0 replies; 6+ messages in thread
From: Phil Sutter @ 2025-07-15 15:15 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

Assert that:
- Non-matching interface specs are accepted
- Existing interfaces are hooked into upon flowtable/chain creation
- A new device matching the spec is hooked into immediately
- No stale hooks remain in 'nft list hooks' output
- Wildcard hooks basically work

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 .../features/list_hooks_flowtable_info.sh     |  7 +++
 .../netdev_chain_name_based_hook_0.json-nft   | 34 ++++++++++++++
 .../dumps/netdev_chain_name_based_hook_0.nft  |  5 +++
 .../chains/netdev_chain_name_based_hook_0     | 44 ++++++++++++++++++
 .../testcases/flowtable/0016name_based_hook_0 | 45 +++++++++++++++++++
 .../dumps/0016name_based_hook_0.json-nft      | 32 +++++++++++++
 .../flowtable/dumps/0016name_based_hook_0.nft |  6 +++
 7 files changed, 173 insertions(+)
 create mode 100755 tests/shell/features/list_hooks_flowtable_info.sh
 create mode 100644 tests/shell/testcases/chains/dumps/netdev_chain_name_based_hook_0.json-nft
 create mode 100644 tests/shell/testcases/chains/dumps/netdev_chain_name_based_hook_0.nft
 create mode 100755 tests/shell/testcases/chains/netdev_chain_name_based_hook_0
 create mode 100755 tests/shell/testcases/flowtable/0016name_based_hook_0
 create mode 100644 tests/shell/testcases/flowtable/dumps/0016name_based_hook_0.json-nft
 create mode 100644 tests/shell/testcases/flowtable/dumps/0016name_based_hook_0.nft

diff --git a/tests/shell/features/list_hooks_flowtable_info.sh b/tests/shell/features/list_hooks_flowtable_info.sh
new file mode 100755
index 0000000000000..58bc57e040959
--- /dev/null
+++ b/tests/shell/features/list_hooks_flowtable_info.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# check for flowtable info in 'list hooks' output
+
+unshare -n bash -c " \
+$NFT \"table inet t { flowtable ft { hook ingress priority 0; devices = { lo }; }; }\"; \
+$NFT list hooks netdev device lo | grep -q flowtable\ inet\ t\ ft"
diff --git a/tests/shell/testcases/chains/dumps/netdev_chain_name_based_hook_0.json-nft b/tests/shell/testcases/chains/dumps/netdev_chain_name_based_hook_0.json-nft
new file mode 100644
index 0000000000000..00706271e96a4
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/netdev_chain_name_based_hook_0.json-nft
@@ -0,0 +1,34 @@
+{
+  "nftables": [
+    {
+      "metainfo": {
+        "version": "VERSION",
+        "release_name": "RELEASE_NAME",
+        "json_schema_version": 1
+      }
+    },
+    {
+      "table": {
+        "family": "netdev",
+        "name": "t",
+        "handle": 0
+      }
+    },
+    {
+      "chain": {
+        "family": "netdev",
+        "table": "t",
+        "name": "c",
+        "handle": 0,
+        "dev": [
+          "foo*",
+          "lo"
+        ],
+        "type": "filter",
+        "hook": "ingress",
+        "prio": 0,
+        "policy": "accept"
+      }
+    }
+  ]
+}
diff --git a/tests/shell/testcases/chains/dumps/netdev_chain_name_based_hook_0.nft b/tests/shell/testcases/chains/dumps/netdev_chain_name_based_hook_0.nft
new file mode 100644
index 0000000000000..ac5acacd12e6d
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/netdev_chain_name_based_hook_0.nft
@@ -0,0 +1,5 @@
+table netdev t {
+	chain c {
+		type filter hook ingress devices = { "foo*", "lo" } priority filter; policy accept;
+	}
+}
diff --git a/tests/shell/testcases/chains/netdev_chain_name_based_hook_0 b/tests/shell/testcases/chains/netdev_chain_name_based_hook_0
new file mode 100755
index 0000000000000..8a8a601784084
--- /dev/null
+++ b/tests/shell/testcases/chains/netdev_chain_name_based_hook_0
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_ifname_based_hooks)
+
+cspec=' chain netdev t c '
+$NFT add table netdev t
+$NFT add $cspec '{ type filter hook ingress priority 0; devices = { lo, foo* }; }'
+$NFT list hooks netdev device lo | grep -q "$cspec" || {
+	echo "Existing device lo not hooked into chain as expected"
+	exit 1
+}
+
+[[ $($NFT list hooks | grep -c "$cspec") -eq 1 ]] || {
+	echo "Chain hooks into more than just lo"
+	exit 2
+}
+
+ip link add foo1 type dummy
+$NFT list hooks netdev device foo1 | grep -q "$cspec" || {
+	echo "Chain did not hook into new device foo1"
+	exit 3
+}
+[[ $($NFT list hooks | grep -c "$cspec") -eq 2 ]] || {
+	echo "Chain expected to hook into exactly two devices"
+	exit 4
+}
+
+ip link del foo1
+$NFT list hooks netdev device foo1 | grep -q "$cspec" && {
+	echo "Chain still hooks into removed device foo1"
+	exit 5
+}
+[[ $($NFT list hooks | grep -c "$cspec") -eq 1 ]] || {
+	echo "Chain expected to hook into just lo"
+	exit 6
+}
+
+for ((i = 0; i < 100; i++)); do
+	ip link add foo$i type dummy
+done
+[[ $($NFT list hooks | grep -c "$cspec") -eq 101 ]] || {
+	echo "Chain did not hook into all 100 new devices"
+	exit 7
+}
diff --git a/tests/shell/testcases/flowtable/0016name_based_hook_0 b/tests/shell/testcases/flowtable/0016name_based_hook_0
new file mode 100755
index 0000000000000..9a55596027158
--- /dev/null
+++ b/tests/shell/testcases/flowtable/0016name_based_hook_0
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_ifname_based_hooks)
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_list_hooks_flowtable_info)
+
+ftspec=' flowtable ip t ft '
+$NFT add table t
+$NFT add $ftspec '{ hook ingress priority 0; devices = { lo, foo* }; }'
+$NFT list hooks netdev device lo | grep -q "$ftspec" || {
+	echo "Existing device lo not hooked into flowtable as expected"
+	exit 1
+}
+
+[[ $($NFT list hooks | grep -c "$ftspec") -eq 1 ]] || {
+	echo "Flowtable hooks into more than just lo"
+	exit 2
+}
+
+ip link add foo1 type dummy
+$NFT list hooks netdev device foo1 | grep -q "$ftspec" || {
+	echo "Flowtable did not hook into new device foo1"
+	exit 3
+}
+[[ $($NFT list hooks | grep -c "$ftspec") -eq 2 ]] || {
+	echo "Flowtable expected to hook into exactly two devices"
+	exit 4
+}
+
+ip link del foo1
+$NFT list hooks netdev device foo1 | grep -q "$ftspec" && {
+	echo "Flowtable still hooks into removed device foo1"
+	exit 5
+}
+[[ $($NFT list hooks | grep -c "$ftspec") -eq 1 ]] || {
+	echo "Flowtable expected to hook into just lo"
+	exit 6
+}
+
+for ((i = 0; i < 100; i++)); do
+	ip link add foo$i type dummy
+done
+[[ $($NFT list hooks | grep -c "$ftspec") -eq 101 ]] || {
+	echo "Flowtable did not hook into all 100 new devices"
+	exit 7
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0016name_based_hook_0.json-nft b/tests/shell/testcases/flowtable/dumps/0016name_based_hook_0.json-nft
new file mode 100644
index 0000000000000..93e263323ff95
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0016name_based_hook_0.json-nft
@@ -0,0 +1,32 @@
+{
+  "nftables": [
+    {
+      "metainfo": {
+        "version": "VERSION",
+        "release_name": "RELEASE_NAME",
+        "json_schema_version": 1
+      }
+    },
+    {
+      "table": {
+        "family": "ip",
+        "name": "t",
+        "handle": 0
+      }
+    },
+    {
+      "flowtable": {
+        "family": "ip",
+        "name": "ft",
+        "table": "t",
+        "handle": 0,
+        "hook": "ingress",
+        "prio": 0,
+        "dev": [
+          "foo*",
+          "lo"
+        ]
+      }
+    }
+  ]
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0016name_based_hook_0.nft b/tests/shell/testcases/flowtable/dumps/0016name_based_hook_0.nft
new file mode 100644
index 0000000000000..b4810664a956f
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0016name_based_hook_0.nft
@@ -0,0 +1,6 @@
+table ip t {
+	flowtable ft {
+		hook ingress priority filter
+		devices = { "foo*", "lo" }
+	}
+}
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [nft PATCH v2 1/3] mnl: Support simple wildcards in netdev hooks
  2025-07-15 15:15 ` [nft PATCH v2 1/3] mnl: Support simple wildcards in " Phil Sutter
@ 2025-07-16  9:47   ` Florian Westphal
  2025-07-16 10:26     ` Phil Sutter
  0 siblings, 1 reply; 6+ messages in thread
From: Florian Westphal @ 2025-07-16  9:47 UTC (permalink / raw)
  To: Phil Sutter; +Cc: Pablo Neira Ayuso, netfilter-devel

Phil Sutter <phil@nwl.cc> wrote:
> +		len = strlen(dev_array[0].ifname) + 1;
> +		if (dev_array[0].ifname[len - 2] == '*')
> +			len -= 2;

Not obvious to me, is there a guarantee that 'len' is 2?
And, what if len yields 0 here?

> +			if (dev_array[i].ifname[len - 2] == '*')
> +				len -= 2;
> +			mnl_attr_put(nlh, NFTA_DEVICE_NAME, len, dev_array[i].ifname);
>  			mnl_attr_nest_end(nlh, nest_dev);
>  		}
>  	}
> @@ -2084,14 +2090,17 @@ static void mnl_nft_ft_devs_build(struct nlmsghdr *nlh, struct cmd *cmd)
>  {
>  	const struct expr *dev_expr = cmd->flowtable->dev_expr;
>  	const struct nft_dev *dev_array;
> +	int i, len, num_devs = 0;
>  	struct nlattr *nest_dev;
> -	int i, num_devs= 0;
>  
>  	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, dev_array[i].location);
> -		mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME, dev_array[i].ifname);
> +		len = strlen(dev_array[i].ifname) + 1;
> +		if (dev_array[i].ifname[len - 2] == '*')
> +			len -= 2;
> +		mnl_attr_put(nlh, NFTA_DEVICE_NAME, len, dev_array[i].ifname);

This (test, subtract, put) is a repeating pattern, perhaps this warrants a helper?

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [nft PATCH v2 1/3] mnl: Support simple wildcards in netdev hooks
  2025-07-16  9:47   ` Florian Westphal
@ 2025-07-16 10:26     ` Phil Sutter
  0 siblings, 0 replies; 6+ messages in thread
From: Phil Sutter @ 2025-07-16 10:26 UTC (permalink / raw)
  To: Florian Westphal; +Cc: Pablo Neira Ayuso, netfilter-devel

On Wed, Jul 16, 2025 at 11:47:09AM +0200, Florian Westphal wrote:
> Phil Sutter <phil@nwl.cc> wrote:
> > +		len = strlen(dev_array[0].ifname) + 1;
> > +		if (dev_array[0].ifname[len - 2] == '*')
> > +			len -= 2;
> 
> Not obvious to me, is there a guarantee that 'len' is 2?
> And, what if len yields 0 here?

Oh, right. Same goes for the introduced libnftnl helpers.

> > +			if (dev_array[i].ifname[len - 2] == '*')
> > +				len -= 2;
> > +			mnl_attr_put(nlh, NFTA_DEVICE_NAME, len, dev_array[i].ifname);
> >  			mnl_attr_nest_end(nlh, nest_dev);
> >  		}
> >  	}
> > @@ -2084,14 +2090,17 @@ static void mnl_nft_ft_devs_build(struct nlmsghdr *nlh, struct cmd *cmd)
> >  {
> >  	const struct expr *dev_expr = cmd->flowtable->dev_expr;
> >  	const struct nft_dev *dev_array;
> > +	int i, len, num_devs = 0;
> >  	struct nlattr *nest_dev;
> > -	int i, num_devs= 0;
> >  
> >  	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, dev_array[i].location);
> > -		mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME, dev_array[i].ifname);
> > +		len = strlen(dev_array[i].ifname) + 1;
> > +		if (dev_array[i].ifname[len - 2] == '*')
> > +			len -= 2;
> > +		mnl_attr_put(nlh, NFTA_DEVICE_NAME, len, dev_array[i].ifname);
> 
> This (test, subtract, put) is a repeating pattern, perhaps this warrants a helper?

It's even there already, (still) named 'mnl_attr_put_ifname' in my
libnftnl patch. I'll export it (with correct prefix) for use in
nftables.

Thanks, Phil

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2025-07-16 10:26 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-15 15:15 [nft PATCH v2 0/3] Support wildcard netdev hooks Phil Sutter
2025-07-15 15:15 ` [nft PATCH v2 1/3] mnl: Support simple wildcards in " Phil Sutter
2025-07-16  9:47   ` Florian Westphal
2025-07-16 10:26     ` Phil Sutter
2025-07-15 15:15 ` [nft PATCH v2 2/3] parser_bison: Accept ASTERISK_STRING in flowtable_expr_member Phil Sutter
2025-07-15 15:15 ` [nft PATCH v2 3/3] tests: shell: Test ifname-based hooks Phil Sutter

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.