Revert "CORD-248 Provide host management network connectivity to a VM"

This reverts commit acbc8effe4f4687dd1c2a79c0ae1fa3edab747e5.

Change-Id: Ief4fc512d252d0143e5106e79e7f346d705a2e29
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
index 1697f1a..9e8d313 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
@@ -28,8 +28,6 @@
 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;
@@ -52,7 +50,6 @@
 
 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;
 
 /**
@@ -65,9 +62,6 @@
     @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();
 
@@ -138,26 +132,31 @@
 
     /**
      * Emits ARP reply with fake MAC address for a given ARP request.
-     * It only handles requests for the registered gateway IPs and host IPs.
+     * 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.
      *
      * @param context packet context
      * @param ethPacket ethernet packet
      */
-    private void processArpRequest(PacketContext context, Ethernet ethPacket) {
+    private void processArpPacket(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.trace("Failed to find MAC for {}", targetIp);
-            forwardManagementArpRequest(context, ethPacket);
+            log.debug("Failed to find MAC for {}", targetIp.toString());
+            context.block();
             return;
         }
 
-        log.trace("Send ARP reply for {} with {}", targetIp, replyMac);
+        log.trace("Send ARP reply for {} with {}", targetIp.toString(), replyMac.toString());
         Ethernet ethReply = ARP.buildArpReply(
                 targetIp,
                 replyMac,
@@ -175,62 +174,6 @@
         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.
      *
@@ -240,7 +183,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);
+            log.debug("Gateway {} is not registered to ARP proxy", gatewayIp.toString());
             return;
         }
 
@@ -303,7 +246,7 @@
                 .orElse(null);
 
         if (host != null) {
-            log.trace("Found MAC from host service for {}", targetIp);
+            log.trace("Found MAC from host service for {}", targetIp.toString());
             return host.mac();
         } else {
             return MacAddress.NONE;
@@ -321,18 +264,7 @@
             if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
                 return;
             }
-
-            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;
-            }
+            processArpPacket(context, ethPacket);
         }
     }
 
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
index c45fef3..df3d336 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
@@ -28,7 +28,6 @@
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.cluster.LeadershipService;
 import org.onosproject.cluster.NodeId;
-import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.behaviour.BridgeDescription;
 import org.onosproject.net.behaviour.DefaultBridgeDescription;
 import org.onosproject.net.behaviour.InterfaceConfig;
@@ -74,7 +73,6 @@
 
 import java.util.List;
 import java.util.Objects;
-import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.stream.Collectors;
@@ -316,9 +314,12 @@
             log.warn("Failed to get node for {}", deviceId);
             return null;
         }
+        Port port = deviceService.getPorts(deviceId).stream()
+                .filter(p -> portName(p).contains(node.dataIface()) &&
+                        p.isEnabled())
+                .findFirst().orElse(null);
 
-        Optional<PortNumber> port = getPortNumber(deviceId, node.dataIface());
-        return port.isPresent() ? port.get() : null;
+        return port == null ? null : port.number();
     }
 
     /**
@@ -343,39 +344,11 @@
      * @return port number
      */
     public PortNumber tunnelPort(DeviceId deviceId) {
-        Optional<PortNumber> port = getPortNumber(deviceId, DEFAULT_TUNNEL);
-        return port.isPresent() ? port.get() : null;
-    }
+        Port port = deviceService.getPorts(deviceId).stream()
+                .filter(p -> portName(p).contains(DEFAULT_TUNNEL))
+                .findFirst().orElse(null);
 
