public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next 0/2] Add selftests for ntuple (NFC) rules
@ 2026-04-07 16:49 Dimitri Daskalakis
  2026-04-07 16:49 ` [PATCH net-next 1/2] selftests: drv-net: Add ntuple (NFC) flow steering test Dimitri Daskalakis
  2026-04-07 16:49 ` [PATCH net-next 2/2] selftests: drv-net: ntuple: Add dst-ip, src-port, dst-port fields Dimitri Daskalakis
  0 siblings, 2 replies; 3+ messages in thread
From: Dimitri Daskalakis @ 2026-04-07 16:49 UTC (permalink / raw)
  To: David S . Miller
  Cc: Andrew Lunn, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Shuah Khan, Willem de Bruijn, Petr Machata, David Wei,
	Chris J Arges, Carolina Jubran, Dimitri Daskalakis, netdev,
	linux-kselftest

From: Dimitri Daskalakis <daskald@meta.com>

Thoroughly testing a device's NFC implementation can be tedious. The more
features a device supports, the more combinations to validate.

This series aims to ease that burden, validating the most common NFC rule
combinations.

Dimitri Daskalakis (2):
  selftests: drv-net: Add ntuple (NFC) flow steering test
  selftests: drv-net: ntuple: Add dst-ip, src-port, dst-port fields

 .../testing/selftests/drivers/net/hw/Makefile |   1 +
 .../selftests/drivers/net/hw/ntuple.py        | 162 ++++++++++++++++++
 2 files changed, 163 insertions(+)
 create mode 100755 tools/testing/selftests/drivers/net/hw/ntuple.py

-- 
2.52.0


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

* [PATCH net-next 1/2] selftests: drv-net: Add ntuple (NFC) flow steering test
  2026-04-07 16:49 [PATCH net-next 0/2] Add selftests for ntuple (NFC) rules Dimitri Daskalakis
@ 2026-04-07 16:49 ` Dimitri Daskalakis
  2026-04-07 16:49 ` [PATCH net-next 2/2] selftests: drv-net: ntuple: Add dst-ip, src-port, dst-port fields Dimitri Daskalakis
  1 sibling, 0 replies; 3+ messages in thread
From: Dimitri Daskalakis @ 2026-04-07 16:49 UTC (permalink / raw)
  To: David S . Miller
  Cc: Andrew Lunn, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Shuah Khan, Willem de Bruijn, Petr Machata, David Wei,
	Chris J Arges, Carolina Jubran, Dimitri Daskalakis, netdev,
	linux-kselftest

From: Dimitri Daskalakis <daskald@meta.com>

Add a test for ethtool NFC (ntuple) flow steering rules. The test
creates an ntuple rule matching on various flow fields and verifies
that traffic is steered to the correct queue.

The test forces all traffic to queue 0 via the indirection table,
then installs an ntuple rule to steer select traffic to a specific
queue. The test then verifies the expected number of packets is received
on the queue.

This test has variants for TCP/UDP over IPv4/IPv6, with rules matching
the source IP. Additional match fields will be added in the next commit.

 TAP version 13
 1..4
 ok 1 ntuple.queue.tcp4.src_ip
 ok 2 ntuple.queue.udp4.src_ip
 ok 3 ntuple.queue.tcp6.src_ip
 ok 4 ntuple.queue.udp6.src_ip
 # Totals: pass:4 fail:0 xfail:0 xpass:0 skip:0 error:0

Signed-off-by: Dimitri Daskalakis <daskald@meta.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 .../testing/selftests/drivers/net/hw/Makefile |   1 +
 .../selftests/drivers/net/hw/ntuple.py        | 145 ++++++++++++++++++
 2 files changed, 146 insertions(+)
 create mode 100755 tools/testing/selftests/drivers/net/hw/ntuple.py

diff --git a/tools/testing/selftests/drivers/net/hw/Makefile b/tools/testing/selftests/drivers/net/hw/Makefile
index deeca3f8d080..1f4ebe70c34c 100644
--- a/tools/testing/selftests/drivers/net/hw/Makefile
+++ b/tools/testing/selftests/drivers/net/hw/Makefile
@@ -35,6 +35,7 @@ TEST_PROGS = \
 	loopback.sh \
 	nic_timestamp.py \
 	nk_netns.py \
+	ntuple.py \
 	pp_alloc_fail.py \
 	rss_api.py \
 	rss_ctx.py \
