Support NodePort communication  model at k8s passthrough mode

Change-Id: I2179ebc9a4812493619c56aa270d8fc4821efbb2
diff --git a/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/Constants.java b/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/Constants.java
index d017c50..2c60c79 100644
--- a/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/Constants.java
+++ b/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/Constants.java
@@ -39,6 +39,8 @@
     public static final String DEFAULT_SERVICE_IP_NAT_MODE_STR = NAT_STATELESS;
     public static final String CONTROLLER_MAC_STR = "fe:00:00:00:00:10";
     public static final String SERVICE_FAKE_MAC_STR = "fe:00:00:00:00:20";
+    public static final String NODE_FAKE_IP_STR = "172.172.172.172";
+    public static final String NODE_FAKE_MAC_STR = "fe:00:00:00:00:80";
 
     public static final MacAddress DEFAULT_GATEWAY_MAC =
                         MacAddress.valueOf(DEFAULT_GATEWAY_MAC_STR);
@@ -102,8 +104,6 @@
     public static final int PRIORITY_FORCED_ACL_RULE = 50000;
     public static final int PRIORITY_ICMP_PROBE_RULE = 50000;
     public static final int PRIORITY_NODE_PORT_RULE = 42000;
-    public static final int PRIORITY_NODE_PORT_REMOTE_RULE = 41500;
-    public static final int PRIORITY_NODE_PORT_INTER_RULE = 40000;
     public static final int PRIORITY_DEFAULT_RULE = 0;
 
     // flow table index
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sNodePortHandler.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sNodePortHandler.java
index 0a549cf..c87dfe7 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sNodePortHandler.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sNodePortHandler.java
@@ -67,17 +67,15 @@
 import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
 import static org.onosproject.k8snetworking.api.Constants.NODE_IP_PREFIX;
 import static org.onosproject.k8snetworking.api.Constants.PRIORITY_CIDR_RULE;
-import static org.onosproject.k8snetworking.api.Constants.PRIORITY_NODE_PORT_INTER_RULE;
-import static org.onosproject.k8snetworking.api.Constants.PRIORITY_NODE_PORT_REMOTE_RULE;
+import static org.onosproject.k8snetworking.api.Constants.PRIORITY_INTER_ROUTING_RULE;
 import static org.onosproject.k8snetworking.api.Constants.PRIORITY_NODE_PORT_RULE;
 import static org.onosproject.k8snetworking.api.Constants.ROUTING_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.SRC;
+import static org.onosproject.k8snetworking.api.Constants.TUN_ENTRY_TABLE;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getBclassIpPrefixFromCidr;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getPropertyValue;
-import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.tunnelPortNumByNetId;
-import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.unshiftIpDomain;
-import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildExtension;
 import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildLoadExtension;
+import static org.onosproject.k8snode.api.K8sApiConfig.Mode.PASSTHROUGH;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -94,8 +92,6 @@
     private static final int HOST_CIDR = 32;
     private static final String SERVICE_CIDR = "serviceCidr";
     private static final String B_CLASS_SUFFIX = "0.0/16";
-    private static final String C_CLASS_SUFFIX = ".0/24";
-
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected CoreService coreService;
@@ -167,120 +163,68 @@
         String clusterIp = service.getSpec().getClusterIP();
         for (ServicePort servicePort : service.getSpec().getPorts()) {
             setNodeToServiceRules(k8sNode, clusterIp, servicePort, install);
-            setServiceToNodeLocalRules(k8sNode, clusterIp, servicePort, install);
-            setServiceToNodeRemoteRules(k8sNode, clusterIp, servicePort, install);
-            setExtToIngrRules(k8sNode, servicePort, install);
+            setServiceToNodeRules(k8sNode, clusterIp, servicePort, install);
         }
+    }
 
+    private void setIntgToExtRules(K8sNode k8sNode, String serviceCidr,
+                                   boolean install) {
+        // for local traffic, we add default flow rules for steering traffic from
+        // integration bridge to external bridge through patch port
+        // for remote traffic, we add default flow rules for steering traffic from
+        // integration bridge to tun bridge through patch port
         k8sNodeService.completeNodes().forEach(n -> {
             String podCidr = k8sNetworkService.network(n.hostname()).cidr();
             String fullCidr = NODE_IP_PREFIX + "." +
                     podCidr.split("\\.")[2] + "." + B_CLASS_SUFFIX;
 
-            if (n.equals(k8sNode)) {
-                setIntgToExtLocalRules(k8sNode, getServiceCidr(), fullCidr, install);
+            TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+                    .matchEthType(Ethernet.TYPE_IPV4)
+                    .matchIPSrc(IpPrefix.valueOf(serviceCidr))
+                    .matchIPDst(IpPrefix.valueOf(fullCidr));
+
+            PortNumber output;
+            if (n.hostname().equals(k8sNode.hostname())) {
+                output = k8sNode.intgToExtPatchPortNum();
             } else {
-                setIntgToExtRemoteRules(k8sNode, n, getServiceCidr(), fullCidr, install);
+                output = k8sNode.intgToTunPortNum();
             }
+
+            TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
+                    .setOutput(output);
+
+            k8sFlowRuleService.setRule(
+                    appId,
+                    k8sNode.intgBridge(),
+                    sBuilder.build(),
+                    tBuilder.build(),
+                    PRIORITY_CIDR_RULE,
+                    ROUTING_TABLE,
+                    install);
         });
-
-        setDefaultExtEgrRule(k8sNode, install);
     }
 
-    private void setDefaultExtEgrRule(K8sNode k8sNode, boolean install) {
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
-                .matchInPort(PortNumber.LOCAL)
-                .matchEthType(Ethernet.TYPE_IPV4);
+    private void setTunToIntgRules(K8sNode k8sNode, boolean install) {
+        String podCidr = k8sNetworkService.network(k8sNode.hostname()).cidr();
+        String fullCidr = NODE_IP_PREFIX + "." +
+                podCidr.split("\\.")[2] + "." + B_CLASS_SUFFIX;
 
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
-                .setOutput(k8sNode.extBridgePortNum());
-
-        k8sFlowRuleService.setRule(
-                appId,
-                k8sNode.extBridge(),
-                sBuilder.build(),
-                tBuilder.build(),
-                PRIORITY_NODE_PORT_INTER_RULE,
-                EXT_ENTRY_TABLE,
-                install);
-    }
-
-    private void setIntgToExtLocalRules(K8sNode k8sNode, String serviceCidr,
-                                        String shiftedCidr, boolean install) {
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+        TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPSrc(IpPrefix.valueOf(serviceCidr))
-                .matchIPDst(IpPrefix.valueOf(shiftedCidr));
+                .matchIPDst(IpPrefix.valueOf(fullCidr))
+                .build();
 
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
-                .setOutput(k8sNode.intgToExtPatchPortNum());
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(k8sNode.tunToIntgPortNum())
+                .build();
 
         k8sFlowRuleService.setRule(
                 appId,
-                k8sNode.intgBridge(),
-                sBuilder.build(),
-                tBuilder.build(),
-                PRIORITY_CIDR_RULE,
-                ROUTING_TABLE,
-                install);
-    }
-
-    private void setIntgToExtRemoteRules(K8sNode k8sNodeLocal, K8sNode k8sNodeRemote,
-                                         String serviceCidr, String shiftedCidr,
-                                         boolean install) {
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPSrc(IpPrefix.valueOf(serviceCidr))
-                .matchIPDst(IpPrefix.valueOf(shiftedCidr));
-
-        ExtensionTreatment remote = buildExtension(deviceService,
-                k8sNodeLocal.intgBridge(), k8sNodeRemote.dataIp().getIp4Address());
-
-        PortNumber portNumber = tunnelPortNumByNetId(
-                    k8sNodeLocal.hostname(), k8sNetworkService, k8sNodeLocal);
-
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
-                .extension(remote, k8sNodeLocal.intgBridge())
-                .setOutput(portNumber);
-
-        k8sFlowRuleService.setRule(
-                appId,
-                k8sNodeLocal.intgBridge(),
-                sBuilder.build(),
-                tBuilder.build(),
-                PRIORITY_CIDR_RULE,
-                ROUTING_TABLE,
-                install);
-    }
-
-    private void setExtToIngrRules(K8sNode k8sNode, ServicePort servicePort,
-                                    boolean install) {
-        String protocol = servicePort.getProtocol();
-        int nodePort = servicePort.getNodePort();
-        DeviceId deviceId = k8sNode.extBridge();
-
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPDst(IpPrefix.valueOf(k8sNode.extBridgeIp(), HOST_CIDR));
-
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
-                .setOutput(PortNumber.LOCAL);
-
-        if (TCP.equals(protocol)) {
-            sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP)
-                    .matchTcpSrc(TpPort.tpPort(nodePort));
-        } else if (UDP.equals(protocol)) {
-            sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP)
-                    .matchUdpSrc(TpPort.tpPort(nodePort));
-        }
-
-        k8sFlowRuleService.setRule(
-                appId,
-                deviceId,
-                sBuilder.build(),
-                tBuilder.build(),
-                PRIORITY_NODE_PORT_RULE,
-                EXT_ENTRY_TABLE,
+                k8sNode.tunBridge(),
+                selector,
+                treatment,
+                PRIORITY_INTER_ROUTING_RULE,
+                TUN_ENTRY_TABLE,
                 install);
     }
 
@@ -295,7 +239,7 @@
 
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPDst(IpPrefix.valueOf(k8sNode.extBridgeIp(), HOST_CIDR));
+                .matchIPDst(IpPrefix.valueOf(k8sNode.nodeIp(), HOST_CIDR));
 
         TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
                 .setIpDst(IpAddress.valueOf(clusterIp));
@@ -328,86 +272,30 @@
                 install);
     }
 
