Initial support VM and container communication via POD and service IP

Change-Id: Ic87beee6ed122ec5551370c2b6a2789edf8fba5b
diff --git a/apps/k8s-networking/BUILD b/apps/k8s-networking/BUILD
index 6ba4011..9729e56 100644
--- a/apps/k8s-networking/BUILD
+++ b/apps/k8s-networking/BUILD
@@ -2,6 +2,8 @@
     "//apps/k8s-networking/api:onos-apps-k8s-networking-api",
     "//apps/k8s-networking/app:onos-apps-k8s-networking-app",
     "@commons_net//jar",
+    "@jersey_client//jar",
+    "@json//jar",
     "@k8s_client//jar",
     "@k8s_model//jar",
     "@okhttp//jar",
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 3731936..d017c50 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
@@ -133,7 +133,7 @@
     public static final int EXT_ENTRY_TABLE = 0;
     public static final int POD_RESOLUTION_TABLE = 11;
 
-    public static final int ROUTER_EXTRY_TABLE = 0;
+    public static final int ROUTER_ENTRY_TABLE = 0;
     public static final int EXT_RESOLUTION_TABLE = 11;
 
     public static final int LOCAL_ENTRY_TABLE = 0;
diff --git a/apps/k8s-networking/app/BUILD b/apps/k8s-networking/app/BUILD
index 725e27d..87c974e 100644
--- a/apps/k8s-networking/app/BUILD
+++ b/apps/k8s-networking/app/BUILD
@@ -5,6 +5,8 @@
     "//apps/k8s-node/api:onos-apps-k8s-node-api",
     "//apps/k8s-networking/api:onos-apps-k8s-networking-api",
     "@commons_net//jar",
+    "@jersey_client//jar",
+    "@json//jar",
     "@k8s_client//jar",
     "@k8s_model//jar",
     "@okhttp//jar",
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/DistributedK8sServiceStore.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/DistributedK8sServiceStore.java
index 225158a..07b122f 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/DistributedK8sServiceStore.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/DistributedK8sServiceStore.java
@@ -23,6 +23,7 @@
 import io.fabric8.kubernetes.api.model.LoadBalancerStatus;
 import io.fabric8.kubernetes.api.model.ManagedFieldsEntry;
 import io.fabric8.kubernetes.api.model.ObjectMeta;
+import io.fabric8.kubernetes.api.model.OwnerReference;
 import io.fabric8.kubernetes.api.model.Service;
 import io.fabric8.kubernetes.api.model.ServicePort;
 import io.fabric8.kubernetes.api.model.ServiceSpec;
@@ -91,6 +92,7 @@
             .register(ClientIPConfig.class)
             .register(ManagedFieldsEntry.class)
             .register(FieldsV1.class)
+            .register(OwnerReference.class)
             .register(LinkedHashMap.class)
             .register(Collection.class)
             .build();
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
new file mode 100644
index 0000000..0c68e20
--- /dev/null
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sOpenstackIntegrationHandler.java
@@ -0,0 +1,203 @@
+/*
+ * 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.k8snetworking.impl;
+
+import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.k8snetworking.api.K8sNetwork;
+import org.onosproject.k8snetworking.api.K8sNetworkService;
+import org.onosproject.k8snode.api.K8sNode;
+import org.onosproject.k8snode.api.K8sNodeEvent;
+import org.onosproject.k8snode.api.K8sNodeListener;
+import org.onosproject.k8snode.api.K8sNodeService;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
+import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.SERVICE_IP_CIDR_DEFAULT;
+import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getBclassIpPrefixFromCidr;
+import static org.onosproject.k8snode.api.K8sApiConfig.Mode.PASSTHROUGH;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides kubernetes and openstack integration feature.
+ */
+@Component(immediate = true)
+public class K8sOpenstackIntegrationHandler {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String K8S_NODE_IP = "k8sNodeIp";
+    private static final String OS_K8S_INT_PORT_NAME = "osK8sIntPortName";
+    private static final String POD_CIDR = "podCidr";
+    private static final String SERVICE_CIDR = "serviceCidr";
+    private static final String POD_GW_IP = "podGwIp";
+    private static final String K8S_INT_OS_PORT_MAC = "k8sIntOsPortMac";
+    private static final String ONOS_PORT = "8181";
+    private static final String OS_K8S_INTEGRATION_EP = "onos/openstacknetworking/integration/";
+    private static final String ONOS_USERNAME = "karaf";
+    private static final String ONOS_PASSWORD = "karaf";
+    private static final String B_CLASS_SUFFIX = ".0.0/16";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected LeadershipService leadershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected K8sNodeService k8sNodeService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected K8sNetworkService k8sNetworkService;
+
+    private final InternalK8sNodeListener k8sNodeListener =
+            new InternalK8sNodeListener();
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+
+    private ApplicationId appId;
+    private NodeId localNodeId;
+
+    @Activate
+    protected void activate() {
+        appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
+        localNodeId = clusterService.getLocalNode().id();
+        k8sNodeService.addListener(k8sNodeListener);
+
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        k8sNodeService.removeListener(k8sNodeListener);
+
+        log.info("Stopped");
+    }
+
+    private void setCniPtNodeRules(K8sNode k8sNode, boolean install) {
+        K8sNetwork network = k8sNetworkService.network(k8sNode.hostname());
+        String k8sNodeIp = k8sNode.nodeIp().toString();
+        String gatewayIp = network.gatewayIp().toString();
+        String nodePodCidr = network.cidr();
+        String srcPodPrefix = getBclassIpPrefixFromCidr(nodePodCidr);
+        String podCidr = srcPodPrefix + B_CLASS_SUFFIX;
+        String osK8sIntPortName = k8sNode.osToK8sIntgPatchPortName();
+        String k8sIntOsPortMac = k8sNode.portMacByName(k8sNode.k8sIntgToOsPatchPortName()).toString();
+
+        String path = install ? "node/pt-install" : "node/pt-uninstall";
+
+        String jsonString = "";
+
+        try {
+            jsonString = new JSONObject()
+                    .put(K8S_NODE_IP, k8sNodeIp)
+                    .put(POD_GW_IP, gatewayIp)
+                    .put(POD_CIDR, podCidr)
+                    .put(SERVICE_CIDR, SERVICE_IP_CIDR_DEFAULT)
+                    .put(OS_K8S_INT_PORT_NAME, osK8sIntPortName)
+                    .put(K8S_INT_OS_PORT_MAC, k8sIntOsPortMac)
+                    .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 rules.");
+        }
+    }
+
+    private class InternalK8sNodeListener implements K8sNodeListener {
+
+        @Override
+        public boolean isRelevant(K8sNodeEvent event) {
+            return event.subject().mode() == PASSTHROUGH;
+        }
+
+        private boolean isRelevantHelper() {
+            return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
+        }
+
+        @Override
+        public void event(K8sNodeEvent event) {
+            switch (event.type()) {
+                case K8S_NODE_COMPLETE:
+                    eventExecutor.execute(() -> processNodeCompletion(event.subject()));
+                    break;
+                case K8S_NODE_INCOMPLETE:
+                    eventExecutor.execute(() -> processNodeIncompletion(event.subject()));
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        private void processNodeCompletion(K8sNode k8sNode) {
+            if (!isRelevantHelper()) {
+                return;
+            }
+
+            setCniPtNodeRules(k8sNode, true);
+        }
+
+        private void processNodeIncompletion(K8sNode k8sNode) {
+            if (!isRelevantHelper()) {
+                return;
+            }
+
+            setCniPtNodeRules(k8sNode, false);
+        }
+    }
+}
\ No newline at end of file
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sPodPortMapper.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sPodPortMapper.java
index 5239ee4..dfc77cf 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sPodPortMapper.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sPodPortMapper.java
@@ -170,10 +170,13 @@
                     annotations.put(PORT_ID, p.portId());
                     annotations.put(NETWORK_ID, p.networkId());
                     annotations.put(DEVICE_ID, p.deviceId().toString());
-                    annotations.put(PORT_NUMBER, p.portNumber().toString());
                     annotations.put(IP_ADDRESS, p.ipAddress().toString());
                     annotations.put(MAC_ADDRESS, p.macAddress().toString());
 
+                    if (p.portNumber() != null) {
+                        annotations.put(PORT_NUMBER, p.portNumber().toString());
+                    }
+
                     client.pods().inNamespace(pod.getMetadata().getNamespace())
                             .withName(pod.getMetadata().getName())
                             .edit()
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sRoutingSnatHandler.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sRoutingSnatHandler.java
index 56fe815..15c184c 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sRoutingSnatHandler.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sRoutingSnatHandler.java
@@ -34,8 +34,6 @@
 import org.onosproject.k8snetworking.api.K8sPort;
 import org.onosproject.k8snetworking.util.RulePopulatorUtil;
 import org.onosproject.k8snode.api.K8sHost;
-import org.onosproject.k8snode.api.K8sHostEvent;
-import org.onosproject.k8snode.api.K8sHostListener;
 import org.onosproject.k8snode.api.K8sHostService;
 import org.onosproject.k8snode.api.K8sNode;
 import org.onosproject.k8snode.api.K8sNodeEvent;
@@ -72,7 +70,7 @@
 import static org.onosproject.k8snetworking.api.Constants.PRIORITY_DEFAULT_RULE;
 import static org.onosproject.k8snetworking.api.Constants.PRIORITY_EXTERNAL_ROUTING_RULE;
 import static org.onosproject.k8snetworking.api.Constants.PRIORITY_STATEFUL_SNAT_RULE;
-import static org.onosproject.k8snetworking.api.Constants.ROUTER_EXTRY_TABLE;
+import static org.onosproject.k8snetworking.api.Constants.ROUTER_ENTRY_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.ROUTING_TABLE;
 import static org.onosproject.k8snetworking.util.RulePopulatorUtil.CT_NAT_SRC_FLAG;
 import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildMoveArpShaToThaExtension;
@@ -132,8 +130,6 @@
             new InternalK8sNetworkListener();
     private final InternalK8sNodeListener k8sNodeListener =
             new InternalK8sNodeListener();
-    private final InternalK8sHostListener k8sHostListener =
-            new InternalK8sHostListener();
     private final ExecutorService eventExecutor = newSingleThreadExecutor(
             groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
 
@@ -148,14 +144,12 @@
         leadershipService.runForLeadership(appId.name());
         k8sNetworkService.addListener(k8sNetworkListener);
         k8sNodeService.addListener(k8sNodeListener);
-        k8sHostService.addListener(k8sHostListener);
 
         log.info("Started");
     }
 
     @Deactivate
     protected void deactivate() {
-        k8sHostService.removeListener(k8sHostListener);
         k8sNodeService.removeListener(k8sNodeListener);
         k8sNetworkService.removeListener(k8sNetworkListener);
         leadershipService.withdraw(appId.name());
@@ -334,6 +328,9 @@
     private void setRouterSnatUpstreamRule(K8sNode k8sNode,
                                            K8sRouterBridge bridge,
                                            boolean install) {
+        if (k8sNode.routerPortNum() == null) {
+            return;
+        }
 
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
@@ -350,13 +347,17 @@
                 selector,
                 treatment,
                 PRIORITY_DEFAULT_RULE,
-                ROUTER_EXTRY_TABLE,
+                ROUTER_ENTRY_TABLE,
                 install);
     }
 
     private void setRouterSnatDownstreamRule(K8sNode k8sNode,
                                              K8sRouterBridge bridge,
                                              boolean install) {
+        if (k8sNode.routerPortNum() == null) {
+            return;
+        }
+
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchInPort(k8sNode.routerPortNum())
@@ -373,10 +374,24 @@
                 selector,
                 treatment,
                 PRIORITY_DEFAULT_RULE,
-                ROUTER_EXTRY_TABLE,
+                ROUTER_ENTRY_TABLE,
                 install);
     }
 
+    private void setRouterSnatRules(K8sNode k8sNode, boolean install) {
+        for (K8sHost host : k8sHostService.completeHosts()) {
+            if (host.nodeNames().contains(k8sNode.hostname())) {
+                K8sRouterBridge bridge = host.routerBridges().stream()
+                        .filter(b -> b.segmentId() == k8sNode.segmentId())
+                        .findAny().orElse(null);
+                if (bridge != null) {
+                    setRouterSnatUpstreamRule(k8sNode, bridge, install);
+                    setRouterSnatDownstreamRule(k8sNode, bridge, install);
+                }
+            }
+        }
+    }
+
     private class InternalK8sNodeListener implements K8sNodeListener {
 
         private boolean isRelevantHelper() {
@@ -406,6 +421,7 @@
             setExtIntfArpRule(k8sNode, true);
             setExtSnatDownstreamRule(k8sNode, true);
             setContainerToExtRule(k8sNode, true);
+            setRouterSnatRules(k8sNode, true);
         }
 
         private void processNodeUpdate(K8sNode k8sNode) {
@@ -415,47 +431,6 @@
         }
     }
 
-    private class InternalK8sHostListener implements K8sHostListener {
-
-        private boolean isRelevantHelper() {
-            return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
-        }
-
-        @Override
-        public void event(K8sHostEvent event) {
-            switch (event.type()) {
-                case K8S_HOST_COMPLETE:
-                    eventExecutor.execute(() -> processNodeCompletion(event.subject()));
-                    break;
-                case K8S_HOST_INCOMPLETE:
-                default:
-                    break;
-            }
-        }
-
-        private void processNodeCompletion(K8sHost k8sHost) {
-            if (!isRelevantHelper()) {
-                return;
-            }
-
-            for (String name : k8sHost.nodeNames()) {
-                K8sNode node = k8sNodeService.node(name);
-                if (node == null) {
-                    return;
-                }
-                K8sRouterBridge bridge = k8sHost.routerBridges().stream()
-                        .filter(b -> b.segmentId() == node.segmentId())
-                        .findAny().orElse(null);
-                if (bridge == null) {
-                    return;
-                }
-
-                setRouterSnatUpstreamRule(node, bridge, true);
-                setRouterSnatDownstreamRule(node, bridge, true);
-            }
-        }
-    }
-
     private class InternalK8sNetworkListener implements K8sNetworkListener {
 
         private boolean isRelevantHelper() {
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sServiceHandler.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sServiceHandler.java
index 93d913d..45b592e 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sServiceHandler.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sServiceHandler.java
@@ -58,7 +58,6 @@
 import org.onosproject.k8snode.api.K8sNodeListener;
 import org.onosproject.k8snode.api.K8sNodeService;
 import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
@@ -120,9 +119,7 @@
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.nodeIpGatewayIpMap;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.podByIp;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.portNumberByName;
-import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.tunnelPortNumByNetId;
 import static org.onosproject.k8snetworking.util.RulePopulatorUtil.CT_NAT_DST_FLAG;
-import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildExtension;
 import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildGroupBucket;
 import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildLoadExtension;
 import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildResubmitExtension;
@@ -643,34 +640,23 @@
                     tBuilder.setEthSrc(mac);
                 }
                 tBuilder.transition(STAT_EGRESS_TABLE);
-            } else {
-                K8sNode localNode = k8sNodeService.node(network.name());
-
-                tBuilder.setOutput(n.intgToTunPortNum());
-
-                PortNumber portNum = tunnelPortNumByNetId(network.networkId(),
-                        k8sNetworkService, n);
 
                 // install rules into tunnel bridge
-                TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
-                        .extension(buildExtension(
-                                deviceService,
-                                n.tunBridge(),
-                                localNode.dataIp().getIp4Address()),
-                                n.tunBridge())
-                        .setTunnelId(Long.valueOf(network.segmentId()))
-                        .setOutput(portNum)
+                TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                        .setOutput(n.tunToIntgPortNum())
                         .build();
 
                 k8sFlowRuleService.setRule(
                         appId,
                         n.tunBridge(),
                         sBuilder.build(),
-                        treatmentToRemote,
+                        treatment,
                         PRIORITY_CIDR_RULE,
                         TUN_ENTRY_TABLE,
                         install
                 );
+            } else {
+                tBuilder.setOutput(n.intgToTunPortNum());
             }
 
             k8sFlowRuleService.setRule(
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 f6c1d8b..50f3357 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
@@ -236,36 +236,43 @@
 
     private void processArpRequest(PacketContext context, Ethernet ethPacket) {
         ARP arpPacket = (ARP) ethPacket.getPayload();
-        K8sPort srcPort = k8sNetworkService.ports().stream()
+        K8sPort srcK8sPort = k8sNetworkService.ports().stream()
                 .filter(p -> p.macAddress().equals(ethPacket.getSourceMAC()))
                 .findAny().orElse(null);
 
-        if (srcPort == null && !context.inPacket().receivedFrom().port()
-                .equals(PortNumber.LOCAL)) {
+        PortNumber srcPortNum = context.inPacket().receivedFrom().port();
+        DeviceId srcDeviceId = context.inPacket().receivedFrom().deviceId();
+        boolean isEntryPort = false;
+
+        for (K8sNode node : k8sNodeService.completeNodes()) {
+            if (srcDeviceId.equals(node.intgBridge()) &&
+                    srcPortNum.equals(node.intgEntryPortNum())) {
+                isEntryPort = true;
+            }
+        }
+
+        // if the ARP request is not initiated from regular k8s ports nor
+        // integration bridge entry port, we simply ignore the ARP request...
+        if (srcK8sPort == null && !isEntryPort) {
             log.warn("Failed to find source port(MAC:{})", ethPacket.getSourceMAC());
             return;
         }
 
-        // FIXME: this is a workaround for storing host GW MAC address,
-        // need to find a way to store the MAC address in persistent way
-        if (context.inPacket().receivedFrom().port().equals(PortNumber.LOCAL)) {
-            gwMacAddress = ethPacket.getSourceMAC();
-        }
-
         IpAddress targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
 
+        // look up the MAC address from regular k8s ports
         MacAddress replyMac = k8sNetworkService.ports().stream()
                 //        .filter(p -> p.networkId().equals(srcPort.networkId()))
                 .filter(p -> p.ipAddress().equals(targetIp))
                 .map(K8sPort::macAddress)
                 .findAny().orElse(null);
 
-        long gwIpCnt = k8sNetworkService.networks().stream()
-                .filter(n -> n.gatewayIp().equals(targetIp))
-                .count();
-
-        if (gwIpCnt > 0) {
-            replyMac = gwMacAddress;
+        // look up the MAC address from special integration entry port (e.g., LOCAL, k8s-int-os)
+        for (K8sNetwork network : k8sNetworkService.networks()) {
+            if (network.gatewayIp().equals(targetIp)) {
+                K8sNode node = k8sNodeService.node(network.name());
+                replyMac = node.intgEntryPortMac();
+            }
         }
 
         if (replyMac == null) {
@@ -334,10 +341,6 @@
         }
 
         if (replyMac == null) {
-            replyMac = MacAddress.valueOf(gatewayMac);
-        }
-
-        if (replyMac == null) {
             log.debug("Failed to find MAC address for {}", targetIp);
             return;
         }
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 3efe41b..8083e88 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
@@ -250,7 +250,7 @@
             }
 
             if (sameHost) {
-                TrafficSelector selector = DefaultTrafficSelector.builder()
+                TrafficSelector originalSelector = DefaultTrafficSelector.builder()
                         .matchEthType(Ethernet.TYPE_IPV4)
                         .matchIPSrc(IpPrefix.valueOf(srcNode.podCidr()))
                         .matchIPDst(IpPrefix.valueOf(dstNode.podCidr()))
@@ -263,7 +263,22 @@
                 k8sFlowRuleService.setRule(
                         appId,
                         dstNode.tunBridge(),
-                        selector,
+                        originalSelector,
+                        treatment,
+                        PRIORITY_INTER_NODE_RULE,
+                        TUN_ENTRY_TABLE,
+                        install);
+
+                TrafficSelector transformedSelector = DefaultTrafficSelector.builder()
+                        .matchEthType(Ethernet.TYPE_IPV4)
+                        .matchIPSrc(IpPrefix.valueOf(shiftIpDomain(srcNode.podCidr(), SHIFTED_IP_PREFIX)))
+                        .matchIPDst(IpPrefix.valueOf(dstNode.podCidr()))
+                        .build();
+
+                k8sFlowRuleService.setRule(
+                        appId,
+                        dstNode.tunBridge(),
+                        transformedSelector,
                         treatment,
                         PRIORITY_INTER_NODE_RULE,
                         TUN_ENTRY_TABLE,
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingHandler.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingHandler.java
index a0ccf5d..bec0540 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingHandler.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingHandler.java
@@ -36,6 +36,7 @@
 import org.onosproject.k8snode.api.K8sNodeListener;
 import org.onosproject.k8snode.api.K8sNodeService;
 import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.DriverService;
@@ -445,7 +446,12 @@
     private class InternalK8sNetworkListener implements K8sNetworkListener {
 
         private boolean isRelevantHelper(K8sNetworkEvent event) {
-            return mastershipService.isLocalMaster(event.port().deviceId());
+            DeviceId deviceId = event.port().deviceId();
+            if (deviceId == null) {
+                return false;
+            } else {
+                return mastershipService.isLocalMaster(deviceId);
+            }
         }
 
         @Override
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 b73c118..c4d7ac3 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
@@ -612,6 +612,29 @@
     }
 
     @Override
+    public MacAddress intgEntryPortMac() {
+        return macAddress(intgBridge, intgEntryPortName());
+    }
+
+    @Override
+    public MacAddress portMacByName(String portName) {
+        if (portName == null) {
+            return null;
+        } else {
+            return macAddress(this.intgBridge, portName);
+        }
+    }
+
+    @Override
+    public PortNumber portNumByName(String portName) {
+        if (portName == null) {
+            return null;
+        } else {
+            return portNumber(this.intgBridge, portName);
+        }
+    }
+
+    @Override
     public PortNumber intgEntryPortNum() {
         if (mode == PASSTHROUGH) {
             return portNumber(intgBridge, k8sIntgToOsPatchPortName());
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 eabb13a..5c13bc1 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
@@ -259,6 +259,29 @@
     String intgEntryPortName();
 
     /**
+     * Returns the entry port MAC address.
+     *
+     * @return entry port MAC address
+     */
+    MacAddress intgEntryPortMac();
+
+    /**
+     * Returns the port MAC address with the given patch port name.
+     *
+     * @param portName patch port name
+     * @return port MAC address
+     */
+    MacAddress portMacByName(String portName);
+
+    /**
+     * Returns the port number with the given patch port name.
+     *
+     * @param portName patch port name
+     * @return port number
+     */
+    PortNumber portNumByName(String portName);
+
+    /**
      * Return the port number of integration bridge's entry port.
      *
      * @return port number
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 e05c0d4..cf776d1 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
@@ -83,6 +83,7 @@
     public static final int PRIORITY_SWITCHING_RULE = 30000;
     public static final int PRIORITY_FLAT_JUMP_UPSTREAM_RULE = 41000;
     public static final int PRIORITY_FLAT_UPSTREAM_RULE = 41000;
+    public static final int PRIORITY_FLAT_ARP_UPSTREAM_RULE = 41500;
     public static final int PRIORITY_FLAT_DOWNSTREAM_RULE = 42000;
     public static final int PRIORITY_DHCP_RULE = 42000;
     public static final int PRIORITY_ADMIN_RULE = 32000;
@@ -99,6 +100,9 @@
     public static final int PRIORITY_FORCED_ACL_RULE = 50000;
     public static final int PRIORITY_ICMP_PROBE_RULE = 50000;
 
+    public static final int PRIORITY_CNI_PT_ARP_RULE = 41500;
+    public static final int PRIORITY_CNI_PT_IP_RULE = 41400;
+
     // flow table index
     public static final int STAT_INBOUND_TABLE = 0;
     public static final int VTAP_INBOUND_TABLE = 1;
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
new file mode 100644
index 0000000..660764e
--- /dev/null
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackK8sIntegrationService.java
@@ -0,0 +1,54 @@
+/*
+ * 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.openstacknetworking.api;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+
+/**
+ * An interface which defines how to integrate openstack with kubernetes.
+ */
+public interface OpenstackK8sIntegrationService {
+
+    /**
+     * Installs K8S pass-through CNI related flow rules.
+     *
+     * @param k8sNodeIp         kubernetes node IP address
+     * @param podCidr           kubernetes POD CIDR
+     * @param serviceCidr       kubernetes service CIDR
+     * @param podGatewayIp      kubernetes POD's gateway IP
+     * @param osK8sIntPortName  openstack k8s integration patch port name
+     * @param k8sIntOsPortMac   k8s integration to openstack patch port MAC
+     */
+    void installCniPtNodeRules(IpAddress k8sNodeIp, IpPrefix podCidr,
+                               IpPrefix serviceCidr, IpAddress podGatewayIp,
+                               String osK8sIntPortName, MacAddress k8sIntOsPortMac);
+
+    /**
+     * Uninstalls K8S pass-through CNI related flow rules.
+     *
+     * @param k8sNodeIp         kubernetes node IP address
+     * @param podCidr           kubernetes POD CIDR
+     * @param serviceCidr       kubernetes service CIDR
+     * @param podGatewayIp      kubernetes POD's gateway IP
+     * @param osK8sIntPortName  openstack k8s integration patch port name
+     * @param k8sIntOsPortMac   k8s integration to openstack patch port MAC
+     */
+    void uninstallCniPtNodeRules(IpAddress k8sNodeIp, IpPrefix podCidr,
+                                 IpPrefix serviceCidr, IpAddress podGatewayIp,
+                                 String osK8sIntPortName, MacAddress k8sIntOsPortMac);
+}
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
new file mode 100644
index 0000000..2f5c1d1
--- /dev/null
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackK8sIntegrationManager.java
@@ -0,0 +1,250 @@
+/*
+ * 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.openstacknetworking.impl;
+
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
+import org.onosproject.openstacknetworking.api.OpenstackGroupRuleService;
+import org.onosproject.openstacknetworking.api.OpenstackK8sIntegrationService;
+import org.onosproject.openstacknetworking.api.OpenstackNetwork;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+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.STAT_FLAT_OUTBOUND_TABLE;
+import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.FLAT;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.shiftIpDomain;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of openstack kubernetes integration service.
+ */
+
+@Component(
+    immediate = true,
+    service = { OpenstackK8sIntegrationService.class }
+)
+public class OpenstackK8sIntegrationManager implements OpenstackK8sIntegrationService {
+
+    protected final Logger log = getLogger(getClass());
+
+    public static final String SHIFTED_IP_PREFIX = "172.10";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected OpenstackFlowRuleService osFlowRuleService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected OpenstackGroupRuleService osGroupRuleService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected OpenstackNodeService osNodeService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected OpenstackNetworkService osNetworkService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected InstancePortService instancePortService;
+
+    private ApplicationId appId;
+
+    @Activate
+    protected void activate() {
+        appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        log.info("Stopped");
+    }
+
+    @Override
+    public void installCniPtNodeRules(IpAddress k8sNodeIp,
+                                      IpPrefix podCidr, IpPrefix serviceCidr,
+                                      IpAddress podGatewayIp, String osK8sIntPortName,
+                                      MacAddress k8sIntOsPortMac) {
+        setNodeToPodIpRules(k8sNodeIp, podCidr, serviceCidr,
+                podGatewayIp, osK8sIntPortName, k8sIntOsPortMac, true);
+        setPodToNodeIpRules(k8sNodeIp, podGatewayIp, osK8sIntPortName, true);
+    }
+
+    @Override
+    public void uninstallCniPtNodeRules(IpAddress k8sNodeIp,
+                                        IpPrefix podCidr, IpPrefix serviceCidr,
+                                        IpAddress podGatewayIp, String osK8sIntPortName,
+                                        MacAddress k8sIntOsPortMac) {
+        setNodeToPodIpRules(k8sNodeIp, podCidr, serviceCidr,
+                podGatewayIp, osK8sIntPortName, k8sIntOsPortMac, false);
+        setPodToNodeIpRules(k8sNodeIp, podGatewayIp, osK8sIntPortName, 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);
+
+        if (osNode == null) {
+            return;
+        }
+
+        PortNumber osK8sIntPortNum = osNode.portNumByName(osK8sIntPortName);
+
+        if (osK8sIntPortNum == null) {
+            return;
+        }
+
+        TrafficSelector originalPodSelector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPSrc(IpPrefix.valueOf(k8sNodeIp, 32))
+                .matchIPDst(podCidr)
+                .build();
+
+        TrafficSelector transformedPodSelector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPSrc(IpPrefix.valueOf(k8sNodeIp, 32))
+                .matchIPDst(IpPrefix.valueOf(shiftIpDomain(podCidr.toString(), SHIFTED_IP_PREFIX)))
+                .build();
+
+        TrafficSelector serviceSelector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPSrc(IpPrefix.valueOf(k8sNodeIp, 32))
+                .matchIPDst(serviceCidr)
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setIpSrc(gatewayIp)
+                .setEthSrc(k8sIntOsPortMac)
+                .setOutput(osK8sIntPortNum)
+                .build();
+
+        osFlowRuleService.setRule(
+                appId,
+                osNode.intgBridge(),
+                originalPodSelector,
+                treatment,
+                PRIORITY_CNI_PT_IP_RULE,
+                FLAT_TABLE,
+                install
+        );
+
+        osFlowRuleService.setRule(
+                appId,
+                osNode.intgBridge(),
+                transformedPodSelector,
+                treatment,
+                PRIORITY_CNI_PT_IP_RULE,
+                FLAT_TABLE,
+                install
+        );
+
+        osFlowRuleService.setRule(
+                appId,
+                osNode.intgBridge(),
+                serviceSelector,
+                treatment,
+                PRIORITY_CNI_PT_IP_RULE,
+                FLAT_TABLE,
+                install
+        );
+    }
+
+    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);
+
+        if (instPort == null) {
+            return;
+        }
+
+        DeviceId deviceId = instPort.deviceId();
+        OpenstackNode osNode = osNodeService.node(deviceId);
+
+        if (osNode == null) {
+            return;
+        }
+
+        PortNumber osK8sIntPortNum = osNode.portNumByName(osK8sIntPortName);
+
+        if (osK8sIntPortNum == null) {
+            return;
+        }
+
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(IpPrefix.valueOf(gatewayIp, 32))
+                .matchInPort(osK8sIntPortNum)
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setIpDst(k8sNodeIp)
+                .setEthDst(instPort.macAddress())
+                .transition(STAT_FLAT_OUTBOUND_TABLE)
+                .build();
+
+        osFlowRuleService.setRule(
+                appId,
+                osNode.intgBridge(),
+                selector,
+                treatment,
+                PRIORITY_CNI_PT_IP_RULE,
+                DHCP_TABLE,
+                install
+        );
+    }
+}
\ No newline at end of file
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
index f061510..5d7c347 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
@@ -718,13 +718,13 @@
                         osNetworkService, remoteNode);
 
                 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
-                .extension(buildExtension(
-                        deviceService,
-                        remoteNode.intgBridge(),
-                        localNode.dataIp().getIp4Address()),
-                        remoteNode.intgBridge())
-                .setOutput(portNum)
-                .build();
+                        .extension(buildExtension(
+                                deviceService,
+                                remoteNode.intgBridge(),
+                                localNode.dataIp().getIp4Address()),
+                                remoteNode.intgBridge())
+                        .setOutput(portNum)
+                        .build();
 
                 osFlowRuleService.setRule(
                         appId,
@@ -812,9 +812,9 @@
     }
 
     private void processFlowTableRules(OpenstackNode osNode,
-                                        String segId, String netId,
-                                        boolean isTunnel,
-                                        boolean install) {
+                                       String segId, String netId,
+                                       boolean isTunnel,
+                                       boolean install) {
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
                 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
                 .matchArpOp(ARP.OP_REQUEST);
@@ -1096,8 +1096,8 @@
 
             netIds.stream()
                     .filter(nid -> osNetworkService.networkType(nid) == VXLAN ||
-                                    osNetworkService.networkType(nid) == GRE ||
-                                    osNetworkService.networkType(nid) == GENEVE)
+                            osNetworkService.networkType(nid) == GRE ||
+                            osNetworkService.networkType(nid) == GENEVE)
                     .forEach(nid -> {
                         String segId = osNetworkService.segmentId(nid);
                         setBaseVnetArpRuleForBroadcastMode(osNode, segId, nid, true, install);
@@ -1119,7 +1119,7 @@
                         .forEach(p -> {
                             setArpRequestRule(p, install);
                             setArpReplyRule(p, install);
-                });
+                        });
             } else {
                 // we do nothing for proxy mode
             }
@@ -1190,4 +1190,4 @@
             setArpReplyRule(event.subject(), false);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
index 87b8d50..6df6aac 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
@@ -1518,6 +1518,34 @@
         }
     }
 
+    /**
+     * Returns a shifted IP address.
+     *
+     * @param ipAddress     IP address to be shifted
+     * @param shiftPrefix   A IP prefix used in shifted IP address
+     * @return shifted Ip address
+     */
+    public static String shiftIpDomain(String ipAddress, String shiftPrefix) {
+        String origIpPrefix = ipAddress.split("\\.")[0] + "." + ipAddress.split("\\.")[1];
+        return StringUtils.replace(ipAddress, origIpPrefix, shiftPrefix);
+    }
+
+    /**
+     * Returns an unshifted IP address.
+     *
+     * @param ipAddress     IP address to be unshifted
+     * @param ipPrefix      IP prefix which to be used for unshifting
+     * @param cidr          a POD network CIDR
+     * @return unshifted IP address
+     */
+    public static String unshiftIpDomain(String ipAddress,
+                                         String ipPrefix,
+                                         String cidr) {
+
+        String origIpPrefix = cidr.split("\\.")[0] + "." + cidr.split("\\.")[1];
+        return StringUtils.replace(ipAddress, ipPrefix, origIpPrefix);
+    }
+
     private static String getDhcpOptionValue(NeutronPort port, String optionNameStr) {
         ObjectNode node = modelEntityToJson(port, NeutronPort.class);
 
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
new file mode 100644
index 0000000..c38984b
--- /dev/null
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackK8sIntegrationWebResource.java
@@ -0,0 +1,111 @@
+/*
+ * 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.openstacknetworking.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.openstacknetworking.api.OpenstackK8sIntegrationService;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.InputStream;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static org.onlab.util.Tools.readTreeFromStream;
+
+/**
+ * REST interface for integrating openstack and kubernetes.
+ */
+@Path("integration")
+public class OpenstackK8sIntegrationWebResource extends AbstractWebResource {
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String K8S_NODE_IP = "k8sNodeIp";
+    private static final String OS_K8S_INT_PORT_NAME = "osK8sIntPortName";
+    private static final String POD_CIDR = "podCidr";
+    private static final String SERVICE_CIDR = "serviceCidr";
+    private static final String POD_GW_IP = "podGwIp";
+    private static final String K8S_INT_OS_PORT_MAC = "k8sIntOsPortMac";
+
+    private final OpenstackK8sIntegrationService intService =
+            get(OpenstackK8sIntegrationService.class);
+
+    /**
+     * Installs CNI pass-through related flow rules for each kubernetes nodes.
+     *
+     * @param input     JSON string
+     * @return 200 ok, 400 BAD_REQUEST if the json is malformed
+     * @throws IOException exception
+     */
+    @PUT
+    @Path("node/pt-install")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response installCniPtNodeRules(InputStream input) throws IOException {
+        log.trace("Install K8S CNI pass-through node rules");
+
+        JsonNode json = readTreeFromStream(mapper().enable(INDENT_OUTPUT), input);
+        IpAddress k8sNodeIp = IpAddress.valueOf(json.get(K8S_NODE_IP).asText());
+        IpPrefix podCidr = IpPrefix.valueOf(json.get(POD_CIDR).asText());
+        IpPrefix serviceCidr = IpPrefix.valueOf(json.get(SERVICE_CIDR).asText());
+        IpAddress podGwIp = IpAddress.valueOf(json.get(POD_GW_IP).asText());
+        String osK8sIntPortName = json.get(OS_K8S_INT_PORT_NAME).asText();
+        MacAddress k8sIntOsPortMac = MacAddress.valueOf(json.get(K8S_INT_OS_PORT_MAC).asText());
+
+        intService.installCniPtNodeRules(k8sNodeIp, podCidr, serviceCidr,
+                podGwIp, osK8sIntPortName, k8sIntOsPortMac);
+
+        return Response.ok().build();
+    }
+
+    /**
+     * Installs CNI pass-through related flow rules for each kubernetes nodes.
+     *
+     * @param input     JSON string
+     * @return 200 ok, 400 BAD_REQUEST if the json is malformed
+     * @throws IOException exception
+     */
+    @PUT
+    @Path("node/pt-uninstall")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response uninstallCniPtNodeRules(InputStream input) throws IOException {
+        log.trace("Uninstall K8S CNI pass-through node rules");
+
+        JsonNode json = readTreeFromStream(mapper().enable(INDENT_OUTPUT), input);
+        IpAddress k8sNodeIp = IpAddress.valueOf(json.get(K8S_NODE_IP).asText());
+        IpPrefix podCidr = IpPrefix.valueOf(json.get(POD_CIDR).asText());
+        IpPrefix serviceCidr = IpPrefix.valueOf(json.get(SERVICE_CIDR).asText());
+        IpAddress podGwIp = IpAddress.valueOf(json.get(POD_GW_IP).asText());
+        String osK8sIntPortName = json.get(OS_K8S_INT_PORT_NAME).asText();
+        MacAddress k8sIntOsPortMac = MacAddress.valueOf(json.get(K8S_INT_OS_PORT_MAC).asText());
+
+        intService.uninstallCniPtNodeRules(k8sNodeIp, podCidr, serviceCidr,
+                podGwIp, osK8sIntPortName, k8sIntOsPortMac);
+
+        return Response.ok().build();
+    }
+}
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkingWebApplication.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkingWebApplication.java
index f215efe..f6b3e65 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkingWebApplication.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkingWebApplication.java
@@ -33,7 +33,8 @@
                 OpenstackSecurityGroupRuleWebResource.class,
                 OpenstackSecurityGroupWebResource.class,
                 OpenstackSubnetWebResource.class,
-                OpenstackManagementWebResource.class
+                OpenstackManagementWebResource.class,
+                OpenstackK8sIntegrationWebResource.class
         );
     }
 }
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManagerTest.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManagerTest.java
index 5e5dd9a..3c4c812 100644
--- a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManagerTest.java
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManagerTest.java
@@ -23,6 +23,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.junit.TestUtils;
+import org.onlab.packet.IpAddress;
 import org.onosproject.cluster.ClusterServiceAdapter;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreServiceAdapter;
@@ -637,6 +638,11 @@
         }
 
         @Override
+        public OpenstackNode node(IpAddress mgmtIp) {
+            return null;
+        }
+
+        @Override
         public void addVfPort(OpenstackNode osNode, String portName) {
         }
 
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackNodeServiceAdapter.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackNodeServiceAdapter.java
index cbd4802..a3b96be 100644
--- a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackNodeServiceAdapter.java
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackNodeServiceAdapter.java
@@ -16,6 +16,7 @@
 package org.onosproject.openstacknetworking.impl;
 
 import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.IpAddress;
 import org.onosproject.net.DeviceId;
 import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackNodeListener;
@@ -58,6 +59,11 @@
     }
 
     @Override
+    public OpenstackNode node(IpAddress mgmtIp) {
+        return null;
+    }
+
+    @Override
     public void addVfPort(OpenstackNode osNode, String portName) {
     }
 
diff --git a/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/DefaultOpenstackNode.java b/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/DefaultOpenstackNode.java
index 0d72a2b..e058aaa 100644
--- a/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/DefaultOpenstackNode.java
+++ b/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/DefaultOpenstackNode.java
@@ -20,6 +20,7 @@
 import org.onlab.osgi.DefaultServiceDirectory;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
+import org.onosproject.net.Annotations;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
@@ -242,6 +243,24 @@
     }
 
     @Override
