Skip to content
This repository was archived by the owner on Dec 20, 2023. It is now read-only.

Commit 5ef6e1e

Browse files
author
Jay Logue
committed
Yet more fixes to simnet
-- Fixed support for tap interfaces within virtual nodes. Unlike a veth-pair, the two ends of a linux tap device cannot be in different namespaces. To make it possible for a tap interface in a node namespace to be connected to bridge in a “network” namespace, simnet now constructs a “trunk line” veth-pair that spans the two namespaces, and a “tap bridge” in the node namespace. The tap interface is connected to the tap bridge, along with one of the trunk interfaces. The other trunk interface is connected to the bridge in the “network” namespace. This allows packets to flow between the process holding the tap fd and the virtual network. -- Eliminated the use of special short interface names when creating tap interfaces. These interfaces now use the names “wifi”, “thread”, etc., just like normal interfaces. -- Disabled automatic assignment of IPv6 link-local addresses on interfaces that don’t need them (e.g. the bridge interfaces in “network” namespaces, and the ends of a veth-pair that are plugged into bridges). -- Simplified the two-device simnet layout.
1 parent d42cdfa commit 5ef6e1e

File tree

2 files changed

+86
-40
lines changed

2 files changed

+86
-40
lines changed

src/tools/simnet/lib/simnet/layouts/two-devices.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,6 @@
3939
meshLocalPrefix = 'fd42:4242:4242::/64'
4040
)
4141

42-
LegacyThreadNetwork(
43-
name = 'legacy'
44-
)
45-
4642
#===============================================================================
4743
# Devices
4844
#===============================================================================
@@ -56,21 +52,21 @@
5652
isIP4DefaultGateway = True
5753
)
5854

59-
# Weave device with WiFi, Thread and Legacy Thread networks
55+
# Weave device with WiFi and Thread networks
6056
WeaveDevice(
6157
name = 'dev1',
6258
weaveNodeId = 1,
6359
weaveFabricId = 1,
6460
wifiNetwork = 'wifi',
6561
threadNetwork = 'thread',
66-
legacyNetwork = 'legacy',
62+
useLwIP = False
6763
)
6864

69-
# Weave device with Thread and Legacy Thread networks only
65+
# Weave device with Thread network only
7066
WeaveDevice(
7167
name = 'dev2',
7268
weaveFabricId = 1,
7369
weaveNodeId = 2,
7470
threadNetwork = 'thread',
75-
legacyNetwork = 'legacy'
71+
useLwIP = False
7672
)

src/tools/simnet/simnet.py

Lines changed: 82 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,9 @@ def _buildNetwork(self):
350350

351351
# Create a virtual ethernet bridge to simulate the network
352352
addBridge(self.brName, nsName=self.nsName)
353+
354+
# Disable automatic address generation on the host interface for the bridge.
355+
setInterfaceAddressGenerationMode(self.brName, mode='none', nsName=self.nsName)
353356