-    private void setServiceToNodeLocalRules(K8sNode k8sNode,
-                                            String clusterIp,
-                                            ServicePort servicePort,
-                                            boolean install) {
+    private void setServiceToNodeRules(K8sNode k8sNode,
+                                       String clusterIp,
+                                       ServicePort servicePort,
+                                       boolean install) {
         String protocol = servicePort.getProtocol();
         int nodePort = servicePort.getNodePort();
         int svcPort = servicePort.getPort();
         DeviceId deviceId = k8sNode.extBridge();
 
-        String extBridgeIp = k8sNode.extBridgeIp().toString();
-        String extBridgePrefix = getBclassIpPrefixFromCidr(extBridgeIp);
+        String nodeIp = k8sNode.nodeIp().toString();
+        String nodeIpPrefix = getBclassIpPrefixFromCidr(nodeIp);
 
-        String podCidr = k8sNetworkService.network(k8sNode.hostname()).cidr();
-        String nodePrefix = NODE_IP_PREFIX + "." + podCidr.split("\\.")[2];
-
-        if (extBridgePrefix == null) {
+        if (nodeIpPrefix == null) {
             return;
         }
 
-        String shiftedIp = unshiftIpDomain(extBridgeIp, extBridgePrefix, nodePrefix);
-
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchInPort(k8sNode.extToIntgPatchPortNum())
-                .matchIPSrc(IpPrefix.valueOf(IpAddress.valueOf(clusterIp), HOST_CIDR))
-                .matchIPDst(IpPrefix.valueOf(IpAddress.valueOf(shiftedIp), HOST_CIDR));
-
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
-                .setIpSrc(k8sNode.extBridgeIp())
-                .setEthSrc(k8sNode.extBridgeMac());
-
-        if (TCP.equals(protocol)) {
-            sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP)
-                    .matchTcpSrc(TpPort.tpPort(svcPort));
-            tBuilder.setTcpSrc(TpPort.tpPort(nodePort));
-        } else if (UDP.equals(protocol)) {
-            sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP)
-                    .matchUdpSrc(TpPort.tpPort(svcPort));
-            tBuilder.setUdpSrc(TpPort.tpPort(nodePort));
-        }
-
-        String gatewayIp = k8sNode.extGatewayIp().toString();
-        String gatewayPrefix = getBclassIpPrefixFromCidr(gatewayIp);
-
-        if (gatewayPrefix == null) {
-            return;
-        }
-
-        ExtensionTreatment loadTreatment = buildLoadExtension(
-                deviceService.getDevice(deviceId), B_CLASS, DST, gatewayPrefix);
-        tBuilder.extension(loadTreatment, deviceId)
-                .setOutput(PortNumber.LOCAL);
-
-        k8sFlowRuleService.setRule(
-                appId,
-                deviceId,
-                sBuilder.build(),
-                tBuilder.build(),
-                PRIORITY_NODE_PORT_RULE,
-                EXT_ENTRY_TABLE,
-                install);
-    }
-
-    private void setServiceToNodeRemoteRules(K8sNode k8sNode,
-                                             String clusterIp,
-                                             ServicePort servicePort,
-                                             boolean install) {
-        String protocol = servicePort.getProtocol();
-        int nodePort = servicePort.getNodePort();
-        int svcPort = servicePort.getPort();
-        DeviceId deviceId = k8sNode.extBridge();
-
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchInPort(k8sNode.extToIntgPatchPortNum())
                 .matchIPSrc(IpPrefix.valueOf(IpAddress.valueOf(clusterIp), HOST_CIDR));
 
         TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
-                .setIpSrc(k8sNode.extBridgeIp())
-                .setEthSrc(k8sNode.extBridgeMac());
+                .setIpSrc(k8sNode.nodeIp())
+                .setEthSrc(k8sNode.nodeMac());
 
         if (TCP.equals(protocol)) {
             sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP)
@@ -419,24 +307,30 @@
             tBuilder.setUdpSrc(TpPort.tpPort(nodePort));
         }
 
-        String gatewayIp = k8sNode.extGatewayIp().toString();
-        String prefix = getBclassIpPrefixFromCidr(gatewayIp);
-
-        if (prefix == null) {
-            return;
-        }
-
         ExtensionTreatment loadTreatment = buildLoadExtension(
-                deviceService.getDevice(deviceId), B_CLASS, DST, prefix);
-        tBuilder.extension(loadTreatment, deviceId)
-                .setOutput(k8sNode.extBridgePortNum());
+                deviceService.getDevice(deviceId), B_CLASS, DST, nodeIpPrefix);
+        tBuilder.extension(loadTreatment, deviceId);
+
+        // in passthrough mode, we steer the traffic to the openstack intg bridge
+        // in normal mode, we steer the traffic to the local port
+        if (k8sNode.mode() == PASSTHROUGH) {
+            PortNumber output = k8sNode.portNumByName(k8sNode.extBridge(),
+                    k8sNode.k8sExtToOsPatchPortName());
+            if (output == null) {
+                log.warn("Kubernetes external to OpenStack patch port is null");
+                return;
+            }
+            tBuilder.setOutput(output);
+        } else {
+            tBuilder.setOutput(PortNumber.LOCAL);
+        }
 
         k8sFlowRuleService.setRule(
                 appId,
                 deviceId,
                 sBuilder.build(),
                 tBuilder.build(),
-                PRIORITY_NODE_PORT_REMOTE_RULE,
+                PRIORITY_NODE_PORT_RULE,
                 EXT_ENTRY_TABLE,
                 install);
     }
@@ -504,6 +398,9 @@
             k8sServiceService.services().stream()
                     .filter(s -> NODE_PORT_TYPE.equals(s.getSpec().getType()))
                     .forEach(s -> processNodePortEvent(k8sNode, s, true));
+
+            setIntgToExtRules(k8sNode, getServiceCidr(), true);
+            setTunToIntgRules(k8sNode, true);
         }
     }
 }
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sOpenstackIntegrationHandler.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sOpenstackIntegrationHandler.java
index 0c68e20..9e24daa 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sOpenstackIntegrationHandler.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sOpenstackIntegrationHandler.java
@@ -63,6 +63,7 @@
 
     private static final String K8S_NODE_IP = "k8sNodeIp";
     private static final String OS_K8S_INT_PORT_NAME = "osK8sIntPortName";
+    private static final String OS_K8S_EXT_PORT_NAME = "osK8sExtPortName";
     private static final String POD_CIDR = "podCidr";
     private static final String SERVICE_CIDR = "serviceCidr";
     private static final String POD_GW_IP = "podGwIp";
@@ -121,7 +122,8 @@
         String srcPodPrefix = getBclassIpPrefixFromCidr(nodePodCidr);
         String podCidr = srcPodPrefix + B_CLASS_SUFFIX;
         String osK8sIntPortName = k8sNode.osToK8sIntgPatchPortName();
-        String k8sIntOsPortMac = k8sNode.portMacByName(k8sNode.k8sIntgToOsPatchPortName()).toString();
+        String k8sIntOsPortMac = k8sNode.portMacByName(k8sNode.intgBridge(),
+                k8sNode.k8sIntgToOsPatchPortName()).toString();
 
         String path = install ? "node/pt-install" : "node/pt-uninstall";
 
@@ -159,6 +161,43 @@
         }
     }
 
+    private void setCniPtNodePortRules(K8sNode k8sNode, boolean install) {
+        String k8sNodeIp = k8sNode.nodeIp().toString();
+        String osK8sExtPortName = k8sNode.osToK8sExtPatchPortName();
+
+        String path = install ? "nodeport/pt-install" : "nodeport/pt-uninstall";
+
+        String jsonString = "";
+
+        try {
+            jsonString = new JSONObject()
+                    .put(K8S_NODE_IP, k8sNodeIp)
+                    .put(SERVICE_CIDR, SERVICE_IP_CIDR_DEFAULT)
+                    .put(OS_K8S_EXT_PORT_NAME, osK8sExtPortName)
+                    .toString();
+            log.info("push integration configuration {}", jsonString);
+        } catch (JSONException e) {
+            log.error("Failed to generate JSON string");
+            return;
+        }
+
+        HttpAuthenticationFeature feature =
+                HttpAuthenticationFeature.basic(ONOS_USERNAME, ONOS_PASSWORD);
+
+        final Client client = ClientBuilder.newClient();
+        client.register(feature);
+        String host = "http://" + k8sNode.managementIp().toString() + ":" + ONOS_PORT + "/";
+        String endpoint = host + OS_K8S_INTEGRATION_EP;
+        WebTarget wt = client.target(endpoint).path(path);
+        Response response = wt.request(MediaType.APPLICATION_JSON_TYPE)
+                .put(Entity.json(jsonString));
+        final int status = response.getStatus();
+
+        if (status != 200) {
+            log.error("Failed to install/uninstall openstack k8s CNI PT node port rules.");
+        }
+    }
+
     private class InternalK8sNodeListener implements K8sNodeListener {
 
         @Override
@@ -190,6 +229,7 @@
             }
 
             setCniPtNodeRules(k8sNode, true);
+            setCniPtNodePortRules(k8sNode, true);
         }
 
         private void processNodeIncompletion(K8sNode k8sNode) {
@@ -198,6 +238,7 @@
             }
 
             setCniPtNodeRules(k8sNode, false);
+            setCniPtNodePortRules(k8sNode, false);
         }
     }
 }
\ No newline at end of file
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sRoutingArpHandler.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sRoutingArpHandler.java
index 93bde9b..1514f6c 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sRoutingArpHandler.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sRoutingArpHandler.java
@@ -178,7 +178,6 @@
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_ARP)
                 .matchArpOp(ARP.OP_REPLY)
-                .matchArpSpa(Ip4Address.valueOf(k8sNode.extGatewayIp().toString()))
                 .build();
 
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingArpHandler.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingArpHandler.java
index d5c047f..9780435 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingArpHandler.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingArpHandler.java
@@ -15,11 +15,13 @@
  */
 package org.onosproject.k8snetworking.impl;
 
+import org.apache.commons.net.util.SubnetUtils;
 import org.onlab.packet.ARP;
 import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onlab.util.KryoNamespace;
@@ -38,9 +40,10 @@
 import org.onosproject.k8snetworking.api.K8sServiceService;
 import org.onosproject.k8snode.api.K8sHostService;
 import org.onosproject.k8snode.api.K8sNode;
+import org.onosproject.k8snode.api.K8sNodeAdminService;
 import org.onosproject.k8snode.api.K8sNodeEvent;
+import org.onosproject.k8snode.api.K8sNodeInfo;
 import org.onosproject.k8snode.api.K8sNodeListener;
-import org.onosproject.k8snode.api.K8sNodeService;
 import org.onosproject.mastership.MastershipService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
@@ -76,11 +79,14 @@
 import java.util.stream.Collectors;
 
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.packet.IpAddress.Version.INET;
 import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.k8snetworking.api.Constants.ARP_BROADCAST_MODE;
 import static org.onosproject.k8snetworking.api.Constants.ARP_PROXY_MODE;
 import static org.onosproject.k8snetworking.api.Constants.ARP_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
+import static org.onosproject.k8snetworking.api.Constants.NODE_FAKE_IP_STR;
+import static org.onosproject.k8snetworking.api.Constants.NODE_FAKE_MAC_STR;
 import static org.onosproject.k8snetworking.api.Constants.NODE_IP_PREFIX;
 import static org.onosproject.k8snetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
 import static org.onosproject.k8snetworking.api.Constants.SERVICE_FAKE_MAC_STR;
@@ -90,6 +96,7 @@
 import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.GATEWAY_MAC;
 import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.GATEWAY_MAC_DEFAULT;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.allK8sDevices;
+import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getGatewayIp;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getPropertyValue;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.unshiftIpDomain;
 
@@ -139,7 +146,7 @@
     protected StorageService storageService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    protected K8sNodeService k8sNodeService;
+    protected K8sNodeAdminService k8sNodeService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected K8sHostService k8sHostService;
@@ -159,8 +166,6 @@
     /** ARP processing mode, broadcast | proxy (default). */
     protected String arpMode = ARP_MODE_DEFAULT;
 
-    private MacAddress gwMacAddress;
-
     private ConsistentMap<IpAddress, MacAddress> extHostMacStore;
 
     private final ExecutorService eventExecutor = newSingleThreadExecutor(
@@ -305,20 +310,23 @@
                 String targetIpPrefix = targetIp.toString().split("\\.")[1];
                 String nodePrefix = NODE_IP_PREFIX + "." + targetIpPrefix;
 
-                String exBridgeCidr = k8sNodeService.completeNodes().stream()
-                        .map(n -> n.extBridgeIp().toString()).findAny().orElse(null);
+                String origNodeCidr = k8sNodeService.completeNodes().stream()
+                        .map(n -> n.nodeIp().toString()).findAny().orElse(null);
 
-                if (exBridgeCidr != null) {
-                    String extBridgeIp = unshiftIpDomain(targetIp.toString(),
-                            nodePrefix, exBridgeCidr);
+                if (origNodeCidr != null) {
+                    String origNodeIp = unshiftIpDomain(targetIp.toString(),
+                            nodePrefix, origNodeCidr);
+                    IpPrefix k8sNodeIpCidr = IpPrefix.valueOf(IpAddress.valueOf(origNodeCidr), 24);
+                    SubnetUtils k8sNodeSubnet = new SubnetUtils(k8sNodeIpCidr.toString());
+                    String k8sNodeGateway = getGatewayIp(k8sNodeIpCidr.toString()).toString();
+                    String seekIp = "";
 
-                    replyMac = k8sNodeService.completeNodes().stream()
-                            .filter(n -> extBridgeIp.equals(n.extBridgeIp().toString()))
-                            .map(K8sNode::extBridgeMac).findAny().orElse(null);
-
-                    if (replyMac == null) {
-                        replyMac = extHostMacStore.asJavaMap().get(
-                                IpAddress.valueOf(extBridgeIp));
+                    if (!k8sNodeSubnet.getInfo().isInRange(origNodeIp)) {
+                        replyMac = extHostMacStore.asJavaMap().get(IpAddress.valueOf(k8sNodeGateway));
+                        seekIp = k8sNodeGateway;
+                    } else {
+                        replyMac = extHostMacStore.asJavaMap().get(IpAddress.valueOf(origNodeIp));
+                        seekIp = origNodeIp;
                     }
 
                     // if the source hosts are not in k8s cluster range,
@@ -328,9 +336,11 @@
                         K8sNode k8sNode = k8sNodeService.node(cp.deviceId());
 
                         if (k8sNode != null) {
-                            setArpRequest(k8sNode.extBridgeMac().toBytes(),
-                                    k8sNode.extBridgeIp().toOctets(),
-                                    IpAddress.valueOf(extBridgeIp).toOctets(),
+                            // we use fake IP and MAC address as a source to
+                            // query destination MAC address
+                            setArpRequest(MacAddress.valueOf(NODE_FAKE_MAC_STR).toBytes(),
+                                    IpAddress.valueOf(NODE_FAKE_IP_STR).toOctets(),
+                                    IpAddress.valueOf(seekIp).toOctets(),
                                     k8sNode);
                             context.block();
                             return;
@@ -363,20 +373,34 @@
 
     private void processArpReply(PacketContext context, Ethernet ethPacket) {
         ARP arpPacket = (ARP) ethPacket.getPayload();
-        ConnectPoint cp = context.inPacket().receivedFrom();
-        K8sNode k8sNode = k8sNodeService.node(cp.deviceId());
 
-        if (k8sNode != null &&
-                ethPacket.getDestinationMAC().equals(k8sNode.extBridgeMac())) {
-            IpAddress srcIp = IpAddress.valueOf(IpAddress.Version.INET,
-                    arpPacket.getSenderProtocolAddress());
-            MacAddress srcMac = MacAddress.valueOf(arpPacket.getSenderHardwareAddress());
+        IpAddress srcIp = IpAddress.valueOf(INET, arpPacket.getSenderProtocolAddress());
+        MacAddress srcMac = MacAddress.valueOf(arpPacket.getSenderHardwareAddress());
+        IpAddress dstIp = IpAddress.valueOf(INET, arpPacket.getTargetProtocolAddress());
 
+        if (dstIp.equals(IpAddress.valueOf(NODE_FAKE_IP_STR))) {
             // we only add the host IP - MAC map store once,
             // mutable MAP scenario is not considered for now
             if (!extHostMacStore.containsKey(srcIp)) {
                 extHostMacStore.put(srcIp, srcMac);
             }
+
+            K8sNode k8sNode = k8sNodeService.nodes().stream()
+                    .filter(n -> n.nodeIp().equals(srcIp))
+                    .findAny().orElse(null);
+
+            if (k8sNode == null) {
+                return;
+            } else {
+                if (k8sNode.nodeInfo().nodeMac() != null) {
+                    return;
+                }
+            }
+
+            // we update node MAC address which will be referred in node port scenario
+            K8sNodeInfo nodeInfo = new K8sNodeInfo(k8sNode.nodeIp(), srcMac);
+            K8sNode updatedNode = k8sNode.updateNodeInfo(nodeInfo);
+            k8sNodeService.updateNode(updatedNode);
         }
     }
 
@@ -385,12 +409,20 @@
         Ethernet ethRequest = ARP.buildArpRequest(senderMac,
                                                   senderIp, targetIp, VlanId.NO_VID);
 
+        // TODO: we need to find a way of sending out ARP request to learn
+        //  MAC addresses in NORMAL mode
+        PortNumber k8sExtToOsPatchPort = k8sNode.portNumByName(k8sNode.extBridge(),
+                k8sNode.k8sExtToOsPatchPortName());
+        if (k8sExtToOsPatchPort == null) {
+            log.warn("Kubernetes external to OpenStack patch port is null");
+            return;
+        }
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .setOutput(k8sNode.intgToExtPatchPortNum())
+                .setOutput(k8sExtToOsPatchPort)
                 .build();
 
         packetService.emit(new DefaultOutboundPacket(
-                k8sNode.intgBridge(),
+                k8sNode.extBridge(),
                 treatment,
                 ByteBuffer.wrap(ethRequest.serialize())));
     }
@@ -466,6 +498,7 @@
             }
 
             setDefaultArpRule(node, true);
+            learnK8sNodeMac(node);
         }
 
         private void processNodeIncompletion(K8sNode node) {
@@ -516,5 +549,17 @@
                     install
             );
         }
+
+        private void learnK8sNodeMac(K8sNode k8sNode) {
+            // if we already have a learned MAC address, we skip learning process
+            if (k8sNode.nodeMac() != null) {
+                return;
+            }
+
+            setArpRequest(MacAddress.valueOf(NODE_FAKE_MAC_STR).toBytes(),
+                    IpAddress.valueOf(NODE_FAKE_IP_STR).toOctets(),
+                    k8sNode.nodeIp().toOctets(),
+                    k8sNode);
+        }
     }
 }
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingGatewayHandler.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingGatewayHandler.java
index 8083e88..2c580cb 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingGatewayHandler.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingGatewayHandler.java
@@ -64,6 +64,7 @@
 import static org.onosproject.k8snetworking.api.Constants.HOST_PREFIX;
 import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
 import static org.onosproject.k8snetworking.api.Constants.LOCAL_ENTRY_TABLE;
+import static org.onosproject.k8snetworking.api.Constants.NODE_IP_PREFIX;
 import static org.onosproject.k8snetworking.api.Constants.PRIORITY_ARP_REPLY_RULE;
 import static org.onosproject.k8snetworking.api.Constants.PRIORITY_GATEWAY_RULE;
 import static org.onosproject.k8snetworking.api.Constants.PRIORITY_INTER_NODE_RULE;
@@ -283,6 +284,23 @@
                         PRIORITY_INTER_NODE_RULE,
                         TUN_ENTRY_TABLE,
                         install);
+
+                String nodeIpPrefix = NODE_IP_PREFIX + ".0.0.0/8";
+
+                TrafficSelector nodePortSelector = DefaultTrafficSelector.builder()
+                        .matchEthType(Ethernet.TYPE_IPV4)
+                        .matchIPSrc(IpPrefix.valueOf(nodeIpPrefix))
+                        .matchIPDst(IpPrefix.valueOf(dstNode.podCidr()))
+                        .build();
+
+                k8sFlowRuleService.setRule(
+                        appId,
+                        dstNode.tunBridge(),
+                        nodePortSelector,
+                        treatment,
+                        PRIORITY_INTER_NODE_RULE,
+                        TUN_ENTRY_TABLE,
+                        install);
             }
         }
     }
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/K8sNetworkingUtil.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/K8sNetworkingUtil.java
index e43ef00..1f4d09a 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/K8sNetworkingUtil.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/K8sNetworkingUtil.java
@@ -298,6 +298,19 @@
     }
 
     /**
+     * Obtains gateway IP address of the given subnet.
+     *
+     * @param cidr CIDR
+     * @return gateway IP address
+     */
+    public static IpAddress getGatewayIp(String cidr) {
+        SubnetUtils utils = new SubnetUtils(cidr);
+        utils.setInclusiveHostCount(false);
+        SubnetUtils.SubnetInfo info = utils.getInfo();
+        return IpAddress.valueOf(info.getLowAddress());
+    }
+
+    /**
      * Obtains flow group key from the given id.
      *
      * @param groupId flow group identifier
diff --git a/apps/k8s-networking/app/src/test/java/org/onosproject/k8snetworking/util/K8sNetworkUtilTest.java b/apps/k8s-networking/app/src/test/java/org/onosproject/k8snetworking/util/K8sNetworkUtilTest.java
index b90e770..ee34e47 100644
--- a/apps/k8s-networking/app/src/test/java/org/onosproject/k8snetworking/util/K8sNetworkUtilTest.java
+++ b/apps/k8s-networking/app/src/test/java/org/onosproject/k8snetworking/util/K8sNetworkUtilTest.java
@@ -24,6 +24,7 @@
 import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertTrue;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.existingContainerPortByMac;
+import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getGatewayIp;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getSubnetIps;
 
 /**
@@ -49,6 +50,13 @@
         assertEquals(0, dClassIps.size());
     }
 
+    @Test
+    public void testGetGatewayIp() {
+        String classCidr = "10.10.10.0/24";
+        IpAddress gatewayIp = getGatewayIp(classCidr);
+        assertEquals("10.10.10.1", gatewayIp.toString());
+    }
+
     /**
      * Tests the existing container port by MAC.
      */
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sNode.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sNode.java
index c4d7ac3..e914977 100644
--- a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sNode.java
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sNode.java
@@ -80,7 +80,7 @@
     private final DeviceId tunBridge;
     private final IpAddress managementIp;
     private final IpAddress dataIp;
-    private final IpAddress nodeIp;
+    private final K8sNodeInfo nodeInfo;
     private final K8sNodeState state;
     private final K8sExternalNetwork extNetwork;
     private final String podCidr;
@@ -103,7 +103,7 @@
      * @param tunBridge         tunnel bridge
      * @param managementIp      management IP address
      * @param dataIp            data IP address
-     * @param nodeIp            node IP address
+     * @param nodeInfo          node info
      * @param state             node state
      * @param extNetwork        external network
      * @param podCidr           POD CIDR
@@ -112,7 +112,7 @@
                              int segmentId, Mode mode, DeviceId intgBridge,
                              DeviceId extBridge, DeviceId localBridge,
                              DeviceId tunBridge, IpAddress managementIp,
-                             IpAddress dataIp, IpAddress nodeIp, K8sNodeState state,
+                             IpAddress dataIp, K8sNodeInfo nodeInfo, K8sNodeState state,
                              K8sExternalNetwork extNetwork, String podCidr) {
         this.clusterName = clusterName;
         this.hostname = hostname;
@@ -125,7 +125,7 @@
         this.tunBridge = tunBridge;
         this.managementIp = managementIp;
         this.dataIp = dataIp;
-        this.nodeIp = nodeIp;
+        this.nodeInfo = nodeInfo;
         this.state = state;
         this.extNetwork = extNetwork;
         this.podCidr = podCidr;
@@ -237,7 +237,7 @@
                 .tunBridge(tunBridge)
                 .managementIp(managementIp)
                 .dataIp(dataIp)
-                .nodeIp(nodeIp)
+                .nodeInfo(nodeInfo)
                 .state(state)
                 .extBridgeIp(extNetwork.extBridgeIp())
                 .extGatewayIp(extNetwork.extGatewayIp())
@@ -261,7 +261,7 @@
                 .tunBridge(tunBridge)
                 .managementIp(managementIp)
                 .dataIp(dataIp)
-                .nodeIp(nodeIp)
+                .nodeInfo(nodeInfo)
                 .state(state)
                 .extBridgeIp(extNetwork.extBridgeIp())
                 .extGatewayIp(extNetwork.extGatewayIp())
@@ -285,7 +285,7 @@
                 .tunBridge(tunBridge)
                 .managementIp(managementIp)
                 .dataIp(dataIp)
-                .nodeIp(nodeIp)
+                .nodeInfo(nodeInfo)
                 .state(state)
                 .extBridgeIp(extNetwork.extBridgeIp())
                 .extGatewayIp(extNetwork.extGatewayIp())
@@ -309,7 +309,7 @@
                 .tunBridge(deviceId)
                 .managementIp(managementIp)
                 .dataIp(dataIp)
-                .nodeIp(nodeIp)
+                .nodeInfo(nodeInfo)
                 .state(state)
                 .extBridgeIp(extNetwork.extBridgeIp())
                 .extGatewayIp(extNetwork.extGatewayIp())
@@ -330,8 +330,22 @@
     }
 
     @Override
+    public K8sNodeInfo nodeInfo() {
+        return nodeInfo;
+    }
+
+    @Override
     public IpAddress nodeIp() {
-        return nodeIp;
+        return nodeInfo.nodeIp();
+    }
+
+    @Override
+    public MacAddress nodeMac() {
+        if (nodeInfo == null) {
+            return null;
+        } else {
+            return nodeInfo.nodeMac();
+        }
     }
 
     @Override
@@ -358,7 +372,7 @@
                 .tunBridge(tunBridge)
                 .managementIp(managementIp)
                 .dataIp(dataIp)
-                .nodeIp(nodeIp)
+                .nodeInfo(nodeInfo)
                 .state(newState)
                 .extBridgeIp(extNetwork.extBridgeIp())
                 .extGatewayIp(extNetwork.extGatewayIp())
@@ -382,7 +396,7 @@
                 .tunBridge(tunBridge)
                 .managementIp(managementIp)
                 .dataIp(dataIp)
-                .nodeIp(nodeIp)
+                .nodeInfo(nodeInfo)
                 .state(state)
                 .extBridgeIp(extNetwork.extBridgeIp())
                 .extGatewayIp(extNetwork.extGatewayIp())
@@ -393,6 +407,30 @@
     }
 
     @Override
+    public K8sNode updateNodeInfo(K8sNodeInfo newNodeInfo) {
+        return new Builder()
+                .hostname(hostname)
+                .clusterName(clusterName)
+                .type(type)
+                .segmentId(segmentId)
+                .mode(mode)
+                .intgBridge(intgBridge)
+                .extBridge(extBridge)
+                .localBridge(localBridge)
+                .tunBridge(tunBridge)
+                .managementIp(managementIp)
+                .dataIp(dataIp)
+                .nodeInfo(newNodeInfo)
+                .state(state)
+                .extBridgeIp(extNetwork.extBridgeIp())
+                .extGatewayIp(extNetwork.extGatewayIp())
+                .extGatewayMac(extNetwork.extGatewayMac())
+                .extIntf(extNetwork.extIntf())
+                .podCidr(podCidr)
+                .build();
+    }
+
+    @Override
     public PortNumber grePortNum() {
         return tunnelPortNum(grePortName());
     }
@@ -617,20 +655,20 @@
     }
 
     @Override
-    public MacAddress portMacByName(String portName) {
+    public MacAddress portMacByName(DeviceId deviceId, String portName) {
         if (portName == null) {
             return null;
         } else {
-            return macAddress(this.intgBridge, portName);
+            return macAddress(deviceId, portName);
         }
     }
 
     @Override
-    public PortNumber portNumByName(String portName) {
+    public PortNumber portNumByName(DeviceId deviceId, String portName) {
         if (portName == null) {
             return null;
         } else {
-            return portNumber(this.intgBridge, portName);
+            return portNumber(deviceId, portName);
         }
     }
 
@@ -843,7 +881,7 @@
                     tunBridge.equals(that.tunBridge) &&
                     managementIp.equals(that.managementIp) &&
                     dataIp.equals(that.dataIp) &&
-                    nodeIp.equals(that.nodeIp) &&
+                    nodeInfo.equals(that.nodeInfo) &&
                     extNetwork.equals(that.extNetwork) &&
                     podCidr.equals(that.podCidr) &&
                     state == that.state;
@@ -855,7 +893,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(clusterName, hostname, type, segmentId, mode, intgBridge, extBridge,
-                localBridge, tunBridge, managementIp, dataIp, nodeIp, state, extNetwork, podCidr);
+                localBridge, tunBridge, managementIp, dataIp, nodeInfo, state, extNetwork, podCidr);
     }
 
     @Override
@@ -872,7 +910,7 @@
                 .add("tunBridge", tunBridge)
                 .add("managementIp", managementIp)
                 .add("dataIp", dataIp)
-                .add("nodeIp", nodeIp)
+                .add("nodeInfo", nodeInfo)
                 .add("state", state)
                 .add("extBridgeIp", extNetwork.extBridgeIp())
                 .add("extGatewayIp", extNetwork.extGatewayIp())
@@ -937,7 +975,7 @@
                 .extIntf(node.extIntf())
                 .managementIp(node.managementIp())
                 .dataIp(node.dataIp())
-                .nodeIp(node.nodeIp())
+                .nodeInfo(node.nodeInfo())
                 .state(node.state())
                 .extBridgeIp(node.extBridgeIp())
                 .extGatewayIp(node.extGatewayIp())
@@ -959,7 +997,7 @@
         private DeviceId tunBridge;
         private IpAddress managementIp;
         private IpAddress dataIp;
-        private IpAddress nodeIp;
+        private K8sNodeInfo nodeInfo;
         private K8sNodeState state;
         private K8sApiConfig apiConfig;
         private String extIntf;
@@ -978,7 +1016,7 @@
             checkArgument(type != null, NOT_NULL_MSG, "type");
             checkArgument(state != null, NOT_NULL_MSG, "state");
             checkArgument(managementIp != null, NOT_NULL_MSG, "management IP");
-            checkArgument(nodeIp != null, NOT_NULL_MSG, "node IP");
+            checkArgument(nodeInfo != null, NOT_NULL_MSG, "node info");
 
             if (StringUtils.isEmpty(clusterName)) {
                 clusterName = DEFAULT_CLUSTER_NAME;
@@ -1006,7 +1044,7 @@
                     tunBridge,
                     managementIp,
                     dataIp,
-                    nodeIp,
+                    nodeInfo,
                     state,
                     extNetwork,
                     podCidr);
@@ -1079,8 +1117,8 @@
         }
 
         @Override
-        public Builder nodeIp(IpAddress nodeIp) {
-            this.nodeIp = nodeIp;
+        public Builder nodeInfo(K8sNodeInfo nodeInfo) {
+            this.nodeInfo = nodeInfo;
             return this;
         }
 
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNode.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNode.java
index 5c13bc1..4609f25 100644
--- a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNode.java
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNode.java
@@ -187,13 +187,27 @@
     IpAddress dataIp();
 
     /**
+     * Returns the kubernetes node info.
+     *
+     * @return node info; null if node info not exists
+     */
+    K8sNodeInfo nodeInfo();
+
+    /**
      * Returns the kubernetes node IP address.
      *
-     * @return ip address; null if node has no IP address
+     * @return node IP address; null if the node IP not exists
      */
     IpAddress nodeIp();
 
     /**
+     * Returns the kubernetes node MAC address.
+     *
+     * @return node MAC address; null if the node MAC not exists
+     */
+    MacAddress nodeMac();
+
+    /**
      * Returns the initialization state of the node.
      *
      * @return node state
@@ -224,6 +238,14 @@
     K8sNode updateExtGatewayMac(MacAddress macAddress);
 
     /**
+     * Returns new kubernetes node instance with given node info.
+     *
+     * @param nodeInfo updated node info
+     * @return updated kubernetes node
+     */
+    K8sNode updateNodeInfo(K8sNodeInfo nodeInfo);
+
+    /**
      * Returns GRE port name.
      *
      * @return GRE port name
@@ -268,18 +290,20 @@
     /**
      * Returns the port MAC address with the given patch port name.
      *
+     * @param deviceId device identifier
      * @param portName patch port name
      * @return port MAC address
      */
-    MacAddress portMacByName(String portName);
+    MacAddress portMacByName(DeviceId deviceId, String portName);
 
     /**
      * Returns the port number with the given patch port name.
      *
+     * @param deviceId device identifier
      * @param portName patch port name
      * @return port number
      */
-    PortNumber portNumByName(String portName);
+    PortNumber portNumByName(DeviceId deviceId, String portName);
 
     /**
      * Return the port number of integration bridge's entry port.
@@ -677,12 +701,12 @@
         Builder dataIp(IpAddress dataIp);
 
         /**
-         * Returns the kubernetes node builder with supplied node IP address.
+         * Returns the kubernetes node builder with supplied node info.
          *
-         * @param nodeIp node IP address
+         * @param nodeInfo node info
          * @return kubernetes node builder
          */
-        Builder nodeIp(IpAddress nodeIp);
+        Builder nodeInfo(K8sNodeInfo nodeInfo);
 
         /**
          * Returns kubernetes node builder with supplied node state.
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNodeInfo.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNodeInfo.java
new file mode 100644
index 0000000..ecd266e
--- /dev/null
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNodeInfo.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.api;
+
+import com.google.common.base.MoreObjects;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+
+import java.util.Objects;
+
+/**
+ * Kubernetes node info class.
+ */
+public class K8sNodeInfo {
+    private final IpAddress nodeIp;
+    private final MacAddress nodeMac;
+
+    /**
+     * Default constructor.
+     *
+     * @param nodeIp        node IP address
+     * @param nodeMac       node MAC address
+     */
+    public K8sNodeInfo(IpAddress nodeIp, MacAddress nodeMac) {
+        this.nodeIp = nodeIp;
+        this.nodeMac = nodeMac;
+    }
+
+    /**
+     * Obtains Kubernetes node IP address.
+     *
+     * @return node IP address
+     */
+    public IpAddress nodeIp() {
+        return nodeIp;
+    }
+
+    /**
+     * Obtains Kubernetes node MAC address.
+     *
+     * @return node MAC address
+     */
+    public MacAddress nodeMac() {
+        return nodeMac;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        K8sNodeInfo that = (K8sNodeInfo) o;
+        return Objects.equals(nodeIp, that.nodeIp) &&
+                Objects.equals(nodeMac, that.nodeMac);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(nodeIp, nodeMac);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("nodeIp", nodeIp)
+                .add("nodeMac", nodeMac)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/apps/k8s-node/api/src/test/java/org/onosproject/k8snode/api/DefaultK8sNodeTest.java b/apps/k8s-node/api/src/test/java/org/onosproject/k8snode/api/DefaultK8sNodeTest.java
index dec1d48..c953c8b 100644
--- a/apps/k8s-node/api/src/test/java/org/onosproject/k8snode/api/DefaultK8sNodeTest.java
+++ b/apps/k8s-node/api/src/test/java/org/onosproject/k8snode/api/DefaultK8sNodeTest.java
@@ -20,6 +20,7 @@
 import org.junit.Test;
 import org.onlab.packet.ChassisId;
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
 import org.onosproject.k8snode.api.K8sNode.Type;
 import org.onosproject.net.DefaultDevice;
 import org.onosproject.net.Device;
@@ -52,6 +53,8 @@
     private static final IpAddress MANAGEMENT_IP = IpAddress.valueOf("10.10.10.10");
     private static final IpAddress DATA_IP = IpAddress.valueOf("20.20.20.20");
     private static final IpAddress NODE_IP = IpAddress.valueOf("30.30.30.30");
+    private static final MacAddress NODE_MAC = MacAddress.valueOf("fa:00:00:00:00:08");
+    private static final K8sNodeInfo NODE_INFO = new K8sNodeInfo(NODE_IP, NODE_MAC);
 
     private static final String BRIDGE_INTF_1 = "eth1";
     private static final String BRIDGE_INTF_2 = "eth2";
@@ -78,6 +81,7 @@
             DEVICE_1,
             BRIDGE_INTF_1,
             TEST_IP,
+            NODE_INFO,
             INIT,
             EXT_BRIDGE_IP_1,
             EXT_GATEWAY_IP_1,
@@ -93,6 +97,7 @@
             DEVICE_1,
             BRIDGE_INTF_1,
             TEST_IP,
+            NODE_INFO,
             INIT,
             EXT_BRIDGE_IP_1,
             EXT_GATEWAY_IP_1,
@@ -108,6 +113,7 @@
             DEVICE_2,
             BRIDGE_INTF_2,
             TEST_IP,
+            NODE_INFO,
             INIT,
             EXT_BRIDGE_IP_2,
             EXT_GATEWAY_IP_2,
@@ -125,7 +131,7 @@
                 .segmentId(SEGMENT_ID_1)
                 .managementIp(MANAGEMENT_IP)
                 .dataIp(DATA_IP)
-                .nodeIp(NODE_IP)
+                .nodeInfo(NODE_INFO)
                 .intgBridge(DEVICE_1.id())
                 .extBridge(DEVICE_1.id())
                 .localBridge(DEVICE_1.id())
@@ -198,7 +204,7 @@
                 .extIntf(BRIDGE_INTF_1)
                 .managementIp(TEST_IP)
                 .dataIp(TEST_IP)
-                .nodeIp(NODE_IP)
+                .nodeInfo(NODE_INFO)
                 .state(INIT)
                 .extBridgeIp(EXT_BRIDGE_IP_1)
                 .extGatewayIp(EXT_GATEWAY_IP_1)
@@ -222,7 +228,7 @@
                 .extIntf(BRIDGE_INTF_1)
                 .managementIp(TEST_IP)
                 .dataIp(TEST_IP)
-                .nodeIp(NODE_IP)
+                .nodeInfo(NODE_INFO)
                 .state(INIT)
                 .extBridgeIp(EXT_BRIDGE_IP_1)
                 .extGatewayIp(EXT_GATEWAY_IP_1)
@@ -247,7 +253,7 @@
                 .tunBridge(DEVICE_1.id())
                 .extIntf(BRIDGE_INTF_1)
                 .dataIp(TEST_IP)
-                .nodeIp(NODE_IP)
+                .nodeInfo(NODE_INFO)
                 .state(INIT)
                 .extBridgeIp(EXT_BRIDGE_IP_1)
                 .extGatewayIp(EXT_GATEWAY_IP_1)
@@ -261,7 +267,7 @@
         assertEquals(node.type(), MINION);
         assertEquals(node.managementIp(), MANAGEMENT_IP);
         assertEquals(node.dataIp(), DATA_IP);
-        assertEquals(node.nodeIp(), NODE_IP);
+        assertEquals(node.nodeInfo(), NODE_INFO);
     }
 
     private static Device createDevice(long devIdNum) {
@@ -278,7 +284,7 @@
     private static K8sNode createNode(String clusterName, String hostname, Type type,
                                       int segmentId, Device intgBridge, Device extBridge,
                                       Device localBridge, Device tunBridge, String bridgeIntf,
-                                      IpAddress ipAddr, K8sNodeState state,
+                                      IpAddress ipAddr, K8sNodeInfo info, K8sNodeState state,
                                       IpAddress extBridgeIp, IpAddress extGatewayIp,
                                       String podCidr) {
         return DefaultK8sNode.builder()
@@ -293,7 +299,7 @@
                 .extIntf(bridgeIntf)
                 .managementIp(ipAddr)
                 .dataIp(ipAddr)
-                .nodeIp(ipAddr)
+                .nodeInfo(info)
                 .state(state)
                 .extBridgeIp(extBridgeIp)
                 .extGatewayIp(extGatewayIp)
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/K8sNodeCodec.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/K8sNodeCodec.java
index 4f4e6bf..6e00655 100644
--- a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/K8sNodeCodec.java
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/K8sNodeCodec.java
@@ -24,6 +24,7 @@
 import org.onosproject.codec.JsonCodec;
 import org.onosproject.k8snode.api.DefaultK8sNode;
 import org.onosproject.k8snode.api.K8sNode;
+import org.onosproject.k8snode.api.K8sNodeInfo;
 import org.onosproject.k8snode.api.K8sNodeState;
 import org.onosproject.net.DeviceId;
 import org.slf4j.Logger;
@@ -49,6 +50,7 @@
     private static final String MANAGEMENT_IP = "managementIp";
     private static final String DATA_IP = "dataIp";
     private static final String NODE_IP = "nodeIp";
+    private static final String NODE_MAC = "nodeMac";
     private static final String INTEGRATION_BRIDGE = "integrationBridge";
     private static final String EXTERNAL_BRIDGE = "externalBridge";
     private static final String LOCAL_BRIDGE = "localBridge";
@@ -95,6 +97,10 @@
             result.put(DATA_IP, node.dataIp().toString());
         }
 
+        if (node.nodeMac() != null) {
+            result.put(NODE_MAC, node.nodeMac().toString());
+        }
+
         if (node.extIntf() != null) {
             result.put(EXTERNAL_INTF, node.extIntf());
         }
@@ -135,13 +141,15 @@
         String nIp = nullIsIllegal(json.get(NODE_IP).asText(),
                 NODE_IP + MISSING_MESSAGE);
 
+        K8sNodeInfo nodeInfo = new K8sNodeInfo(IpAddress.valueOf(nIp), null);
+
         DefaultK8sNode.Builder nodeBuilder = DefaultK8sNode.builder()
                 .clusterName(clusterName)
                 .hostname(hostname)
                 .type(K8sNode.Type.valueOf(type))
                 .state(K8sNodeState.INIT)
                 .managementIp(IpAddress.valueOf(mIp))
-                .nodeIp(IpAddress.valueOf(nIp));
+                .nodeInfo(nodeInfo);
 
         if (json.get(DATA_IP) != null) {
             nodeBuilder.dataIp(IpAddress.valueOf(json.get(DATA_IP).asText()));
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DefaultK8sApiConfigHandler.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DefaultK8sApiConfigHandler.java
index 30606f0..c688740 100644
--- a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DefaultK8sApiConfigHandler.java
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DefaultK8sApiConfigHandler.java
@@ -39,6 +39,7 @@
 import org.onosproject.k8snode.api.K8sHostState;
 import org.onosproject.k8snode.api.K8sNode;
 import org.onosproject.k8snode.api.K8sNodeAdminService;
+import org.onosproject.k8snode.api.K8sNodeInfo;
 import org.onosproject.k8snode.api.K8sRouterBridge;
 import org.onosproject.k8snode.api.K8sTunnelBridge;
 import org.osgi.service.component.annotations.Activate;
@@ -274,7 +275,7 @@
                 .hostname(hostname)
                 .managementIp(managementIp)
                 .dataIp(dataIp)
-                .nodeIp(nodeIp)
+                .nodeInfo(new K8sNodeInfo(nodeIp, null))
                 .extIntf(extIntf)
                 .type(nodeType)
                 .segmentId(config.segmentId())
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DistributedK8sNodeStore.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DistributedK8sNodeStore.java
index 8d3e926..90ec2a0 100644
--- a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DistributedK8sNodeStore.java
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DistributedK8sNodeStore.java
@@ -25,6 +25,7 @@
 import org.onosproject.k8snode.api.K8sExternalNetwork;
 import org.onosproject.k8snode.api.K8sNode;
 import org.onosproject.k8snode.api.K8sNodeEvent;
+import org.onosproject.k8snode.api.K8sNodeInfo;
 import org.onosproject.k8snode.api.K8sNodeState;
 import org.onosproject.k8snode.api.K8sNodeStore;
 import org.onosproject.k8snode.api.K8sNodeStoreDelegate;
@@ -79,6 +80,7 @@
             .register(K8sNode.class)
             .register(DefaultK8sNode.class)
             .register(K8sNode.Type.class)
+            .register(K8sNodeInfo.class)
             .register(K8sNodeState.class)
             .register(K8sApiConfig.Mode.class)
             .register(K8sExternalNetwork.class)
diff --git a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sNodeCodecTest.java b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sNodeCodecTest.java
index ae31130..d2061d2 100644
--- a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sNodeCodecTest.java
+++ b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sNodeCodecTest.java
@@ -28,6 +28,7 @@
 import org.onosproject.core.CoreService;
 import org.onosproject.k8snode.api.DefaultK8sNode;
 import org.onosproject.k8snode.api.K8sNode;
+import org.onosproject.k8snode.api.K8sNodeInfo;
 import org.onosproject.k8snode.api.K8sNodeState;
 import org.onosproject.net.DeviceId;
 
@@ -86,7 +87,7 @@
                 .state(K8sNodeState.INIT)
                 .managementIp(IpAddress.valueOf("10.10.10.1"))
                 .dataIp(IpAddress.valueOf("20.20.20.2"))
-                .nodeIp(IpAddress.valueOf("30.30.30.3"))
+                .nodeInfo(new K8sNodeInfo(IpAddress.valueOf("30.30.30.3"), null))
                 .intgBridge(DeviceId.deviceId("kbr-int"))
                 .extIntf("eth1")
                 .extBridgeIp(IpAddress.valueOf("10.10.10.5"))
diff --git a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/K8sNodeManagerTest.java b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/K8sNodeManagerTest.java
index e336164..a1df1b6 100644
--- a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/K8sNodeManagerTest.java
+++ b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/K8sNodeManagerTest.java
@@ -23,6 +23,7 @@
 import org.onlab.junit.TestUtils;
 import org.onlab.packet.ChassisId;
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
 import org.onosproject.cluster.ClusterServiceAdapter;
 import org.onosproject.cluster.LeadershipServiceAdapter;
 import org.onosproject.core.ApplicationId;
@@ -32,6 +33,7 @@
 import org.onosproject.k8snode.api.DefaultK8sNode;
 import org.onosproject.k8snode.api.K8sNode;
 import org.onosproject.k8snode.api.K8sNodeEvent;
+import org.onosproject.k8snode.api.K8sNodeInfo;
 import org.onosproject.k8snode.api.K8sNodeListener;
 import org.onosproject.k8snode.api.K8sNodeState;
 import org.onosproject.net.DefaultDevice;
@@ -74,6 +76,10 @@
     private static final String MINION_2_HOSTNAME = "minion_2";
     private static final String MINION_3_HOSTNAME = "minion_3";
 
+    private static final IpAddress NODE_IP = IpAddress.valueOf("30.30.30.30");
+    private static final MacAddress NODE_MAC = MacAddress.valueOf("fa:00:00:00:00:08");
+    private static final K8sNodeInfo NODE_INFO = new K8sNodeInfo(NODE_IP, NODE_MAC);
+
     private static final Device MINION_1_INTG_DEVICE = createDevice(1);
     private static final Device MINION_2_INTG_DEVICE = createDevice(2);
     private static final Device MINION_3_INTG_DEVICE = createDevice(3);
@@ -100,6 +106,7 @@
             MINION_1_LOCAL_DEVICE,
             MINION_1_TUN_DEVICE,
             IpAddress.valueOf("10.100.0.1"),
+            NODE_INFO,
             INIT
     );
     private static final K8sNode MINION_2 = createNode(
@@ -111,6 +118,7 @@
             MINION_2_LOCAL_DEVICE,
             MINION_2_TUN_DEVICE,
             IpAddress.valueOf("10.100.0.2"),
+            NODE_INFO,
             INIT
     );
     private static final K8sNode MINION_3 = createNode(
@@ -122,6 +130,7 @@
             MINION_3_LOCAL_DEVICE,
             MINION_3_TUN_DEVICE,
             IpAddress.valueOf("10.100.0.3"),
+            NODE_INFO,
             COMPLETE
     );
 
@@ -349,7 +358,8 @@
     private static K8sNode createNode(String clusterName, String hostname, K8sNode.Type type,
                                       Device intgBridge, Device extBridge,
                                       Device localBridge, Device tunBridge,
-                                      IpAddress ipAddr, K8sNodeState state) {
+                                      IpAddress ipAddr, K8sNodeInfo nodeInfo,
+                                      K8sNodeState state) {
         return DefaultK8sNode.builder()
                 .hostname(hostname)
                 .clusterName(clusterName)
@@ -360,7 +370,7 @@
                 .tunBridge(tunBridge.id())
                 .managementIp(ipAddr)
                 .dataIp(ipAddr)
-                .nodeIp(ipAddr)
+                .nodeInfo(nodeInfo)
                 .state(state)
                 .build();
     }
diff --git a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/web/K8sNodeWebResourceTest.java b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/web/K8sNodeWebResourceTest.java
index 9908e99..b99c881 100644
--- a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/web/K8sNodeWebResourceTest.java
+++ b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/web/K8sNodeWebResourceTest.java
@@ -30,6 +30,7 @@
 import org.onosproject.k8snode.api.K8sApiConfigAdminService;
 import org.onosproject.k8snode.api.K8sNode;
 import org.onosproject.k8snode.api.K8sNodeAdminService;
+import org.onosproject.k8snode.api.K8sNodeInfo;
 import org.onosproject.k8snode.api.K8sNodeState;
 import org.onosproject.k8snode.codec.HostNodesInfoCodec;
 import org.onosproject.k8snode.codec.K8sApiConfigCodec;
@@ -97,7 +98,7 @@
                 .type(K8sNode.Type.MINION)
                 .dataIp(IpAddress.valueOf("10.134.34.222"))
                 .managementIp(IpAddress.valueOf("10.134.231.30"))
-                .nodeIp(IpAddress.valueOf("30.30.30.3"))
+                .nodeInfo(new K8sNodeInfo(IpAddress.valueOf("30.30.30.3"), null))
                 .intgBridge(DeviceId.deviceId("of:00000000000000a1"))
                 .extBridge(DeviceId.deviceId("of:00000000000000b1"))
                 .state(K8sNodeState.INIT)
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
index cf776d1..1ee3ee2 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
@@ -102,6 +102,9 @@
 
     public static final int PRIORITY_CNI_PT_ARP_RULE = 41500;
     public static final int PRIORITY_CNI_PT_IP_RULE = 41400;
+    public static final int PRIORITY_CNI_PT_NODE_PORT_IP_RULE = 42500;
+    public static final int PRIORITY_CNI_PT_NODE_PORT_ARP_RULE = 43000;
+    public static final int PRIORITY_CNI_PT_NODE_PORT_ARP_EXT_RULE = 41500;
 
     // flow table index
     public static final int STAT_INBOUND_TABLE = 0;
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackK8sIntegrationService.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackK8sIntegrationService.java
index 660764e..15c1ec0 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackK8sIntegrationService.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackK8sIntegrationService.java
@@ -51,4 +51,20 @@
     void uninstallCniPtNodeRules(IpAddress k8sNodeIp, IpPrefix podCidr,
                                  IpPrefix serviceCidr, IpAddress podGatewayIp,
                                  String osK8sIntPortName, MacAddress k8sIntOsPortMac);
+
+    /**
+     * Installs K8S pass-through CNI related node port flow rules.
+     *
+     * @param k8sNodeIp         kubernetes node IP address
+     * @param osK8sExtPortName  openstack k8s external patch port name
+     */
+    void installCniPtNodePortRules(IpAddress k8sNodeIp, String osK8sExtPortName);
+
+    /**
+     * Uninstalls K8S pass-through CNI related node port flow rules.
+     *
+     * @param k8sNodeIp         kubernetes node IP address
+     * @param osK8sExtPortName  openstack k8s external patch port name
+     */
+    void uninstallCniPtNodePortRules(IpAddress k8sNodeIp, String osK8sExtPortName);
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackK8sIntegrationManager.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackK8sIntegrationManager.java
index 2f5c1d1..ab99742 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackK8sIntegrationManager.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackK8sIntegrationManager.java
@@ -16,14 +16,19 @@
 package org.onosproject.openstacknetworking.impl;
 
 
+import org.onlab.packet.ARP;
 import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
-import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
@@ -38,6 +43,7 @@
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
 import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackNodeService;
+import org.openstack4j.model.network.Network;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Deactivate;
@@ -45,12 +51,22 @@
 import org.osgi.service.component.annotations.ReferenceCardinality;
 import org.slf4j.Logger;
 
+import java.util.Map;
+import java.util.Objects;
+
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
 import static org.onosproject.openstacknetworking.api.Constants.DHCP_TABLE;
 import static org.onosproject.openstacknetworking.api.Constants.FLAT_TABLE;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CNI_PT_IP_RULE;
+import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CNI_PT_NODE_PORT_ARP_EXT_RULE;
+import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CNI_PT_NODE_PORT_ARP_RULE;
+import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_CNI_PT_NODE_PORT_IP_RULE;
 import static org.onosproject.openstacknetworking.api.Constants.STAT_FLAT_OUTBOUND_TABLE;
 import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.FLAT;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.shiftIpDomain;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.structurePortName;
+import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildPortRangeMatches;
+import static org.onosproject.openstacknode.api.Constants.INTEGRATION_TO_PHYSICAL_PREFIX;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -65,7 +81,10 @@
 
     protected final Logger log = getLogger(getClass());
 
-    public static final String SHIFTED_IP_PREFIX = "172.10";
+    private static final String SHIFTED_IP_PREFIX = "172.10";
+    private static final int NODE_PORT_MIN = 30000;
+    private static final int NODE_PORT_MAX = 32767;
+    public static final String NODE_FAKE_IP_STR = "172.172.172.172";
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected CoreService coreService;
@@ -85,6 +104,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected InstancePortService instancePortService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected DeviceService deviceService;
+
     private ApplicationId appId;
 
     @Activate
@@ -119,22 +141,30 @@
         setPodToNodeIpRules(k8sNodeIp, podGatewayIp, osK8sIntPortName, false);
     }
 
+    @Override
+    public void installCniPtNodePortRules(IpAddress k8sNodeIp, String osK8sExtPortName) {
+        setNodePortIngressRules(k8sNodeIp, osK8sExtPortName, true);
+        setNodePortEgressRules(k8sNodeIp, osK8sExtPortName, true);
+
+        setArpRequestRules(k8sNodeIp, osK8sExtPortName, true);
+        setArpReplyRules(k8sNodeIp, osK8sExtPortName, true);
+    }
+
+    @Override
+    public void uninstallCniPtNodePortRules(IpAddress k8sNodeIp, String osK8sExtPortName) {
+        setNodePortIngressRules(k8sNodeIp, osK8sExtPortName, false);
+        setNodePortEgressRules(k8sNodeIp, osK8sExtPortName, false);
+
+        setArpRequestRules(k8sNodeIp, osK8sExtPortName, false);
+        setArpReplyRules(k8sNodeIp, osK8sExtPortName, false);
+    }
+
     private void setNodeToPodIpRules(IpAddress k8sNodeIp,
                                      IpPrefix podCidr, IpPrefix serviceCidr,
                                      IpAddress gatewayIp, String osK8sIntPortName,
                                      MacAddress k8sIntOsPortMac, boolean install) {
 
-        InstancePort instPort = instancePortService.instancePorts().stream().filter(p -> {
-            OpenstackNetwork.Type netType = osNetworkService.networkType(p.networkId());
-            return netType == FLAT && p.ipAddress().equals(k8sNodeIp);
-        }).findAny().orElse(null);
-
-        if (instPort == null) {
-            return;
-        }
-
-        DeviceId deviceId = instPort.deviceId();
-        OpenstackNode osNode = osNodeService.node(deviceId);
+        OpenstackNode osNode = osNodeByNodeIp(k8sNodeIp);
 
         if (osNode == null) {
             return;
@@ -203,17 +233,13 @@
 
     private void setPodToNodeIpRules(IpAddress k8sNodeIp, IpAddress gatewayIp,
                                      String osK8sIntPortName, boolean install) {
-        InstancePort instPort = instancePortService.instancePorts().stream().filter(p -> {
-            OpenstackNetwork.Type netType = osNetworkService.networkType(p.networkId());
-            return netType == FLAT && p.ipAddress().equals(k8sNodeIp);
-        }).findAny().orElse(null);
+        InstancePort instPort = instPortByNodeIp(k8sNodeIp);
 
         if (instPort == null) {
             return;
         }
 
-        DeviceId deviceId = instPort.deviceId();
-        OpenstackNode osNode = osNodeService.node(deviceId);
+        OpenstackNode osNode = osNodeByNodeIp(k8sNodeIp);
 
         if (osNode == null) {
             return;
@@ -247,4 +273,233 @@
                 install
         );
     }
+
+    private void setNodePortIngressRules(IpAddress k8sNodeIp,
+                                         String osK8sExtPortName,
+                                         boolean install) {
+        OpenstackNode osNode = osNodeByNodeIp(k8sNodeIp);
+
+        if (osNode == null) {
+            return;
+        }
+
+        PortNumber osK8sExtPortNum = portNumberByNodeIpAndPortName(k8sNodeIp, osK8sExtPortName);
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(osK8sExtPortNum)
+                .build();
+
+        Map<TpPort, TpPort> portRangeMatchMap =
+                buildPortRangeMatches(NODE_PORT_MIN, NODE_PORT_MAX);
+
+        portRangeMatchMap.forEach((key, value) -> {
+            TrafficSelector.Builder tcpSelectorBuilder = DefaultTrafficSelector.builder()
+                    .matchEthType(Ethernet.TYPE_IPV4)
+                    .matchIPDst(IpPrefix.valueOf(k8sNodeIp, 32))
+                    .matchIPProtocol(IPv4.PROTOCOL_TCP)
+                    .matchTcpDstMasked(key, value);
+
+            TrafficSelector.Builder udpSelectorBuilder = DefaultTrafficSelector.builder()
+                    .matchEthType(Ethernet.TYPE_IPV4)
+                    .matchIPDst(IpPrefix.valueOf(k8sNodeIp, 32))
+                    .matchIPProtocol(IPv4.PROTOCOL_UDP)
+                    .matchUdpDstMasked(key, value);
+
+            osFlowRuleService.setRule(
+                    appId,
+                    osNode.intgBridge(),
+                    tcpSelectorBuilder.build(),
+                    treatment,
+                    PRIORITY_CNI_PT_NODE_PORT_IP_RULE,
+                    FLAT_TABLE,
+                    install
+            );
+
+            osFlowRuleService.setRule(
+                    appId,
+                    osNode.intgBridge(),
+                    udpSelectorBuilder.build(),
+                    treatment,
+                    PRIORITY_CNI_PT_NODE_PORT_IP_RULE,
+                    FLAT_TABLE,
+                    install
+            );
+        });
+    }
+
+    private void setNodePortEgressRules(IpAddress k8sNodeIp,
+                                        String osK8sExtPortName,
+                                        boolean install) {
+        InstancePort instPort = instPortByNodeIp(k8sNodeIp);
+
+        if (instPort == null) {
+            return;
+        }
+
+        OpenstackNode osNode = osNodeByNodeIp(k8sNodeIp);
+
+        if (osNode == null) {
+            return;
+        }
+
+        PortNumber osK8sExtPortNum = portNumberByNodeIpAndPortName(k8sNodeIp, osK8sExtPortName);
+
+        Port phyPort = phyPortByInstPort(instPort);
+
+        if (phyPort == null) {
+            log.warn("No phys interface found for instance port {}", instPort);
+            return;
+        }
+
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchInPort(osK8sExtPortNum)
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setEthSrc(instPort.macAddress())
+                .setOutput(phyPort.number())
+                .build();
+
+        osFlowRuleService.setRule(
+                appId,
+                osNode.intgBridge(),
+                selector,
+                treatment,
+                PRIORITY_CNI_PT_NODE_PORT_IP_RULE,
+                DHCP_TABLE,
+                install
+        );
+    }
+
+    private void setArpRequestRules(IpAddress k8sNodeIp, String osK8sExtPortName, boolean install) {
+        InstancePort instPort = instPortByNodeIp(k8sNodeIp);
+
+        if (instPort == null) {
+            return;
+        }
+
+        OpenstackNode osNode = osNodeByNodeIp(k8sNodeIp);
+
+        if (osNode == null) {
+            return;
+        }
+
+        PortNumber osK8sExtPortNum = portNumberByNodeIpAndPortName(k8sNodeIp, osK8sExtPortName);
+
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_ARP)
+                .matchArpOp(ARP.OP_REQUEST)
+                .matchInPort(osK8sExtPortNum)
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .transition(STAT_FLAT_OUTBOUND_TABLE)
+                .build();
+
+        osFlowRuleService.setRule(
+                appId,
+                osNode.intgBridge(),
+                selector,
+                treatment,
+                PRIORITY_CNI_PT_NODE_PORT_ARP_RULE,
+                DHCP_TABLE,
+                install
+        );
+
+        Port phyPort = phyPortByInstPort(instPort);
+
+        if (phyPort == null) {
+            log.warn("No phys interface found for instance port {}", instPort);
+            return;
+        }
+
+        TrafficTreatment extTreatment = DefaultTrafficTreatment.builder()
+                .setOutput(phyPort.number())
+                .build();
+
+        osFlowRuleService.setRule(
+                appId,
+                osNode.intgBridge(),
+                selector,
+                extTreatment,
+                PRIORITY_CNI_PT_NODE_PORT_ARP_EXT_RULE,
+                FLAT_TABLE,
+                install
+        );
+    }
+
+    private void setArpReplyRules(IpAddress k8sNodeIp, String osK8sExtPortName, boolean install) {
+        OpenstackNode osNode = osNodeByNodeIp(k8sNodeIp);
+
+        if (osNode == null) {
+            return;
+        }
+
+        PortNumber osK8sExtPortNum = portNumberByNodeIpAndPortName(k8sNodeIp, osK8sExtPortName);
+
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_ARP)
+                .matchArpOp(ARP.OP_REPLY)
+                .matchArpTpa(Ip4Address.valueOf(NODE_FAKE_IP_STR))
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(osK8sExtPortNum)
+                .build();
+
+        osFlowRuleService.setRule(
+                appId,
+                osNode.intgBridge(),
+                selector,
+                treatment,
+                PRIORITY_CNI_PT_NODE_PORT_ARP_RULE,
+                FLAT_TABLE,
+                install
+        );
+    }
+
+    private InstancePort instPortByNodeIp(IpAddress k8sNodeIp) {
+        return instancePortService.instancePorts().stream().filter(p -> {
+            OpenstackNetwork.Type netType = osNetworkService.networkType(p.networkId());
+            return netType == FLAT && p.ipAddress().equals(k8sNodeIp);
+        }).findAny().orElse(null);
+    }
+
+    private OpenstackNode osNodeByNodeIp(IpAddress k8sNodeIp) {
+        InstancePort instPort = instPortByNodeIp(k8sNodeIp);
+
+        if (instPort == null) {
+            return null;
+        }
+
+        return osNodeService.node(instPort.deviceId());
+    }
+
+    private PortNumber portNumberByNodeIpAndPortName(IpAddress k8sNodeIp, String portName) {
+        OpenstackNode osNode = osNodeByNodeIp(k8sNodeIp);
+
+        if (osNode == null) {
+            return null;
+        }
+
+        return osNode.portNumByName(portName);
+    }
+
+    private Port phyPortByInstPort(InstancePort instPort) {
+        Network network = osNetworkService.network(instPort.networkId());
+
+        if (network == null) {
+            log.warn("The network does not exist");
+            return null;
+        }
+
+        return deviceService.getPorts(instPort.deviceId()).stream()
+                .filter(port -> {
+                    String annotPortName = port.annotations().value(PORT_NAME);
+                    String portName = structurePortName(INTEGRATION_TO_PHYSICAL_PREFIX
+                            + network.getProviderPhyNet());
+                    return Objects.equals(annotPortName, portName);
+                }).findAny().orElse(null);
+    }
 }
\ No newline at end of file
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupHandler.java
index 8e8bc83..70f197a 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupHandler.java
@@ -17,7 +17,6 @@
 package org.onosproject.openstacknetworking.impl;
 
 import com.google.common.base.Strings;
-import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IPv4;
@@ -119,6 +118,7 @@
 import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.USE_SECURITY_GROUP_DEFAULT;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValueAsBoolean;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
+import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildPortRangeMatches;
 import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.computeCtMaskFlag;
 import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.computeCtStateFlag;
 import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.niciraConnTrackTreatmentBuilder;
@@ -140,14 +140,7 @@
 
     private static final int VM_IP_PREFIX = 32;
 
-    private static final String STR_ZERO = "0";
-    private static final String STR_ONE = "1";
     private static final String STR_NULL = "null";
-    private static final String STR_PADDING = "0000000000000000";
-    private static final int MASK_BEGIN_IDX = 0;
-    private static final int MASK_MAX_IDX = 16;
-    private static final int MASK_RADIX = 2;
-    private static final int PORT_RADIX = 16;
 
     /** Apply OpenStack security group rule for VM traffic. */
     private boolean useSecurityGroup = USE_SECURITY_GROUP_DEFAULT;
@@ -845,88 +838,6 @@
                 });
     }
 
