CORD-248 Provide host management network connectivity to a VM

Change-Id: I6f632e118bd11f4f469aae0476d2d35fc2b7c3eb
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
index 9e8d313..1697f1a 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
@@ -28,6 +28,8 @@
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.xosclient.api.VtnService;
 import org.opencord.cordvtn.api.CordVtnConfig;
@@ -50,6 +52,7 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.onosproject.xosclient.api.VtnServiceApi.NetworkType.PRIVATE;
+import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.MANAGEMENT;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -62,6 +65,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected PacketService packetService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CordVtnNodeManager nodeManager;
+
     private final PacketProcessor packetProcessor = new InternalPacketProcessor();
     private final Map<Ip4Address, MacAddress> gateways = Maps.newConcurrentMap();
 
@@ -132,31 +138,26 @@
 
     /**
      * Emits ARP reply with fake MAC address for a given ARP request.
-     * It only handles requests for the registered service IPs, and the other
-     * requests can be handled by other ARP handlers like openstackSwitching or
-     * proxyArp, for example.
+     * It only handles requests for the registered gateway IPs and host IPs.
      *
      * @param context packet context
      * @param ethPacket ethernet packet
      */
-    private void processArpPacket(PacketContext context, Ethernet ethPacket) {
+    private void processArpRequest(PacketContext context, Ethernet ethPacket) {
         ARP arpPacket = (ARP) ethPacket.getPayload();
-        if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
-           return;
-        }
-
         Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
 
         MacAddress gatewayMac = gateways.get(targetIp);
-        MacAddress replyMac = gatewayMac != null ? gatewayMac : getMacFromHostService(targetIp);
+        MacAddress replyMac = gatewayMac != null ? gatewayMac :
+                getMacFromHostService(targetIp);
 
         if (replyMac.equals(MacAddress.NONE)) {
-            log.debug("Failed to find MAC for {}", targetIp.toString());
-            context.block();
+            log.trace("Failed to find MAC for {}", targetIp);
+            forwardManagementArpRequest(context, ethPacket);
             return;
         }
 
-        log.trace("Send ARP reply for {} with {}", targetIp.toString(), replyMac.toString());
+        log.trace("Send ARP reply for {} with {}", targetIp, replyMac);
         Ethernet ethReply = ARP.buildArpReply(
                 targetIp,
                 replyMac,
@@ -174,6 +175,62 @@
         context.block();
     }
 
+    private void processArpReply(PacketContext context, Ethernet ethPacket) {
+        ARP arpPacket = (ARP) ethPacket.getPayload();
+        Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
+
+        DeviceId deviceId = context.inPacket().receivedFrom().deviceId();
+        Host host = hostService.getHostsByIp(targetIp).stream()
+                .filter(h -> h.location().deviceId().equals(deviceId))
+                .findFirst()
+                .orElse(null);
+
+        if (host == null) {
+            // do nothing for the unknown ARP reply
+            log.trace("No host found for {} in {}", targetIp, deviceId);
+            context.block();
+            return;
+        }
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(host.location().port())
+                .build();
+
+        packetService.emit(new DefaultOutboundPacket(
+                deviceId,
+                treatment,
+                ByteBuffer.wrap(ethPacket.serialize())));
+
+        context.block();
+    }
+
+    private void forwardManagementArpRequest(PacketContext context, Ethernet ethPacket) {
+        DeviceId deviceId = context.inPacket().receivedFrom().deviceId();
+        PortNumber hostMgmtPort = nodeManager.hostManagementPort(deviceId);
+        Host host = hostService.getConnectedHosts(context.inPacket().receivedFrom())
+                .stream()
+                .findFirst().orElse(null);
+
+        if (host == null ||
+                !Instance.of(host).serviceType().equals(MANAGEMENT) ||
+                hostMgmtPort == null) {
+            context.block();
+            return;
+        }
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(hostMgmtPort)
+                .build();
+
+        packetService.emit(new DefaultOutboundPacket(
+                context.inPacket().receivedFrom().deviceId(),
+                treatment,
+                ByteBuffer.wrap(ethPacket.serialize())));
+
+        log.trace("Forward ARP request to management network");
+        context.block();
+    }
+
     /**
      * Emits gratuitous ARP when a gateway mac address has been changed.
      *
@@ -183,7 +240,7 @@
     private void sendGratuitousArp(IpAddress gatewayIp, Set<Instance> instances) {
         MacAddress gatewayMac = gateways.get(gatewayIp.getIp4Address());
         if (gatewayMac == null) {
-            log.debug("Gateway {} is not registered to ARP proxy", gatewayIp.toString());
+            log.debug("Gateway {} is not registered to ARP proxy", gatewayIp);
             return;
         }
 
@@ -246,7 +303,7 @@
                 .orElse(null);
 
         if (host != null) {
-            log.trace("Found MAC from host service for {}", targetIp.toString());
+            log.trace("Found MAC from host service for {}", targetIp);
             return host.mac();
         } else {
             return MacAddress.NONE;
@@ -264,7 +321,18 @@
             if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
                 return;
             }
-            processArpPacket(context, ethPacket);
+
+            ARP arpPacket = (ARP) ethPacket.getPayload();
+            switch (arpPacket.getOpCode()) {
+                case ARP.OP_REQUEST:
+                    processArpRequest(context, ethPacket);
+                    break;
+                case ARP.OP_REPLY:
+                    processArpReply(context, ethPacket);
+                    break;
+                default:
+                    break;
+            }
         }
     }