diff --git a/tools/testing/selftests/drivers/net/hw/ntuple.py b/tools/testing/selftests/drivers/net/hw/ntuple.py
new file mode 100755
index 000000000000..c50b76198fba
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/hw/ntuple.py
@@ -0,0 +1,145 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+"""Test ethtool NFC (ntuple) flow steering rules."""
+
+import random
+from enum import Enum, auto
+from lib.py import ksft_run, ksft_exit
+from lib.py import ksft_eq, ksft_ge
+from lib.py import ksft_variants, KsftNamedVariant
+from lib.py import EthtoolFamily, NetDrvEpEnv, NetdevFamily
+from lib.py import KsftSkipEx
+from lib.py import cmd, ethtool, defer, rand_port, bkg, wait_port_listen
+
+
+class NtupleField(Enum):
+    SRC_IP = auto()
+
+
+def _require_ntuple(cfg):
+    features = ethtool(f"-k {cfg.ifname}", json=True)[0]
+    if not features["ntuple-filters"]["active"]:
+        raise KsftSkipEx("Ntuple filters not enabled on the device: " + str(features["ntuple-filters"]))
+
+
+def _get_rx_cnts(cfg, prev=None):
+    """Get Rx packet counts for all queues, as a simple list of integers
+       if @prev is specified the prev counts will be subtracted"""
+    cfg.wait_hw_stats_settle()
+    data = cfg.netdevnl.qstats_get({"ifindex": cfg.ifindex, "scope": ["queue"]}, dump=True)
+    data = [x for x in data if x['queue-type'] == "rx"]
+    max_q = max([x["queue-id"] for x in data])
+    queue_stats = [0] * (max_q + 1)
+    for q in data:
+        queue_stats[q["queue-id"]] = q["rx-packets"]
+        if prev and q["queue-id"] < len(prev):
+            queue_stats[q["queue-id"]] -= prev[q["queue-id"]]
+    return queue_stats
+
+
+def _ntuple_rule_add(cfg, flow_spec):
+    """Install an NFC rule via ethtool."""
+
+    output = ethtool(f"-N {cfg.ifname} {flow_spec}").stdout
+    rule_id = int(output.split()[-1])
+    defer(ethtool, f"-N {cfg.ifname} delete {rule_id}")
+
+
+def _setup_isolated_queue(cfg):
+    """Default all traffic to queue 0, and pick a random queue to
+       steer NFC traffic to."""
+
+    channels = cfg.ethnl.channels_get({'header': {'dev-index': cfg.ifindex}})
+    ch_max = channels['combined-max']
+    qcnt = channels['combined-count']
+
+    if ch_max < 2:
+        raise KsftSkipEx(f"Need at least 2 combined channels, max is {ch_max}")
+
+    desired_queues = min(ch_max, 4)
+    if qcnt >= desired_queues:
+        desired_queues = qcnt
+    else:
+        ethtool(f"-L {cfg.ifname} combined {desired_queues}")
+        defer(ethtool, f"-L {cfg.ifname} combined {qcnt}")
+
+    ethtool(f"-X {cfg.ifname} equal 1")
+    defer(ethtool, f"-X {cfg.ifname} default")
+
+    return random.randint(1, desired_queues - 1)
+
+
+def _send_traffic(cfg, ipver, proto, dst_port, pkt_cnt=40):
+    """Generate traffic with the desired flow signature."""
+
+    cfg.require_cmd("socat", remote=True)
+
+    socat_proto = proto.upper()
+    dst_addr = f"[{cfg.addr_v['6']}]" if ipver == '6' else cfg.addr_v['4']
+
+    extra_opts = ",nodelay" if proto == "tcp" else ",shut-null"
+
+    listen_cmd = (f"socat -{ipver} -t 2 -u "
+                  f"{socat_proto}-LISTEN:{dst_port},reuseport /dev/null")
+    with bkg(listen_cmd, exit_wait=True):
+        wait_port_listen(dst_port, proto=proto)
+        send_cmd = f"""
+        bash -c 'for i in $(seq {pkt_cnt}); do echo msg; sleep 0.02; done' |
+        socat -{ipver} -u - \
+            {socat_proto}:{dst_addr}:{dst_port},reuseaddr{extra_opts}
+        """
+        cmd(send_cmd, shell=True, host=cfg.remote)
+
+
+def _add_ntuple_rule_and_send_traffic(cfg, ipver, proto, fields, test_queue):
+    dst_port = rand_port()
+    flow_parts = [f"flow-type {proto}{ipver}"]
+
+    for field in fields:
+        if field == NtupleField.SRC_IP:
+            flow_parts.append(f"src-ip {cfg.remote_addr_v[ipver]}")
+
+    flow_parts.append(f"action {test_queue}")
+    _ntuple_rule_add(cfg, " ".join(flow_parts))
+    _send_traffic(cfg, ipver, proto, dst_port=dst_port)
+
+
+def _ntuple_variants():
+    for ipver in ["4", "6"]:
+        for proto in ["tcp", "udp"]:
+            for fields in [[NtupleField.SRC_IP]]:
+                name = ".".join(f.name.lower() for f in fields)
+                yield KsftNamedVariant(f"{proto}{ipver}.{name}",
+                                      ipver, proto, fields)
+
+
+@ksft_variants(_ntuple_variants())
+def queue(cfg, ipver, proto, fields):
+    """Test that an NFC rule steers traffic to the correct queue."""
+
+    cfg.require_ipver(ipver)
+    _require_ntuple(cfg)
+
+    test_queue = _setup_isolated_queue(cfg)
+
+    cnts = _get_rx_cnts(cfg)
+    _add_ntuple_rule_and_send_traffic(cfg, ipver, proto, fields, test_queue)
+    cnts = _get_rx_cnts(cfg, prev=cnts)
+
+    ksft_ge(cnts[test_queue], 40, f"Traffic on test queue {test_queue}: {cnts}")
+    sum_idle = sum(cnts) - cnts[0] - cnts[test_queue]
+    ksft_eq(sum_idle, 0, f"Traffic on idle queues: {cnts}")
+
+
+def main() -> None:
+    """Ksft boilerplate main."""
+
+    with NetDrvEpEnv(__file__, nsim_test=False) as cfg:
+        cfg.ethnl = EthtoolFamily()
+        cfg.netdevnl = NetdevFamily()
+        ksft_run([queue], args=(cfg,))
+    ksft_exit()
+
+
+if __name__ == "__main__":
+    main()
-- 
2.52.0


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