-    private int binLower(String binStr, int bits) {
-        StringBuilder outBin = new StringBuilder(
-                        binStr.substring(MASK_BEGIN_IDX, MASK_MAX_IDX - bits));
-        for (int i = 0; i < bits; i++) {
-            outBin.append(STR_ZERO);
-        }
-
-        return Integer.parseInt(outBin.toString(), MASK_RADIX);
-    }
-
-    private int binHigher(String binStr, int bits) {
-        StringBuilder outBin = new StringBuilder(
-                        binStr.substring(MASK_BEGIN_IDX, MASK_MAX_IDX - bits));
-        for (int i = 0; i < bits; i++) {
-            outBin.append(STR_ONE);
-        }
-
-        return Integer.parseInt(outBin.toString(), MASK_RADIX);
-    }
-
-    private int testMasks(String binStr, int start, int end) {
-        int mask = MASK_BEGIN_IDX;
-        for (; mask <= MASK_MAX_IDX; mask++) {
-            int maskStart = binLower(binStr, mask);
-            int maskEnd = binHigher(binStr, mask);
-            if (maskStart < start || maskEnd > end) {
-                return mask - 1;
-            }
-        }
-
-        return mask;
-    }
-
-    private String getMask(int bits) {
-        switch (bits) {
-            case 0:  return "ffff";
-            case 1:  return "fffe";
-            case 2:  return "fffc";
-            case 3:  return "fff8";
-            case 4:  return "fff0";
-            case 5:  return "ffe0";
-            case 6:  return "ffc0";
-            case 7:  return "ff80";
-            case 8:  return "ff00";
-            case 9:  return "fe00";
-            case 10: return "fc00";
-            case 11: return "f800";
-            case 12: return "f000";
-            case 13: return "e000";
-            case 14: return "c000";
-            case 15: return "8000";
-            case 16: return "0000";
-            default: return null;
-        }
-    }
-
-    private Map<TpPort, TpPort> buildPortRangeMatches(int portMin, int portMax) {
-
-        boolean processing = true;
-        int start = portMin;
-        Map<TpPort, TpPort> portMaskMap = Maps.newHashMap();
-        while (processing) {
-            String minStr = Integer.toBinaryString(start);
-            String binStrMinPadded = STR_PADDING.substring(minStr.length()) + minStr;
-
-            int mask = testMasks(binStrMinPadded, start, portMax);
-            int maskStart = binLower(binStrMinPadded, mask);
-            int maskEnd = binHigher(binStrMinPadded, mask);
-
-            log.debug("start : {} port/mask = {} / {} ", start, getMask(mask), maskStart);
-            portMaskMap.put(TpPort.tpPort(maskStart), TpPort.tpPort(
-                    Integer.parseInt(Objects.requireNonNull(getMask(mask)), PORT_RADIX)));
-
-            start = maskEnd + 1;
-            if (start > portMax) {
-                processing = false;
-            }
-        }
-
-        return portMaskMap;
-    }
-
     private class InternalInstancePortListener implements InstancePortListener {
 
         @Override
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/RulePopulatorUtil.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/RulePopulatorUtil.java
index de5295c..aca49de 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/RulePopulatorUtil.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/RulePopulatorUtil.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.openstacknetworking.util;
 
+import com.google.common.collect.Maps;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.TpPort;
@@ -38,6 +39,8 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 
 import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_LOAD;
 import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_ARP_SHA_TO_THA;
@@ -82,6 +85,14 @@
     private static final int PORT_MIN_FLAG = 4;
     private static final int PORT_MAX_FLAG = 5;
 
+    private static final String STR_ZERO = "0";
+    private static final String STR_ONE = "1";
+    private static final String STR_PADDING = "0000000000000000";
+    private static final int MASK_BEGIN_IDX = 0;
+    private static final int MASK_MAX_IDX = 16;
+    private static final int MASK_RADIX = 2;
+    private static final int PORT_RADIX = 16;
+
     // Refer to http://openvswitch.org/support/dist-docs/ovs-fields.7.txt for the values
     public static final long CT_STATE_NONE = 0;
     public static final long CT_STATE_NEW = 0x01;
@@ -405,6 +416,95 @@
         return ctMaskFlag;
     }
 
