Implement NodeIP to ServiceIP translation feature

Change-Id: I49bda2cddfb1c22b362b4b48105efd316c3dad36
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 419e7eb..8de62c8 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
@@ -45,9 +45,12 @@
 
     public static final String SHIFTED_IP_CIDR = "172.10.0.0/16";
     public static final String SHIFTED_IP_PREFIX = "172.10";
+    public static final String NODE_IP_PREFIX = "182";
 
     public static final String SRC = "src";
     public static final String DST = "dst";
+    public static final String A_CLASS = "a";
+    public static final String B_CLASS = "b";
 
     public static final String PORT_NAME_PREFIX_CONTAINER = "veth";
 
@@ -79,6 +82,9 @@
     public static final int PRIORITY_ARP_FLOOD_RULE = 39000;
     public static final int PRIORITY_FORCED_ACL_RULE = 50000;
     public static final int PRIORITY_ICMP_PROBE_RULE = 50000;
+    public static final int PRIORITY_NODE_PORT_RULE = 42000;
+    public static final int PRIORITY_NODE_PORT_REMOTE_RULE = 41500;
+    public static final int PRIORITY_NODE_PORT_INTER_RULE = 40000;
 
     // flow table index
     public static final int STAT_INBOUND_TABLE = 0;
@@ -100,5 +106,8 @@
     public static final int VTAP_OUTBOUND_MIRROR_TABLE = 72;
     public static final int FORWARDING_TABLE = 80;
     public static final int ERROR_TABLE = 100;
-    public static final int GW_COMMON_TABLE = 0;
+
+    public static final int EXT_ENTRY_TABLE = 0;
+    public static final int POD_RESOLUTION_TABLE = 11;
+    public static final int INBOUND_TABLE = 10;
 }
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sNodePortHandler.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sNodePortHandler.java
new file mode 100644
index 0000000..c8a0802
--- /dev/null
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sNodePortHandler.java
@@ -0,0 +1,506 @@
+/*
+ * Copyright 2019-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 io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.api.model.ServicePort;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.TpPort;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cfg.ConfigProperty;
+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.K8sFlowRuleService;
+import org.onosproject.k8snetworking.api.K8sNetworkService;
+import org.onosproject.k8snetworking.api.K8sServiceEvent;
+import org.onosproject.k8snetworking.api.K8sServiceListener;
+import org.onosproject.k8snetworking.api.K8sServiceService;
+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.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;
+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.net.flow.instructions.ExtensionTreatment;
+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 java.util.Objects;
+import java.util.Set;
+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.B_CLASS;
+import static org.onosproject.k8snetworking.api.Constants.DST;
+import static org.onosproject.k8snetworking.api.Constants.EXT_ENTRY_TABLE;
+import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
+import static org.onosproject.k8snetworking.api.Constants.NODE_IP_PREFIX;
+import static org.onosproject.k8snetworking.api.Constants.PRIORITY_CIDR_RULE;
+import static org.onosproject.k8snetworking.api.Constants.PRIORITY_NODE_PORT_INTER_RULE;
+import static org.onosproject.k8snetworking.api.Constants.PRIORITY_NODE_PORT_REMOTE_RULE;
+import static org.onosproject.k8snetworking.api.Constants.PRIORITY_NODE_PORT_RULE;
+import static org.onosproject.k8snetworking.api.Constants.ROUTING_TABLE;
+import static org.onosproject.k8snetworking.api.Constants.SRC;
+import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getBclassIpPrefixFromCidr;
+import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getPropertyValue;
+import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.unshiftIpDomain;
+import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildExtension;
+import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildLoadExtension;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides service port exposure using node port.
+ */
+@Component(immediate = true)
+public class K8sNodePortHandler {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String NODE_PORT_TYPE = "NodePort";
+    private static final String TCP = "TCP";
+    private static final String UDP = "UDP";
+    private static final int HOST_CIDR = 32;
+    private static final String SERVICE_CIDR = "serviceCidr";
+    private static final String B_CLASS_SUFFIX = "0.0/16";
+    private static final String C_CLASS_SUFFIX = ".0/24";
+
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected ComponentConfigService configService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected DriverService driverService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected LeadershipService leadershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected MastershipService mastershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected K8sNodeService k8sNodeService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected K8sNetworkService k8sNetworkService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected K8sServiceService k8sServiceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected K8sFlowRuleService k8sFlowRuleService;
+
+    private final InternalK8sServiceListener k8sServiceListener =
+            new InternalK8sServiceListener();
+    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();
+        leadershipService.runForLeadership(appId.name());
+        k8sNodeService.addListener(k8sNodeListener);
+        k8sServiceService.addListener(k8sServiceListener);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        k8sNodeService.removeListener(k8sNodeListener);
+        k8sServiceService.removeListener(k8sServiceListener);
+        leadershipService.withdraw(appId.name());
+        eventExecutor.shutdown();
+
+        log.info("Stopped");
+    }
+
+    private void processNodePortEvent(K8sNode k8sNode, Service service, boolean install) {
+
+        String clusterIp = service.getSpec().getClusterIP();
+        for (ServicePort servicePort : service.getSpec().getPorts()) {
+            setNodeToServiceRules(k8sNode, clusterIp, servicePort, install);
+            setServiceToNodeLocalRules(k8sNode, clusterIp, servicePort, install);
+            setServiceToNodeRemoteRules(k8sNode, clusterIp, servicePort, install);
+            setExtToIngrRules(k8sNode, servicePort, install);
+        }
+
+        k8sNodeService.completeNodes().forEach(n -> {
+            String podCidr = k8sNetworkService.network(n.hostname()).cidr();
+            String fullCidr = NODE_IP_PREFIX + "." +
+                    podCidr.split("\\.")[2] + "." + B_CLASS_SUFFIX;
+
+            if (n.equals(k8sNode)) {
+                setIntgToExtLocalRules(k8sNode, getServiceCidr(), fullCidr, install);
+            } else {
+                setIntgToExtRemoteRules(k8sNode, n, getServiceCidr(), fullCidr, install);
+            }
+        });
+
+        setDefaultExtEgrRule(k8sNode, install);
+    }
+
+    private void setDefaultExtEgrRule(K8sNode k8sNode, boolean install) {
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+                .matchInPort(PortNumber.LOCAL)
+                .matchEthType(Ethernet.TYPE_IPV4);
+
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
+                .setOutput(k8sNode.extBridgePortNum());
+
+        k8sFlowRuleService.setRule(
+                appId,
+                k8sNode.extBridge(),
+                sBuilder.build(),
+                tBuilder.build(),
+                PRIORITY_NODE_PORT_INTER_RULE,
+                EXT_ENTRY_TABLE,
+                install);
+    }
+
+    private void setIntgToExtLocalRules(K8sNode k8sNode, String serviceCidr,
+                                        String shiftedCidr, boolean install) {
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPSrc(IpPrefix.valueOf(serviceCidr))
+                .matchIPDst(IpPrefix.valueOf(shiftedCidr));
+
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
+                .setOutput(k8sNode.intgToExtPatchPortNum());
+
+        k8sFlowRuleService.setRule(
+                appId,
+                k8sNode.intgBridge(),
+                sBuilder.build(),
+                tBuilder.build(),
+                PRIORITY_CIDR_RULE,
+                ROUTING_TABLE,
+                install);
+    }
+
+    private void setIntgToExtRemoteRules(K8sNode k8sNodeLocal, K8sNode k8sNodeRemote,
+                                         String serviceCidr, String shiftedCidr,
+                                         boolean install) {
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPSrc(IpPrefix.valueOf(serviceCidr))
+                .matchIPDst(IpPrefix.valueOf(shiftedCidr));
+
+        ExtensionTreatment remote = buildExtension(deviceService,
+                k8sNodeLocal.intgBridge(), k8sNodeRemote.dataIp().getIp4Address());
+
+        // TODO: need to consider other network types
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
+                .extension(remote, k8sNodeLocal.intgBridge())
+                .setOutput(k8sNodeLocal.vxlanPortNum());
+
+        k8sFlowRuleService.setRule(
+                appId,
+                k8sNodeLocal.intgBridge(),
+                sBuilder.build(),
+                tBuilder.build(),
+                PRIORITY_CIDR_RULE,
+                ROUTING_TABLE,
+                install);
+    }
+
+    private void setExtToIngrRules(K8sNode k8sNode, ServicePort servicePort,
+                                    boolean install) {
+        String protocol = servicePort.getProtocol();
+        int nodePort = servicePort.getNodePort();
+        DeviceId deviceId = k8sNode.extBridge();
+
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(IpPrefix.valueOf(k8sNode.extBridgeIp(), HOST_CIDR));
+
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
+                .setOutput(PortNumber.LOCAL);
+
+        if (TCP.equals(protocol)) {
+            sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP)
+                    .matchTcpSrc(TpPort.tpPort(nodePort));
+        } else if (UDP.equals(protocol)) {
+            sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP)
+                    .matchUdpSrc(TpPort.tpPort(nodePort));
+        }
+
+        k8sFlowRuleService.setRule(
+                appId,
+                deviceId,
+                sBuilder.build(),
+                tBuilder.build(),
+                PRIORITY_NODE_PORT_RULE,
+                EXT_ENTRY_TABLE,
+                install);
+    }
+
+    private void setNodeToServiceRules(K8sNode k8sNode,
+                                       String clusterIp,
+                                       ServicePort servicePort,
+                                       boolean install) {
+        String protocol = servicePort.getProtocol();
+        int nodePort = servicePort.getNodePort();
+        int svcPort = servicePort.getPort();
+        DeviceId deviceId = k8sNode.extBridge();
+
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(IpPrefix.valueOf(k8sNode.extBridgeIp(), HOST_CIDR));
+
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
+                .setIpDst(IpAddress.valueOf(clusterIp));
+
+        if (TCP.equals(protocol)) {
+            sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP)
+                    .matchTcpDst(TpPort.tpPort(nodePort));
+            tBuilder.setTcpDst(TpPort.tpPort(svcPort));
+        } else if (UDP.equals(protocol)) {
+            sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP)
+                    .matchUdpDst(TpPort.tpPort(nodePort));
+            tBuilder.setUdpDst(TpPort.tpPort(svcPort));
+        }
+
+        String podCidr = k8sNetworkService.network(k8sNode.hostname()).cidr();
+        String prefix = NODE_IP_PREFIX + "." + podCidr.split("\\.")[2];
+
+        ExtensionTreatment loadTreatment = buildLoadExtension(
+                deviceService.getDevice(deviceId), B_CLASS, SRC, prefix);
+        tBuilder.extension(loadTreatment, deviceId)
+                .setOutput(k8sNode.extToIntgPatchPortNum());
+
+        k8sFlowRuleService.setRule(
+                appId,
+                k8sNode.extBridge(),
+                sBuilder.build(),
+                tBuilder.build(),
+                PRIORITY_NODE_PORT_RULE,
+                EXT_ENTRY_TABLE,
+                install);
+    }
+
+    private void setServiceToNodeLocalRules(K8sNode k8sNode,
+                                            String clusterIp,
+                                            ServicePort servicePort,
+                                            boolean install) {
+        String protocol = servicePort.getProtocol();
+        int nodePort = servicePort.getNodePort();
+        int svcPort = servicePort.getPort();
+        DeviceId deviceId = k8sNode.extBridge();
+
+        String extBridgeIp = k8sNode.extBridgeIp().toString();
+        String extBridgePrefix = getBclassIpPrefixFromCidr(extBridgeIp);
+
+        String podCidr = k8sNetworkService.network(k8sNode.hostname()).cidr();
+        String nodePrefix = NODE_IP_PREFIX + "." + podCidr.split("\\.")[2];
+
+        if (extBridgePrefix == null) {
+            return;
+        }
+
+        String shiftedIp = unshiftIpDomain(extBridgeIp, extBridgePrefix, nodePrefix);
+
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchInPort(k8sNode.extToIntgPatchPortNum())
+                .matchIPSrc(IpPrefix.valueOf(IpAddress.valueOf(clusterIp), HOST_CIDR))
+                .matchIPDst(IpPrefix.valueOf(IpAddress.valueOf(shiftedIp), HOST_CIDR));
+
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
+                .setIpSrc(k8sNode.extBridgeIp())
+                .setEthSrc(k8sNode.extBridgeMac());
+
+        if (TCP.equals(protocol)) {
+            sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP)
+                    .matchTcpSrc(TpPort.tpPort(svcPort));
+            tBuilder.setTcpSrc(TpPort.tpPort(nodePort));
+        } else if (UDP.equals(protocol)) {
+            sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP)
+                    .matchUdpSrc(TpPort.tpPort(svcPort));
+            tBuilder.setUdpSrc(TpPort.tpPort(nodePort));
+        }
+
+        String gatewayIp = k8sNode.extGatewayIp().toString();
+        String gatewayPrefix = getBclassIpPrefixFromCidr(gatewayIp);
+
+        if (gatewayPrefix == null) {
+            return;
+        }
+
+        ExtensionTreatment loadTreatment = buildLoadExtension(
+                deviceService.getDevice(deviceId), B_CLASS, DST, gatewayPrefix);
+        tBuilder.extension(loadTreatment, deviceId)
+                .setOutput(PortNumber.LOCAL);
+
+        k8sFlowRuleService.setRule(
+                appId,
+                deviceId,
+                sBuilder.build(),
+                tBuilder.build(),
+                PRIORITY_NODE_PORT_RULE,
+                EXT_ENTRY_TABLE,
+                install);
+    }
+
+    private void setServiceToNodeRemoteRules(K8sNode k8sNode,
+                                             String clusterIp,
+                                             ServicePort servicePort,
+                                             boolean install) {
+        String protocol = servicePort.getProtocol();
+        int nodePort = servicePort.getNodePort();
+        int svcPort = servicePort.getPort();
+        DeviceId deviceId = k8sNode.extBridge();
+
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchInPort(k8sNode.extToIntgPatchPortNum())
+                .matchIPSrc(IpPrefix.valueOf(IpAddress.valueOf(clusterIp), HOST_CIDR));
+
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
+                .setIpSrc(k8sNode.extBridgeIp())
+                .setEthSrc(k8sNode.extBridgeMac());
+
+        if (TCP.equals(protocol)) {
+            sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP)
+                    .matchTcpSrc(TpPort.tpPort(svcPort));
+            tBuilder.setTcpSrc(TpPort.tpPort(nodePort));
+        } else if (UDP.equals(protocol)) {
+            sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP)
+                    .matchUdpSrc(TpPort.tpPort(svcPort));
+            tBuilder.setUdpSrc(TpPort.tpPort(nodePort));
+        }
+
+        String gatewayIp = k8sNode.extGatewayIp().toString();
+        String prefix = getBclassIpPrefixFromCidr(gatewayIp);
+
+        if (prefix == null) {
+            return;
+        }
+
+        ExtensionTreatment loadTreatment = buildLoadExtension(
+                deviceService.getDevice(deviceId), B_CLASS, DST, prefix);
+        tBuilder.extension(loadTreatment, deviceId)
+                .setOutput(k8sNode.extBridgePortNum());
+
+        k8sFlowRuleService.setRule(
+                appId,
+                deviceId,
+                sBuilder.build(),
+                tBuilder.build(),
+                PRIORITY_NODE_PORT_REMOTE_RULE,
+                EXT_ENTRY_TABLE,
+                install);
+    }
+
+    private String getServiceCidr() {
+        Set<ConfigProperty> properties =
+                configService.getProperties(K8sServiceHandler.class.getName());
+        return getPropertyValue(properties, SERVICE_CIDR);
+    }
+
+    private class InternalK8sServiceListener implements K8sServiceListener {
+
+        private boolean isRelevantHelper() {
+            return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
+        }
+
+        @Override
+        public void event(K8sServiceEvent event) {
+            switch (event.type()) {
+                case K8S_SERVICE_CREATED:
+                case K8S_SERVICE_UPDATED:
+                    eventExecutor.execute(() -> processServiceCreation(event.subject()));
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        private void processServiceCreation(Service service) {
+            if (!isRelevantHelper()) {
+                return;
+            }
+
+            if (NODE_PORT_TYPE.equals(service.getSpec().getType())) {
+                k8sNodeService.completeNodes().forEach(n ->
+                    processNodePortEvent(n, service, true)
+                );
+            }
+        }
+    }
+
+    private class InternalK8sNodeListener implements K8sNodeListener {
+
+        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:
+                default:
+                    break;
+            }
+        }
+
+        private void processNodeCompletion(K8sNode k8sNode) {
+            if (!isRelevantHelper()) {
+                return;
+            }
+
+            k8sServiceService.services().stream()
+                    .filter(s -> NODE_PORT_TYPE.equals(s.getSpec().getType()))
+                    .forEach(s -> processNodePortEvent(k8sNode, s, true));
+        }
+    }
+}
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 349043f..04913b6 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
@@ -60,8 +60,9 @@
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.k8snetworking.api.Constants.DEFAULT_GATEWAY_MAC;
-import static org.onosproject.k8snetworking.api.Constants.GW_COMMON_TABLE;
+import static org.onosproject.k8snetworking.api.Constants.EXT_ENTRY_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
+import static org.onosproject.k8snetworking.api.Constants.POD_RESOLUTION_TABLE;
 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.ROUTING_TABLE;
