[ONOS-3499] Add the set treatments of ARP_SPA, ARP_SHA and ARP_OP.

Change-Id: I7e1927bcf26e7c25fede43aa864a1b71ae2e8b49
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 22bff7d..40291f57 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
@@ -489,6 +489,21 @@
         }
 
         @Override
+        public Builder setArpSpa(IpAddress addr) {
+            return add(Instructions.modArpSpa(addr));
+        }
+
+        @Override
+        public Builder setArpSha(MacAddress addr) {
+            return add(Instructions.modArpSha(addr));
+        }
+
+        @Override
+        public Builder setArpOp(short op) {
+            return add(Instructions.modL3ArpOp(op));
+        }
+
+        @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 06b6ffa..3e57925 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
@@ -424,6 +424,30 @@
         Builder setUdpDst(TpPort port);
 
         /**
+         * Sets the arp src ip address.
+         *
+         * @param addr an ip
+         * @return a treatment builder
+         */
+        Builder setArpSpa(IpAddress addr);
+
+        /**
+         * Sets the arp src mac address.
+         *
+         * @param addr a macaddress
+         * @return a treatment builder
+         */
+        Builder setArpSha(MacAddress addr);
+
+        /**
+         * Sets the arp operation.
+         *
+         * @param op the value of arp operation.
+         * @return a treatment builder.
+         */
+        Builder setArpOp(short op);
+
+        /**
          * 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 4643b31..8ed882c8 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
@@ -35,6 +35,9 @@
 import org.onosproject.net.flow.instructions.L1ModificationInstruction.ModOduSignalIdInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.L3SubType;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpIPInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpEthInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpOpInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModTtlInstruction;
 import org.onosproject.net.flow.instructions.L4ModificationInstruction.L4SubType;
@@ -299,6 +302,39 @@
     }
 
     /**
+     * Creates a L3 ARP IP src modification.
+     *
+     * @param addr the ip address to modify to
+     * @return a L3 modification
+     */
+    public static L3ModificationInstruction modArpSpa(IpAddress addr) {
+        checkNotNull(addr, "Src l3 ARP IP address cannot be null");
+        return new ModArpIPInstruction(L3SubType.ARP_SPA, addr);
+    }
+
+    /**
+     * Creates a l3 ARP Ether src modification.
+     *
+     * @param addr the mac address to modify to
+     * @return a l3 modification
+     */
+    public static L3ModificationInstruction modArpSha(MacAddress addr) {
+        checkNotNull(addr, "Src l3 ARP address cannot be null");
+        return new ModArpEthInstruction(L3SubType.ARP_SHA, addr);
+    }
+
+    /**
+     * Creates a l3 ARP operation modification.
+     *
+     * @param op the ARP operation to modify to
+     * @return a l3 modification
+     */
+    public static L3ModificationInstruction modL3ArpOp(short op) {
+        checkNotNull(op, "Arp operation cannot be null");
+        return new ModArpOpInstruction(L3SubType.ARP_OP, op);
+    }
+
+    /**
      * Creates a push MPLS header instruction.
      *
      * @return a L2 modification.
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 4181950..0efe9a7 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
@@ -20,6 +20,7 @@
 import java.util.Objects;
 
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
 
 /**
  * Abstraction of a single traffic treatment step.
@@ -68,7 +69,22 @@
         /**
          * Copy TTL in.
          */
-        TTL_IN
+        TTL_IN,
+
+        /**
+         * ARP IP src modification.
+         */
+        ARP_SPA,
+
+        /**
+         * ARP Ether src modification.
+         */
+        ARP_SHA,
+
+        /**
+         * Arp operation modification.
+         */
+        ARP_OP
 
         //TODO: remaining types
     }
@@ -133,6 +149,150 @@
     }
 
     /**
+     * Represents a L3 ARP IP src/dst modification instruction.
+     */
+    public static final class ModArpIPInstruction extends L3ModificationInstruction {
+
+        private final L3SubType subtype;
+        private final IpAddress ip;
+
+        ModArpIPInstruction(L3SubType subType, IpAddress addr) {
+
+            this.subtype = subType;
+            this.ip = addr;
+        }
+
+        @Override
+        public L3SubType subtype() {
+            return this.subtype;
+        }
+
+        public IpAddress ip() {
+            return this.ip;
+        }
+
+        @Override
+        public String toString() {
+            return toStringHelper(subtype().toString())
+                    .add("ip", ip).toString();
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(type(), subtype(), ip);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof ModArpIPInstruction) {
+                ModArpIPInstruction that = (ModArpIPInstruction) obj;
+                return  Objects.equals(ip, that.ip) &&
+                        Objects.equals(this.subtype(), that.subtype());
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Represents a L3 ARP Ether src/dst modification instruction.
+     */
+    public static final class ModArpEthInstruction extends L3ModificationInstruction {
+
+        private final L3SubType subtype;
+        private final MacAddress mac;
+
+        ModArpEthInstruction(L3SubType subType, MacAddress addr) {
+
+            this.subtype = subType;
+            this.mac = addr;
+        }
+
+        @Override
+        public L3SubType subtype() {
+            return this.subtype;
+        }
+
+        public MacAddress mac() {
+            return this.mac;
+        }
+
+        @Override
+        public String toString() {
+            return toStringHelper(subtype().toString())
+                    .add("mac", mac).toString();
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(type(), subtype(), mac);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof ModArpEthInstruction) {
+                ModArpEthInstruction that = (ModArpEthInstruction) obj;
+                return  Objects.equals(mac, that.mac) &&
+                        Objects.equals(this.subtype(), that.subtype());
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Represents a L3 ARP operation modification instruction.
+     */
+    public static final class ModArpOpInstruction extends L3ModificationInstruction {
+
+        private final L3SubType subtype;
+        private final short op;
+
+        ModArpOpInstruction(L3SubType subType, short op) {
+
+            this.subtype = subType;
+            this.op = op;
+        }
+
+        @Override
+        public L3SubType subtype() {
+            return this.subtype;
+        }
+
+        public long op() {
+            return this.op;
+        }
+
+        @Override
+        public String toString() {
+            return toStringHelper(subtype().toString())
+                    .add("op", op).toString();
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(type(), subtype(), op);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof ModArpOpInstruction) {
+                ModArpOpInstruction that = (ModArpOpInstruction) obj;
+                return  Objects.equals(op, that.op) &&
+                        Objects.equals(this.subtype(), that.subtype());
+            }
+            return false;
+        }
+    }
+
+    /**
      * Represents a L3 IPv6 Flow Label (RFC 6437) modification instruction
      * (20 bits unsigned integer).
      */
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 f9da1c6..3521ca4 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
@@ -45,6 +45,9 @@
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.PushHeaderInstructions;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpIPInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpEthInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpOpInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction;
 import org.onosproject.net.flow.instructions.L4ModificationInstruction;
 import org.onosproject.net.flow.instructions.L4ModificationInstruction.ModTransportPortInstruction;
@@ -61,6 +64,7 @@
 import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
 import org.projectfloodlight.openflow.protocol.match.Match;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
+import org.projectfloodlight.openflow.types.ArpOpcode;
 import org.projectfloodlight.openflow.types.CircuitSignalID;
 import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.IPv4Address;
@@ -430,6 +434,19 @@
                 int flowLabel = flowLabelInstruction.flowLabel();
                 oxm = factory().oxms().ipv6Flabel(IPv6FlowLabel.of(flowLabel));
                 break;
+            case ARP_SPA:
+                ModArpIPInstruction aip = (ModArpIPInstruction) i;
+                ip4 = aip.ip().getIp4Address();
+                oxm = factory().oxms().arpSpa(IPv4Address.of(ip4.toInt()));
+                break;
+            case ARP_SHA:
+                ModArpEthInstruction ei = (ModArpEthInstruction) i;
+                oxm = factory().oxms().arpSha(MacAddress.of(ei.mac().toLong()));
+                break;
+            case ARP_OP:
+                ModArpOpInstruction oi = (ModArpOpInstruction) i;
+                oxm = factory().oxms().arpOp(ArpOpcode.of((int) oi.op()));
+                break;
             case DEC_TTL:
                 return factory().actions().decNwTtl();
             case TTL_IN: