ONOS-2403 Support setting TCP/UDP port action in flow rule for OpenFlow13

Change-Id: I4ce84aba9db03a66ebcfb34959c11cc4acadb07b
diff --git a/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java b/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
index fe657e6..06c49ba 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
@@ -224,6 +224,7 @@
                 case L0MODIFICATION:
                 case L2MODIFICATION:
                 case L3MODIFICATION:
+                case L4MODIFICATION:
                     current.add(instruction);
                     break;
                 case TABLE:
@@ -390,6 +391,26 @@
         }
 
         @Override
+        public TrafficTreatment.Builder setTcpSrc(short port) {
+            return add(Instructions.modTcpSrc(port));
+        }
+
+        @Override
+        public TrafficTreatment.Builder setTcpDst(short port) {
+            return add(Instructions.modTcpDst(port));
+        }
+
+        @Override
+        public TrafficTreatment.Builder setUdpSrc(short port) {
+            return add(Instructions.modUdpSrc(port));
+        }
+
+        @Override
+        public TrafficTreatment.Builder setUdpDst(short port) {
+            return add(Instructions.modUdpDst(port));
+        }
+
+        @Override
         public TrafficTreatment build() {
             //Don't add DROP instruction by default when instruction
             //set is empty. This will be handled in DefaultSingleTablePipeline
diff --git a/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java b/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java
index 35ef6d3..d125a9f 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java
@@ -319,6 +319,38 @@
         Builder setTunnelId(long tunnelId);
 
         /**
+         * Sets the src TCP port.
+         *
+         * @param port a port number
+         * @return a treatment builder
+         */
+        Builder setTcpSrc(short port);
+
+        /**
+         * Sets the dst TCP port.
+         *
+         * @param port a port number
+         * @return a treatment builder
+         */
+        Builder setTcpDst(short port);
+
+        /**
+         * Sets the src UDP port.
+         *
+         * @param port a port number
+         * @return a treatment builder
+         */
+        Builder setUdpSrc(short port);
+
+        /**
+         * Sets the dst UDP port.
+         *
+         * @param port a port number
+         * @return a treatment builder
+         */
+        Builder setUdpDst(short port);
+
+        /**
          * Builds an immutable traffic treatment descriptor.
          * <p>
          * If the treatment is empty when build() is called, it will add a default
diff --git a/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java b/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
index 0467a91..3cacfa5 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
@@ -32,6 +32,8 @@
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModTtlInstruction;
+import org.onosproject.net.flow.instructions.L4ModificationInstruction.L4SubType;
+import org.onosproject.net.flow.instructions.L4ModificationInstruction.ModTransportPortInstruction;
 
 import java.util.Objects;
 
@@ -361,6 +363,50 @@
     }
 
     /**
+     * Creates a TCP src modification.
+     *
+     * @param port the TCP port number to modify to
+     * @return a L4 modification
+     */
+    public static L4ModificationInstruction modTcpSrc(short port) {
+       checkNotNull(port, "Src TCP port cannot be null");
+       return new ModTransportPortInstruction(L4SubType.TCP_SRC, port);
+    }
+
+    /**
+     * Creates a TCP dst modification.
+     *
+     * @param port the TCP port number to modify to
+     * @return a L4 modification
+     */
+    public static L4ModificationInstruction modTcpDst(short port) {
+        checkNotNull(port, "Dst TCP port cannot be null");
+        return new ModTransportPortInstruction(L4SubType.TCP_DST, port);
+    }
+
+    /**
+     * Creates a UDP src modification.
+     *
+     * @param port the UDP port number to modify to
+     * @return a L4 modification
+     */
+    public static L4ModificationInstruction modUdpSrc(short port) {
+        checkNotNull(port, "Src UDP port cannot be null");
+        return new ModTransportPortInstruction(L4SubType.UDP_SRC, port);
+    }
+
+    /**
+     * Creates a UDP dst modification.
+     *
+     * @param port the UDP port number to modify to
+     * @return a L4 modification
+     */
+    public static L4ModificationInstruction modUdpDst(short port) {
+        checkNotNull(port, "Dst UDP port cannot be null");
+        return new ModTransportPortInstruction(L4SubType.UDP_DST, port);
+    }
+
+    /**
      *  Drop instruction.
      */
     public static final class DropInstruction implements Instruction {
diff --git a/core/api/src/main/java/org/onosproject/net/flow/instructions/L4ModificationInstruction.java b/core/api/src/main/java/org/onosproject/net/flow/instructions/L4ModificationInstruction.java
index 09f02fb..c78f639 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/instructions/L4ModificationInstruction.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/instructions/L4ModificationInstruction.java
@@ -66,12 +66,12 @@
     /**
      * Represents a L4 src/dst modification instruction.
      */
-    public static final class ModL4PortInstruction extends L4ModificationInstruction {
+    public static final class ModTransportPortInstruction extends L4ModificationInstruction {
 
         private final L4SubType subtype;
         private final short port;
 
-        public ModL4PortInstruction(L4SubType subtype, short port) {
+        public ModTransportPortInstruction(L4SubType subtype, short port) {
             this.subtype = subtype;
             this.port = port;
         }
@@ -101,8 +101,8 @@
             if (this == obj) {
                 return true;
             }
-            if (obj instanceof ModL4PortInstruction) {
-                ModL4PortInstruction that = (ModL4PortInstruction) obj;
+            if (obj instanceof ModTransportPortInstruction) {
+                ModTransportPortInstruction that = (ModTransportPortInstruction) obj;
                 return  Objects.equals(port, that.port) &&
                         Objects.equals(this.subtype(), that.subtype());
             }
diff --git a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
index b68e1d6..2f34408 100644
--- a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
+++ b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
@@ -127,6 +127,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.host.DefaultHostDescription;
 import org.onosproject.net.host.HostDescription;
 import org.onosproject.net.intent.ConnectivityIntent;
@@ -354,6 +355,9 @@
                     L3ModificationInstruction.ModIPInstruction.class,
                     L3ModificationInstruction.ModIPv6FlowLabelInstruction.class,
                     L3ModificationInstruction.ModTtlInstruction.class,
+                    L4ModificationInstruction.class,
+                    L4ModificationInstruction.L4SubType.class,
+                    L4ModificationInstruction.ModTransportPortInstruction.class,
                     RoleInfo.class,
                     FlowRuleBatchEvent.class,
                     FlowRuleBatchEvent.Type.class,
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
index 6760462..4a3872c 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
@@ -71,6 +71,7 @@
 import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.Masked;
 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.U8;
@@ -408,6 +409,26 @@
             OFOxm<U64> tunnelId = (OFOxm<U64>) oxm;
             builder.setTunnelId(tunnelId.getValue().getValue());
             break;
+        case TCP_DST:
+            @SuppressWarnings("unchecked")
+            OFOxm<TransportPort> tcpdst = (OFOxm<TransportPort>) oxm;
+            builder.setTcpDst((short) tcpdst.getValue().getPort());
+            break;
+        case TCP_SRC:
+            @SuppressWarnings("unchecked")
+            OFOxm<TransportPort> tcpsrc = (OFOxm<TransportPort>) oxm;
+            builder.setTcpSrc((short) tcpsrc.getValue().getPort());
+            break;
+        case UDP_DST:
+            @SuppressWarnings("unchecked")
+            OFOxm<TransportPort> udpdst = (OFOxm<TransportPort>) oxm;
+            builder.setUdpDst((short) udpdst.getValue().getPort());
+            break;
+        case UDP_SRC:
+            @SuppressWarnings("unchecked")
+            OFOxm<TransportPort> udpsrc = (OFOxm<TransportPort>) oxm;
+            builder.setUdpSrc((short) udpsrc.getValue().getPort());
+            break;
         case ARP_OP:
         case ARP_SHA:
         case ARP_SPA:
@@ -455,10 +476,6 @@
         case OCH_SIGTYPE_BASIC:
         case SCTP_DST:
         case SCTP_SRC:
-        case TCP_DST:
-        case TCP_SRC:
-        case UDP_DST:
-        case UDP_SRC:
         default:
             log.warn("Set field type {} not yet implemented.", oxm.getMatchField().id);
             break;
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
index 7bbc375..58c19ad 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
@@ -39,6 +39,8 @@
 import org.onosproject.net.flow.instructions.L3ModificationInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction;
+import org.onosproject.net.flow.instructions.L4ModificationInstruction;
+import org.onosproject.net.flow.instructions.L4ModificationInstruction.ModTransportPortInstruction;
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFFlowAdd;
 import org.projectfloodlight.openflow.protocol.OFFlowDelete;
@@ -61,6 +63,7 @@
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.OFVlanVidMatch;
 import org.projectfloodlight.openflow.types.TableId;
+import org.projectfloodlight.openflow.types.TransportPort;
 import org.projectfloodlight.openflow.types.U32;
 import org.projectfloodlight.openflow.types.U64;
 import org.projectfloodlight.openflow.types.VlanPcp;
@@ -214,6 +217,9 @@
                 case L3MODIFICATION:
                     actions.add(buildL3Modification(i));
                     break;
+                case L4MODIFICATION:
+                    actions.add(buildL4Modification(i));
+                    break;
                 case OUTPUT:
                     OutputInstruction out = (OutputInstruction) i;
                     OFActionOutput.Builder action = factory().actions().buildOutput()
@@ -327,7 +333,7 @@
                 ModMplsLabelInstruction mplsLabel =
                         (ModMplsLabelInstruction) l2m;
                 oxm = factory().oxms().mplsLabel(U32.of(mplsLabel.label()
-                                                                  .longValue()));
+                                                                .longValue()));
                 break;
             case DEC_MPLS_TTL:
                 return factory().actions().decMplsTtl();
@@ -402,4 +408,36 @@
         return null;
     }
 
+    private OFAction buildL4Modification(Instruction i) {
+        L4ModificationInstruction l4m = (L4ModificationInstruction) i;
+        ModTransportPortInstruction tp;
+        OFOxm<?> oxm = null;
+        switch (l4m.subtype()) {
+            case TCP_SRC:
+                tp = (ModTransportPortInstruction) l4m;
+                oxm = factory().oxms().tcpSrc(TransportPort.of(tp.port()));
+                break;
+            case TCP_DST:
+                tp = (ModTransportPortInstruction) l4m;
+                oxm = factory().oxms().tcpDst(TransportPort.of(tp.port()));
+                break;
+            case UDP_SRC:
+                tp = (ModTransportPortInstruction) l4m;
+                oxm = factory().oxms().udpSrc(TransportPort.of(tp.port()));
+                break;
+            case UDP_DST:
+                tp = (ModTransportPortInstruction) l4m;
+                oxm = factory().oxms().udpDst(TransportPort.of(tp.port()));
+                break;
+            default:
+                log.warn("Unimplemented action type {}.", l4m.subtype());
+                break;
+        }
+
+        if (oxm != null) {
+            return factory().actions().buildSetField().setField(oxm).build();
+        }
+        return null;
+    }
+
 }