Implement NodeIP to ServiceIP translation feature

Change-Id: I49bda2cddfb1c22b362b4b48105efd316c3dad36
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) {