From: Daniel Borkmann <daniel@iogearbox.net>
To: netdev@vger.kernel.org
Cc: bpf@vger.kernel.org, kuba@kernel.org, davem@davemloft.net,
razor@blackwall.org, pabeni@redhat.com, willemb@google.com,
sdf@fomichev.me, john.fastabend@gmail.com, martin.lau@kernel.org,
jordan@jrife.io, maciej.fijalkowski@intel.com,
magnus.karlsson@intel.com, dw@davidwei.uk, toke@redhat.com,
yangzhenze@bytedance.com, wangdongdong.6@bytedance.com
Subject: [PATCH net-next v8 15/16] selftests/net: Add env for container based tests
Date: Thu, 29 Jan 2026 23:28:29 +0100 [thread overview]
Message-ID: <20260129222830.439687-16-daniel@iogearbox.net> (raw)
In-Reply-To: <20260129222830.439687-1-daniel@iogearbox.net>
From: David Wei <dw@davidwei.uk>
Add an env NetDrvContEnv for container based selftests. This automates
the setup of a netns, netkit pair with one inside the netns, and a BPF
program that forwards skbs from the NETIF host inside the container.
Currently only netkit is used, but other virtual netdevs e.g. veth can
be used too.
Expect netkit container datapath selftests to have a publicly routable
IP prefix to assign to netkit in a container, such that packets will
land on eth0. The BPF skb forward program will then forward such packets
from the host netns to the container netns.
Signed-off-by: David Wei <dw@davidwei.uk>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
.../testing/selftests/drivers/net/README.rst | 7 +
.../drivers/net/hw/lib/py/__init__.py | 7 +-
.../selftests/drivers/net/lib/py/__init__.py | 7 +-
.../selftests/drivers/net/lib/py/env.py | 120 ++++++++++++++++++
4 files changed, 135 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/README.rst b/tools/testing/selftests/drivers/net/README.rst
index eb838ae94844..b94e81c2e030 100644
--- a/tools/testing/selftests/drivers/net/README.rst
+++ b/tools/testing/selftests/drivers/net/README.rst
@@ -62,6 +62,13 @@ LOCAL_V4, LOCAL_V6, REMOTE_V4, REMOTE_V6
Local and remote endpoint IP addresses.
+LOCAL_PREFIX_V4, LOCAL_PREFIX_V6
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Local IP prefix/subnet which can be used to allocate extra IP addresses (for
+network name spaces behind macvlan, veth, netkit devices). DUT must be
+reachable using these addresses from the endpoint.
+
REMOTE_TYPE
~~~~~~~~~~~
diff --git a/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py b/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py
index d5d247eca6b7..022008249313 100644
--- a/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py
+++ b/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py
@@ -3,6 +3,7 @@
"""
Driver test environment (hardware-only tests).
NetDrvEnv and NetDrvEpEnv are the main environment classes.
+NetDrvContEnv extends NetDrvEpEnv with netkit container support.
Former is for local host only tests, latter creates / connects
to a remote endpoint. See NIPA wiki for more information about
running and writing driver tests.
@@ -29,7 +30,7 @@ try:
from net.lib.py import ksft_eq, ksft_ge, ksft_in, ksft_is, ksft_lt, \
ksft_ne, ksft_not_in, ksft_raises, ksft_true, ksft_gt, ksft_not_none
from drivers.net.lib.py import GenerateTraffic, Remote, Iperf3Runner
- from drivers.net.lib.py import NetDrvEnv, NetDrvEpEnv
+ from drivers.net.lib.py import NetDrvEnv, NetDrvEpEnv, NetDrvContEnv
__all__ = ["NetNS", "NetNSEnter", "NetdevSimDev",
"EthtoolFamily", "NetdevFamily", "NetshaperFamily",
@@ -44,8 +45,8 @@ try:
"ksft_eq", "ksft_ge", "ksft_in", "ksft_is", "ksft_lt",
"ksft_ne", "ksft_not_in", "ksft_raises", "ksft_true", "ksft_gt",
"ksft_not_none", "ksft_not_none",
- "NetDrvEnv", "NetDrvEpEnv", "GenerateTraffic", "Remote",
- "Iperf3Runner"]
+ "NetDrvEnv", "NetDrvEpEnv", "NetDrvContEnv", "GenerateTraffic",
+ "Remote", "Iperf3Runner"]
except ModuleNotFoundError as e:
print("Failed importing `net` library from kernel sources")
print(str(e))
diff --git a/tools/testing/selftests/drivers/net/lib/py/__init__.py b/tools/testing/selftests/drivers/net/lib/py/__init__.py
index 8b75faa9af6d..be3a8a936882 100644
--- a/tools/testing/selftests/drivers/net/lib/py/__init__.py
+++ b/tools/testing/selftests/drivers/net/lib/py/__init__.py
@@ -3,6 +3,7 @@
"""
Driver test environment.
NetDrvEnv and NetDrvEpEnv are the main environment classes.
+NetDrvContEnv extends NetDrvEpEnv with netkit container support.
Former is for local host only tests, latter creates / connects
to a remote endpoint. See NIPA wiki for more information about
running and writing driver tests.
@@ -43,12 +44,12 @@ try:
"ksft_ne", "ksft_not_in", "ksft_raises", "ksft_true", "ksft_gt",
"ksft_not_none", "ksft_not_none"]
- from .env import NetDrvEnv, NetDrvEpEnv
+ from .env import NetDrvEnv, NetDrvEpEnv, NetDrvContEnv
from .load import GenerateTraffic, Iperf3Runner
from .remote import Remote
- __all__ += ["NetDrvEnv", "NetDrvEpEnv", "GenerateTraffic", "Remote",
- "Iperf3Runner"]
+ __all__ += ["NetDrvEnv", "NetDrvEpEnv", "NetDrvContEnv", "GenerateTraffic",
+ "Remote", "Iperf3Runner"]
except ModuleNotFoundError as e:
print("Failed importing `net` library from kernel sources")
print(str(e))
diff --git a/tools/testing/selftests/drivers/net/lib/py/env.py b/tools/testing/selftests/drivers/net/lib/py/env.py
index 41cc248ac848..d13fd9128c1f 100644
--- a/tools/testing/selftests/drivers/net/lib/py/env.py
+++ b/tools/testing/selftests/drivers/net/lib/py/env.py
@@ -1,6 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
+import ipaddress
import os
+import re
import time
from pathlib import Path
from lib.py import KsftSkipEx, KsftXfailEx
@@ -8,6 +10,7 @@ from lib.py import ksft_setup, wait_file
from lib.py import cmd, ethtool, ip, CmdExitFailure
from lib.py import NetNS, NetdevSimDev
from .remote import Remote
+from . import bpftool
class NetDrvEnvBase:
@@ -289,3 +292,120 @@ class NetDrvEpEnv(NetDrvEnvBase):
data.get('stats-block-usecs', 0) / 1000 / 1000
time.sleep(self._stats_settle_time)
+
+
+class NetDrvContEnv(NetDrvEpEnv):
+ """
+ Class for an environment with a netkit pair setup for forwarding traffic
+ between the physical interface and a network namespace.
+ """
+
+ def __init__(self, src_path, rxqueues=1, **kwargs):
+ super().__init__(src_path, **kwargs)
+
+ self.require_ipver("6")
+ local_prefix = self.env.get("LOCAL_PREFIX_V6")
+ if not local_prefix:
+ raise KsftSkipEx("LOCAL_PREFIX_V6 required")
+
+ local_prefix = local_prefix.rstrip("/64").rstrip("::").rstrip(":")
+ self.ipv6_prefix = f"{local_prefix}::"
+ self.nk_host_ipv6 = f"{local_prefix}::2:1"
+ self.nk_guest_ipv6 = f"{local_prefix}::2:2"
+
+ self.netns = None
+ self._nk_host_ifname = None
+ self._nk_guest_ifname = None
+ self._tc_attached = False
+ self._bpf_prog_pref = None
+ self._bpf_prog_id = None
+ self._init_ns_attached = False
+
+ ip(f"link add type netkit mode l2 forward peer forward numrxqueues {rxqueues}")
+
+ all_links = ip("-d link show", json=True)
+ netkit_links = [link for link in all_links
+ if link.get('linkinfo', {}).get('info_kind') == 'netkit'
+ and 'UP' not in link.get('flags', [])]
+
+ if len(netkit_links) != 2:
+ raise KsftSkipEx("Failed to create netkit pair")
+
+ netkit_links.sort(key=lambda x: x['ifindex'])
+ self._nk_host_ifname = netkit_links[1]['ifname']
+ self._nk_guest_ifname = netkit_links[0]['ifname']
+ self.nk_host_ifindex = netkit_links[1]['ifindex']
+ self.nk_guest_ifindex = netkit_links[0]['ifindex']
+
+ self._setup_ns()
+ self._attach_bpf()
+
+ def __del__(self):
+ if self._tc_attached:
+ cmd(f"tc filter del dev {self.ifname} ingress pref {self._bpf_prog_pref}")
+ self._tc_attached = False
+
+ if self._nk_host_ifname:
+ cmd(f"ip link del dev {self._nk_host_ifname}")
+ self._nk_host_ifname = None
+ self._nk_guest_ifname = None
+
+ if self._init_ns_attached:
+ cmd("ip netns del init", fail=False)
+ self._init_ns_attached = False
+
+ if self.netns:
+ del self.netns
+ self.netns = None
+
+ super().__del__()
+
+ def _setup_ns(self):
+ self.netns = NetNS()
+ cmd("ip netns attach init 1")
+ self._init_ns_attached = True
+ ip("netns set init 0", ns=self.netns)
+ ip(f"link set dev {self._nk_guest_ifname} netns {self.netns.name}")
+ ip(f"link set dev {self._nk_host_ifname} up")
+ ip(f"-6 addr add fe80::1/64 dev {self._nk_host_ifname} nodad")
+ ip(f"-6 route add {self.nk_guest_ipv6}/128 via fe80::2 dev {self._nk_host_ifname}")
+
+ ip("link set lo up", ns=self.netns)
+ ip(f"link set dev {self._nk_guest_ifname} up", ns=self.netns)
+ ip(f"-6 addr add fe80::2/64 dev {self._nk_guest_ifname}", ns=self.netns)
+ ip(f"-6 addr add {self.nk_guest_ipv6}/64 dev {self._nk_guest_ifname} nodad", ns=self.netns)
+ ip(f"-6 route add default via fe80::1 dev {self._nk_guest_ifname}", ns=self.netns)
+
+ def _attach_bpf(self):
+ bpf_obj = self.test_dir / "nk_forward.bpf.o"
+ if not bpf_obj.exists():
+ raise KsftSkipEx("BPF prog not found")
+
+ cmd(f"tc filter add dev {self.ifname} ingress bpf obj {bpf_obj} sec tc/ingress direct-action")
+ self._tc_attached = True
+
+ tc_info = cmd(f"tc filter show dev {self.ifname} ingress").stdout
+ match = re.search(r'pref (\d+).*nk_forward\.bpf.*id (\d+)', tc_info)
+ if not match:
+ raise Exception("Failed to get BPF prog ID")
+ self._bpf_prog_pref = int(match.group(1))
+ self._bpf_prog_id = int(match.group(2))
+
+ prog_info = bpftool(f"prog show id {self._bpf_prog_id}", json=True)
+ map_ids = prog_info.get("map_ids", [])
+
+ bss_map_id = None
+ for map_id in map_ids:
+ map_info = bpftool(f"map show id {map_id}", json=True)
+ if map_info.get("name").endswith("bss"):
+ bss_map_id = map_id
+
+ if bss_map_id is None:
+ raise Exception("Failed to find .bss map")
+
+ ipv6_addr = ipaddress.IPv6Address(self.ipv6_prefix)
+ ipv6_bytes = ipv6_addr.packed
+ ifindex_bytes = self.nk_host_ifindex.to_bytes(4, byteorder='little')
+ value = ipv6_bytes + ifindex_bytes
+ value_hex = ' '.join(f'{b:02x}' for b in value)
+ bpftool(f"map update id {bss_map_id} key hex 00 00 00 00 value hex {value_hex}")
--
2.43.0
next prev parent reply other threads:[~2026-01-29 22:29 UTC|newest]
Thread overview: 41+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-29 22:28 [PATCH net-next v8 00/16] netkit: Support for io_uring zero-copy and AF_XDP Daniel Borkmann
2026-01-29 22:28 ` [PATCH net-next v8 01/16] net: Add queue-create operation Daniel Borkmann
2026-01-29 22:28 ` [PATCH net-next v8 02/16] net: Implement netdev_nl_queue_create_doit Daniel Borkmann
2026-01-31 23:45 ` Jakub Kicinski
2026-03-05 4:38 ` Daniel Borkmann
2026-03-06 2:10 ` Jakub Kicinski
2026-03-06 5:49 ` Daniel Borkmann
2026-01-29 22:28 ` [PATCH net-next v8 03/16] net: Add lease info to queue-get response Daniel Borkmann
2026-01-31 23:43 ` Jakub Kicinski
2026-02-01 0:15 ` Jakub Kicinski
2026-01-29 22:28 ` [PATCH net-next v8 04/16] net, ethtool: Disallow leased real rxqs to be resized Daniel Borkmann
2026-01-31 23:45 ` Jakub Kicinski
2026-01-29 22:28 ` [PATCH net-next v8 05/16] net: Slightly simplify net_mp_{open,close}_rxq Daniel Borkmann
2026-01-31 23:48 ` Jakub Kicinski
2026-01-29 22:28 ` [PATCH net-next v8 06/16] net: Proxy net_mp_{open,close}_rxq for leased queues Daniel Borkmann
2026-02-01 0:02 ` Jakub Kicinski
2026-02-01 22:09 ` David Wei
2026-01-29 22:28 ` [PATCH net-next v8 07/16] net: Proxy netdev_queue_get_dma_dev " Daniel Borkmann
2026-02-01 0:04 ` Jakub Kicinski
2026-02-01 22:23 ` David Wei
2026-01-29 22:28 ` [PATCH net-next v8 08/16] xsk: Extend xsk_rcv_check validation Daniel Borkmann
2026-01-29 22:28 ` [PATCH net-next v8 09/16] xsk: Proxy pool management for leased queues Daniel Borkmann
2026-01-29 22:28 ` [PATCH net-next v8 10/16] netkit: Add single device mode for netkit Daniel Borkmann
2026-01-29 22:28 ` [PATCH net-next v8 11/16] netkit: Implement rtnl_link_ops->alloc and ndo_queue_create Daniel Borkmann
2026-02-01 0:19 ` Jakub Kicinski
2026-02-01 22:27 ` David Wei
2026-01-29 22:28 ` [PATCH net-next v8 12/16] netkit: Add netkit notifier to check for unregistering devices Daniel Borkmann
2026-01-29 22:28 ` [PATCH net-next v8 13/16] netkit: Add xsk support for af_xdp applications Daniel Borkmann
2026-01-29 22:28 ` [PATCH net-next v8 14/16] selftests/net: Add bpf skb forwarding program Daniel Borkmann
2026-01-29 22:28 ` Daniel Borkmann [this message]
2026-02-01 0:38 ` [PATCH net-next v8 15/16] selftests/net: Add env for container based tests Jakub Kicinski
2026-02-01 22:53 ` David Wei
2026-02-02 18:41 ` Jakub Kicinski
2026-02-10 0:25 ` David Wei
2026-02-05 2:08 ` Bobby Eshleman
2026-02-05 2:34 ` Bobby Eshleman
2026-02-10 17:30 ` David Wei
2026-01-29 22:28 ` [PATCH net-next v8 16/16] selftests/net: Add netkit container tests Daniel Borkmann
2026-02-01 0:24 ` Jakub Kicinski
2026-02-01 22:54 ` David Wei
2026-02-05 1:44 ` Bobby Eshleman
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260129222830.439687-16-daniel@iogearbox.net \
--to=daniel@iogearbox.net \
--cc=bpf@vger.kernel.org \
--cc=davem@davemloft.net \
--cc=dw@davidwei.uk \
--cc=john.fastabend@gmail.com \
--cc=jordan@jrife.io \
--cc=kuba@kernel.org \
--cc=maciej.fijalkowski@intel.com \
--cc=magnus.karlsson@intel.com \
--cc=martin.lau@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=razor@blackwall.org \
--cc=sdf@fomichev.me \
--cc=toke@redhat.com \
--cc=wangdongdong.6@bytedance.com \
--cc=willemb@google.com \
--cc=yangzhenze@bytedance.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox