@@ -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
14741476class 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
16931733def 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+
17211771def 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