Support NodePort communication  model at k8s passthrough mode

Change-Id: I2179ebc9a4812493619c56aa270d8fc4821efbb2
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();
+    }
 }