Add support for unicast ethernet faces to utility and helper methods.
Refs #5321
Change-Id: I2e28d092853fc21f59109cfbc23b946d30626a8c
diff --git a/minindn/apps/nlsr.py b/minindn/apps/nlsr.py
index 12a12d7..43da64f 100644
--- a/minindn/apps/nlsr.py
+++ b/minindn/apps/nlsr.py
@@ -30,7 +30,7 @@
from mininet.node import Switch
from minindn.apps.application import Application
-from minindn.util import scp, copyExistentFile
+from minindn.util import scp, copyExistentFile, MACToEther
from minindn.helpers.nfdc import Nfdc
from minindn.minindn import Minindn
@@ -41,7 +41,7 @@
SYNC_PSYNC = 'psync'
def __init__(self, node, logLevel='NONE', security=False, sync=SYNC_PSYNC,
- faceType='udp', nFaces=3, routingType=ROUTING_LINK_STATE, faceDict=None):
+ faceType=Nfdc.PROTOCOL_UDP, nFaces=3, routingType=ROUTING_LINK_STATE, faceDict=None):
Application.__init__(self, node)
try:
from mn_wifi.node import Node_wifi
@@ -92,7 +92,8 @@
warn('Check that each node has one radius value and one or two angle value(s).')
sys.exit(1)
- self.neighborIPs = []
+ self.neighborLocations = []
+ self.interfaceForNeighbor = dict()
possibleConfPaths = ['/usr/local/etc/ndn/nlsr.conf.sample', '/etc/ndn/nlsr.conf.sample']
copyExistentFile(node, possibleConfPaths, '{}/nlsr.conf'.format(self.homeDir))
@@ -107,8 +108,12 @@
Minindn.sleep(1)
def createFaces(self):
- for ip in self.neighborIPs:
- Nfdc.createFace(self.node, ip, self.faceType, isPermanent=True)
+ for location in self.neighborLocations:
+ if self.faceType == Nfdc.PROTOCOL_ETHER:
+ localIntf = self.interfaceForNeighbor[location]
+ Nfdc.createFace(self.node, location, self.faceType, localInterface=localIntf, isPermanent=True)
+ else:
+ Nfdc.createFace(self.node, location, self.faceType, isPermanent=True)
@staticmethod
def createKey(host, name, outputFile):
@@ -224,19 +229,27 @@
if node1 == self.node:
other = node2
- ip = other.IP(str(link.intf2))
+ if self.faceType == Nfdc.PROTOCOL_ETHER:
+ location = MACToEther(link.intf2.MAC())
+ else:
+ location = link.intf2.IP()
else:
other = node1
- ip = other.IP(str(link.intf1))
+ if self.faceType == Nfdc.PROTOCOL_ETHER:
+ location = MACToEther(link.intf1.MAC())
+ else:
+ location = link.intf1.IP()
linkCost = intf.params.get('delay', '10ms').replace('ms', '')
- self.neighborIPs.append(ip)
+ self.neighborLocations.append(location)
+ if self.faceType == Nfdc.PROTOCOL_ETHER:
+ self.interfaceForNeighbor[location] = intf
self.node.cmd('{} -a neighbors.neighbor \
<<<\'name {}{}-site/%C1.Router/cs/{} face-uri {}://{}\n link-cost {}\''
.format(self.infocmd, self.network, other.name, other.name,
- self.faceType, ip, linkCost))
+ self.faceType, location, linkCost))
def __editNeighborsSectionManual(self):
diff --git a/minindn/helpers/ndn_routing_helper.py b/minindn/helpers/ndn_routing_helper.py
index d614926..7ea0de9 100644
--- a/minindn/helpers/ndn_routing_helper.py
+++ b/minindn/helpers/ndn_routing_helper.py
@@ -38,6 +38,8 @@
from mininet.log import info, debug, error, warn
from minindn.helpers.nfdc import Nfdc as nfdc
+from minindn.util import MACToEther
+
UNKNOWN_DISTANCE = -1
HYPERBOLIC_COST_ADJUSTMENT_FACTOR = 1000
@@ -297,8 +299,13 @@
def globalRoutingHelperHandler(self):
info('Creating faces and adding routes to FIB\n')
+ if self.faceType == nfdc.PROTOCOL_ETHER:
+ add_route_method = self.addNodeRoutesEther
+ else:
+ add_route_method = self.addNodeRoutes
+
res = Parallel(n_jobs=-1, require='sharedmem',
- prefer="threads", verbose=1)(delayed(self.addNodeRoutes)(host) for host in self.net.hosts)
+ prefer="threads", verbose=1)(delayed(add_route_method)(host) for host in self.net.hosts)
info('Processed all the routes to NFD\n')
@@ -308,10 +315,20 @@
:param Node node: Node from net object
"""
- neighborIPs = self.getNeighbor(node)
+ neighborIPs = self.getNeighborIP(node)
neighborFaces = self.createFaces(node, neighborIPs)
self.routeAdd(node, neighborFaces)
+ def addNodeRoutesEther(self, node):
+ """
+ Create faces to neighbors and add all routes for one node
+
+ :param Node node: Node from net object
+ """
+ neighborAddrs = self.getNeighborEther(node)
+ neighborFaces = self.createEtherFaces(node, neighborAddrs)
+ self.routeAdd(node, neighborFaces)
+
def addOrigin(self, nodes, prefix):
"""
Add prefix/s as origin on node/s
@@ -355,6 +372,15 @@
neighborFaces[k] = faceID
return neighborFaces
+ def createEtherFaces(self, node, neighborLocations):
+ neighborFaces = {}
+ for k, intfLocTuple in neighborLocations.items():
+ localIntf, etherAddr = intfLocTuple
+ faceID = nfdc.createFace(node, etherAddr, self.faceType, self.permanentFaces, localIntf)
+ if not isinstance(faceID, str): raise ValueError(faceID)
+ neighborFaces[k] = faceID
+ return neighborFaces
+
def routeAdd(self, node, neighborFaces):
"""
@@ -373,8 +399,9 @@
for prefix in prefixes:
# Register routes to all the available destination name prefix/s
nfdc.registerRoute(node, prefix, neighborFaces[nextHop], cost=cost)
+
@staticmethod
- def getNeighbor(node):
+ def getNeighborIP(node):
# Nodes to IP mapping
neighborIPs = defaultdict()
for intf in node.intfList():
@@ -392,3 +419,21 @@
# Used later to create faces
neighborIPs[other.name] = ip
return neighborIPs
+
+ @staticmethod
+ def getNeighborEther(node):
+ # Nodes to IP mapping
+ neighborLocations = defaultdict()
+ for intf in node.intfList():
+ link = intf.link
+ if link:
+ node1, node2 = link.intf1.node, link.intf2.node
+ if node1 == node:
+ other = node2
+ intf_location = (link.intf1.name, MACToEther(link.intf2.MAC()))
+ else:
+ other = node1
+ intf_location = (link.intf2.name, MACToEther(link.intf1.MAC()))
+ # Used later to create faces
+ neighborLocations[other.name] = intf_location
+ return neighborLocations
\ No newline at end of file
diff --git a/minindn/helpers/nfdc.py b/minindn/helpers/nfdc.py
index f2558d5..08aeccf 100644
--- a/minindn/helpers/nfdc.py
+++ b/minindn/helpers/nfdc.py
@@ -23,6 +23,7 @@
from mininet.log import debug, warn
from minindn.minindn import Minindn
+from minindn.util import MACToEther
# If needed (e.g. to speed up the process), use a smaller (or larger value)
# based on your machines resource (CPU, memory)
@@ -52,6 +53,8 @@
'expires {}'.format(expirationInMillis) if expirationInMillis else ''
)
else:
+ if protocol == "ether":
+ remoteNode = MACToEther(remoteNode)
cmd = ('nfdc route add {} {}://{} origin {} cost {} {}{}{}').format(
namePrefix,
protocol,
@@ -66,22 +69,33 @@
Minindn.sleep(SLEEP_TIME)
@staticmethod
- def unregisterRoute(node, namePrefix, remoteNode, origin=255):
+ def unregisterRoute(node, namePrefix, remoteNode, protocol=PROTOCOL_UDP, origin=255):
cmd = ""
if remoteNode.isdigit() and not protocol == "fd":
- cmd = 'nfdc route remove {} {} {}'.format(namePrefix, remoteNode, origin)
+ cmd = 'nfdc route remove {} {} origin {}'.format(namePrefix, remoteNode, origin)
else:
- cmd = 'nfdc route remove {} {} {}'.format(namePrefix, remoteNode, origin)
+ if protocol == "ether":
+ remoteNode = MACToEther(remoteNode)
+ cmd = 'nfdc route remove {} {}://{} origin {}'.format(namePrefix, protocol, remoteNode, origin)
debug(node.cmd(cmd))
Minindn.sleep(SLEEP_TIME)
@staticmethod
- def createFace(node, remoteNodeAddress, protocol='udp', isPermanent=False, allowExisting=True):
+ def createFace(node, remoteNodeAddress, protocol=PROTOCOL_UDP, isPermanent=False, localInterface='', allowExisting=True):
'''Create face in node's NFD instance. Returns FaceID of created face or -1 if failed.'''
- cmd = ('nfdc face create {}://{} {}'.format(
+ if protocol == "ether" and not localInterface:
+ warn("Cannot create ethernet face without local interface!")
+ return
+ elif protocol != "ether" and localInterface:
+ warn("Cannot create non-ethernet face with local interface specified!")
+ return
+ elif protocol == "ether" and localInterface:
+ remoteNodeAddress = MACToEther(remoteNodeAddress)
+ cmd = ('nfdc face create {}://{} {}{}'.format(
protocol,
remoteNodeAddress,
- 'permanent' if isPermanent else 'persistent'
+ 'local dev://{} '.format(localInterface) if localInterface else '',
+ 'persistency permanent' if isPermanent else 'persistency persistent'
))
output = node.cmd(cmd)
debug(output)
@@ -95,10 +109,12 @@
return -1
@staticmethod
- def destroyFace(node, remoteNode, protocol='udp'):
+ def destroyFace(node, remoteNode, protocol=PROTOCOL_UDP):
if remoteNode.isdigit() and not protocol == "fd":
- debug(node.cmd('nfdc face destroy {}'.format(protocol, remoteNode)))
+ debug(node.cmd('nfdc face destroy {}'.format(remoteNode)))
else:
+ if protocol == "ether":
+ remoteNode = MACToEther(remoteNode)
debug(node.cmd('nfdc face destroy {}://{}'.format(protocol, remoteNode)))
Minindn.sleep(SLEEP_TIME)
@@ -116,13 +132,17 @@
Minindn.sleep(SLEEP_TIME)
@staticmethod
- def getFaceId(node, remoteNodeAddress, localEndpoint=None, protocol="udp", portNum="6363"):
+ def getFaceId(node, remoteNodeAddress, localEndpoint=None, protocol=PROTOCOL_UDP, portNum="6363"):
'''Returns the faceId for a remote node based on FaceURI, or -1 if a face is not found'''
#Should this be cached or is the hit not worth it?
local = ""
if localEndpoint:
local = " local {}".format(localEndpoint)
- output = node.cmd("nfdc face list remote {}://{}:{}{}".format(protocol, remoteNodeAddress, portNum, local))
+ if protocol == "ether":
+ remoteNodeAddress = MACToEther(remoteNodeAddress)
+ output = node.cmd("nfdc face list remote {}://{}{}".format(protocol, remoteNodeAddress, local))
+ else:
+ output = node.cmd("nfdc face list remote {}://{}:{}{}".format(protocol, remoteNodeAddress, portNum, local))
debug(output)
Minindn.sleep(SLEEP_TIME)
# This is fragile but we don't have that many better options
diff --git a/minindn/util.py b/minindn/util.py
index 23092da..d44f20c 100644
--- a/minindn/util.py
+++ b/minindn/util.py
@@ -28,6 +28,10 @@
from mininet.cli import CLI
+from mininet.log import error
+
+import re
+
sshbase = ['ssh', '-q', '-t', '-i/home/mininet/.ssh/id_rsa']
scpbase = ['scp', '-i', '/home/mininet/.ssh/id_rsa']
devnull = open('/dev/null', 'w')
@@ -84,6 +88,17 @@
return host.popen(cmd, cwd=host.params['params']['homeDir'],
env=popenGetEnv(host, envDict), **params)
+def MACToEther(mac):
+ # We use the regex filters from face-uri.cpp in ndn-cxx with minor modifications
+ if re.match('^\[((?:[a-fA-F0-9]{1,2}\:){5}(?:[a-fA-F0-9]{1,2}))\]$', mac):
+ return mac
+ elif re.match('^((?:[a-fA-F0-9]{1,2}\:){5}(?:[a-fA-F0-9]{1,2}))$', mac):
+ # URI syntax requires nfdc to use brackets for MAC and ethernet addresses due
+ # to the use of colons as separators. Incomplete brackets are a code issue.
+ return '[%s]' % mac
+ error('Potentially malformed MAC address, passing without alteration: %s' % mac)
+ return mac
+
class MiniNDNCLI(CLI):
prompt = 'mini-ndn> '
def __init__(self, mininet, stdin=sys.stdin, script=None):