+    public MacAddress portMacByName(String portName) {
+        if (portName == null) {
+            return null;
+        } else {
+            return macAddress(this.intgBridge, portName);
+        }
+    }
+
+    @Override
+    public PortNumber portNumByName(String portName) {
+        if (portName == null) {
+            return null;
+        } else {
+            return portNumber(this.intgBridge, portName);
+        }
+    }
+
+    @Override
     public MacAddress vlanPortMac() {
         if (vlanIntf == null) {
             return null;
@@ -394,6 +413,25 @@
         return neutronConfig;
     }
 
+    private MacAddress macAddress(DeviceId deviceId, String portName) {
+        Port port = port(deviceId, portName);
+        Annotations annots = port.annotations();
+        return annots != null ? MacAddress.valueOf(annots.value(PORT_MAC)) : null;
+    }
+
+    private PortNumber portNumber(DeviceId deviceId, String portName) {
+        Port port = port(deviceId, portName);
+        return port != null ? port.number() : null;
+    }
+
+    private Port port(DeviceId deviceId, String portName) {
+        DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
+        return deviceService.getPorts(deviceId).stream()
+                .filter(p -> p.isEnabled() &&
+                        Objects.equals(p.annotations().value(PORT_NAME), portName))
+                .findAny().orElse(null);
+    }
+
     /**
      * Returns new builder instance.
      *
diff --git a/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java b/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java
index 8a0e1eb..6396b29 100644
--- a/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java
+++ b/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java
@@ -130,6 +130,22 @@
     PortNumber patchPortNum();
 
     /**
+     * Returns the port MAC address with the given patch port name.
+     *
+     * @param portName patch port name
+     * @return port MAC address
+     */
+    MacAddress portMacByName(String portName);
+
+    /**
+     * Returns the port number with the given patch port name.
+     *
+     * @param portName patch port name
+     * @return port number
+     */
+    PortNumber portNumByName(String portName);
+
+    /**
      * Returns the vlan port MAC address.
      *
      * @return mac address; null if vlan port does not exist
diff --git a/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeService.java b/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeService.java
index a692109..8f641f5 100644
--- a/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeService.java
+++ b/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeService.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.openstacknode.api;
 
+import org.onlab.packet.IpAddress;
 import org.onosproject.event.ListenerService;
 import org.onosproject.net.DeviceId;
 import org.onosproject.openstacknode.api.OpenstackNode.NodeType;
@@ -77,6 +78,14 @@
     OpenstackNode node(DeviceId deviceId);
 
     /**
+     * Returns the node with the specified management IP address.
+     *
+     * @param mgmtIp management IP
+     * @return openstack node
+     */
+    OpenstackNode node(IpAddress mgmtIp);
+
+    /**
      * Adds the vf port to the given openstack node.
      *
      * @param osNode openstack node
diff --git a/apps/openstacknode/api/src/test/java/org/onosproject/openstacknode/api/OpenstackNodeAdapter.java b/apps/openstacknode/api/src/test/java/org/onosproject/openstacknode/api/OpenstackNodeAdapter.java
index 5e6f703..1922ec1 100644
--- a/apps/openstacknode/api/src/test/java/org/onosproject/openstacknode/api/OpenstackNodeAdapter.java
+++ b/apps/openstacknode/api/src/test/java/org/onosproject/openstacknode/api/OpenstackNodeAdapter.java
@@ -94,6 +94,16 @@
     }
 
     @Override
+    public MacAddress portMacByName(String portName) {
+        return null;
+    }
+
+    @Override
+    public PortNumber portNumByName(String portName) {
+        return null;
+    }
+
+    @Override
     public MacAddress vlanPortMac() {
         return null;
     }
diff --git a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/OpenstackNodeManager.java b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/OpenstackNodeManager.java
index 0060e03..cf123ce 100644
--- a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/OpenstackNodeManager.java
+++ b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/OpenstackNodeManager.java
@@ -17,6 +17,7 @@
 
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.IpAddress;
 import org.onlab.util.Tools;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.cluster.LeadershipService;
@@ -287,6 +288,13 @@
     }
 
     @Override
+    public OpenstackNode node(IpAddress mgmtIp) {
+        return osNodeStore.nodes().stream()
+                .filter(osNode -> Objects.equals(osNode.managementIp(), mgmtIp))
+                .findFirst().orElse(null);
+    }
+
+    @Override
     public void addVfPort(OpenstackNode osNode, String portName) {
         log.trace("addVfPort called");
 
diff --git a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/impl/TestOpenstackNodeManager.java b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/impl/TestOpenstackNodeManager.java
index ea9b8c6..9a4781b 100644
--- a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/impl/TestOpenstackNodeManager.java
+++ b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/impl/TestOpenstackNodeManager.java
@@ -18,6 +18,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import org.onlab.packet.IpAddress;
 import org.onosproject.net.DeviceId;
 import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
@@ -78,7 +79,12 @@
                 .findFirst().orElse(null);
     }
 
-        @Override
+    @Override
+    public OpenstackNode node(IpAddress mgmtIp) {
+        return null;
+    }
+
+    @Override
         public void addVfPort(OpenstackNode osNode, String portName) {
     }
 
diff --git a/apps/openstackvtap/app/src/test/java/org/onosproject/openstackvtap/impl/OpenstackVtapTest.java b/apps/openstackvtap/app/src/test/java/org/onosproject/openstackvtap/impl/OpenstackVtapTest.java
index 67910e6..4bf65d6 100644
--- a/apps/openstackvtap/app/src/test/java/org/onosproject/openstackvtap/impl/OpenstackVtapTest.java
+++ b/apps/openstackvtap/app/src/test/java/org/onosproject/openstackvtap/impl/OpenstackVtapTest.java
@@ -194,6 +194,11 @@
         }
 
         @Override
+        public OpenstackNode node(IpAddress mgmtIp) {
+            return null;
+        }
+
+        @Override
         public void addVfPort(OpenstackNode osNode, String portName) {
         }