Add support for unicast ethernet faces to utility and helper methods.

Refs #5321

Change-Id: I2e28d092853fc21f59109cfbc23b946d30626a8c
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