+    /**
+     * Computes port and port mask value from port min/max values.
+     *
+     * @param portMin port min value
+     * @param portMax port max value
+     * @return Port Mask value
+     */
+    public static Map<TpPort, TpPort> buildPortRangeMatches(int portMin, int portMax) {
+
+        boolean processing = true;
+        int start = portMin;
+        Map<TpPort, TpPort> portMaskMap = Maps.newHashMap();
+        while (processing) {
+            String minStr = Integer.toBinaryString(start);
+            String binStrMinPadded = STR_PADDING.substring(minStr.length()) + minStr;
+
+            int mask = testMasks(binStrMinPadded, start, portMax);
+            int maskStart = binLower(binStrMinPadded, mask);
+            int maskEnd = binHigher(binStrMinPadded, mask);
+
+            log.debug("start : {} port/mask = {} / {} ", start, getMask(mask), maskStart);
+            portMaskMap.put(TpPort.tpPort(maskStart), TpPort.tpPort(
+                    Integer.parseInt(Objects.requireNonNull(getMask(mask)), PORT_RADIX)));
+
+            start = maskEnd + 1;
+            if (start > portMax) {
+                processing = false;
+            }
+        }
+
+        return portMaskMap;
+    }
+
+    private static int binLower(String binStr, int bits) {
+        StringBuilder outBin = new StringBuilder(
+                binStr.substring(MASK_BEGIN_IDX, MASK_MAX_IDX - bits));
+        for (int i = 0; i < bits; i++) {
+            outBin.append(STR_ZERO);
+        }
+
+        return Integer.parseInt(outBin.toString(), MASK_RADIX);
+    }
+
+    private static int binHigher(String binStr, int bits) {
+        StringBuilder outBin = new StringBuilder(
+                binStr.substring(MASK_BEGIN_IDX, MASK_MAX_IDX - bits));
+        for (int i = 0; i < bits; i++) {
+            outBin.append(STR_ONE);
+        }
+
+        return Integer.parseInt(outBin.toString(), MASK_RADIX);
+    }
+
+    private static int testMasks(String binStr, int start, int end) {
+        int mask = MASK_BEGIN_IDX;
+        for (; mask <= MASK_MAX_IDX; mask++) {
+            int maskStart = binLower(binStr, mask);
+            int maskEnd = binHigher(binStr, mask);
+            if (maskStart < start || maskEnd > end) {
+                return mask - 1;
+            }
+        }
+
+        return mask;
+    }
+
+    private static String getMask(int bits) {
+        switch (bits) {
+            case 0:  return "ffff";
+            case 1:  return "fffe";
+            case 2:  return "fffc";
+            case 3:  return "fff8";
+            case 4:  return "fff0";
+            case 5:  return "ffe0";
+            case 6:  return "ffc0";
+            case 7:  return "ff80";
+            case 8:  return "ff00";
+            case 9:  return "fe00";
+            case 10: return "fc00";
+            case 11: return "f800";
+            case 12: return "f000";
+            case 13: return "e000";
+            case 14: return "c000";
+            case 15: return "8000";
+            case 16: return "0000";
+            default: return null;
+        }
+    }
+
     private static boolean checkTreatmentResolver(Device device) {
         if (device == null || !device.is(ExtensionTreatmentResolver.class)) {
             log.warn("Nicira extension treatment is not supported");
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackK8sIntegrationWebResource.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackK8sIntegrationWebResource.java
index c38984b..ef3353b 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackK8sIntegrationWebResource.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackK8sIntegrationWebResource.java
@@ -45,6 +45,7 @@
 
     private static final String K8S_NODE_IP = "k8sNodeIp";
     private static final String OS_K8S_INT_PORT_NAME = "osK8sIntPortName";
+    private static final String OS_K8S_EXT_PORT_NAME = "osK8sExtPortName";
     private static final String POD_CIDR = "podCidr";
     private static final String SERVICE_CIDR = "serviceCidr";
     private static final String POD_GW_IP = "podGwIp";
@@ -56,7 +57,7 @@
     /**
      * Installs CNI pass-through related flow rules for each kubernetes nodes.
      *
-     * @param input     JSON string
+     * @param input JSON string
      * @return 200 ok, 400 BAD_REQUEST if the json is malformed
      * @throws IOException exception
      */
@@ -82,9 +83,9 @@
     }
 
     /**
-     * Installs CNI pass-through related flow rules for each kubernetes nodes.
+     * Uninstalls CNI pass-through related flow rules for each kubernetes nodes.
      *
-     * @param input     JSON string
+     * @param input JSON string
      * @return 200 ok, 400 BAD_REQUEST if the json is malformed
      * @throws IOException exception
      */
@@ -108,4 +109,50 @@
 
         return Response.ok().build();
     }
