[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <aYy/xmgPNArOFOr7@devvm11784.nha0.facebook.com>
Date: Wed, 11 Feb 2026 09:43:34 -0800
From: Bobby Eshleman <bobbyeshleman@...il.com>
To: David Wei <dw@...idwei.uk>
Cc: netdev@...r.kernel.org, Andrew Lunn <andrew+netdev@...n.ch>,
"David S. Miller" <davem@...emloft.net>,
Eric Dumazet <edumazet@...gle.com>,
Jakub Kicinski <kuba@...nel.org>, Paolo Abeni <pabeni@...hat.com>,
Joe Damato <joe@...a.to>, Wei Wang <weibunny@...a.com>,
Stanislav Fomichev <sdf@...ichev.me>,
Nikolay Aleksandrov <razor@...ckwall.org>
Subject: Re: [PATCH net-next 3/4] selftests/net: Add env for container based
tests
On Tue, Feb 10, 2026 at 12:04:18PM -0800, David Wei wrote:
> 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@...idwei.uk>
> Signed-off-by: Daniel Borkmann <daniel@...earbox.net>
> ---
> .../testing/selftests/drivers/net/README.rst | 19 ++
> .../drivers/net/hw/lib/py/__init__.py | 7 +-
> .../selftests/drivers/net/lib/py/__init__.py | 7 +-
> .../selftests/drivers/net/lib/py/env.py | 163 ++++++++++++++++++
> 4 files changed, 190 insertions(+), 6 deletions(-)
>
> diff --git a/tools/testing/selftests/drivers/net/README.rst b/tools/testing/selftests/drivers/net/README.rst
> index eb838ae94844..39370a83f238 100644
> --- a/tools/testing/selftests/drivers/net/README.rst
> +++ b/tools/testing/selftests/drivers/net/README.rst
> @@ -62,6 +62,25 @@ LOCAL_V4, LOCAL_V6, REMOTE_V4, REMOTE_V6
>
> Local and remote endpoint IP addresses.
>
> +LOCAL_PREFIX_V4, LOCAL_PREFIX_V6
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I think LOCAL_PREFIX_V4 is not supported in this series?
> +
> +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.
> +
> + +-------------+ +----------------------------+
> + | INIT NS | | TEST NS |
> + | +---------+ | | +------------------------+ |
> + | | NETIF | | bpf | | Netkit | |
> + | | |-|--------|>| nk_guest | |
> + | +---------+ | | | {LOCAL_PREFIX_V6}::2:2 | |
> + | +---------+ | | +------------------------+ |
> + | | Netkit | | +----------------------------+
> + | | nk_host | |
> + | +---------+ |
> + +-------------+
> +
> 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 a18e21069f7a..6b55068d5370 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..857ae0f37516 100644
> --- a/tools/testing/selftests/drivers/net/lib/py/env.py
> +++ b/tools/testing/selftests/drivers/net/lib/py/env.py
> @@ -1,13 +1,16 @@
> # SPDX-License-Identifier: GPL-2.0
>
> +import ipaddress
> import os
> import time
> +import json
> from pathlib import Path
> from lib.py import KsftSkipEx, KsftXfailEx
> 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, RtnlFamily, Netlink
>
>
> class NetDrvEnvBase:
> @@ -289,3 +292,163 @@ 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.
> + +-------------+ +----------------------------+
> + | INIT NS | | TEST NS |
> + | +---------+ | | +------------------------+ |
> + | | NETIF | | bpf | | Netkit | |
> + | | |-|--------|>| nk_guest | |
> + | +---------+ | | | {LOCAL_PREFIX_V6}::2:2 | |
> + | +---------+ | | +------------------------+ |
> + | | Netkit | | +----------------------------+
> + | | nk_host | |
> + | +---------+ |
> + +-------------+
> + """
> +
> + def __init__(self, src_path, rxqueues=1, **kwargs):
> + super().__init__(src_path, **kwargs)
> +
> + self.netns = None
> + self._nk_host_ifname = None
> + self._nk_guest_ifname = None
> + self._tc_clsact_added = False
> + self._tc_attached = False
> + self._bpf_prog_pref = None
> + self._bpf_prog_id = None
> + self._init_ns_attached = False
> +
> + 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"
> +
> + rtnl = RtnlFamily()
> + rtnl.newlink(
> + {
> + "linkinfo": {
> + "kind": "netkit",
> + "data": {
> + "mode": "l2",
> + "policy": "forward",
> + "peer-policy": "forward",
> + },
> + },
> + "num-rx-queues": rxqueues,
> + },
> + flags=[Netlink.NLM_F_CREATE, Netlink.NLM_F_EXCL],
> + )
> +
> + 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
I know this was resolved before, but unfortunately I think these might
have to be getattr (or another approach) for the case of
super().__init__() failing? I found one bad setup may result in
resolve_remote_ifc() failing and cascading up:
Traceback (most recent call last):
File "/data/users/bobbyeshleman/linux-worktrees/tcp-dm-netkit/./tools/testing/selftests/drivers/net/hw/nk_netns.py", line 29, in <module>
main()
File "/data/users/bobbyeshleman/linux-worktrees/tcp-dm-netkit/./tools/testing/selftests/drivers/net/hw/nk_netns.py", line 23, in main
with NetDrvContEnv(__file__) as cfg:
File "/data/users/bobbyeshleman/linux-worktrees/tcp-dm-netkit/tools/testing/selftests/drivers/net/lib/py/env.py", line 315, in __init__
super().__init__(src_path, **kwargs)
File "/data/users/bobbyeshleman/linux-worktrees/tcp-dm-netkit/tools/testing/selftests/drivers/net/lib/py/env.py", line 173, in __init__
self.remote_ifname = self.resolve_remote_ifc()
File "/data/users/bobbyeshleman/linux-worktrees/tcp-dm-netkit/tools/testing/selftests/drivers/net/lib/py/env.py", line 234, in resolve_remote_ifc
return v6[0]["ifname"] if v6 else v4[0]["ifname"]
TypeError: 'NoneType' object is not subscriptable
Exception ignored in: <function NetDrvContEnv.__del__ at 0x7fab5578c1f0>
Traceback (most recent call last):
File "/data/users/bobbyeshleman/linux-worktrees/tcp-dm-netkit/tools/testing/selftests/drivers/net/lib/py/env.py", line 370, in __del__
if self._tc_attached:
AttributeError: 'NetDrvContEnv' object has no attribute '_tc_attached'
Best,
Bobby
Powered by blists - more mailing lists