Support Nicira load extension, add L4 mod builder for group buckets

Change-Id: Ic16b4d853daed38792aeb199be732aa868c26bad
diff --git a/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionTreatmentType.java b/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionTreatmentType.java
index 4029bcf..85cc81c 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionTreatmentType.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionTreatmentType.java
@@ -46,6 +46,7 @@
         NICIRA_MOV_TUN_ID_TO_TUN_ID(11),
         NICIRA_MOV_NSH_C2_TO_TUN_ID(12),
         NICIRA_RESUBMIT_TABLE(14),
+        NICIRA_LOAD(20),
         NICIRA_PUSH_NSH(38),
         NICIRA_POP_NSH(39),
         NICIRA_CT(40),
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraExtensionTreatmentInterpreter.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraExtensionTreatmentInterpreter.java
index edc8a1e..e80809b 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraExtensionTreatmentInterpreter.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraExtensionTreatmentInterpreter.java
@@ -16,8 +16,7 @@
 
 package org.onosproject.driver.extensions;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onlab.util.Tools.nullIsIllegal;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.Lists;
 import com.google.common.primitives.Bytes;
 import org.onlab.packet.Ip4Address;
@@ -38,6 +37,7 @@
 import org.projectfloodlight.openflow.protocol.action.OFActionExperimenter;
 import org.projectfloodlight.openflow.protocol.action.OFActionNicira;
 import org.projectfloodlight.openflow.protocol.action.OFActionNiciraCt;
+import org.projectfloodlight.openflow.protocol.action.OFActionNiciraLoad;
 import org.projectfloodlight.openflow.protocol.action.OFActionNiciraMove;
 import org.projectfloodlight.openflow.protocol.action.OFActionNiciraNat;
 import org.projectfloodlight.openflow.protocol.action.OFActionNiciraResubmit;
@@ -62,13 +62,15 @@
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.U16;
 import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.U64;
 import org.projectfloodlight.openflow.types.U8;
 
-import com.fasterxml.jackson.databind.node.ObjectNode;
-
 import java.util.ArrayList;
 import java.util.List;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+
 /**
  * Interpreter for Nicira OpenFlow treatment extensions.
  */
@@ -98,6 +100,7 @@
     private static final int SUB_TYPE_RESUBMIT = 1;
     private static final int SUB_TYPE_RESUBMIT_TABLE = 14;
     private static final int SUB_TYPE_MOVE = 6;
+    private static final int SUB_TYPE_LOAD = 7;
     private static final int SUB_TYPE_CT = 35;
     private static final int SUB_TYPE_NAT = 36;
     private static final int SUB_TYPE_CT_CLEAR = 43;
@@ -111,6 +114,7 @@
     private static final String NICIRA_NSH_SI = "niciraNshSi";
     private static final String NICIRA_NSH_CH = "niciraNshCh";
     private static final String NICIRA_MOVE = "niciraMove";
+    private static final String NICIRA_LOAD = "niciraLoad";
 
     private static final String TYPE = "type";
 
@@ -226,6 +230,9 @@
         if (extensionTreatmentType.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_CT_CLEAR.type())) {
             return true;
         }
+        if (extensionTreatmentType.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_LOAD.type())) {
+            return true;
+        }
         return false;
     }
 
@@ -326,6 +333,15 @@
             action.setDst(mov.dst());
             return action.build();
         }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_LOAD.type())) {
+            NiciraLoad load = (NiciraLoad) extensionTreatment;
+            OFActionNiciraLoad.Builder action = factory.actions()
+                    .buildNiciraLoad();
+            action.setOfsNbits(load.ofsNbits());
+            action.setDst(load.dst());
+            action.setValue(U64.of(load.value()));
+            return action.build();
+        }
         if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_CT.type())) {
             NiciraCt niciraCt = (NiciraCt) extensionTreatment;
             OFActionNiciraCt.Builder ctAction = factory.actions().buildNiciraCt();
@@ -576,6 +592,10 @@
                                 new ArrayList<>());
                     case SUB_TYPE_CT_CLEAR:
                         return new NiciraCtClear();
+                    case SUB_TYPE_LOAD:
+                        OFActionNiciraLoad loadAction = (OFActionNiciraLoad) nicira;
+                        return new NiciraLoad(loadAction.getOfsNbits(),
+                                loadAction.getDst(), loadAction.getValue().getValue());
                     default:
                         throw new UnsupportedOperationException("Driver does not support extension subtype "
                                 + nicira.getSubtype());
@@ -675,6 +695,9 @@
         if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_CT_CLEAR.type())) {
             return new NiciraCtClear();
         }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_LOAD.type())) {
+            return new NiciraLoad();
+        }
         throw new UnsupportedOperationException("Driver does not support extension type " + type.toString());
     }
 
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraLoad.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraLoad.java
new file mode 100644
index 0000000..c1108cb
--- /dev/null
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraLoad.java
@@ -0,0 +1,138 @@
+/*
+ * 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.driver.extensions;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Maps;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.flow.AbstractExtension;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Nicira load extension instruction.
+ */
+public class NiciraLoad extends AbstractExtension implements ExtensionTreatment {
+    private int ofsNbits;
+    private long dst;
+    private long value;
+
+    private final KryoNamespace appKryo = new KryoNamespace.Builder()
+            .register(Map.class)
+            .register(HashMap.class)
+            .build();
+
+    /**
+     * Empty constructor.
+     */
+    public NiciraLoad() {
+    }
+
+    /**
+     * Creates a new load treatment.
+     *
+     * @param ofsNbits off set and nBits
+     * @param dst       destination
+     * @param value     value
+     */
+    public NiciraLoad(int ofsNbits, long dst, long value) {
+        this.ofsNbits = ofsNbits;
+        this.dst = dst;
+        this.value = value;
+    }
+
+    /**
+     * Gets load nBits.
+     *
+     * @return nBits
+     */
+    public int ofsNbits() {
+        return ofsNbits;
+    }
+
+    /**
+     * Gets load destination.
+     *
+     * @return load destination
+     */
+    public long dst() {
+        return dst;
+    }
+
+    /**
+     * Gets load value.
+     *
+     * @return load value
+     */
+    public long value() {
+        return value;
+    }
+
+    @Override
+    public ExtensionTreatmentType type() {
+        return ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_LOAD.type();
+    }
+
+    @Override
+    public byte[] serialize() {
+        Map<String, Object> values = Maps.newHashMap();
+        values.put("ofsNbits", ofsNbits);
+        values.put("dst", dst);
+        values.put("value", value);
+        return appKryo.serialize(values);
+    }
+
+    @Override
+    public void deserialize(byte[] data) {
+        Map<String, Object> values = appKryo.deserialize(data);
+        ofsNbits = (int) values.get("ofsNbits");
+        dst = (long) values.get("dst");
+        value = (long) values.get("value");
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        NiciraLoad that = (NiciraLoad) o;
+        return ofsNbits == that.ofsNbits &&
+                dst == that.dst &&
+                value == that.value &&
+                Objects.equals(this.type(), that.type());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(ofsNbits, dst, value);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("ofsNbits", ofsNbits)
+                .add("dst", dst)
+                .add("value", value)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java b/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java
index 1847fb4..7ebafd9 100644
--- a/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java
+++ b/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java
@@ -31,6 +31,7 @@
 import org.onosproject.net.flow.instructions.L0ModificationInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction;
+import org.onosproject.net.flow.instructions.L4ModificationInstruction;
 import org.onosproject.net.group.GroupBucket;
 import org.onosproject.net.group.GroupBuckets;
 import org.onosproject.net.group.GroupDescription;
@@ -54,6 +55,7 @@
 import org.projectfloodlight.openflow.types.OFGroup;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.OFVlanVidMatch;
+import org.projectfloodlight.openflow.types.TransportPort;
 import org.projectfloodlight.openflow.types.U32;
 import org.projectfloodlight.openflow.types.U64;
 import org.projectfloodlight.openflow.types.VlanPcp;
@@ -252,6 +254,9 @@
                 case L3MODIFICATION:
                     actions.add(buildL3Modification(i));
                     break;
+                case L4MODIFICATION:
+                    actions.add(buildL4Modification(i));
+                    break;
                 case OUTPUT:
                     Instructions.OutputInstruction out =
                             (Instructions.OutputInstruction) i;
@@ -413,6 +418,38 @@
         return null;
     }
 
+    protected OFAction buildL4Modification(Instruction i) {
+        L4ModificationInstruction l4m = (L4ModificationInstruction) i;
+        L4ModificationInstruction.ModTransportPortInstruction tp;
+        OFOxm<?> oxm = null;
+        switch (l4m.subtype()) {
+            case TCP_SRC:
+                tp = (L4ModificationInstruction.ModTransportPortInstruction) l4m;
+                oxm = factory.oxms().tcpSrc(TransportPort.of(tp.port().toInt()));
+                break;
+            case TCP_DST:
+                tp = (L4ModificationInstruction.ModTransportPortInstruction) l4m;
+                oxm = factory.oxms().tcpDst(TransportPort.of(tp.port().toInt()));
+                break;
+            case UDP_SRC:
+                tp = (L4ModificationInstruction.ModTransportPortInstruction) l4m;
+                oxm = factory.oxms().udpSrc(TransportPort.of(tp.port().toInt()));
+                break;
+            case UDP_DST:
+                tp = (L4ModificationInstruction.ModTransportPortInstruction) l4m;
+                oxm = factory.oxms().udpDst(TransportPort.of(tp.port().toInt()));
+                break;
+            default:
+                log.warn("Unimplemented action type {}.", l4m.subtype());
+                break;
+        }
+
+        if (oxm != null) {
+            return factory.actions().buildSetField().setField(oxm).build();
+        }
+        return null;
+    }
+
     private OFGroupType getOFGroupType(GroupDescription.Type groupType) {
         switch (groupType) {
             case INDIRECT: