Skip to content

Commit

Permalink
create dummy for unconnected interface
Browse files Browse the repository at this point in the history
An interface with no connection is currently omitted from the target
network namespace.  Instead, add a Linux "dummy" interface and assign
IP addresses.
  • Loading branch information
eklabn committed Dec 13, 2023
1 parent 55e0099 commit 226f45a
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 1 deletion.
28 changes: 28 additions & 0 deletions munet/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2674,6 +2674,34 @@ def add_host(self, name, cls=LinuxNamespace, **kwargs):

return self.hosts[name]

def add_dummy(self, node1, if1, mtu=None, **intf_constraints):
"""Add a dummy for an interface with no link."""

try:
name1 = node1.name
except AttributeError:
if node1 in self.switches:
node1 = self.switches[node1]
else:
node1 = self.hosts[node1]
name1 = node1.name

lname = "{}:{}".format(name1, if1)
self.logger.debug("%s: add_dummy %s", self, lname)
lhost = self.hosts[name1]

nsif1 = lhost.get_ns_ifname(if1)
lhost.cmd_raises_nsonly(f"ip link add name {nsif1} type dummy")

if mtu:
lhost.cmd_raises_nsonly(f"ip link set {nsif1} mtu {mtu}")
lhost.cmd_raises_nsonly(f"ip link set {nsif1} up")
lhost.register_interface(if1)

# Setup interface constraints if provided
if intf_constraints:
node1.set_intf_constraints(if1, **intf_constraints)

def add_link(self, node1, node2, if1, if2, mtu=None, **intf_constraints):
"""Add a link between switch and node or 2 nodes.
Expand Down
47 changes: 46 additions & 1 deletion munet/native.py
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,31 @@ def get_ifname(self, netname):
return c["name"]
return None

def set_dummy_addr(self, cconf):
if ip := cconf.get("ip"):
ipaddr = ipaddress.ip_interface(ip)
assert ipaddr.version == 4
else:
ipaddr = None

if ip := cconf.get("ipv6"):
ip6addr = ipaddress.ip_interface(ip)
assert ipaddr.version == 6
else:
ip6addr = None

if "physical" in cconf or self.is_vm:
return

ifname = cconf["name"]
for ip in (ipaddr, ip6addr):
if ip is None:
continue
self.set_intf_addr(ifname, ip)
ipcmd = "ip " if ip.version == 4 else "ip -6 "
self.logger.debug("%s: adding %s to unconnected intf %s", self, ip, ifname)
self.intf_ip_cmd(ifname, ipcmd + f"addr add {ip} dev {ifname}")

def set_lan_addr(self, switch, cconf):
if ip := cconf.get("ip"):
ipaddr = ipaddress.ip_interface(ip)
Expand Down Expand Up @@ -2756,8 +2781,9 @@ async def _async_build(self, logger=None):
if "connections" not in nconf:
continue
for cconf in nconf["connections"]:
# Eventually can add support for unconnected intf here.
if "to" not in cconf:
# unconnected intf
await self.add_dummy_link(node, cconf)
continue
to = cconf["to"]
if to in self.switches:
Expand All @@ -2778,6 +2804,25 @@ def autonumber(self):
def autonumber(self, value):
self.topoconf["networks-autonumber"] = bool(value)

async def add_dummy_link(self, node1, c1=None):
c1 = {} if c1 is None else c1

if "name" not in c1:
c1["name"] = node1.get_next_intf_name()
if1 = c1["name"]

do_add_dummy = True
if "hostintf" in c1:
await n.add_host_intf(c1["hostintf"], c1["name"], mtu=c1.get("mtu"))
do_add_dummy = False
elif "physical" in c1:
await n.add_phy_intf(c1["physical"], c1["name"])
do_add_dummy = False

if do_add_dummy:
super().add_dummy(node1, if1, **c1)
node1.set_dummy_addr(c1)

async def add_native_link(self, node1, node2, c1=None, c2=None):
"""Add a link between switch and node or 2 nodes."""
isp2p = False
Expand Down
31 changes: 31 additions & 0 deletions tests/unconnected/munet.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
version: 1
topology:
networks-autonumber: true
ipv6-enable: true
networks:
- name: net0
mtu: 5000
nodes:
- name: r1
connections:
- to: net0
name: eth0
mtu: 4500
- name: unconnected
ip: 172.16.0.1/24
mtu: 9000
cmd: |
ip addr show
ip -6 addr show
which ping
tail -f /dev/null
- name: r2
connections:
- to: "net0"
name: eth0
mtu: 4500
cmd: |
ip addr show
ip -6 addr show
which ping
tail -f /dev/null
37 changes: 37 additions & 0 deletions tests/unconnected/test_unconnected.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
# SPDX-License-Identifier: GPL-2.0-or-later
#
# June 28 2023, Eric Kinzie <[email protected]>
#
# Copyright 2023, LabN Consulting, L.L.C.
#
"Testing of unconnected interface."
import logging

import pytest

# All tests are coroutines
pytestmark = pytest.mark.asyncio


@pytest.mark.parametrize(
"unet_perfunc", ["munet", "noinit", "noinit-noshell"], indirect=["unet_perfunc"]
)
async def test_unconnected_presence(unet_perfunc):
unet = unet_perfunc
rc, o, e = await unet.hosts["r1"].async_cmd_status(f"ip addr show dev unconnected")
assert rc == 0
assert o.find("mtu 9000") > -1
assert o.find("inet 172.16.0.1/24") > -1


async def test_basic_ping(unet_perfunc):
unet = unet_perfunc
r1eth0 = unet.hosts["r1"].get_intf_addr("eth0").ip
logging.debug("r1eth0 is %s", r1eth0)
rc, o, e = await unet.hosts["r2"].async_cmd_status(
f"ip ro add 172.16.0.0/24 via {r1eth0}"
)
assert rc == 0
o = await unet.hosts["r2"].async_cmd_raises(f"ping -w1 -c1 172.16.0.1")
logging.debug("ping r2 output: %s", o)

0 comments on commit 226f45a

Please sign in to comment.