Commit 1880f272 authored by Jakub Kicinski's avatar Jakub Kicinski

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: default avatarWillem de Bruijn <willemb@google.com>
Link: https://lore.kernel.org/r/20240420025237.3309296-4-kuba@kernel.orgSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 54338929
...@@ -23,8 +23,41 @@ or:: ...@@ -23,8 +23,41 @@ or::
# Variable set in a file # Variable set in a file
NETIF=eth0 NETIF=eth0
Please note that the config parser is very simple, if there are
any non-alphanumeric characters in the value it needs to be in
double quotes.
NETIF NETIF
~~~~~ ~~~~~
Name of the netdevice against which the test should be executed. Name of the netdevice against which the test should be executed.
When empty or not set software devices will be used. When empty or not set software devices will be used.
LOCAL_V4, LOCAL_V6, REMOTE_V4, REMOTE_V6
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Local and remote endpoint IP addresses.
REMOTE_TYPE
~~~~~~~~~~~
Communication method used to run commands on the remote endpoint.
Test framework has built-in support for ``netns`` and ``ssh`` channels.
``netns`` assumes the "remote" interface is part of the same
host, just moved to the specified netns.
``ssh`` communicates with remote endpoint over ``ssh`` and ``scp``.
Using persistent SSH connections is strongly encouraged to avoid
the latency of SSH connection setup on every command.
Communication methods are defined by classes in ``lib/py/remote_{name}.py``.
It should be possible to add a new method without modifying any of
the framework, by simply adding an appropriately named file to ``lib/py``.
REMOTE_ARGS
~~~~~~~~~~~
Arguments used to construct the communication channel.
Communication channel dependent::
for netns - name of the "remote" namespace
for ssh - name/address of the remote host
...@@ -4,7 +4,8 @@ import os ...@@ -4,7 +4,8 @@ import os
import shlex import shlex
from pathlib import Path from pathlib import Path
from lib.py import ip from lib.py import ip
from lib.py import NetdevSimDev from lib.py import NetNS, NetdevSimDev
from .remote import Remote
def _load_env_file(src_path): def _load_env_file(src_path):
...@@ -59,3 +60,98 @@ class NetDrvEnv: ...@@ -59,3 +60,98 @@ class NetDrvEnv:
self._ns = None self._ns = None
class NetDrvEpEnv:
"""
Class for an environment with a local device and "remote endpoint"
which can be used to send traffic in.
For local testing it creates two network namespaces and a pair
of netdevsim devices.
"""
# Network prefixes used for local tests
nsim_v4_pfx = "192.0.2."
nsim_v6_pfx = "2001:db8::"
def __init__(self, src_path):
self.env = _load_env_file(src_path)
# Things we try to destroy
self.remote = None
# These are for local testing state
self._netns = None
self._ns = None
self._ns_peer = None
if "NETIF" in self.env:
self.dev = ip("link show dev " + self.env['NETIF'], json=True)[0]
self.v4 = self.env.get("LOCAL_V4")
self.v6 = self.env.get("LOCAL_V6")
self.remote_v4 = self.env.get("REMOTE_V4")
self.remote_v6 = self.env.get("REMOTE_V6")
kind = self.env["REMOTE_TYPE"]
args = self.env["REMOTE_ARGS"]
else:
self.create_local()
self.dev = self._ns.nsims[0].dev
self.v4 = self.nsim_v4_pfx + "1"
self.v6 = self.nsim_v6_pfx + "1"
self.remote_v4 = self.nsim_v4_pfx + "2"
self.remote_v6 = self.nsim_v6_pfx + "2"
kind = "netns"
args = self._netns.name
self.remote = Remote(kind, args, src_path)
self.addr = self.v6 if self.v6 else self.v4
self.remote_addr = self.remote_v6 if self.remote_v6 else self.remote_v4
self.ifname = self.dev['ifname']
self.ifindex = self.dev['ifindex']
def create_local(self):
self._netns = NetNS()
self._ns = NetdevSimDev()
self._ns_peer = NetdevSimDev(ns=self._netns)
with open("/proc/self/ns/net") as nsfd0, \
open("/var/run/netns/" + self._netns.name) as nsfd1:
ifi0 = self._ns.nsims[0].ifindex
ifi1 = self._ns_peer.nsims[0].ifindex
NetdevSimDev.ctrl_write('link_device',
f'{nsfd0.fileno()}:{ifi0} {nsfd1.fileno()}:{ifi1}')
ip(f" addr add dev {self._ns.nsims[0].ifname} {self.nsim_v4_pfx}1/24")
ip(f"-6 addr add dev {self._ns.nsims[0].ifname} {self.nsim_v6_pfx}1/64 nodad")
ip(f" link set dev {self._ns.nsims[0].ifname} up")
ip(f" addr add dev {self._ns_peer.nsims[0].ifname} {self.nsim_v4_pfx}2/24", ns=self._netns)
ip(f"-6 addr add dev {self._ns_peer.nsims[0].ifname} {self.nsim_v6_pfx}2/64 nodad", ns=self._netns)
ip(f" link set dev {self._ns_peer.nsims[0].ifname} up", ns=self._netns)
def __enter__(self):
return self
def __exit__(self, ex_type, ex_value, ex_tb):
"""
__exit__ gets called at the end of a "with" block.
"""
self.__del__()
def __del__(self):
if self._ns:
self._ns.remove()
self._ns = None
if self._ns_peer:
self._ns_peer.remove()
self._ns_peer = None
if self._netns:
del self._netns
self._netns = None
if self.remote:
del self.remote
self.remote = None
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
from .consts import KSRC from .consts import KSRC
from .ksft import * from .ksft import *
from .netns import NetNS
from .nsim import * from .nsim import *
from .utils import * from .utils import *
from .ynl import NlError, YnlFamily, EthtoolFamily, NetdevFamily, RtnlFamily from .ynl import NlError, YnlFamily, EthtoolFamily, NetdevFamily, RtnlFamily
# SPDX-License-Identifier: GPL-2.0
from .utils import ip
import random
import string
class NetNS:
def __init__(self, name=None):
if name:
self.name = name
else:
self.name = ''.join(random.choice(string.ascii_lowercase) for _ in range(8))
ip('netns add ' + self.name)
def __del__(self):
if self.name:
ip('netns del ' + self.name)
self.name = None
def __enter__(self):
return self
def __exit__(self, ex_type, ex_value, ex_tb):
self.__del__()
def __str__(self):
return self.name
def __repr__(self):
return f"NetNS({self.name})"
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment