Implement k8s service IP to pod IP translation using Nicira load ext

Change-Id: I147a9adb68b2bf597c1876a64bf77dedca9698b3
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 2647d70..7325976 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,6 +20,7 @@
 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;
@@ -48,6 +49,7 @@
 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.
@@ -284,4 +286,24 @@
 
         return ipMap;
     }
+
+    /**
+     * Returns a set of unshifted IP addresses.
+     *
+     * @param ipAddress     shifted IP address
+     * @param service       kubernetes network service
+     * @return unshifted IP addresses
+     */
+    public static Set<String> unshiftIpDomain(String ipAddress, K8sNetworkService service) {
+
+        Set<String> unshiftedIps = Sets.newConcurrentHashSet();
+
+        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));
+        });
+
+        return unshiftedIps;
+    }
 }
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 8192015..7605a5e 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,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_LOAD;
 import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_RESUBMIT_TABLE;
 import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -77,14 +78,27 @@
     private static final int PORT_MIN_FLAG = 4;
     private static final int PORT_MAX_FLAG = 5;
 
-    public static final long CT_STATE_NONE = 0;
-    public static final long CT_STATE_NEW = 0x01;
-    public static final long CT_STATE_EST = 0x02;
-    public static final long CT_STATE_NOT_TRK = 0x20;
-    public static final long CT_STATE_TRK = 0x20;
+    private static final long CT_STATE_NONE = 0;
+    private static final long CT_STATE_NEW = 0x01;
+    private static final long CT_STATE_EST = 0x02;
+    private static final long CT_STATE_NOT_TRK = 0x20;
+    private static final long CT_STATE_TRK = 0x20;
 
     private static final String TABLE_EXTENSION = "table";
 
+    private static final String OFF_SET_N_BITS = "ofsNbits";
+    private static final String DESTINATION = "dst";
+    private static final String VALUE = "value";
+
+    private static final int SRC_IP = 0x00000e04;
+    private static final int DST_IP = 0x00001004;
+
+    private static final String SRC = "src";
+    private static final String DST = "dst";
+
+    private static final int OFF_SET_BIT = 16;
+    private static final int REMAINDER_BIT = 16;
+
     // not intended for direct invocation from external
     private RulePopulatorUtil() {
     }
@@ -253,7 +267,7 @@
     /**
      * Returns the nicira resubmit extension treatment with given table ID.
      *
-     * @param device        device identifier
+     * @param device        device instance
      * @param tableId       table identifier
      * @return resubmit extension treatment
      */
@@ -278,6 +292,66 @@
     }
 
     /**
+     * Returns the nicira load extension treatment.
+     *
+     * @param device        device instance
+     * @param ipType        IP type (src|dst)
+     * @param shift         shift (e.g., 10.10., 20.20.,)
+     * @return load extension treatment
+     */
+    public static ExtensionTreatment buildLoadExtension(Device device,
+                                                        String ipType,
+                                                        String shift) {
+        if (device == null || !device.is(ExtensionTreatmentResolver.class)) {
+            log.warn("Nicira extension treatment is not supported");
+            return null;
+        }
+
+        ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
+        ExtensionTreatment treatment =
+                resolver.getExtensionInstruction(NICIRA_LOAD.type());
+
+        long dst = 0L;
+
+        if (SRC.equalsIgnoreCase(ipType)) {
+            dst = SRC_IP;
+        } else if (DST.equals(ipType)) {
+            dst = DST_IP;
+        }
+
+        long value = calculateUpperBit(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);
+
+        try {
+            treatment.setPropertyValue(OFF_SET_N_BITS, ofsNbits);
+            treatment.setPropertyValue(DESTINATION, dst);
+            treatment.setPropertyValue(VALUE, value);
+            return treatment;
+        } catch (ExtensionPropertyException e) {
+            log.error("Failed to set nicira load extension treatment for {}",
+                    device.id());
+            return null;
+        }
+    }
+
+    /**
+     * Calculate IP address upper string into integer.
+     *
+     * @param shift IP address upper two octets with dot
+     * @return calculated integer
+     */
+    private static int calculateUpperBit(String shift) {
+        String[] strArray = shift.split("\\.");
+
+        int firstOctet = Integer.valueOf(strArray[0]);
+        int secondOctet = Integer.valueOf(strArray[1]);
+
+        return firstOctet << 8 | secondOctet;
+    }
+
+    /**
      * Builder class for OVS Connection Tracking feature actions.
      */
     public static final class NiciraConnTrackTreatmentBuilder {