Skip to content

Commit 1880f27

Browse files
committed
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>
1 parent 5433892 commit 1880f27

File tree

4 files changed

+162
-1
lines changed

4 files changed

+162
-1
lines changed

tools/testing/selftests/drivers/net/README.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,41 @@ or::
2323
# Variable set in a file
2424
NETIF=eth0
2525

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+
2630
NETIF
2731
~~~~~
2832

2933
Name of the netdevice against which the test should be executed.
3034
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

tools/testing/selftests/drivers/net/lib/py/env.py

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
import shlex
55
from pathlib import Path
66
from lib.py import ip
7-
from lib.py import NetdevSimDev
7+
from lib.py import NetNS, NetdevSimDev
8+
from .remote import Remote
89

910

1011
def _load_env_file(src_path):
@@ -59,3 +60,98 @@ def __del__(self):
5960
self._ns = None
6061

6162

63+
class NetDrvEpEnv:
64+
"""
65+
Class for an environment with a local device and "remote endpoint"
66+
which can be used to send traffic in.
67+
68+
For local testing it creates two network namespaces and a pair
69+
of netdevsim devices.
70+
"""
71+
72+
# Network prefixes used for local tests
73+
nsim_v4_pfx = "192.0.2."
74+
nsim_v6_pfx = "2001:db8::"
75+
76+
def __init__(self, src_path):
77+
78+
self.env = _load_env_file(src_path)
79+
80+
# Things we try to destroy
81+
self.remote = None
82+
# These are for local testing state
83+
self._netns = None
84+
self._ns = None
85+
self._ns_peer = None
86+
87+
if "NETIF" in self.env:
88+
self.dev = ip("link show dev " + self.env['NETIF'], json=True)[0]
89+
90+
self.v4 = self.env.get("LOCAL_V4")
91+
self.v6 = self.env.get("LOCAL_V6")
92+
self.remote_v4 = self.env.get("REMOTE_V4")
93+
self.remote_v6 = self.env.get("REMOTE_V6")
94+
kind = self.env["REMOTE_TYPE"]
95+
args = self.env["REMOTE_ARGS"]
96+
else:
97+
self.create_local()
98+
99+
self.dev = self._ns.nsims[0].dev
100+
101+
self.v4 = self.nsim_v4_pfx + "1"
102+
self.v6 = self.nsim_v6_pfx + "1"
103+
self.remote_v4 = self.nsim_v4_pfx + "2"
104+
self.remote_v6 = self.nsim_v6_pfx + "2"
105+
kind = "netns"
106+
args = self._netns.name
107+
108+
self.remote = Remote(kind, args, src_path)
109+
110+
self.addr = self.v6 if self.v6 else self.v4
111+
self.remote_addr = self.remote_v6 if self.remote_v6 else self.remote_v4
112+
113+
self.ifname = self.dev['ifname']
114+
self.ifindex = self.dev['ifindex']
115+
116+
def create_local(self):
117+
self._netns = NetNS()
118+
self._ns = NetdevSimDev()
119+
self._ns_peer = NetdevSimDev(ns=self._netns)
120+
121+
with open("/proc/self/ns/net") as nsfd0, \
122+
open("/var/run/netns/" + self._netns.name) as nsfd1:
123+
ifi0 = self._ns.nsims[0].ifindex
124+
ifi1 = self._ns_peer.nsims[0].ifindex
125+
NetdevSimDev.ctrl_write('link_device',
126+
f'{nsfd0.fileno()}:{ifi0} {nsfd1.fileno()}:{ifi1}')
127+
128+
ip(f" addr add dev {self._ns.nsims[0].ifname} {self.nsim_v4_pfx}1/24")
129+
ip(f"-6 addr add dev {self._ns.nsims[0].ifname} {self.nsim_v6_pfx}1/64 nodad")
130+
ip(f" link set dev {self._ns.nsims[0].ifname} up")
131+
132+
ip(f" addr add dev {self._ns_peer.nsims[0].ifname} {self.nsim_v4_pfx}2/24", ns=self._netns)
133+
ip(f"-6 addr add dev {self._ns_peer.nsims[0].ifname} {self.nsim_v6_pfx}2/64 nodad", ns=self._netns)
134+
ip(f" link set dev {self._ns_peer.nsims[0].ifname} up", ns=self._netns)
135+
136+
def __enter__(self):
137+
return self
138+
139+
def __exit__(self, ex_type, ex_value, ex_tb):
140+
"""
141+
__exit__ gets called at the end of a "with" block.
142+
"""
143+
self.__del__()
144+
145+
def __del__(self):
146+
if self._ns:
147+
self._ns.remove()
148+
self._ns = None
149+
if self._ns_peer:
150+
self._ns_peer.remove()
151+
self._ns_peer = None
152+
if self._netns:
153+
del self._netns
154+
self._netns = None
155+
if self.remote:
156+
del self.remote
157+
self.remote = None

tools/testing/selftests/net/lib/py/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from .consts import KSRC
44
from .ksft import *
5+
from .netns import NetNS
56
from .nsim import *
67
from .utils import *
78
from .ynl import NlError, YnlFamily, EthtoolFamily, NetdevFamily, RtnlFamily
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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})"

0 commit comments

Comments
 (0)