* [PATCH net-next 2/2] selftests: drv-net: ntuple: Add dst-ip, src-port, dst-port fields
  2026-04-07 16:49 [PATCH net-next 0/2] Add selftests for ntuple (NFC) rules Dimitri Daskalakis
  2026-04-07 16:49 ` [PATCH net-next 1/2] selftests: drv-net: Add ntuple (NFC) flow steering test Dimitri Daskalakis
@ 2026-04-07 16:49 ` Dimitri Daskalakis
  1 sibling, 0 replies; 3+ messages in thread
From: Dimitri Daskalakis @ 2026-04-07 16:49 UTC (permalink / raw)
  To: David S . Miller
  Cc: Andrew Lunn, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Shuah Khan, Willem de Bruijn, Petr Machata, David Wei,
	Chris J Arges, Carolina Jubran, Dimitri Daskalakis, netdev,
	linux-kselftest

From: Dimitri Daskalakis <daskald@meta.com>

Extend the ntuple flow steering test to cover dst-ip, src-port, and
dst-port fields. The test supports arbitrary combinations of the fields,
for now we test src_ip/dst_ip, and src_ip/dst_ip/src_port/dst_port.

The tests currently match full fields, but we can consider adding
support for masked fields in the future.

 TAP version 13
 1..24
 ok 1 ntuple.queue.tcp4.src_ip
 ok 2 ntuple.queue.tcp4.dst_ip
 ok 3 ntuple.queue.tcp4.src_port
 ok 4 ntuple.queue.tcp4.dst_port
 ok 5 ntuple.queue.tcp4.src_ip.dst_ip
 ok 6 ntuple.queue.tcp4.src_ip.dst_ip.src_port.dst_port
 ok 7 ntuple.queue.udp4.src_ip
 ok 8 ntuple.queue.udp4.dst_ip
 ok 9 ntuple.queue.udp4.src_port
 ok 10 ntuple.queue.udp4.dst_port
 ok 11 ntuple.queue.udp4.src_ip.dst_ip
 ok 12 ntuple.queue.udp4.src_ip.dst_ip.src_port.dst_port
 ok 13 ntuple.queue.tcp6.src_ip
 ok 14 ntuple.queue.tcp6.dst_ip
 ok 15 ntuple.queue.tcp6.src_port
 ok 16 ntuple.queue.tcp6.dst_port
 ok 17 ntuple.queue.tcp6.src_ip.dst_ip
 ok 18 ntuple.queue.tcp6.src_ip.dst_ip.src_port.dst_port
 ok 19 ntuple.queue.udp6.src_ip
 ok 20 ntuple.queue.udp6.dst_ip
 ok 21 ntuple.queue.udp6.src_port
 ok 22 ntuple.queue.udp6.dst_port
 ok 23 ntuple.queue.udp6.src_ip.dst_ip
 ok 24 ntuple.queue.udp6.src_ip.dst_ip.src_port.dst_port
 # Totals: pass:24 fail:0 xfail:0 xpass:0 skip:0 error:0