-    /**
-     * Returns host management interface port number if exists.
-     *
-     * @param deviceId integration bridge device id
-     * @return port number; null if it does not exist
-     */
-    public PortNumber hostManagementPort(DeviceId deviceId) {
-        CordVtnNode node = nodeByBridgeId(deviceId);
-        if (node == null) {
-            log.warn("Failed to get node for {}", deviceId);
-            return null;
-        }
-
-        if (node.hostMgmtIface().isPresent()) {
-            Optional<PortNumber> port = getPortNumber(deviceId, node.hostMgmtIface().get());
-            return port.isPresent() ? port.get() : null;
-        } else {
-            return null;
-        }
-    }
-
-    private Optional<PortNumber> getPortNumber(DeviceId deviceId, String portName) {
-        PortNumber port = deviceService.getPorts(deviceId).stream()
-                .filter(p -> p.annotations().value(AnnotationKeys.PORT_NAME).equals(portName) &&
-                        p.isEnabled())
-                .map(Port::number)
-                .findAny()
-                .orElse(null);
-        return Optional.ofNullable(port);
+        return port == null ? null : port.number();
     }
 
     /**
@@ -425,7 +398,8 @@
             ovsdbClient.disconnect();
         }
 
-        pipeline.initPipeline(node);
+        pipeline.initPipeline(node, dataPort(node.integrationBridgeId()),
+                              tunnelPort(node.integrationBridgeId()));
 
         // adds existing instances to the host list
         deviceService.getPorts(node.integrationBridgeId()).stream()
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnPipeline.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnPipeline.java
index 4e01d2b..2aa9f1f 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnPipeline.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnPipeline.java
@@ -27,8 +27,7 @@
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.TpPort;
 import org.onlab.packet.VlanId;
-import org.onosproject.net.AnnotationKeys;
-import org.onosproject.net.Port;
+import org.onlab.util.ItemNotFoundException;
 import org.opencord.cordvtn.api.Constants;
 import org.opencord.cordvtn.api.CordVtnNode;
 import org.onosproject.core.ApplicationId;
@@ -51,11 +50,8 @@
 import org.onosproject.net.flow.instructions.ExtensionTreatment;
 import org.slf4j.Logger;
 
-import java.util.Optional;
-
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
-import static org.opencord.cordvtn.api.Constants.DEFAULT_TUNNEL;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -95,8 +91,6 @@
     public static final int VXLAN_UDP_PORT = 4789;
     public static final VlanId VLAN_WAN = VlanId.vlanId((short) 500);
 
-    public static final String PROPERTY_TUNNEL_DST = "tunnelDst";
-
     private ApplicationId appId;
 
     @Activate
@@ -121,84 +115,19 @@
      * Installs table miss rule to a give device.
      *
      * @param node cordvtn node
+     * @param dataPort data plane port number
+     * @param tunnelPort tunnel port number
      */
-    public void initPipeline(CordVtnNode node) {
+    public void initPipeline(CordVtnNode node, PortNumber dataPort, PortNumber tunnelPort) {
         checkNotNull(node);
 
-        Optional<PortNumber> dataPort = getPortNumber(node.integrationBridgeId(), node.dataIface());
-        Optional<PortNumber> tunnelPort = getPortNumber(node.integrationBridgeId(), DEFAULT_TUNNEL);
-        if (!dataPort.isPresent() || !tunnelPort.isPresent()) {
-            log.warn("Node is not in COMPLETE state");
-            return;
-        }
-
-        Optional<PortNumber> hostMgmtPort = Optional.empty();
-        if (node.hostMgmtIface().isPresent()) {
-            hostMgmtPort = getPortNumber(node.integrationBridgeId(), node.hostMgmtIface().get());
-        }
-
-        processTableZero(node.integrationBridgeId(),
-                         dataPort.get(),
-                         node.dataIp().ip(),
-                         node.localMgmtIp().ip());
-
-        processInPortTable(node.integrationBridgeId(),
-                           tunnelPort.get(),
-                           dataPort.get(),
-                           hostMgmtPort);
-
-        processAccessTypeTable(node.integrationBridgeId(), dataPort.get());
-        processVlanTable(node.integrationBridgeId(), dataPort.get());
+        processTableZero(node.integrationBridgeId(), dataPort, node.dataIp().ip());
+        processInPortTable(node.integrationBridgeId(), tunnelPort, dataPort);
+        processAccessTypeTable(node.integrationBridgeId(), dataPort);
+        processVlanTable(node.integrationBridgeId(), dataPort);
     }
 
-    private void processTableZero(DeviceId deviceId, PortNumber dataPort, IpAddress dataIp,
-                                  IpAddress localMgmtIp) {
-        vxlanShuttleRule(deviceId, dataPort, dataIp);
-        localManagementBaseRule(deviceId, localMgmtIp.getIp4Address());
-
-        // take all vlan tagged packet to the VLAN table
-        TrafficSelector selector = DefaultTrafficSelector.builder()
-                .matchVlanId(VlanId.ANY)
-                .build();
-
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .transition(TABLE_VLAN)
-                .build();
-
-        FlowRule flowRule = DefaultFlowRule.builder()
-                .fromApp(appId)
-                .withSelector(selector)
-                .withTreatment(treatment)
-                .withPriority(PRIORITY_MANAGEMENT)
-                .forDevice(deviceId)
-                .forTable(TABLE_ZERO)
-                .makePermanent()
-                .build();
-
-        processFlowRule(true, flowRule);
-
-        // take all other packets to the next table
-        selector = DefaultTrafficSelector.builder()
-                .build();
-
-        treatment = DefaultTrafficTreatment.builder()
-                .transition(TABLE_IN_PORT)
-                .build();
-
-        flowRule = DefaultFlowRule.builder()
-                .fromApp(appId)
-                .withSelector(selector)
-                .withTreatment(treatment)
-                .withPriority(PRIORITY_ZERO)
-                .forDevice(deviceId)
-                .forTable(TABLE_ZERO)
-                .makePermanent()
-                .build();
-
-        processFlowRule(true, flowRule);
-    }
-
-    private void vxlanShuttleRule(DeviceId deviceId, PortNumber dataPort, IpAddress dataIp) {
+    private void processTableZero(DeviceId deviceId, PortNumber dataPort, IpAddress dataIp) {
         // take vxlan packet out onto the physical port
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchInPort(PortNumber.LOCAL)
@@ -289,98 +218,52 @@
                 .build();
 
         processFlowRule(true, flowRule);
-    }
 
-    private void localManagementBaseRule(DeviceId deviceId, Ip4Address localMgmtIp) {
-        TrafficSelector selector = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_ARP)
-                .matchArpTpa(localMgmtIp)
-                .build();
-
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .setOutput(PortNumber.LOCAL)
-                .build();
-
-        FlowRule flowRule = DefaultFlowRule.builder()
-                .fromApp(appId)
-                .withSelector(selector)
-                .withTreatment(treatment)
-                .withPriority(CordVtnPipeline.PRIORITY_MANAGEMENT)
-                .forDevice(deviceId)
-                .forTable(CordVtnPipeline.TABLE_ZERO)
-                .makePermanent()
-                .build();
-
-        processFlowRule(true, flowRule);
-
+        // take all else to the next table
         selector = DefaultTrafficSelector.builder()
-                .matchInPort(PortNumber.LOCAL)
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPSrc(localMgmtIp.toIpPrefix())
                 .build();
 
         treatment = DefaultTrafficTreatment.builder()
-                .transition(CordVtnPipeline.TABLE_DST_IP)
+                .transition(TABLE_IN_PORT)
                 .build();
 
         flowRule = DefaultFlowRule.builder()
                 .fromApp(appId)
                 .withSelector(selector)
                 .withTreatment(treatment)
-                .withPriority(CordVtnPipeline.PRIORITY_MANAGEMENT)
+                .withPriority(PRIORITY_ZERO)
                 .forDevice(deviceId)
-                .forTable(CordVtnPipeline.TABLE_ZERO)
+                .forTable(TABLE_ZERO)
                 .makePermanent()
                 .build();
 
         processFlowRule(true, flowRule);
 
+        // take all vlan tagged packet to the VLAN table
         selector = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPDst(localMgmtIp.toIpPrefix())
+                .matchVlanId(VlanId.ANY)
                 .build();
 
         treatment = DefaultTrafficTreatment.builder()
-                .setOutput(PortNumber.LOCAL)
+                .transition(TABLE_VLAN)
                 .build();
 
         flowRule = DefaultFlowRule.builder()
                 .fromApp(appId)
                 .withSelector(selector)
                 .withTreatment(treatment)
-                .withPriority(CordVtnPipeline.PRIORITY_MANAGEMENT)
+                .withPriority(PRIORITY_MANAGEMENT)
                 .forDevice(deviceId)
-                .forTable(CordVtnPipeline.TABLE_ZERO)
-                .makePermanent()
-                .build();
-
-        processFlowRule(true, flowRule);
-
-        selector = DefaultTrafficSelector.builder()
-                .matchInPort(PortNumber.LOCAL)
-                .matchEthType(Ethernet.TYPE_ARP)
-                .matchArpSpa(localMgmtIp)
-                .build();
-
-        treatment = DefaultTrafficTreatment.builder()
-                .setOutput(PortNumber.CONTROLLER)
-                .build();
-
-        flowRule = DefaultFlowRule.builder()
-                .fromApp(appId)
-                .withSelector(selector)
-                .withTreatment(treatment)
-                .withPriority(CordVtnPipeline.PRIORITY_MANAGEMENT)
-                .forDevice(deviceId)
-                .forTable(CordVtnPipeline.TABLE_ZERO)
+                .forTable(TABLE_ZERO)
                 .makePermanent()
                 .build();
 
         processFlowRule(true, flowRule);
     }
 
-    private void processInPortTable(DeviceId deviceId, PortNumber tunnelPort, PortNumber dataPort,
-                                    Optional<PortNumber> hostMgmtPort) {
+    private void processInPortTable(DeviceId deviceId, PortNumber tunnelPort, PortNumber dataPort) {
+        checkNotNull(tunnelPort);
+
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchInPort(tunnelPort)
                 .build();
@@ -420,28 +303,6 @@
                 .build();
 
         processFlowRule(true, flowRule);
-
-        if (hostMgmtPort.isPresent()) {
-            selector = DefaultTrafficSelector.builder()
-                    .matchInPort(hostMgmtPort.get())
-                    .build();
-
-            treatment = DefaultTrafficTreatment.builder()
-                    .transition(TABLE_DST_IP)
-                    .build();
-
-            flowRule = DefaultFlowRule.builder()
-                    .fromApp(appId)
-                    .withSelector(selector)
-                    .withTreatment(treatment)
-                    .withPriority(PRIORITY_DEFAULT)
-                    .forDevice(deviceId)
-                    .forTable(TABLE_IN_PORT)
-                    .makePermanent()
-                    .build();
-
-            processFlowRule(true, flowRule);
-        }
     }
 
     private void processAccessTypeTable(DeviceId deviceId, PortNumber dataPort) {
@@ -523,30 +384,23 @@
     }
 
     public ExtensionTreatment tunnelDstTreatment(DeviceId deviceId, Ip4Address remoteIp) {
-        Device device = deviceService.getDevice(deviceId);
-        if (device != null && !device.is(ExtensionTreatmentResolver.class)) {
-            log.error("The extension treatment is not supported");
-            return null;
-        }
-
-        ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
-        ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
         try {
-            treatment.setPropertyValue(PROPERTY_TUNNEL_DST, remoteIp);
+            Device device = deviceService.getDevice(deviceId);
+            if (!device.is(ExtensionTreatmentResolver.class)) {
+                log.error("The extension treatment is not supported");
+                return null;
+
+            }
+
+            ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
+            ExtensionTreatment treatment =
+                    resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
+            treatment.setPropertyValue("tunnelDst", remoteIp);
             return treatment;
-        } catch (ExtensionPropertyException e) {
-            log.warn("Failed to get tunnelDst extension treatment for {}", deviceId);
+        } catch (ItemNotFoundException | UnsupportedOperationException |
+                ExtensionPropertyException e) {
+            log.error("Failed to get extension instruction {}", deviceId);
             return null;
         }
     }
-
-    private Optional<PortNumber> getPortNumber(DeviceId deviceId, String portName) {
-        PortNumber port = deviceService.getPorts(deviceId).stream()
-                .filter(p -> p.annotations().value(AnnotationKeys.PORT_NAME).equals(portName) &&
-                        p.isEnabled())
-                .map(Port::number)
-                .findAny()
-                .orElse(null);
-        return Optional.ofNullable(port);
-    }
 }
diff --git a/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java b/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java
index d43a8c6..6269e98 100644
--- a/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java
+++ b/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java
@@ -70,7 +70,7 @@
 import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
 import static org.onosproject.net.AnnotationKeys.PORT_NAME;
-import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.MANAGEMENT;
+import static org.onosproject.xosclient.api.VtnServiceApi.NetworkType.MANAGEMENT;
 import static org.opencord.cordvtn.api.Constants.*;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -247,7 +247,7 @@
                 .domainServer(DEFAULT_DNS)
                 .assignmentStatus(Option_RangeNotEnforced);
 
-        if (service.serviceType() != MANAGEMENT) {
+        if (service.networkType() != MANAGEMENT) {
             ipBuilder = ipBuilder.routerAddress(service.serviceIp().getIp4Address());
         }
 
diff --git a/src/main/java/org/opencord/cordvtn/impl/handler/ManagementInstanceHandler.java b/src/main/java/org/opencord/cordvtn/impl/handler/ManagementInstanceHandler.java
index 7a77b98..fe30781 100644
--- a/src/main/java/org/opencord/cordvtn/impl/handler/ManagementInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/ManagementInstanceHandler.java
@@ -32,7 +32,6 @@
 import org.opencord.cordvtn.impl.AbstractInstanceHandler;
 import org.opencord.cordvtn.api.Instance;
 import org.opencord.cordvtn.api.InstanceHandler;
-import org.opencord.cordvtn.impl.CordVtnNodeManager;
 import org.opencord.cordvtn.impl.CordVtnPipeline;
 
 import java.util.Optional;
@@ -48,9 +47,6 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CordVtnPipeline pipeline;
 
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CordVtnNodeManager nodeManager;
-
     @Activate
     protected void activate() {
         serviceType = Optional.of(MANAGEMENT);
@@ -64,85 +60,50 @@
 
     @Override
     public void instanceDetected(Instance instance) {
+        log.info("Instance is detected {}", instance);
+
         VtnService service = getVtnService(instance.serviceId());
         if (service == null) {
             log.warn("Failed to get VtnService for {}", instance);
             return;
         }
-
-        switch (service.networkType()) {
-            case MANAGEMENT_LOCAL:
-                log.info("LOCAL management instance detected {}", instance);
-                localManagementRules(instance, true);
-                break;
-            case MANAGEMENT_HOSTS:
-                log.info("HOSTS management instance detected {}", instance);
-                hostsManagementRules(instance, true);
-                break;
-            default:
-                break;
-        }
+        localMgmtNetworkRules(instance, service, true);
     }
 
     @Override
     public void instanceRemoved(Instance instance) {
+        log.info("Instance is removed {}", instance);
+
         VtnService service = getVtnService(instance.serviceId());
         if (service == null) {
             log.warn("Failed to get VtnService for {}", instance);
             return;
         }
 
-        switch (service.networkType()) {
-            case MANAGEMENT_LOCAL:
-                log.info("LOCAL management instance removed {}", instance);
-                localManagementRules(instance, false);
-                break;
-            case MANAGEMENT_HOSTS:
-                log.info("HOSTS management instance removed {}", instance);
-                hostsManagementRules(instance, false);
-                break;
-            default:
-                break;
+        // TODO check if any stale management network rules are
+        localMgmtNetworkRules(instance, service, false);
+    }
+
+    private void localMgmtNetworkRules(Instance instance, VtnService service, boolean install) {
+        managementPerInstanceRule(instance, install);
+        if (install) {
+            managementBaseRule(instance, service, true);
+        } else if (!hostService.getConnectedHosts(instance.deviceId()).stream()
+                .filter(host -> Instance.of(host).serviceId().equals(service.id()))
+                .findAny()
+                .isPresent()) {
+            managementBaseRule(instance, service, false);
         }
     }
 
-    private void localManagementRules(Instance instance, boolean install) {
+    private void managementBaseRule(Instance instance, VtnService service, boolean install) {
         TrafficSelector selector = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPDst(instance.ipAddress().toIpPrefix())
+                .matchEthType(Ethernet.TYPE_ARP)
+                .matchArpTpa(service.serviceIp().getIp4Address())
                 .build();
 
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .setEthDst(instance.mac())
-                .setOutput(instance.portNumber())
-                .build();
-
-        FlowRule flowRule = DefaultFlowRule.builder()
-                .fromApp(appId)
-                .withSelector(selector)
-                .withTreatment(treatment)
-                .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
-                .forDevice(instance.deviceId())
-                .forTable(CordVtnPipeline.TABLE_DST_IP)
-                .makePermanent()
-                .build();
-
-        pipeline.processFlowRule(install, flowRule);
-    }
-
-    private void hostsManagementRules(Instance instance, boolean install) {
-        PortNumber hostMgmtPort = nodeManager.hostManagementPort(instance.deviceId());
-        if (hostMgmtPort == null) {
-            log.warn("Can not find host management port in {}", instance.deviceId());
-            return;
-        }
-
-        TrafficSelector selector = DefaultTrafficSelector.builder()
-                .matchInPort(instance.portNumber())
-                .build();
-
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .setOutput(hostMgmtPort)
+                .setOutput(PortNumber.LOCAL)
                 .build();
 
         FlowRule flowRule = DefaultFlowRule.builder()
@@ -151,7 +112,74 @@
                 .withTreatment(treatment)
                 .withPriority(CordVtnPipeline.PRIORITY_MANAGEMENT)
                 .forDevice(instance.deviceId())
-                .forTable(CordVtnPipeline.TABLE_IN_PORT)
+                .forTable(CordVtnPipeline.TABLE_ZERO)
+                .makePermanent()
+                .build();
+
+        pipeline.processFlowRule(install, flowRule);
+
+        selector = DefaultTrafficSelector.builder()
+                .matchInPort(PortNumber.LOCAL)
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(service.subnet())
+                .build();
+
+        treatment = DefaultTrafficTreatment.builder()
+                .transition(CordVtnPipeline.TABLE_DST_IP)
+                .build();
+
+        flowRule = DefaultFlowRule.builder()
+                .fromApp(appId)
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .withPriority(CordVtnPipeline.PRIORITY_MANAGEMENT)
+                .forDevice(instance.deviceId())
+                .forTable(CordVtnPipeline.TABLE_ZERO)
+                .makePermanent()
+                .build();
+
+        pipeline.processFlowRule(install, flowRule);
+
+        selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(service.serviceIp().toIpPrefix())
+                .build();
+
+        treatment = DefaultTrafficTreatment.builder()
+                .setOutput(PortNumber.LOCAL)
+                .build();
+
+        flowRule = DefaultFlowRule.builder()
+                .fromApp(appId)
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .withPriority(CordVtnPipeline.PRIORITY_MANAGEMENT)
+                .forDevice(instance.deviceId())
+                .forTable(CordVtnPipeline.TABLE_ZERO)
+                .makePermanent()
+                .build();
+
+        pipeline.processFlowRule(install, flowRule);
+    }
+
+    private void managementPerInstanceRule(Instance instance, boolean install) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchInPort(PortNumber.LOCAL)
+                .matchEthType(Ethernet.TYPE_ARP)
+                .matchArpTpa(instance.ipAddress().getIp4Address())
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(instance.portNumber())
+                .build();
+
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .fromApp(appId)
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .withPriority(CordVtnPipeline.PRIORITY_MANAGEMENT)
+                .forDevice(instance.deviceId())
+                .forTable(CordVtnPipeline.TABLE_ZERO)
                 .makePermanent()
                 .build();