354357
# Enable the host interface for the bridge
355358
enableInterface(self.brName, nsName=self.nsName)
@@ -953,7 +956,7 @@ def getLwIPConfig(self):
953956
lwipConfig = ''
954957
for i in self.interfaces:
955958
if isinstance(i, TapInterface):
956-
lwipConfig += i.getLwipConfig()
959+
lwipConfig += i.getLwIPConfig()
957960
if self.ip4DefaultGateway:
958961
lwipConfig += '--ip4-default-gw %s ' % self.ip4DefaultGateway
959962
if self.ip4DNSServers:
@@ -1167,7 +1170,7 @@ def __init__(self, name,
11671170

11681171
if wifiNetwork or wifiInterface:
11691172
if not wifiInterface:
1170-
wifiInterface = 'wifi' if not useLwIP else 'et0'
1173+
wifiInterface = 'wifi'
11711174
self.wifiInterface = self.addInterface(wifiNetwork, name=wifiInterface)
11721175
if not isinstance(self.wifiInterface.network, (WiFiNetwork, HostNetwork)):
11731176
raise ConfigException('Incompatible network %s attached to wifi interface of node %s' % (self.wifiInterface.network.name, name))
@@ -1176,7 +1179,7 @@ def __init__(self, name,
11761179

11771180
if threadNetwork or threadInterface:
11781181
if not threadInterface:
1179-
threadInterface = 'thread' if not useLwIP else 'th0'
1182+
threadInterface = 'thread'
11801183
self.threadInterface = self.addInterface(threadNetwork, name=threadInterface)
11811184
if not isinstance(self.threadInterface.network, (ThreadNetwork, HostNetwork)):
11821185
raise ConfigException('Incompatible network %s attached to thread interface of node %s' % (self.threadInterface.network.name, name))
@@ -1185,7 +1188,7 @@ def __init__(self, name,
11851188

11861189
if legacyNetwork or legacyInterface:
11871190
if not legacyInterface:
1188-
legacyInterface = 'legacy' if not useLwIP else 'al0'
1191+
legacyInterface = 'legacy'
11891192
self.legacyInterface = self.addInterface(legacyNetwork, name=legacyInterface)
11901193
if not isinstance(self.legacyInterface.network, (LegacyThreadNetwork, HostNetwork)):
11911194
raise ConfigException('Incompatible network %s attached to legacy thread interface of node %s' % (self.legacyInterface.network.name, name))
@@ -1194,7 +1197,7 @@ def __init__(self, name,
11941197

11951198
if cellularNetwork or cellularInterface:
11961199
if not cellularInterface:
1197-
cellularInterface = 'cell' if not useLwIP else 'cl0'
1200+
cellularInterface = 'cell'
11981201
self.cellularInterface = self.addInterface(cellularNetwork, name=cellularInterface)
11991202
if not isinstance(self.cellularInterface.network, (WiFiNetwork, Internet, HostNetwork)):
12001203
raise ConfigException('Incompatible network %s attached to cell interface of node %s' % (self.cellularInterface.network.name, name))
@@ -1365,7 +1368,6 @@ def __init__(self, node, network, name):
13651368
self.advertisedIP6Prefixes = []
13661369
self.ip4AutoConfig = None
13671370
self.ip6AutoConfig = None
1368-
self.isTapInterface = False
13691371

13701372
# Form a MAC address for the interface from the base MAC address, the node index and the interface index.
13711373
# Use a 48-bit or 64-bit MAC based on the nature of the connected network.
@@ -1439,15 +1441,15 @@ def buildInterface(self):
14391441
moveInterfaceToNamespace(tmpInterfaceName, nsName=self.node.nsName)
14401442
renameInterface(tmpInterfaceName, self.ifName, nsName=self.node.nsName)
14411443

1444+
# Disable automatic IPv6 link-local address generation on both interfaces. The peer interface
1445+
# doesn't need one, and one will be created for the node interface below.
1446+
setInterfaceAddressGenerationMode(self.ifName, mode='none', nsName=self.node.nsName)
1447+
setInterfaceAddressGenerationMode(self.peerIfName, mode='none', nsName=self.network.nsName)
1448+
14421449
# Enable the virtual interfaces.
14431450
enableInterface(self.ifName, nsName=self.node.nsName)
14441451
enableInterface(self.peerIfName, nsName=self.network.nsName)
14451452

1446-
# Flush any IPv6 link-local addresses automatically created by the network. The appropriate
1447-
# LL addresses will be added later (see below).
1448-
runCmd([ 'ip', '-6', 'addr', 'flush', 'dev', self.ifName, 'scope', 'link' ], nsName=self.node.nsName,
1449-
errMsg='Unable to flush IPv6 LL addresses on interface %s in namespace %s' % (self.ifName, self.node.nsName))
1450-
14511453
# Assign the IPv4 address to the node interface.
14521454
if self.ip4Address != None:
14531455
assignAddressToInterface(self.ifName, self.ip4Address, nsName=self.node.nsName)
@@ -1472,7 +1474,7 @@ def typeSummary(self):
14721474

14731475

14741476
class TapInterface(NodeInterface):
1475-
def __init__(self, node, network, name=None):
1477+
def __init__(self, node, network, name):
14761478

14771479
# If the node is implemented by the host, include the node index in the interface name to
14781480
# ensure it is unique.
@@ -1482,37 +1484,75 @@ def __init__(self, node, network, name=None):
14821484
# Initialize the base class.
14831485
NodeInterface.__init__(self, node, network, name)
14841486

1485-
# Form the name of the tap device from the node index and the name of the network to which it is attached.
1486-
self.tapDevName = namePrefix + network.name + '-' + str(node.nodeIndex)
1487+
self.tapBrName = namePrefix + 'tunbr-' + self.ifName
1488+
self.trunkIfName = namePrefix + 'trunk-' + self.ifName
1489+
self.peerIfName = namePrefix + str(network.networkIndex) + '-' + str(node.nodeIndex) + '-' + str(self.ifIndex)
14871490

14881491
def buildInterface(self):
14891492

1490-
# Create the tap device
1491-
# TODO: set owner and group
1492-
createTapInterface(self.tapDevName)
1493+
# Create a pair of virtual ethernet interfaces that will serve as a 'trunk' line between a bridge in the node's
1494+
# namespace and the bridge in the network's namespace.
1495+
# Use a temporary name for the first interface.
1496+
tmpInterfaceName = namePrefix + 'tmp-' + str(os.getpid()) + str(random.randint(0, 100))
1497+
createVETHPair(tmpInterfaceName, self.peerIfName)
1498+
1499+
# Assign the first trunk interface to the node namespace and rename it to the appropriate name.
1500+
if not self.node.isHostNode:
1501+
moveInterfaceToNamespace(tmpInterfaceName, nsName=self.node.nsName)
1502+
renameInterface(tmpInterfaceName, self.trunkIfName, nsName=self.node.nsName)
14931503

1494-
# Attach the tap interface to the network bridge.
1495-
moveInterfaceToNamespace(self.tapDevName, nsName=self.network.nsName)
1496-
attachInterfaceToBridge(self.tapDevName, self.network.brName, nsName=self.network.nsName)
1504+
# Attach the second trunk interface (the peer interface) to the network bridge.
1505+
moveInterfaceToNamespace(self.peerIfName, nsName=self.network.nsName)
1506+
attachInterfaceToBridge(self.peerIfName, self.network.brName, nsName=self.network.nsName)
14971507

1498-
# Enable the tap interface.
1499-
enableInterface(self.tapDevName, nsName=self.network.nsName)
1508+
# Create a bridge in the node namespace that will bridge between the tap device and the trunk
1509+
# interface.
1510+
addBridge(self.tapBrName, nsName=self.node.nsName)
1511+
1512+
# Attach the first trunk interface to the TAP bridge.
1513+
attachInterfaceToBridge(self.trunkIfName, self.tapBrName, nsName=self.node.nsName)
1514+
1515+
# Create a tap device in the node namespace.
1516+
user = os.environ['SUDO_USER']
1517+
createTapInterface(self.ifName, user=user, group=user, nsName=self.node.nsName)
1518+
1519+
# Attach the tap interface to the TAP bridge.
1520+
attachInterfaceToBridge(self.ifName, self.tapBrName, nsName=self.node.nsName)
1521+
1522+
# Disable automatic address generation on all associated interfaces.
1523+
setInterfaceAddressGenerationMode(self.ifName, mode='none', nsName=self.node.nsName)
1524+
setInterfaceAddressGenerationMode(self.tapBrName, mode='none', nsName=self.node.nsName)
1525+
setInterfaceAddressGenerationMode(self.trunkIfName, mode='none', nsName=self.node.nsName)
1526+
setInterfaceAddressGenerationMode(self.peerIfName, mode='none', nsName=self.network.nsName)
1527+
1528+
# Enable the interfaces.
1529+
enableInterface(self.tapBrName, nsName=self.node.nsName)
1530+
enableInterface(self.trunkIfName, nsName=self.node.nsName)
1531+
enableInterface(self.peerIfName, nsName=self.network.nsName)
1532+
enableInterface(self.ifName, nsName=self.node.nsName)
15001533

15011534
def clearInterface(self):
15021535

15031536
# Delete the tap device.
1504-
deleteInterface(self.tapDevName, nsName=self.network.nsName)
1537+
deleteInterface(self.ifName, nsName=self.node.nsName)
1538+
1539+
# Delete the trunk and peer interfaces.
1540+
deleteInterface(self.trunkIfName, nsName=self.node.nsName)
1541+
deleteInterface(self.peerIfName, nsName=self.network.nsName)
1542+
1543+
# Delete the TAP bridge.
1544+
deleteBridge(self.tapBrName, nsName=self.node.nsName)
15051545

15061546
def typeSummary(self):
1507-
return 'tap device connected to network "%s" via interface %s' % (self.network.name, self.tapDevName)
1547+
return 'tap device "%s" connected to network "%s" via peer interface %s' % (self.ifName, self.network.name, self.peerIfName)
15081548

15091549
def getLwIPConfig(self):
1510-
lwipConfig = '--%s-tap-device %s ' % (i.ifName, i.tapDevName)
1511-
lwipConfig += '--%s-mac-addr %012X ' % (i.ifName, i.macAddress) # TODO: handle case where MAC is 64-bits
1512-
if i.ip4Enabled and i.ip4Address:
1513-
lwipConfig += '--%s-ip4-addr %s ' % (i.ifName, i.ip4Address)
1514-
if i.ip6Enabled and len(i.ip6Addresses) > 0:
1515-
lwipConfig += '--%s-ip6-addrs %s ' % (i.ifName, ','.join([ str(a) for a in i.ip6Addresses ]))
1550+
lwipConfig = '--%s-tap-device %s ' % (self.ifName, self.ifName)
1551+
lwipConfig += '--%s-mac-addr %012X ' % (self.ifName, self.macAddress) # TODO: handle case where MAC is 64-bits
1552+
if self.ip4Enabled and self.ip4Address:
1553+
lwipConfig += '--%s-ip4-addr %s ' % (self.ifName, self.ip4Address)
1554+
if self.ip6Enabled and len(self.ip6Addresses) > 0:
1555+
lwipConfig += '--%s-ip6-addrs %s ' % (self.ifName, ','.join([ str(a) for a in self.ip6Addresses ]))
15161556
return lwipConfig;
15171557

15181558

@@ -1687,8 +1727,8 @@ def createVETHPair(name, peerName):
16871727
runCmd([ 'ip', 'link', 'add', 'name', name, 'type', 'veth', 'peer', 'name', peerName ], errMsg='Unable to create virtual ethernet interface pair %s' % (name))
16881728
time.sleep(0.01)
16891729

1690-
def createTapInterface(name, user='root', group='root'):
1691-
runCmd([ 'tunctl', '-u', user, '-g', group, '-t', name], errMsg='Unable to create tap interface pair %s' % (name))
1730+
def createTapInterface(name, user='root', group='root', nsName=None):
1731+
runCmd([ 'tunctl', '-u', user, '-g', group, '-t', name], errMsg='Unable to create tap interface pair %s' % (name), nsName=nsName)
16921732

16931733
def moveInterfaceToNamespace(ifName, nsName=None):
16941734
runCmd([ 'ip', 'link', 'set', ifName, 'netns', nsName ], errMsg='Unable to assign virtual ethernet interface %s to namespace %s' % (ifName, nsName))
@@ -1718,6 +1758,16 @@ def attachInterfaceToBridge(ifName, brName, nsName=None):
17181758
runCmd([ 'brctl', 'addif', brName, ifName ], nsName=nsName,
17191759
errMsg='Unable to assign virtual ethernet interface %s to bridge %s' % (ifName, brName))
17201760

1761+
def flushLinkLocalAddresses(ifName, nsName=None):
1762+
# Flush any IPv6 link-local addresses automatically created by the system.
1763+
runCmd([ 'ip', '-6', 'addr', 'flush', 'dev', ifName, 'scope', 'link' ], nsName=nsName,
1764+
errMsg='Unable to flush IPv6 LL addresses on interface %s in namespace %s' % (ifName, nsName))
1765+
1766+
def setInterfaceAddressGenerationMode(ifName, mode, nsName=None):
1767+
# Set the IPv6 address generation mode for the interface.
1768+
runCmd([ 'ip', 'link', 'set', 'dev', ifName, 'addrgenmode', mode ], nsName=nsName,
1769+
errMsg='Unable to set the IPv6 address generation mode on interface %s in namespace %s' % (ifName, nsName))
1770+
17211771
def getInterfaces(nsName=None):
17221772
out = runCmd([ 'ip', '-o', 'link', 'show' ], errMsg='Failed to enumerate interfaces', nsName=nsName)
17231773
out = out.replace('\\', '') # move backslashes introduced by -o

0 commit comments

Comments
 (0)