@@ -79,9 +80,12 @@
 
     private final Logger log = getLogger(getClass());
 
-    private static final int POD_PREFIX = 32;
+    private static final int HOST_PREFIX = 32;
 
-    private static final int TP_PORT_MINIMUM_NUM = 1025;
+    // we try to avoid port number overlapping with node port (30000 ~ 32767)
+    // in case the user has customized node port range, the following static
+    // value should be changed accordingly
+    private static final int TP_PORT_MINIMUM_NUM = 32768;
     private static final int TP_PORT_MAXIMUM_NUM = 65535;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
@@ -180,7 +184,7 @@
 
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPDst(IpPrefix.valueOf(k8sPort.ipAddress(), POD_PREFIX));
+                .matchIPDst(IpPrefix.valueOf(k8sPort.ipAddress(), HOST_PREFIX));
 
         TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
                 .setOutput(k8sNode.extToIntgPatchPortNum());
@@ -191,7 +195,7 @@
                 sBuilder.build(),
                 tBuilder.build(),
                 PRIORITY_STATEFUL_SNAT_RULE,
-                GW_COMMON_TABLE,
+                POD_RESOLUTION_TABLE,
                 install);
     }
 
@@ -199,15 +203,15 @@
                                        boolean install) {
         DeviceId deviceId = k8sNode.extBridge();
 
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPDst(IpPrefix.valueOf(k8sNode.extBridgeIp(), POD_PREFIX));
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(IpPrefix.valueOf(k8sNode.extBridgeIp(), HOST_PREFIX));
 
         ExtensionTreatment natTreatment = RulePopulatorUtil
                 .niciraConnTrackTreatmentBuilder(driverService, deviceId)
                 .commit(false)
                 .natAction(true)
-                .table((short) 0)
+                .table((short) POD_RESOLUTION_TABLE)
                 .build();
 
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
@@ -221,7 +225,7 @@
                 sBuilder.build(),
                 treatment,
                 PRIORITY_STATEFUL_SNAT_RULE,
-                GW_COMMON_TABLE,
+                EXT_ENTRY_TABLE,
                 install);
     }
 
@@ -236,6 +240,7 @@
 
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
+                .matchInPort(k8sNode.extToIntgPatchPortNum())
                 .matchEthDst(DEFAULT_GATEWAY_MAC)
                 .build();
 
@@ -264,38 +269,38 @@
                 selector,
                 tBuilder.build(),
                 PRIORITY_STATEFUL_SNAT_RULE,
-                GW_COMMON_TABLE,
+                EXT_ENTRY_TABLE,
                 install);
     }
 
     private void setExtIntfArpRule(K8sNode k8sNode, boolean install) {
+        k8sNodeService.completeNodes().forEach(n -> {
+            Device device = deviceService.getDevice(n.extBridge());
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchEthType(Ethernet.TYPE_ARP)
+                    .matchArpOp(ARP.OP_REQUEST)
+                    .matchArpTpa(Ip4Address.valueOf(k8sNode.extBridgeIp().toString()))
+                    .build();
 
-        Device device = deviceService.getDevice(k8sNode.extBridge());
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setArpOp(ARP.OP_REPLY)
+                    .extension(buildMoveEthSrcToDstExtension(device), device.id())
+                    .extension(buildMoveArpShaToThaExtension(device), device.id())
+                    .extension(buildMoveArpSpaToTpaExtension(device), device.id())
+                    .setArpSpa(Ip4Address.valueOf(k8sNode.extBridgeIp().toString()))
+                    .setArpSha(k8sNode.extBridgeMac())
+                    .setOutput(PortNumber.IN_PORT)
+                    .build();
 
-        TrafficSelector selector = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_ARP)
-                .matchArpOp(ARP.OP_REQUEST)
-                .matchArpTpa(Ip4Address.valueOf(k8sNode.extBridgeIp().toString()))
-                .build();
-
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .setArpOp(ARP.OP_REPLY)
-                .extension(buildMoveEthSrcToDstExtension(device), device.id())
-                .extension(buildMoveArpShaToThaExtension(device), device.id())
-                .extension(buildMoveArpSpaToTpaExtension(device), device.id())
-                .setArpSpa(Ip4Address.valueOf(k8sNode.extBridgeIp().toString()))
-                .setArpSha(k8sNode.extBridgeMac())
-                .setOutput(PortNumber.IN_PORT)
-                .build();
-
-        k8sFlowRuleService.setRule(
-                appId,
-                k8sNode.extBridge(),
-                selector,
-                treatment,
-                PRIORITY_STATEFUL_SNAT_RULE,
-                GW_COMMON_TABLE,
-                install);
+            k8sFlowRuleService.setRule(
+                    appId,
+                    n.extBridge(),
+                    selector,
+                    treatment,
+                    PRIORITY_STATEFUL_SNAT_RULE,
+                    EXT_ENTRY_TABLE,
+                    install);
+        });
     }
 
     private class InternalK8sNodeListener implements K8sNodeListener {
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 9c243f9..f9e0545 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
@@ -86,12 +86,15 @@
 
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.k8snetworking.api.Constants.A_CLASS;
+import static org.onosproject.k8snetworking.api.Constants.B_CLASS;
 import static org.onosproject.k8snetworking.api.Constants.DST;
 import static org.onosproject.k8snetworking.api.Constants.JUMP_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
 import static org.onosproject.k8snetworking.api.Constants.NAT_STATEFUL;
 import static org.onosproject.k8snetworking.api.Constants.NAT_STATELESS;
 import static org.onosproject.k8snetworking.api.Constants.NAT_TABLE;
+import static org.onosproject.k8snetworking.api.Constants.NODE_IP_PREFIX;
 import static org.onosproject.k8snetworking.api.Constants.POD_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.PRIORITY_CIDR_RULE;
 import static org.onosproject.k8snetworking.api.Constants.PRIORITY_CT_RULE;
@@ -108,6 +111,7 @@
 import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.SERVICE_IP_CIDR_DEFAULT;
 import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.SERVICE_IP_NAT_MODE;
 import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.SERVICE_IP_NAT_MODE_DEFAULT;
+import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getBclassIpPrefixFromCidr;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.nodeIpGatewayIpMap;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.tunnelPortNumByNetId;
 import static org.onosproject.k8snetworking.util.RulePopulatorUtil.CT_NAT_DST_FLAG;
@@ -142,6 +146,10 @@
     private static final String UDP = "UDP";
     private static final String SERVICE_IP_NAT_MODE = "serviceIpNatMode";
 
+    private static final String SERVICE_CIDR = "serviceCidr";
+    private static final String B_CLASS_SUFFIX = ".0.0/16";
+    private static final String A_CLASS_SUFFIX = ".0.0.0/8";
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected CoreService coreService;
 
@@ -268,15 +276,32 @@
 
     private void setStatelessServiceNatRules(DeviceId deviceId, boolean install) {
 
-        String srcCidr = k8sNetworkService.network(
+        String srcPodCidr = k8sNetworkService.network(
                 k8sNodeService.node(deviceId).hostname()).cidr();
+        String srcPodPrefix = getBclassIpPrefixFromCidr(srcPodCidr);
+        String fullSrcPodCidr = srcPodPrefix + B_CLASS_SUFFIX;
+        String fullSrcNodeCidr = NODE_IP_PREFIX + A_CLASS_SUFFIX;
+
+        // src: POD -> dst: service (unNAT POD) grouping
+        setSrcDstCidrRules(deviceId, fullSrcPodCidr, serviceCidr, B_CLASS, null,
+                SHIFTED_IP_PREFIX, SRC, JUMP_TABLE, SERVICE_TABLE,
+                PRIORITY_CT_RULE, install);
+        // src: POD (unNAT service) -> dst: shifted POD grouping
+        setSrcDstCidrRules(deviceId, fullSrcPodCidr, SHIFTED_IP_CIDR, B_CLASS, null,
+                srcPodPrefix, DST, JUMP_TABLE, POD_TABLE, PRIORITY_CT_RULE, install);
+
+        // src: node -> dst: service (unNAT POD) grouping
+        setSrcDstCidrRules(deviceId, fullSrcNodeCidr, serviceCidr, A_CLASS,
+                null, null, null, JUMP_TABLE, SERVICE_TABLE,
+                PRIORITY_CT_RULE, install);
+        // src: POD (unNAT service) -> dst: node grouping
+        setSrcDstCidrRules(deviceId, fullSrcPodCidr, fullSrcNodeCidr, A_CLASS,
+                null, null, null, JUMP_TABLE, POD_TABLE,
+                PRIORITY_CT_RULE, install);
 
         k8sNetworkService.networks().forEach(n -> {
-            setSrcDstCidrRules(deviceId, n.cidr(), serviceCidr, null, JUMP_TABLE,
-                    SERVICE_TABLE, PRIORITY_CT_RULE, install);
-            setSrcDstCidrRules(deviceId, n.cidr(), SHIFTED_IP_CIDR, null, JUMP_TABLE,
-                    POD_TABLE, PRIORITY_CT_RULE, install);
-            setSrcDstCidrRules(deviceId, srcCidr, n.cidr(), n.segmentId(), ROUTING_TABLE,
+            setSrcDstCidrRules(deviceId, fullSrcPodCidr, n.cidr(), B_CLASS,
+                    n.segmentId(), null, null, ROUTING_TABLE,
                     STAT_OUTBOUND_TABLE, PRIORITY_INTER_ROUTING_RULE, install);
         });
 
@@ -287,8 +312,11 @@
     }
 
     private void setSrcDstCidrRules(DeviceId deviceId, String srcCidr,
-                                    String dstCidr, String segId, int installTable,
-                                    int transitTable, int priority, boolean install) {
+                                    String dstCidr, String cidrClass,
+                                    String segId, String shiftPrefix,
+                                    String shiftType, int installTable,
+                                    int transitTable, int priority,
+                                    boolean install) {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchIPSrc(IpPrefix.valueOf(srcCidr))
@@ -300,6 +328,13 @@
         if (segId != null) {
             tBuilder.setTunnelId(Long.valueOf(segId));
         }
+
+        if (shiftPrefix != null && shiftType != null) {
+            ExtensionTreatment loadTreatment = buildLoadExtension(
+                    deviceService.getDevice(deviceId), cidrClass, shiftType, shiftPrefix);
+            tBuilder.extension(loadTreatment, deviceId);
+        }
+
         tBuilder.transition(transitTable);
 
         k8sFlowRuleService.setRule(
@@ -480,11 +515,7 @@
                     .matchUdpDst(TpPort.tpPort(servicePort));
         }
 
-        ExtensionTreatment loadTreatment = buildLoadExtension(
-                deviceService.getDevice(deviceId), SRC, SHIFTED_IP_PREFIX);
-
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .extension(loadTreatment, deviceId)
                 .group(GroupId.valueOf(groupId))
                 .build();
 
@@ -514,14 +545,7 @@
                     .matchUdpSrc(TpPort.tpPort(podPort));
         }
 
-        String podIpPrefix = podIp.split("\\.")[0] +
-                                            "." + podIp.split("\\.")[1];
-
-        ExtensionTreatment loadTreatment = buildLoadExtension(
-                deviceService.getDevice(deviceId), DST, podIpPrefix);
-
         TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
-                .extension(loadTreatment, deviceId)
                 .setIpSrc(IpAddress.valueOf(serviceIp))
                 .transition(ROUTING_TABLE);
 
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 79a2530..779bbc6 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
@@ -30,6 +30,7 @@
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.k8snetworking.api.K8sFlowRuleService;
+import org.onosproject.k8snetworking.api.K8sNetwork;
 import org.onosproject.k8snetworking.api.K8sNetworkService;
 import org.onosproject.k8snetworking.api.K8sPort;
 import org.onosproject.k8snetworking.api.K8sServiceService;
@@ -71,12 +72,14 @@
 import static org.onosproject.k8snetworking.api.Constants.ARP_PROXY_MODE;
 import static org.onosproject.k8snetworking.api.Constants.ARP_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
+import static org.onosproject.k8snetworking.api.Constants.NODE_IP_PREFIX;
 import static org.onosproject.k8snetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
 import static org.onosproject.k8snetworking.api.Constants.SERVICE_FAKE_MAC_STR;
 import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.ARP_MODE;
 import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.ARP_MODE_DEFAULT;
 import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.GATEWAY_MAC;
 import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.GATEWAY_MAC_DEFAULT;
+import static org.onosproject.k8snetworking.api.Constants.SHIFTED_IP_PREFIX;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getPropertyValue;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.unshiftIpDomain;
 
@@ -94,8 +97,6 @@
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
-    private static final String API_SERVER_CLUSTER_IP = "10.96.0.1";
-
     private static final String GATEWAY_MAC = "gatewayMac";
     private static final String ARP_MODE = "arpMode";
 
@@ -229,16 +230,17 @@
         }
 
         if (replyMac == null) {
-            Set<String> unshiftedIps = unshiftIpDomain(targetIp.toString(), k8sNetworkService);
-            for (String ip : unshiftedIps) {
+            String cidr = k8sNetworkService.networks().stream()
+                    .map(K8sNetwork::cidr).findAny().orElse(null);
+
+            if (cidr != null) {
+                String unshiftedIp = unshiftIpDomain(targetIp.toString(),
+                        SHIFTED_IP_PREFIX, cidr);
+
                 replyMac = k8sNetworkService.ports().stream()
-                        .filter(p -> p.ipAddress().equals(IpAddress.valueOf(ip)))
+                        .filter(p -> p.ipAddress().equals(IpAddress.valueOf(unshiftedIp)))
                         .map(K8sPort::macAddress)
                         .findAny().orElse(null);
-
-                if (replyMac != null) {
-                    break;
-                }
             }
         }
 
@@ -252,6 +254,23 @@
         }
 
         if (replyMac == null) {
+            String targetIpPrefix = targetIp.toString().split("\\.")[1];
+            String nodePrefix = NODE_IP_PREFIX + "." + targetIpPrefix;
+
+            String exBridgeCidr = k8sNodeService.completeNodes().stream()
+                    .map(n -> n.extBridgeIp().toString()).findAny().orElse(null);
+
+            if (exBridgeCidr != null) {
+                String extBridgeIp = unshiftIpDomain(targetIp.toString(),
+                nodePrefix, exBridgeCidr);
+
+                replyMac = k8sNodeService.completeNodes().stream()
+                        .filter(n -> extBridgeIp.equals(n.extBridgeIp().toString()))
+                        .map(K8sNode::extBridgeMac).findAny().orElse(null);
+            }
+        }
+
+        if (replyMac == null) {
             replyMac = MacAddress.valueOf(gatewayMac);
         }
 
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 04fd07c..409d80b 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
@@ -59,7 +59,6 @@
 import static org.onosproject.k8snetworking.api.Constants.ACL_EGRESS_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.ARP_BROADCAST_MODE;
 import static org.onosproject.k8snetworking.api.Constants.ARP_TABLE;
-import static org.onosproject.k8snetworking.api.Constants.DEFAULT_GATEWAY_MAC;
 import static org.onosproject.k8snetworking.api.Constants.FORWARDING_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
 import static org.onosproject.k8snetworking.api.Constants.PRIORITY_SWITCHING_RULE;
@@ -265,7 +264,6 @@
     private void setExtToIntgTunnelTagFlowRules(K8sNode k8sNode, boolean install) {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
-                .matchEthSrc(DEFAULT_GATEWAY_MAC)
                 .matchInPort(k8sNode.intgToExtPatchPortNum())
                 .build();
 
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/K8sNetworkingUtil.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/K8sNetworkingUtil.java
index 7325976..a263359 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/K8sNetworkingUtil.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/K8sNetworkingUtil.java
@@ -20,13 +20,13 @@
 import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
 import io.fabric8.kubernetes.client.ConfigBuilder;
 import io.fabric8.kubernetes.client.DefaultKubernetesClient;
 import io.fabric8.kubernetes.client.KubernetesClient;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.net.util.SubnetUtils;
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.TpPort;
 import org.onosproject.cfg.ConfigProperty;
 import org.onosproject.k8snetworking.api.K8sNetwork;
 import org.onosproject.k8snetworking.api.K8sNetworkService;
@@ -44,12 +44,12 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
 import static org.onosproject.k8snetworking.api.Constants.PORT_NAME_PREFIX_CONTAINER;
-import static org.onosproject.k8snetworking.api.Constants.SHIFTED_IP_PREFIX;
 
 /**
  * An utility that used in kubernetes networking app.
@@ -61,6 +61,14 @@
     private static final String COLON_SLASH = "://";
     private static final String COLON = ":";
 
+    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;
+
     private K8sNetworkingUtil() {
     }
 
@@ -288,22 +296,133 @@
     }
 
     /**
-     * Returns a set of unshifted IP addresses.
+     * Returns an unshifted IP address.
      *
-     * @param ipAddress     shifted IP address
-     * @param service       kubernetes network service
-     * @return unshifted IP addresses
+     * @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 Set<String> unshiftIpDomain(String ipAddress, K8sNetworkService service) {
+    public static String unshiftIpDomain(String ipAddress,
+                                         String ipPrefix,
+                                         String cidr) {
 
-        Set<String> unshiftedIps = Sets.newConcurrentHashSet();
+        String origIpPrefix = cidr.split("\\.")[0] + "." + cidr.split("\\.")[1];
+        return StringUtils.replace(ipAddress, ipPrefix, origIpPrefix);
+    }
 
-        service.networks().forEach(n -> {
-            String cidr = n.cidr();
-            String origIpPrefix = cidr.split("\\.")[0] + "." + cidr.split("\\.")[1];
-            unshiftedIps.add(StringUtils.replace(ipAddress, SHIFTED_IP_PREFIX, origIpPrefix));
-        });
+    /**
+     * Returns the B class IP prefix of the given CIDR.
+     *
+     * @param cidr  CIDR
+     * @return IP prefix
+     */
+    public static String getBclassIpPrefixFromCidr(String cidr) {
+        if (cidr == null) {
+            return null;
+        }
+        return cidr.split("\\.")[0] + "." + cidr.split("\\.")[1];
+    }
 
-        return unshiftedIps;
+    /**
+     * Returns the A class IP prefix of the given CIDR.
+     *
+     * @param cidr  CIDR
+     * @return IP prefix
+     */
+    public static String getAclassIpPrefixFromCidr(String cidr) {
+        if (cidr == null) {
+            return null;
+        }
+        return cidr.split("\\.")[0];
+    }
+
+    /**
+     * Returns the map of port range.
+     *
+     * @param portMin minimum port number
+     * @param portMax maximum port number
+     * @return map of port range
+     */
+    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;
+        }
     }
 }
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/RulePopulatorUtil.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/RulePopulatorUtil.java
index a50d30c..89a2e14 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/RulePopulatorUtil.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/util/RulePopulatorUtil.java
@@ -39,6 +39,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import static org.onosproject.k8snetworking.api.Constants.A_CLASS;
+import static org.onosproject.k8snetworking.api.Constants.B_CLASS;
 import static org.onosproject.k8snetworking.api.Constants.DST;
 import static org.onosproject.k8snetworking.api.Constants.SRC;
 import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_LOAD;
@@ -98,7 +100,8 @@
     private static final int SRC_IP = 0x00000e04;
     private static final int DST_IP = 0x00001004;
 
-    private static final int OFF_SET_BIT = 16;
+    private static final int A_CLASS_OFF_SET_BIT = 8;
+    private static final int B_CLASS_OFF_SET_BIT = 16;
     private static final int REMAINDER_BIT = 16;
 
     // not intended for direct invocation from external
@@ -296,11 +299,13 @@
      * Returns the nicira load extension treatment.
      *
      * @param device        device instance
+     * @param cidrClass     CIDR class (a | b)
      * @param ipType        IP type (src|dst)
-     * @param shift         shift (e.g., 10.10., 20.20.,)
+     * @param shift         shift (e.g., 10.10., 20.20., 10, 20,)
      * @return load extension treatment
      */
     public static ExtensionTreatment buildLoadExtension(Device device,
+                                                        String cidrClass,
                                                         String ipType,
                                                         String shift) {
         if (!checkTreatmentResolver(device)) {
@@ -319,10 +324,16 @@
             dst = DST_IP;
         }
 
-        long value = calculateUpperBit(shift);
+        long value = calculateUpperBit(cidrClass, shift);
 
-        // we only rewrite the upper 16 bits with value (A.B.X.Y -> C.D.X.Y)
-        int ofsNbits = OFF_SET_BIT << 6 | (REMAINDER_BIT - 1);
+        // we only rewrite the upper x bits with value
+        int ofsNbits = 0;
+
+        if (A_CLASS.equals(cidrClass)) {
+            ofsNbits = A_CLASS_OFF_SET_BIT << 6 | (REMAINDER_BIT - 1);
+        } else if (B_CLASS.equals(cidrClass)) {
+            ofsNbits = B_CLASS_OFF_SET_BIT << 6 | (REMAINDER_BIT - 1);
+        }
 
         try {
             treatment.setPropertyValue(OFF_SET_N_BITS, ofsNbits);
@@ -336,7 +347,6 @@
         }
     }
 
-
     /**
      * Returns the nicira move source MAC to destination MAC extension treatment.
      *
@@ -385,16 +395,24 @@
     /**
      * Calculate IP address upper string into integer.
      *
+     * @param cidrClass CIDR class type
      * @param shift IP address upper two octets with dot
      * @return calculated integer
      */
-    private static int calculateUpperBit(String shift) {
-        String[] strArray = shift.split("\\.");
+    private static int calculateUpperBit(String cidrClass, String shift) {
 
-        int firstOctet = Integer.valueOf(strArray[0]);
-        int secondOctet = Integer.valueOf(strArray[1]);
+        if (A_CLASS.equals(cidrClass)) {
+            return Integer.valueOf(shift);
+        }
 
-        return firstOctet << 8 | secondOctet;
+        if (B_CLASS.equals(cidrClass)) {
+            String[] strArray = shift.split("\\.");
+            int firstOctet = Integer.valueOf(strArray[0]);
+            int secondOctet = Integer.valueOf(strArray[1]);
+            return firstOctet << 8 | secondOctet;
+        }
+
+        return 0;
     }
 
     private static boolean checkTreatmentResolver(Device device) {