+
+    /**
+     * Installs CNI pass-through related node port flow rules.
+     *
+     * @param input JSON string
+     * @return 200 ok, 400 BAD_REQUEST if the json is malformed
+     * @throws IOException exception
+     */
+    @PUT
+    @Path("nodeport/pt-install")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response installCniPtNodePortRules(InputStream input) throws IOException {
+        log.trace("Install K8S CNI pass-through node port rules");
+
+        JsonNode json = readTreeFromStream(mapper().enable(INDENT_OUTPUT), input);
+        IpAddress k8sNodeIp = IpAddress.valueOf(json.get(K8S_NODE_IP).asText());
+        String osK8sExtPortName = json.get(OS_K8S_EXT_PORT_NAME).asText();
+
+        intService.installCniPtNodePortRules(k8sNodeIp, osK8sExtPortName);
+
+        return Response.ok().build();
+    }
+
+    /**
+     * Uninstalls CNI pass-through related node port flow rules.
+     *
+     * @param input JSON string
+     * @return 200 ok, 400 BAD_REQUEST if the json is malformed
+     * @throws IOException exception
+     */
+    @PUT
+    @Path("nodeport/pt-uninstall")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response uninstallCniPtNodePortRules(InputStream input) throws IOException {
+        log.trace("Uninstall K8S CNI pass-through node port rules");
+
+        JsonNode json = readTreeFromStream(mapper().enable(INDENT_OUTPUT), input);
+        IpAddress k8sNodeIp = IpAddress.valueOf(json.get(K8S_NODE_IP).asText());
+        String osK8sExtPortName = json.get(OS_K8S_EXT_PORT_NAME).asText();
+
+        intService.uninstallCniPtNodePortRules(k8sNodeIp, osK8sExtPortName);
+
+        return Response.ok().build();
+    }
 }