Signed-off-by: Dimitri Daskalakis <daskald@meta.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
 .../selftests/drivers/net/hw/ntuple.py        | 29 +++++++++++++++----
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/tools/testing/selftests/drivers/net/hw/ntuple.py b/tools/testing/selftests/drivers/net/hw/ntuple.py
index c50b76198fba..12f0d395f825 100755
--- a/tools/testing/selftests/drivers/net/hw/ntuple.py
+++ b/tools/testing/selftests/drivers/net/hw/ntuple.py
@@ -9,11 +9,14 @@ from lib.py import ksft_eq, ksft_ge
 from lib.py import ksft_variants, KsftNamedVariant
 from lib.py import EthtoolFamily, NetDrvEpEnv, NetdevFamily
 from lib.py import KsftSkipEx
-from lib.py import cmd, ethtool, defer, rand_port, bkg, wait_port_listen
+from lib.py import cmd, ethtool, defer, rand_ports, bkg, wait_port_listen
 
 
 class NtupleField(Enum):
     SRC_IP = auto()
+    DST_IP = auto()
+    SRC_PORT = auto()
+    DST_PORT = auto()
 
 
 def _require_ntuple(cfg):
@@ -69,7 +72,7 @@ def _setup_isolated_queue(cfg):
     return random.randint(1, desired_queues - 1)
 
 
-def _send_traffic(cfg, ipver, proto, dst_port, pkt_cnt=40):
+def _send_traffic(cfg, ipver, proto, dst_port, src_port, pkt_cnt=40):
     """Generate traffic with the desired flow signature."""
 
     cfg.require_cmd("socat", remote=True)
@@ -86,28 +89,42 @@ def _send_traffic(cfg, ipver, proto, dst_port, pkt_cnt=40):
         send_cmd = f"""
         bash -c 'for i in $(seq {pkt_cnt}); do echo msg; sleep 0.02; done' |
         socat -{ipver} -u - \
-            {socat_proto}:{dst_addr}:{dst_port},reuseaddr{extra_opts}
+            {socat_proto}:{dst_addr}:{dst_port},sourceport={src_port},reuseaddr{extra_opts}
         """
         cmd(send_cmd, shell=True, host=cfg.remote)
 
 
 def _add_ntuple_rule_and_send_traffic(cfg, ipver, proto, fields, test_queue):
-    dst_port = rand_port()
+    ports = rand_ports(2)
+    src_port = ports[0]
+    dst_port = ports[1]
     flow_parts = [f"flow-type {proto}{ipver}"]
 
     for field in fields:
         if field == NtupleField.SRC_IP:
             flow_parts.append(f"src-ip {cfg.remote_addr_v[ipver]}")
+        elif field == NtupleField.DST_IP:
+            flow_parts.append(f"dst-ip {cfg.addr_v[ipver]}")
+        elif field == NtupleField.SRC_PORT:
+            flow_parts.append(f"src-port {src_port}")
+        elif field == NtupleField.DST_PORT:
+            flow_parts.append(f"dst-port {dst_port}")
 
     flow_parts.append(f"action {test_queue}")
     _ntuple_rule_add(cfg, " ".join(flow_parts))
-    _send_traffic(cfg, ipver, proto, dst_port=dst_port)
+    _send_traffic(cfg, ipver, proto, dst_port=dst_port, src_port=src_port)
 
 
 def _ntuple_variants():
     for ipver in ["4", "6"]:
         for proto in ["tcp", "udp"]:
-            for fields in [[NtupleField.SRC_IP]]:
+            for fields in [[NtupleField.SRC_IP],
+                           [NtupleField.DST_IP],
+                           [NtupleField.SRC_PORT],
+                           [NtupleField.DST_PORT],
+                           [NtupleField.SRC_IP, NtupleField.DST_IP],
+                           [NtupleField.SRC_IP, NtupleField.DST_IP,
+                            NtupleField.SRC_PORT, NtupleField.DST_PORT]]:
                 name = ".".join(f.name.lower() for f in fields)
                 yield KsftNamedVariant(f"{proto}{ipver}.{name}",
                                       ipver, proto, fields)
-- 
2.52.0


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

end of thread, other threads:[~2026-04-07 16:50 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-07 16:49 [PATCH net-next 0/2] Add selftests for ntuple (NFC) rules Dimitri Daskalakis
2026-04-07 16:49 ` [PATCH net-next 1/2] selftests: drv-net: Add ntuple (NFC) flow steering test Dimitri Daskalakis
2026-04-07 16:49 ` [PATCH net-next 2/2] selftests: drv-net: ntuple: Add dst-ip, src-port, dst-port fields Dimitri Daskalakis

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox