TrafficTreatment: add support for IP DSCP modifications

Allow modifying IP DSCP field in the headers.

Change-Id: Idd765f40f1baec810273536adcd56bf0e480217f
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 c6a1c16..72cfd3f 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
@@ -515,6 +515,11 @@
         }
 
         @Override
+        public TrafficTreatment.Builder setIpDscp(byte dscpValue) {
+            return add(Instructions.modIpDscp(dscpValue));
+        }
+
+        @Override
         public TrafficTreatment.Builder extension(ExtensionTreatment extension,
                                                   DeviceId deviceId) {
             return add(Instructions.extension(extension, deviceId));
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 f61b017..154f8c6 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
@@ -428,6 +428,14 @@
         Builder piTableAction(PiTableAction piTableAction);
 
         /**
+         * Sets the IP DSCP field.
+         *
+         * @param dscpValue the DSCP value
+         * @return a treatment builder.
+         */
+        Builder setIpDscp(byte dscpValue);
+
+        /**
          * Uses an extension treatment.
          *
          * @param extension extension treatment
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 33b7482..e0c58d3 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
@@ -485,6 +485,16 @@
     }
 
     /**
+     * Creates an IP DSCP modification.
+     *
+     * @param dscpValue the DSCP value to modify to
+     * @return a L3 modification
+     */
+    public static Instruction modIpDscp(byte dscpValue) {
+        return new L3ModificationInstruction.ModDscpInstruction(L3SubType.IP_DSCP, dscpValue);
+    }
+
+    /**
      * Creates an extension instruction.
      *
      * @param extension extension instruction
diff --git a/core/api/src/main/java/org/onosproject/net/flow/instructions/L3ModificationInstruction.java b/core/api/src/main/java/org/onosproject/net/flow/instructions/L3ModificationInstruction.java
index 9fddf89..cc15360 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/instructions/L3ModificationInstruction.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/instructions/L3ModificationInstruction.java
@@ -84,7 +84,12 @@
         /**
          * Arp operation modification.
          */
-        ARP_OP
+        ARP_OP,
+
+        /**
+         * IP DSCP operation modification.
+         */
+        IP_DSCP
     }
 
     /**
@@ -380,4 +385,50 @@
             return false;
         }
     }
+
+    /**
+     * Represents a L3 DSCP modification instruction.
+     */
+    public static final class ModDscpInstruction extends L3ModificationInstruction {
+
+        private final L3SubType subtype;
+        private final byte dscp;
+
+        ModDscpInstruction(L3SubType subtype, byte dscp) {
+            this.subtype = subtype;
+            this.dscp = dscp;
+        }
+
+        @Override
+        public L3SubType subtype() {
+            return this.subtype;
+        }
+
+        @Override
+        public String toString() {
+            return subtype().toString() + SEPARATOR + dscp();
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(type(), subtype(), dscp());
+        }
+
+        public byte dscp() {
+            return this.dscp;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof ModDscpInstruction) {
+                ModDscpInstruction that = (ModDscpInstruction) obj;
+                return  Objects.equals(this.subtype(), that.subtype()) &&
+                        Objects.equals(this.dscp(), that.dscp());
+            }
+            return false;
+        }
+    }
 }
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 ad601c9..abc2e60 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
@@ -75,6 +75,7 @@
 import org.projectfloodlight.openflow.types.IPv4Address;
 import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.IPv6FlowLabel;
+import org.projectfloodlight.openflow.types.IpDscp;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBooleanValue;
 import org.projectfloodlight.openflow.types.OFBufferId;
@@ -447,6 +448,11 @@
                 ip4 = ip.ip().getIp4Address();
                 oxm = factory().oxms().ipv4Dst(IPv4Address.of(ip4.toInt()));
                 break;
+            case IP_DSCP:
+                L3ModificationInstruction.ModDscpInstruction dscp = (L3ModificationInstruction.ModDscpInstruction) i;
+                IpDscp ipDscp = IpDscp.of(dscp.dscp());
+                oxm = factory().oxms().ipDscp(ipDscp);
+                break;
             case IPV6_SRC:
                 ip = (ModIPInstruction) i;
                 ip6 = ip.ip().getIp6Address();
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowEntryBuilder.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowEntryBuilder.java
index 9885574..a32466f 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowEntryBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowEntryBuilder.java
@@ -94,6 +94,7 @@
 import org.projectfloodlight.openflow.types.CircuitSignalID;
 import org.projectfloodlight.openflow.types.IPv4Address;
 import org.projectfloodlight.openflow.types.IPv6Address;
+import org.projectfloodlight.openflow.types.IpDscp;
 import org.projectfloodlight.openflow.types.Masked;
 import org.projectfloodlight.openflow.types.OFBooleanValue;
 import org.projectfloodlight.openflow.types.OFVlanVidMatch;
@@ -809,6 +810,11 @@
                 }
             }
             break;
+        case IP_DSCP:
+            @SuppressWarnings("unchecked")
+            OFOxm<IpDscp> ipDscp = (OFOxm<IpDscp>) oxm;
+            builder.setIpDscp(ipDscp.getValue().getDscpValue());
+            break;
         case ARP_THA:
         case ARP_TPA:
         case BSN_EGR_PORT_GROUP_ID:
@@ -842,7 +848,6 @@
         case IPV6_ND_TARGET:
         case IPV6_ND_TLL:
         case IPV6_SRC:
-        case IP_DSCP:
         case IP_ECN:
         case IP_PROTO:
         case METADATA: