From: Wei Wang Add infrastructure to support PSP tests across network namespaces using NetDrvContEnv with netkit pairs. This enables testing PSP device association, where a non-PSP-capable device (e.g. netkit) in a guest namespace is associated with a real PSP device in the host namespace, allowing the guest to perform PSP encryption/decryption through the host's PSP hardware. The topology is: Host NS: psp_dev_local <---> nk_host | | | | (netkit pair) | | Remote NS: psp_dev_peer Guest NS: nk_guest (responder) (PSP tests) env.py: - nk_guest_ifindex is queried after moving the device into the guest namespace, so tests can use it directly for dev-assoc psp.py: - PSP device lookup supports container environments where the PSP device is on the physical interface, not the test interface - Association helpers handle dev-assoc/dev-disassoc with defer-based cleanup to prevent state leaks on test assertion failures - main() tries NetDrvContEnv with primary_rx_redirect and falls back to NetDrvEpEnv, so existing tests continue to work without the container environment Signed-off-by: Wei Wang --- tools/testing/selftests/drivers/net/config | 1 + .../selftests/drivers/net/lib/py/env.py | 3 + tools/testing/selftests/drivers/net/psp.py | 102 +++++++++++++++++- 3 files changed, 102 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/drivers/net/config b/tools/testing/selftests/drivers/net/config index 617de8aaf551..91d4fd410914 100644 --- a/tools/testing/selftests/drivers/net/config +++ b/tools/testing/selftests/drivers/net/config @@ -8,6 +8,7 @@ CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y CONFIG_NETCONSOLE_EXTENDED_LOG=y CONFIG_NETDEVSIM=m +CONFIG_NETKIT=y CONFIG_NET_SCH_ETF=m CONFIG_NET_SCH_FQ=m CONFIG_PPP=y diff --git a/tools/testing/selftests/drivers/net/lib/py/env.py b/tools/testing/selftests/drivers/net/lib/py/env.py index 59b0f2533ab4..b188ee55c76b 100644 --- a/tools/testing/selftests/drivers/net/lib/py/env.py +++ b/tools/testing/selftests/drivers/net/lib/py/env.py @@ -470,6 +470,9 @@ class NetDrvContEnv(NetDrvEpEnv): 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}") + nk_guest_dev = ip(f"link show dev {self.nk_guest_ifname}", + json=True, ns=self.netns)[0] + self.nk_guest_ifindex = nk_guest_dev['ifindex'] 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}") diff --git a/tools/testing/selftests/drivers/net/psp.py b/tools/testing/selftests/drivers/net/psp.py index 6d5114be4c88..015af92c8d7b 100755 --- a/tools/testing/selftests/drivers/net/psp.py +++ b/tools/testing/selftests/drivers/net/psp.py @@ -5,6 +5,7 @@ import errno import fcntl +import os import socket import struct import termios @@ -16,8 +17,10 @@ from lib.py import ksft_true, ksft_eq, ksft_ne, ksft_gt, ksft_raises from lib.py import ksft_not_none from lib.py import ksft_variants, KsftNamedVariant from lib.py import KsftSkipEx -from lib.py import NetDrvEpEnv, PSPFamily, NlError +from lib.py import NetDrvEpEnv, NetDrvContEnv +from lib.py import NlError, PSPFamily from lib.py import bkg, rand_port, wait_port_listen +from lib.py import ip def _get_outq(s): @@ -118,11 +121,13 @@ def _get_stat(cfg, key): # Test case boiler plate # -def _init_psp_dev(cfg): +def _init_psp_dev(cfg, use_psp_ifindex=False): if not hasattr(cfg, 'psp_dev_id'): # Figure out which local device we are testing against + # For NetDrvContEnv: use psp_ifindex instead of ifindex + target_ifindex = cfg.psp_ifindex if use_psp_ifindex else cfg.ifindex for dev in cfg.pspnl.dev_get({}, dump=True): - if dev['ifindex'] == cfg.ifindex: + if dev['ifindex'] == target_ifindex: cfg.psp_info = dev cfg.psp_dev_id = cfg.psp_info['id'] break @@ -597,13 +602,102 @@ def data_mss_adjust(cfg, ipver): _data_mss_adjust(cfg, ipver) +def _try_disassoc(cfg, psp_dev_id, ifindex, nsid=None): + """Best-effort disassociate, ignoring errors if already removed.""" + try: + params = {'id': psp_dev_id, 'ifindex': ifindex} + if nsid is not None: + params['nsid'] = nsid + cfg.pspnl.dev_disassoc(params) + except NlError: + pass + + +def _assoc_nk_guest(cfg): + """Associate nk_guest with PSP device and register cleanup via defer().""" + _init_psp_dev(cfg, True) + + cfg.pspnl.dev_assoc({'id': cfg.psp_dev_id, + 'ifindex': cfg.nk_guest_ifindex, + 'nsid': cfg.psp_dev_peer_nsid}) + defer(_disassoc_nk_guest, cfg, + cfg.psp_dev_id, cfg.nk_guest_ifindex) + + +def _disassoc_nk_guest(cfg, psp_dev_id, nk_guest_ifindex): + """Disassociate nk_guest and reset cfg PSP state.""" + pspnl = PSPFamily() + pspnl.dev_disassoc({'id': psp_dev_id, 'ifindex': nk_guest_ifindex, + 'nsid': cfg.psp_dev_peer_nsid}) + cfg.pspnl = pspnl + del cfg.psp_dev_id + del cfg.psp_info + + +def _get_nsid(ns_name): + """Get the nsid for a namespace.""" + for entry in ip("netns list-id", json=True): + if entry.get("name") == str(ns_name): + return entry["nsid"] + raise KsftSkipEx(f"nsid not found for namespace {ns_name}") + + +def _setup_psp_attributes(cfg): + # pylint: disable=protected-access + """ + Set up PSP-specific attributes on the environment. + + This sets attributes needed for PSP tests based on whether we're using + netdevsim or a real NIC. + """ + if cfg._ns is not None: + # netdevsim case: PSP device is the local dev (in host namespace) + cfg.psp_dev = cfg._ns.nsims[0].dev + cfg.psp_ifname = cfg.psp_dev['ifname'] + cfg.psp_ifindex = cfg.psp_dev['ifindex'] + + # PSP peer device is the remote dev (in _netns, where psp_responder runs) + cfg.psp_dev_peer = cfg._ns_peer.nsims[0].dev + cfg.psp_dev_peer_ifname = cfg.psp_dev_peer['ifname'] + cfg.psp_dev_peer_ifindex = cfg.psp_dev_peer['ifindex'] + else: + # Real NIC case: PSP device is the local interface + cfg.psp_dev = cfg.dev + cfg.psp_ifname = cfg.ifname + cfg.psp_ifindex = cfg.ifindex + + # PSP peer device is the remote interface + cfg.psp_dev_peer = cfg.remote_dev + cfg.psp_dev_peer_ifname = cfg.remote_ifname + cfg.psp_dev_peer_ifindex = cfg.remote_ifindex + + # Get nsid for the guest namespace (netns) where nk_guest is + cfg.psp_dev_peer_nsid = _get_nsid(cfg.netns.name) + + + def main() -> None: """ Ksft boiler plate main """ - with NetDrvEpEnv(__file__) as cfg: + # Make sure LOCAL_PREFIX_V6 is set + if "LOCAL_PREFIX_V6" not in os.environ: + os.environ["LOCAL_PREFIX_V6"] = "2001:db8:2::" + + try: + env = NetDrvContEnv(__file__, primary_rx_redirect=True) + has_cont = True + except KsftSkipEx: + env = NetDrvEpEnv(__file__) + has_cont = False + + with env as cfg: cfg.pspnl = PSPFamily() + if has_cont: + _setup_psp_attributes(cfg) + # Set up responder and communication sock + # psp_responder runs in _netns (remote namespace with psp_dev_peer) responder = cfg.remote.deploy("psp_responder") cfg.comm_port = rand_port() -- 2.52.0