From 21acd05d2636928d02daf8e47be352064566a4ae Mon Sep 17 00:00:00 2001 From: awlane Date: Thu, 13 Jun 2024 21:12:51 -0500 Subject: [PATCH] Add support for unicast ethernet faces to utility and helper methods. Refs #5321 Change-Id: I2e28d092853fc21f59109cfbc23b946d30626a8c --- examples/tests/faces_test.py | 396 ++++++++++++++++++++++++++ minindn/apps/nlsr.py | 31 +- minindn/helpers/ndn_routing_helper.py | 51 +++- minindn/helpers/nfdc.py | 40 ++- minindn/util.py | 15 + 5 files changed, 511 insertions(+), 22 deletions(-) create mode 100644 examples/tests/faces_test.py diff --git a/examples/tests/faces_test.py b/examples/tests/faces_test.py new file mode 100644 index 0000000..0589441 --- /dev/null +++ b/examples/tests/faces_test.py @@ -0,0 +1,396 @@ +# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */ +# +# Copyright (C) 2015-2021, The University of Memphis, +# Arizona Board of Regents, +# Regents of the University of California. +# +# This file is part of Mini-NDN. +# See AUTHORS.md for a complete list of Mini-NDN authors and contributors. +# +# Mini-NDN is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Mini-NDN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Mini-NDN, e.g., in COPYING.md file. +# If not, see . + +from subprocess import PIPE +from time import sleep + +from mininet.log import setLogLevel, info, debug +from mininet.topo import Topo + +from minindn.minindn import Minindn +from minindn.apps.app_manager import AppManager +from minindn.util import MiniNDNCLI, getPopen +from minindn.apps.nfd import Nfd +from minindn.apps.nlsr import Nlsr +from minindn.helpers.experiment import Experiment +from minindn.helpers.nfdc import Nfdc +from minindn.helpers.ndn_routing_helper import NdnRoutingHelper + +PREFIX = "/example" + +def printOutput(output): + _out = output.decode("utf-8").split("\n") + for _line in _out: + info(_line + "\n") + +def udp_run(): + Minindn.cleanUp() + Minindn.verifyDependencies() + + # Topology can be created/modified using Mininet topo object + topo = Topo() + info("Setup\n") + # add hosts + a = topo.addHost('a') + b = topo.addHost('b') + c = topo.addHost('c') + + # add links + topo.addLink(a, b, delay='10ms', bw=10) # bw = bandwidth + topo.addLink(b, c, delay='10ms', bw=10) + topo.addLink(a, c, delay='10ms', bw=10) + + ndn = Minindn(topo=topo) + ndn.start() + + # configure and start nfd on each node + info("Configuring NFD\n") + AppManager(ndn, ndn.net.hosts, Nfd, logLevel="DEBUG") + + """ + There are multiple ways of setting up routes in Mini-NDN + refer: https://minindn.memphis.edu/experiment.html#routing-options + It can also be set manually as follows. The important bit to note here + is the use of the Nfdc command + """ + links = {"a":["b", "c"], "b":["c"]} + nfdc_batches = dict() + for first in links: + for second in links[first]: + host1 = ndn.net[first] + host2 = ndn.net[second] + interface = host2.connectionsTo(host1)[0][0] + interface_ip = interface.IP() + Nfdc.createFace(host1, interface_ip) + Nfdc.registerRoute(host1, PREFIX, interface_ip, cost=0) + Nfdc.setStrategy(host1, PREFIX, Nfdc.STRATEGY_ASF) + sleep(1) + debug(ndn.net["a"].cmd("nfdc face list")) + debug(ndn.net["a"].cmd("nfdc fib list")) + debug(ndn.net["a"].cmd("nfdc strategy show /example")) + + # Start ping server + info("Starting pings...\n") + pingserver_log = open("{}/c/ndnpingserver.log".format(ndn.workDir), "w") + getPopen(ndn.net["c"], "ndnpingserver {}".format(PREFIX), stdout=pingserver_log,\ + stderr=pingserver_log) + + # start ping client + ping1 = getPopen(ndn.net["a"], "ndnping {} -c 5".format(PREFIX), stdout=PIPE, stderr=PIPE) + ping1.wait() + printOutput(ping1.stdout.read()) + + links = {"a":["b", "c"], "b":["c"]} + for first in links: + for second in links[first]: + host1 = ndn.net[first] + host2 = ndn.net[second] + interface = host2.connectionsTo(host1)[0][0] + interface_ip = interface.IP() + Nfdc.unregisterRoute(host1, PREFIX, interface_ip) + Nfdc.destroyFace(host1, interface_ip) + Nfdc.unsetStrategy(host1, PREFIX) + sleep(1) + debug(ndn.net["a"].cmd("nfdc face list")) + debug(ndn.net["a"].cmd("nfdc fib list")) + debug(ndn.net["a"].cmd("nfdc strategy show /example")) + + ping2 = getPopen(ndn.net["a"], "ndnping {} -c 5".format(PREFIX), stdout=PIPE, stderr=PIPE) + ping2.wait() + printOutput(ping2.stdout.read()) + + info("\nExperiment Completed!\n") + # MiniNDNCLI(ndn.net) + ndn.stop() + +def eth_run(): + Minindn.cleanUp() + Minindn.verifyDependencies() + + # Topology can be created/modified using Mininet topo object + topo = Topo() + info("Setup\n") + # add hosts + a = topo.addHost('a') + b = topo.addHost('b') + c = topo.addHost('c') + + # add links + topo.addLink(a, b, delay='10ms', bw=10) # bw = bandwidth + topo.addLink(b, c, delay='10ms', bw=10) + topo.addLink(a, c, delay='10ms', bw=10) + + ndn = Minindn(topo=topo) + ndn.start() + + # configure and start nfd on each node + info("Configuring NFD\n") + AppManager(ndn, ndn.net.hosts, Nfd, logLevel="DEBUG") + + """ + There are multiple ways of setting up routes in Mini-NDN + refer: https://minindn.memphis.edu/experiment.html#routing-options + It can also be set manually as follows. The important bit to note here + is the use of the Nfdc command + """ + links = {"a":["b", "c"], "b":["c"]} + nfdc_batches = dict() + for first in links: + for second in links[first]: + host1 = ndn.net[first] + host2 = ndn.net[second] + sender_interface = host1.connectionsTo(host2)[0][0] + interface = host2.connectionsTo(host1)[0][0] + interface_addr = interface.MAC() + Nfdc.createFace(host1, interface_addr, protocol=Nfdc.PROTOCOL_ETHER, localInterface=sender_interface) + Nfdc.registerRoute(host1, PREFIX, interface_addr, cost=0, protocol=Nfdc.PROTOCOL_ETHER) + Nfdc.setStrategy(host1, PREFIX, Nfdc.STRATEGY_ASF) + sleep(1) + debug(ndn.net["a"].cmd("nfdc face list")) + debug(ndn.net["a"].cmd("nfdc fib list")) + debug(ndn.net["a"].cmd("nfdc strategy show /example")) + + # Start ping server + info("Starting pings...\n") + pingserver_log = open("{}/c/ndnpingserver.log".format(ndn.workDir), "w") + getPopen(ndn.net["c"], "ndnpingserver {}".format(PREFIX), stdout=pingserver_log,\ + stderr=pingserver_log) + + # start ping client + ping1 = getPopen(ndn.net["a"], "ndnping {} -c 5".format(PREFIX), stdout=PIPE, stderr=PIPE) + ping1.wait() + printOutput(ping1.stdout.read()) + + links = {"a":["b", "c"], "b":["c"]} + for first in links: + for second in links[first]: + host1 = ndn.net[first] + host2 = ndn.net[second] + interface = host2.connectionsTo(host1)[0][0] + interface_addr = interface.MAC() + Nfdc.unregisterRoute(host1, PREFIX, interface_addr, protocol=Nfdc.PROTOCOL_ETHER) + Nfdc.destroyFace(host1, interface_addr, protocol=Nfdc.PROTOCOL_ETHER) + Nfdc.unsetStrategy(host1, PREFIX) + sleep(1) + debug(ndn.net["a"].cmd("nfdc face list")) + debug(ndn.net["a"].cmd("nfdc fib list")) + debug(ndn.net["a"].cmd("nfdc strategy show /example")) + + ping2 = getPopen(ndn.net["a"], "ndnping {} -c 5".format(PREFIX), stdout=PIPE, stderr=PIPE) + ping2.wait() + printOutput(ping2.stdout.read()) + + info("\nExperiment Completed!\n") + # MiniNDNCLI(ndn.net) + ndn.stop() + +def udp_nlsr_run(): + Minindn.cleanUp() + Minindn.verifyDependencies() + + # Topology can be created/modified using Mininet topo object + topo = Topo() + info("Setup\n") + # add hosts + a = topo.addHost('a') + b = topo.addHost('b') + c = topo.addHost('c') + + # add links + topo.addLink(a, b, delay='10ms', bw=10) # bw = bandwidth + topo.addLink(b, c, delay='10ms', bw=10) + topo.addLink(a, c, delay='10ms', bw=10) + + ndn = Minindn(topo=topo) + ndn.start() + + nfds = AppManager(ndn, ndn.net.hosts, Nfd) + nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr, faceType=Nfdc.PROTOCOL_UDP, + logLevel='ndn.*=TRACE:nlsr.*=TRACE') + + Experiment.checkConvergence(ndn, ndn.net.hosts, 60, quit=False) + + Experiment.setupPing(ndn.net.hosts, Nfdc.STRATEGY_BEST_ROUTE) + Experiment.startPctPings(ndn.net, 60) + + sleep(70) + + ndn.stop() + +def eth_nlsr_run(): + Minindn.cleanUp() + Minindn.verifyDependencies() + + # Topology can be created/modified using Mininet topo object + topo = Topo() + info("Setup\n") + # add hosts + a = topo.addHost('a') + b = topo.addHost('b') + c = topo.addHost('c') + + # add links + topo.addLink(a, b, delay='10ms', bw=10) # bw = bandwidth + topo.addLink(b, c, delay='10ms', bw=10) + topo.addLink(a, c, delay='10ms', bw=10) + + ndn = Minindn(topo=topo) + ndn.start() + + nfds = AppManager(ndn, ndn.net.hosts, Nfd) + nlsrs = AppManager(ndn, ndn.net.hosts, Nlsr, faceType=Nfdc.PROTOCOL_ETHER, + logLevel='ndn.*=TRACE:nlsr.*=TRACE') + + Experiment.checkConvergence(ndn, ndn.net.hosts, 60, quit=False) + + Experiment.setupPing(ndn.net.hosts, Nfdc.STRATEGY_BEST_ROUTE) + Experiment.startPctPings(ndn.net, 60) + + sleep(70) + + ndn.stop() + +def udp_static_run(): + Minindn.cleanUp() + Minindn.verifyDependencies() + + # Topology can be created/modified using Mininet topo object + topo = Topo() + info("Setup\n") + # add hosts + a = topo.addHost('a') + b = topo.addHost('b') + c = topo.addHost('c') + + # add links + topo.addLink(a, b, delay='10ms', bw=10) # bw = bandwidth + topo.addLink(b, c, delay='10ms', bw=10) + topo.addLink(a, c, delay='10ms', bw=10) + + ndn = Minindn(topo=topo) + ndn.start() + + nfds = AppManager(ndn, ndn.net.hosts, Nfd) + info('Adding static routes to NFD\n') + grh = NdnRoutingHelper(ndn.net, Nfdc.PROTOCOL_UDP) + # For all host, pass ndn.net.hosts or a list, [ndn.net['a'], ..] or [ndn.net.hosts[0],.] + grh.addOrigin([ndn.net['c']], ["/example"]) + grh.calculateNPossibleRoutes() + + ''' + prefix "/abc" is advertise from node A, it should be reachable from all other nodes. + ''' + routesFromA = ndn.net['a'].cmd("nfdc route | grep -v '/localhost/nfd'") + if '/ndn/b-site/b' not in routesFromA or '/ndn/c-site/c' not in routesFromA: + info("Route addition failed\n") + + routesToPrefix = ndn.net['b'].cmd("nfdc fib | grep '/example'") + if '/example' not in routesToPrefix: + info("Missing route to advertised prefix, Route addition failed\n") + ndn.net.stop() + + info('Route addition to NFD completed succesfully\n') + + debug(ndn.net["a"].cmd("nfdc face list")) + debug(ndn.net["a"].cmd("nfdc fib list")) + debug(ndn.net["a"].cmd("nfdc strategy show /example")) + + # Start ping server + info("Starting pings...\n") + pingserver_log = open("{}/c/ndnpingserver.log".format(ndn.workDir), "w") + getPopen(ndn.net["c"], "ndnpingserver {}".format(PREFIX), stdout=pingserver_log,\ + stderr=pingserver_log) + + # start ping client + ping1 = getPopen(ndn.net["a"], "ndnping {} -c 5".format(PREFIX), stdout=PIPE, stderr=PIPE) + ping1.wait() + printOutput(ping1.stdout.read()) + + ndn.stop() + +def eth_static_run(): + Minindn.cleanUp() + Minindn.verifyDependencies() + + # Topology can be created/modified using Mininet topo object + topo = Topo() + info("Setup\n") + # add hosts + a = topo.addHost('a') + b = topo.addHost('b') + c = topo.addHost('c') + + # add links + topo.addLink(a, b, delay='10ms', bw=10) # bw = bandwidth + topo.addLink(b, c, delay='10ms', bw=10) + topo.addLink(a, c, delay='10ms', bw=10) + + ndn = Minindn(topo=topo) + ndn.start() + + nfds = AppManager(ndn, ndn.net.hosts, Nfd) + info('Adding static routes to NFD\n') + grh = NdnRoutingHelper(ndn.net, Nfdc.PROTOCOL_ETHER) + # For all host, pass ndn.net.hosts or a list, [ndn.net['a'], ..] or [ndn.net.hosts[0],.] + grh.addOrigin([ndn.net['c']], ["/example"]) + grh.calculateNPossibleRoutes() + + ''' + prefix "/abc" is advertise from node A, it should be reachable from all other nodes. + ''' + routesFromA = ndn.net['a'].cmd("nfdc route | grep -v '/localhost/nfd'") + if '/ndn/b-site/b' not in routesFromA or '/ndn/c-site/c' not in routesFromA: + info("Route addition failed\n") + + routesToPrefix = ndn.net['b'].cmd("nfdc fib | grep '/example'") + if '/example' not in routesToPrefix: + info("Missing route to advertised prefix, Route addition failed\n") + ndn.net.stop() + + info('Route addition to NFD completed succesfully\n') + + debug(ndn.net["a"].cmd("nfdc face list")) + debug(ndn.net["a"].cmd("nfdc fib list")) + debug(ndn.net["a"].cmd("nfdc strategy show /example")) + + # Start ping server + info("Starting pings...\n") + pingserver_log = open("{}/c/ndnpingserver.log".format(ndn.workDir), "w") + getPopen(ndn.net["c"], "ndnpingserver {}".format(PREFIX), stdout=pingserver_log,\ + stderr=pingserver_log) + + # start ping client + ping1 = getPopen(ndn.net["a"], "ndnping {} -c 5".format(PREFIX), stdout=PIPE, stderr=PIPE) + ping1.wait() + printOutput(ping1.stdout.read()) + + ndn.stop() + +if __name__ == '__main__': + setLogLevel("debug") + udp_run() + eth_run() + udp_nlsr_run() + eth_nlsr_run() + udp_static_run() + eth_static_run() \ No newline at end of file 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 @@ class Nlsr(Application): 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 @@ def __init__(self, node, logLevel='NONE', security=False, sync=SYNC_PSYNC, 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 @@ def start(self): 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 @@ def __editNeighborsSection(self): 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 __init__(self, netObject, faceType=nfdc.PROTOCOL_UDP, routingType="link-stat 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 @@ def addNodeRoutes(self, node): :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 @@ def createFaces(self, node, neighborIPs): 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 @@ def routeAdd(self, node, neighborFaces): 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 @@ def getNeighbor(node): # 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 @@ def registerRoute(node, namePrefix, remoteNode, protocol=PROTOCOL_UDP, origin=25 '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 @@ def registerRoute(node, namePrefix, remoteNode, protocol=PROTOCOL_UDP, origin=25 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 @@ def createFace(node, remoteNodeAddress, protocol='udp', isPermanent=False, allow 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 @@ def unsetStrategy(node, namePrefix): 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 @@ def getPopen(host, cmd, envDict=None, **params): 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):