Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: drv-net: construct environment for running tests which require an endpoint

Nothing surprising here, hopefully. Wrap the variables from
the environment into a class or spawn a netdevsim based env
and pass it to the tests.

Reviewed-by: Willem de Bruijn <willemb@google.com>
Link: https://lore.kernel.org/r/20240420025237.3309296-4-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+162 -1
+33
tools/testing/selftests/drivers/net/README.rst
··· 23 23 # Variable set in a file 24 24 NETIF=eth0 25 25 26 + Please note that the config parser is very simple, if there are 27 + any non-alphanumeric characters in the value it needs to be in 28 + double quotes. 29 + 26 30 NETIF 27 31 ~~~~~ 28 32 29 33 Name of the netdevice against which the test should be executed. 30 34 When empty or not set software devices will be used. 35 + 36 + LOCAL_V4, LOCAL_V6, REMOTE_V4, REMOTE_V6 37 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 38 + 39 + Local and remote endpoint IP addresses. 40 + 41 + REMOTE_TYPE 42 + ~~~~~~~~~~~ 43 + 44 + Communication method used to run commands on the remote endpoint. 45 + Test framework has built-in support for ``netns`` and ``ssh`` channels. 46 + ``netns`` assumes the "remote" interface is part of the same 47 + host, just moved to the specified netns. 48 + ``ssh`` communicates with remote endpoint over ``ssh`` and ``scp``. 49 + Using persistent SSH connections is strongly encouraged to avoid 50 + the latency of SSH connection setup on every command. 51 + 52 + Communication methods are defined by classes in ``lib/py/remote_{name}.py``. 53 + It should be possible to add a new method without modifying any of 54 + the framework, by simply adding an appropriately named file to ``lib/py``. 55 + 56 + REMOTE_ARGS 57 + ~~~~~~~~~~~ 58 + 59 + Arguments used to construct the communication channel. 60 + Communication channel dependent:: 61 + 62 + for netns - name of the "remote" namespace 63 + for ssh - name/address of the remote host
+97 -1
tools/testing/selftests/drivers/net/lib/py/env.py
··· 4 4 import shlex 5 5 from pathlib import Path 6 6 from lib.py import ip 7 - from lib.py import NetdevSimDev 7 + from lib.py import NetNS, NetdevSimDev 8 + from .remote import Remote 8 9 9 10 10 11 def _load_env_file(src_path): ··· 60 59 self._ns = None 61 60 62 61 62 + class NetDrvEpEnv: 63 + """ 64 + Class for an environment with a local device and "remote endpoint" 65 + which can be used to send traffic in. 66 + 67 + For local testing it creates two network namespaces and a pair 68 + of netdevsim devices. 69 + """ 70 + 71 + # Network prefixes used for local tests 72 + nsim_v4_pfx = "192.0.2." 73 + nsim_v6_pfx = "2001:db8::" 74 + 75 + def __init__(self, src_path): 76 + 77 + self.env = _load_env_file(src_path) 78 + 79 + # Things we try to destroy 80 + self.remote = None 81 + # These are for local testing state 82 + self._netns = None 83 + self._ns = None 84 + self._ns_peer = None 85 + 86 + if "NETIF" in self.env: 87 + self.dev = ip("link show dev " + self.env['NETIF'], json=True)[0] 88 + 89 + self.v4 = self.env.get("LOCAL_V4") 90 + self.v6 = self.env.get("LOCAL_V6") 91 + self.remote_v4 = self.env.get("REMOTE_V4") 92 + self.remote_v6 = self.env.get("REMOTE_V6") 93 + kind = self.env["REMOTE_TYPE"] 94 + args = self.env["REMOTE_ARGS"] 95 + else: 96 + self.create_local() 97 + 98 + self.dev = self._ns.nsims[0].dev 99 + 100 + self.v4 = self.nsim_v4_pfx + "1" 101 + self.v6 = self.nsim_v6_pfx + "1" 102 + self.remote_v4 = self.nsim_v4_pfx + "2" 103 + self.remote_v6 = self.nsim_v6_pfx + "2" 104 + kind = "netns" 105 + args = self._netns.name 106 + 107 + self.remote = Remote(kind, args, src_path) 108 + 109 + self.addr = self.v6 if self.v6 else self.v4 110 + self.remote_addr = self.remote_v6 if self.remote_v6 else self.remote_v4 111 + 112 + self.ifname = self.dev['ifname'] 113 + self.ifindex = self.dev['ifindex'] 114 + 115 + def create_local(self): 116 + self._netns = NetNS() 117 + self._ns = NetdevSimDev() 118 + self._ns_peer = NetdevSimDev(ns=self._netns) 119 + 120 + with open("/proc/self/ns/net") as nsfd0, \ 121 + open("/var/run/netns/" + self._netns.name) as nsfd1: 122 + ifi0 = self._ns.nsims[0].ifindex 123 + ifi1 = self._ns_peer.nsims[0].ifindex 124 + NetdevSimDev.ctrl_write('link_device', 125 + f'{nsfd0.fileno()}:{ifi0} {nsfd1.fileno()}:{ifi1}') 126 + 127 + ip(f" addr add dev {self._ns.nsims[0].ifname} {self.nsim_v4_pfx}1/24") 128 + ip(f"-6 addr add dev {self._ns.nsims[0].ifname} {self.nsim_v6_pfx}1/64 nodad") 129 + ip(f" link set dev {self._ns.nsims[0].ifname} up") 130 + 131 + ip(f" addr add dev {self._ns_peer.nsims[0].ifname} {self.nsim_v4_pfx}2/24", ns=self._netns) 132 + ip(f"-6 addr add dev {self._ns_peer.nsims[0].ifname} {self.nsim_v6_pfx}2/64 nodad", ns=self._netns) 133 + ip(f" link set dev {self._ns_peer.nsims[0].ifname} up", ns=self._netns) 134 + 135 + def __enter__(self): 136 + return self 137 + 138 + def __exit__(self, ex_type, ex_value, ex_tb): 139 + """ 140 + __exit__ gets called at the end of a "with" block. 141 + """ 142 + self.__del__() 143 + 144 + def __del__(self): 145 + if self._ns: 146 + self._ns.remove() 147 + self._ns = None 148 + if self._ns_peer: 149 + self._ns_peer.remove() 150 + self._ns_peer = None 151 + if self._netns: 152 + del self._netns 153 + self._netns = None 154 + if self.remote: 155 + del self.remote 156 + self.remote = None
+1
tools/testing/selftests/net/lib/py/__init__.py
··· 2 2 3 3 from .consts import KSRC 4 4 from .ksft import * 5 + from .netns import NetNS 5 6 from .nsim import * 6 7 from .utils import * 7 8 from .ynl import NlError, YnlFamily, EthtoolFamily, NetdevFamily, RtnlFamily
+31
tools/testing/selftests/net/lib/py/netns.py
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + from .utils import ip 4 + import random 5 + import string 6 + 7 + 8 + class NetNS: 9 + def __init__(self, name=None): 10 + if name: 11 + self.name = name 12 + else: 13 + self.name = ''.join(random.choice(string.ascii_lowercase) for _ in range(8)) 14 + ip('netns add ' + self.name) 15 + 16 + def __del__(self): 17 + if self.name: 18 + ip('netns del ' + self.name) 19 + self.name = None 20 + 21 + def __enter__(self): 22 + return self 23 + 24 + def __exit__(self, ex_type, ex_value, ex_tb): 25 + self.__del__() 26 + 27 + def __str__(self): 28 + return self.name 29 + 30 + def __repr__(self): 31 + return f"NetNS({self.name})"