mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-30 20:43:02 +02:00
test: netns: Generalize IsolatedMacVlan to support multiple interfaces
This is needed to create namespaces with MACVLANs from different parent interfaces. The first consumer will be a Two-Port Mac Relay (TPMR), so that we can control the flow of packets between two nodes.
This commit is contained in:
+85
-25
@@ -18,31 +18,53 @@ def setns(fd, nstype):
|
||||
__NR_setns = 308
|
||||
__libc.syscall(__NR_setns, fd, nstype)
|
||||
|
||||
class IsolatedMacVlan:
|
||||
"""Create an isolated interface on top of a PC interface."""
|
||||
def __init__(self, parent, ifname="iface", lo=True):
|
||||
class IsolatedMacVlans:
|
||||
"""A network namespace containing a multiple MACVLANs
|
||||
|
||||
Stacks a MACVLAN on top of each specificed controller interface,
|
||||
and moves those interfaces to a separate namespace, isolating it
|
||||
from all others.
|
||||
|
||||
NOTE: For the simple case when only one interface needs to be
|
||||
mapped, see IsolatedMacVlan below.
|
||||
|
||||
Example:
|
||||
|
||||
netns = IsolatedMacVlans({ "eth2": "a", "eth3": "b" })
|
||||
|
||||
netns:
|
||||
.--------.
|
||||
| a b | (MACVLANs)
|
||||
'-+----+-'
|
||||
| |
|
||||
eth0 eth1 eth2 eth3
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, ifmap, lo=True):
|
||||
self.sleeper = None
|
||||
self.parent, self.ifname, self.lo = parent, ifname, lo
|
||||
self.ifmap, self.lo = ifmap, lo
|
||||
self.ping_timeout = env.ENV.attr("ping_timeout", 5)
|
||||
|
||||
def __enter__(self):
|
||||
def start(self):
|
||||
self.sleeper = subprocess.Popen(["unshare", "-r", "-n", "sh", "-c",
|
||||
"echo && exec sleep infinity"],
|
||||
stdout=subprocess.PIPE)
|
||||
self.sleeper.stdout.readline()
|
||||
|
||||
try:
|
||||
subprocess.run(["ip", "link", "add",
|
||||
"dev", self.ifname,
|
||||
"link", self.parent,
|
||||
"address", self._stable_mac(),
|
||||
"netns", str(self.sleeper.pid),
|
||||
"type", "macvlan"], check=True)
|
||||
self.runsh(f"""
|
||||
while ! ip link show dev {self.ifname}; do
|
||||
sleep 0.1
|
||||
done
|
||||
""")
|
||||
for parent, ifname in self.ifmap.items():
|
||||
subprocess.run(["ip", "link", "add",
|
||||
"dev", ifname,
|
||||
"link", parent,
|
||||
"address", self._stable_mac(parent),
|
||||
"netns", str(self.sleeper.pid),
|
||||
"type", "macvlan"], check=True)
|
||||
self.runsh(f"""
|
||||
while ! ip link show dev {ifname}; do
|
||||
sleep 0.1
|
||||
done
|
||||
""")
|
||||
except Exception as e:
|
||||
self.__exit__(None, None, None)
|
||||
raise e
|
||||
@@ -56,12 +78,18 @@ class IsolatedMacVlan:
|
||||
|
||||
return self
|
||||
|
||||
def __exit__(self, val, typ, tb):
|
||||
def stop(self):
|
||||
self.sleeper.kill()
|
||||
self.sleeper.wait()
|
||||
time.sleep(0.5)
|
||||
|
||||
def _stable_mac(self):
|
||||
def __enter__(self):
|
||||
return self.start()
|
||||
|
||||
def __exit__(self, val, typ, tb):
|
||||
return self.stop()
|
||||
|
||||
def _stable_mac(self, parent):
|
||||
"""Generate address for MACVLAN
|
||||
|
||||
By default, the kernel will assign a random address. This
|
||||
@@ -75,7 +103,7 @@ class IsolatedMacVlan:
|
||||
the resource exhaustion issue.
|
||||
|
||||
"""
|
||||
random.seed(self.parent)
|
||||
random.seed(parent)
|
||||
a = list(random.randbytes(6))
|
||||
a[0] |= 0x02
|
||||
a[0] &= ~0x01
|
||||
@@ -146,13 +174,13 @@ class IsolatedMacVlan:
|
||||
ip -{p} route add {subnet}{prefix_length} via {nexthop}
|
||||
""", check=True)
|
||||
|
||||
def addip(self, addr, prefix_length=24, proto="ipv4"):
|
||||
def addip(self, ifname, addr, prefix_length=24, proto="ipv4"):
|
||||
p=proto[3]
|
||||
|
||||
self.runsh(f"""
|
||||
set -ex
|
||||
ip link set iface up
|
||||
ip -{p} addr add {addr}/{prefix_length} dev iface
|
||||
ip -{p} addr add {addr}/{prefix_length} dev {ifname}
|
||||
""", check=True)
|
||||
|
||||
|
||||
@@ -188,8 +216,7 @@ class IsolatedMacVlan:
|
||||
|
||||
raise Exception(res)
|
||||
|
||||
def must_receive(self, expr, timeout=None, ifname=None, must=True):
|
||||
ifname = ifname if ifname else self.ifname
|
||||
def must_receive(self, expr, ifname, timeout=None, must=True):
|
||||
timeout = timeout if timeout else self.ping_timeout
|
||||
|
||||
tshark = self.run(["tshark", "-nl", f"-i{ifname}",
|
||||
@@ -207,10 +234,43 @@ class IsolatedMacVlan:
|
||||
def must_not_receive(self, *args, **kwargs):
|
||||
self.must_receive(*args, **kwargs, must=False)
|
||||
|
||||
def pcap(self, expr, ifname=None):
|
||||
ifname = ifname if ifname else self.ifname
|
||||
def pcap(self, expr, ifname):
|
||||
return Pcap(self, ifname, expr)
|
||||
|
||||
class IsolatedMacVlan(IsolatedMacVlans):
|
||||
"""A network namespace containing a single MACVLAN
|
||||
|
||||
Stacks a MACVLAN on top of an interface on the controller, and
|
||||
moves that interface to a separate namespace, isolating it from
|
||||
all other interfaces.
|
||||
|
||||
Example:
|
||||
|
||||
netns = IsolatedMacVlan("eth3")
|
||||
|
||||
netns:
|
||||
.-------.
|
||||
| iface | (MACVLAN)
|
||||
'---+---'
|
||||
|
|
||||
eth0 eth1 eth2 eth3
|
||||
|
||||
"""
|
||||
def __init__(self, parent, ifname="iface", lo=True):
|
||||
self._ifname = ifname
|
||||
return super().__init__(ifmap={ parent: ifname }, lo=lo)
|
||||
|
||||
def addip(self, addr, prefix_length=24, proto="ipv4"):
|
||||
return super().addip(ifname=self._ifname, addr=addr, prefix_length=prefix_length, proto=proto)
|
||||
|
||||
def must_receive(self, expr, timeout=None, ifname=None, must=True):
|
||||
ifname = ifname if ifname else self._ifname
|
||||
return super().must_receive(expr=expr, ifname=ifname, timeout=timeout, must=must)
|
||||
|
||||
def pcap(self, expr, ifname=None):
|
||||
ifname = ifname if ifname else self._ifname
|
||||
return super().pcap(expr=expr, ifname=ifname)
|
||||
|
||||
class Pcap:
|
||||
def __init__(self, netns, ifname, expr):
|
||||
self.netns, self.ifname, self.expr = netns, ifname, expr
|
||||
|
||||
Reference in New Issue
Block a user