Implement some of the missing IPv6 flowmod treatments

Work toward ONOS-509

The following treatments are added/implemented:
  - IPV6_SRC
  - IPV6_DST
  - IPV6_FLABEL

Also:
 - Renamed L3ModificationInstruction.L3SubType.IP_SRC and IP_DST
   to IPV4_SRC and IPV4_DST (for naming consistency).
 - Few minor fixes in related code: Javadocs, comments, log messages,
   and IP address usage.

Change-Id: I551056f767a37e7cb6ae7d79f4a3929183500b57
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 91edcbf..98cb3b2 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
@@ -120,7 +120,7 @@
         public Builder setIpDst(IpAddress addr);
 
         /**
-         * Decrease the TTL in IP header by one.
+         * Decrement the TTL in IP header by one.
          *
          * @return a treatment builder
          */
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 8141c9a..3838cdf 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
@@ -29,6 +29,7 @@
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.L3SubType;
 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.onlab.packet.Ethernet;
@@ -144,24 +145,59 @@
     public static L2ModificationInstruction decMplsTtl() {
         return new ModMplsTtlInstruction();
     }
+
     /**
-     * Creates a L3 src modification.
-     * @param addr the ip address to modify to.
+     * Creates a L3 IPv4 src modification.
+     *
+     * @param addr the IPv4 address to modify to
      * @return a L3 modification
      */
     public static L3ModificationInstruction modL3Src(IpAddress addr) {
-        checkNotNull(addr, "Src l3 address cannot be null");
-        return new ModIPInstruction(L3SubType.IP_SRC, addr);
+        checkNotNull(addr, "Src l3 IPv4 address cannot be null");
+        return new ModIPInstruction(L3SubType.IPV4_SRC, addr);
     }
 
     /**
-     * Creates a L3 dst modification.
-     * @param addr the ip address to modify to.
+     * Creates a L3 IPv4 dst modification.
+     *
+     * @param addr the IPv4 address to modify to
      * @return a L3 modification
      */
     public static L3ModificationInstruction modL3Dst(IpAddress addr) {
-        checkNotNull(addr, "Dst l3 address cannot be null");
-        return new ModIPInstruction(L3SubType.IP_DST, addr);
+        checkNotNull(addr, "Dst l3 IPv4 address cannot be null");
+        return new ModIPInstruction(L3SubType.IPV4_DST, addr);
+    }
+
+    /**
+     * Creates a L3 IPv6 src modification.
+     *
+     * @param addr the IPv6 address to modify to
+     * @return a L3 modification
+     */
+    public static L3ModificationInstruction modL3IPv6Src(IpAddress addr) {
+        checkNotNull(addr, "Src l3 IPv6 address cannot be null");
+        return new ModIPInstruction(L3SubType.IPV6_SRC, addr);
+    }
+
+    /**
+     * Creates a L3 IPv6 dst modification.
+     *
+     * @param addr the IPv6 address to modify to
+     * @return a L3 modification
+     */
+    public static L3ModificationInstruction modL3IPv6Dst(IpAddress addr) {
+        checkNotNull(addr, "Dst l3 IPv6 address cannot be null");
+        return new ModIPInstruction(L3SubType.IPV6_DST, addr);
+    }
+
+    /**
+     * Creates a L3 IPv6 Flow Label modification.
+     *
+     * @param flowLabel the IPv6 flow label to modify to (20 bits)
+     * @return a L3 modification
+     */
+    public static L3ModificationInstruction modL3IPv6FlowLabel(int flowLabel) {
+        return new ModIPv6FlowLabelInstruction(flowLabel);
     }
 
     /**
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 93e85ef..f8814a0 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
@@ -31,17 +31,32 @@
      */
     public enum L3SubType {
         /**
-         * Ether src modification.
+         * IPv4 src modification.
          */
-        IP_SRC,
+        IPV4_SRC,
 
         /**
-         * Ether dst modification.
+         * IPv4 dst modification.
          */
-        IP_DST,
+        IPV4_DST,
 
         /**
-         * Decrease TTL.
+         * IPv6 src modification.
+         */
+        IPV6_SRC,
+
+        /**
+         * IPv6 dst modification.
+         */
+        IPV6_DST,
+
+        /**
+         * IPv6 flow label modification.
+         */
+        IPV6_FLABEL,
+
+        /**
+         * Decrement TTL.
          */
         DEC_TTL,
 
@@ -120,6 +135,65 @@
     }
 
     /**
+     * Represents a L3 IPv6 Flow Label (RFC 6437) modification instruction
+     * (20 bits unsigned integer).
+     */
+    public static final class ModIPv6FlowLabelInstruction
+        extends L3ModificationInstruction {
+        private static final int MASK = 0xfffff;
+        private final int flowLabel;            // IPv6 flow label: 20 bits
+
+        /**
+         * Constructor.
+         *
+         * flowLabel the IPv6 flow label to set in the treatment (20 bits)
+         */
+        public ModIPv6FlowLabelInstruction(int flowLabel) {
+            this.flowLabel = flowLabel & MASK;
+        }
+
+        @Override
+        public L3SubType subtype() {
+            return L3SubType.IPV6_FLABEL;
+        }
+
+        /**
+         * Gets the IPv6 flow label to set in the treatment.
+         *
+         * @return the IPv6 flow label to set in the treatment (20 bits)
+         */
+        public int flowLabel() {
+            return this.flowLabel;
+        }
+
+        @Override
+        public String toString() {
+            return toStringHelper(subtype().toString())
+                .add("flowLabel", Long.toHexString(flowLabel)).toString();
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(type(), subtype(), flowLabel);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof ModIPv6FlowLabelInstruction) {
+                ModIPv6FlowLabelInstruction that =
+                    (ModIPv6FlowLabelInstruction) obj;
+                return  Objects.equals(flowLabel, that.flowLabel) &&
+                        Objects.equals(this.type(), that.type()) &&
+                        Objects.equals(this.subtype(), that.subtype());
+            }
+            return false;
+        }
+    }
+
+    /**
      * Represents a L3 TTL modification instruction.
      */
     public static final class ModTtlInstruction extends L3ModificationInstruction {
diff --git a/core/api/src/test/java/org/onosproject/net/flow/instructions/InstructionsTest.java b/core/api/src/test/java/org/onosproject/net/flow/instructions/InstructionsTest.java
index d8b511c..bfb8409 100644
--- a/core/api/src/test/java/org/onosproject/net/flow/instructions/InstructionsTest.java
+++ b/core/api/src/test/java/org/onosproject/net/flow/instructions/InstructionsTest.java
@@ -18,7 +18,6 @@
 import org.junit.Test;
 import org.onosproject.net.PortNumber;
 import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 
@@ -94,6 +93,7 @@
         assertThatClassIsImmutable(L2ModificationInstruction.ModVlanIdInstruction.class);
         assertThatClassIsImmutable(L2ModificationInstruction.ModVlanPcpInstruction.class);
         assertThatClassIsImmutable(L3ModificationInstruction.ModIPInstruction.class);
+        assertThatClassIsImmutable(L3ModificationInstruction.ModIPv6FlowLabelInstruction.class);
         assertThatClassIsImmutable(L2ModificationInstruction.ModMplsLabelInstruction.class);
         assertThatClassIsImmutable(L2ModificationInstruction.PushHeaderInstructions.class);
     }
@@ -377,29 +377,40 @@
                 is(not(equalTo(modVlanPcp2.hashCode()))));
     }
 
-    //  ModIPerInstruction
+    //  ModIPInstruction
 
-    private static final String IP1 = "1.2.3.4/24";
-    private static final String IP2 = "5.6.7.8/24";
-    private IpAddress ip1 = IpPrefix.valueOf(IP1).address();
-    private IpAddress ip2 = IpPrefix.valueOf(IP2).address();
-    private final Instruction modIPInstruction1 = Instructions.modL3Src(ip1);
-    private final Instruction sameAsModIPInstruction1 = Instructions.modL3Src(ip1);
-    private final Instruction modIPInstruction2 = Instructions.modL3Src(ip2);
+    private static final String IP41 = "1.2.3.4";
+    private static final String IP42 = "5.6.7.8";
+    private IpAddress ip41 = IpAddress.valueOf(IP41);
+    private IpAddress ip42 = IpAddress.valueOf(IP42);
+    private final Instruction modIPInstruction1 = Instructions.modL3Src(ip41);
+    private final Instruction sameAsModIPInstruction1 = Instructions.modL3Src(ip41);
+    private final Instruction modIPInstruction2 = Instructions.modL3Src(ip42);
+
+    private static final String IP61 = "1111::2222";
+    private static final String IP62 = "3333::4444";
+    private IpAddress ip61 = IpAddress.valueOf(IP61);
+    private IpAddress ip62 = IpAddress.valueOf(IP62);
+    private final Instruction modIPv6Instruction1 =
+        Instructions.modL3IPv6Src(ip61);
+    private final Instruction sameAsModIPv6Instruction1 =
+        Instructions.modL3IPv6Src(ip61);
+    private final Instruction modIPv6Instruction2 =
+        Instructions.modL3IPv6Src(ip62);
 
     /**
      * Test the modL3Src method.
      */
     @Test
     public void testModL3SrcMethod() {
-        final Instruction instruction = Instructions.modL3Src(ip1);
+        final Instruction instruction = Instructions.modL3Src(ip41);
         final L3ModificationInstruction.ModIPInstruction modIPInstruction =
                 checkAndConvert(instruction,
                         Instruction.Type.L3MODIFICATION,
                         L3ModificationInstruction.ModIPInstruction.class);
-        assertThat(modIPInstruction.ip(), is(equalTo(ip1)));
+        assertThat(modIPInstruction.ip(), is(equalTo(ip41)));
         assertThat(modIPInstruction.subtype(),
-                is(equalTo(L3ModificationInstruction.L3SubType.IP_SRC)));
+                is(equalTo(L3ModificationInstruction.L3SubType.IPV4_SRC)));
     }
 
     /**
@@ -407,20 +418,49 @@
      */
     @Test
     public void testModL3DstMethod() {
-        final Instruction instruction = Instructions.modL3Dst(ip1);
+        final Instruction instruction = Instructions.modL3Dst(ip41);
         final L3ModificationInstruction.ModIPInstruction modIPInstruction =
                 checkAndConvert(instruction,
                         Instruction.Type.L3MODIFICATION,
                         L3ModificationInstruction.ModIPInstruction.class);
-        assertThat(modIPInstruction.ip(), is(equalTo(ip1)));
+        assertThat(modIPInstruction.ip(), is(equalTo(ip41)));
         assertThat(modIPInstruction.subtype(),
-                is(equalTo(L3ModificationInstruction.L3SubType.IP_DST)));
+                is(equalTo(L3ModificationInstruction.L3SubType.IPV4_DST)));
     }
 
     /**
-     * Test the equals() method of the ModEtherInstruction class.
+     * Test the modL3IPv6Src method.
      */
+    @Test
+    public void testModL3IPv6SrcMethod() {
+        final Instruction instruction = Instructions.modL3IPv6Src(ip61);
+        final L3ModificationInstruction.ModIPInstruction modIPInstruction =
+                checkAndConvert(instruction,
+                        Instruction.Type.L3MODIFICATION,
+                        L3ModificationInstruction.ModIPInstruction.class);
+        assertThat(modIPInstruction.ip(), is(equalTo(ip61)));
+        assertThat(modIPInstruction.subtype(),
+                is(equalTo(L3ModificationInstruction.L3SubType.IPV6_SRC)));
+    }
 
+    /**
+     * Test the modL3IPv6Dst method.
+     */
+    @Test
+    public void testModL3IPv6DstMethod() {
+        final Instruction instruction = Instructions.modL3IPv6Dst(ip61);
+        final L3ModificationInstruction.ModIPInstruction modIPInstruction =
+                checkAndConvert(instruction,
+                        Instruction.Type.L3MODIFICATION,
+                        L3ModificationInstruction.ModIPInstruction.class);
+        assertThat(modIPInstruction.ip(), is(equalTo(ip61)));
+        assertThat(modIPInstruction.subtype(),
+                is(equalTo(L3ModificationInstruction.L3SubType.IPV6_DST)));
+    }
+
+    /**
+     * Test the equals() method of the ModIPInstruction class.
+     */
     @Test
     public void testModIPInstructionEquals() throws Exception {
         checkEqualsAndToString(modIPInstruction1,
@@ -429,9 +469,8 @@
     }
 
     /**
-     * Test the hashCode() method of the ModEtherInstruction class.
+     * Test the hashCode() method of the ModIPInstruction class.
      */
-
     @Test
     public void testModIPInstructionHashCode() {
         assertThat(modIPInstruction1.hashCode(),
@@ -440,6 +479,54 @@
                    is(not(equalTo(modIPInstruction2.hashCode()))));
     }
 
+    private final int flowLabel1 = 0x11111;
+    private final int flowLabel2 = 0x22222;
+    private final Instruction modIPv6FlowLabelInstruction1 =
+        Instructions.modL3IPv6FlowLabel(flowLabel1);
+    private final Instruction sameAsModIPv6FlowLabelInstruction1 =
+        Instructions.modL3IPv6FlowLabel(flowLabel1);
+    private final Instruction modIPv6FlowLabelInstruction2 =
+        Instructions.modL3IPv6FlowLabel(flowLabel2);
+
+    /**
+     * Test the modL3IPv6FlowLabel method.
+     */
+    @Test
+    public void testModL3IPv6FlowLabelMethod() {
+        final Instruction instruction =
+            Instructions.modL3IPv6FlowLabel(flowLabel1);
+        final L3ModificationInstruction.ModIPv6FlowLabelInstruction
+            modIPv6FlowLabelInstruction =
+                checkAndConvert(instruction,
+                        Instruction.Type.L3MODIFICATION,
+                        L3ModificationInstruction.ModIPv6FlowLabelInstruction.class);
+        assertThat(modIPv6FlowLabelInstruction.flowLabel(),
+                   is(equalTo(flowLabel1)));
+        assertThat(modIPv6FlowLabelInstruction.subtype(),
+                is(equalTo(L3ModificationInstruction.L3SubType.IPV6_FLABEL)));
+    }
+
+    /**
+     * Test the equals() method of the ModIPv6FlowLabelInstruction class.
+     */
+    @Test
+    public void testModIPv6FlowLabelInstructionEquals() throws Exception {
+        checkEqualsAndToString(modIPv6FlowLabelInstruction1,
+                               sameAsModIPv6FlowLabelInstruction1,
+                               modIPv6FlowLabelInstruction2);
+    }
+
+    /**
+     * Test the hashCode() method of the ModIPv6FlowLabelInstruction class.
+     */
+    @Test
+    public void testModIPv6FlowLabelInstructionHashCode() {
+        assertThat(modIPv6FlowLabelInstruction1.hashCode(),
+                   is(equalTo(sameAsModIPv6FlowLabelInstruction1.hashCode())));
+        assertThat(modIPv6FlowLabelInstruction1.hashCode(),
+                   is(not(equalTo(modIPv6FlowLabelInstruction2.hashCode()))));
+    }
+
     private Instruction modMplsLabelInstruction1 = Instructions.modMplsLabel(1);
     private Instruction sameAsModMplsLabelInstruction1 = Instructions.modMplsLabel(1);
     private Instruction modMplsLabelInstruction2 = Instructions.modMplsLabel(2);
@@ -463,7 +550,6 @@
      * Test the equals(), hashCode and toString() methods of the
      * ModMplsLabelInstruction class.
      */
-
     @Test
     public void testModMplsLabelInstructionEquals() throws Exception {
         checkEqualsAndToString(modMplsLabelInstruction1,
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 7c18820..cda3fda 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
@@ -257,6 +257,7 @@
                     L3ModificationInstruction.class,
                     L3ModificationInstruction.L3SubType.class,
                     L3ModificationInstruction.ModIPInstruction.class,
+                    L3ModificationInstruction.ModIPv6FlowLabelInstruction.class,
                     RoleInfo.class,
                     FlowRuleBatchEvent.class,
                     FlowRuleBatchEvent.Type.class,
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
index b0e4590..3e9bdfc 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
@@ -176,14 +176,14 @@
         ModIPInstruction ip;
         Ip4Address ip4;
         switch (l3m.subtype()) {
-        case IP_DST:
-            ip = (ModIPInstruction) i;
-            ip4 = ip.ip().getIp4Address();
-            return factory().actions().setNwDst(IPv4Address.of(ip4.toInt()));
-        case IP_SRC:
+        case IPV4_SRC:
             ip = (ModIPInstruction) i;
             ip4 = ip.ip().getIp4Address();
             return factory().actions().setNwSrc(IPv4Address.of(ip4.toInt()));
+        case IPV4_DST:
+            ip = (ModIPInstruction) i;
+            ip4 = ip.ip().getIp4Address();
+            return factory().actions().setNwDst(IPv4Address.of(ip4.toInt()));
         default:
             log.warn("Unimplemented action type {}.", l3m.subtype());
             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 2db1e6f..88ec845 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
@@ -16,6 +16,7 @@
 package org.onosproject.provider.of.flow.impl;
 
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficTreatment;
@@ -32,6 +33,7 @@
 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.ModIPv6FlowLabelInstruction;
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFFlowAdd;
 import org.projectfloodlight.openflow.protocol.OFFlowDelete;
@@ -45,6 +47,8 @@
 import org.projectfloodlight.openflow.types.CircuitSignalID;
 import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
+import org.projectfloodlight.openflow.types.IPv6FlowLabel;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBufferId;
 import org.projectfloodlight.openflow.types.OFGroup;
@@ -267,17 +271,34 @@
         L3ModificationInstruction l3m = (L3ModificationInstruction) i;
         ModIPInstruction ip;
         Ip4Address ip4;
+        Ip6Address ip6;
         OFOxm<?> oxm = null;
         switch (l3m.subtype()) {
-        case IP_DST:
+        case IPV4_SRC:
+            ip = (ModIPInstruction) i;
+            ip4 = ip.ip().getIp4Address();
+            oxm = factory().oxms().ipv4Src(IPv4Address.of(ip4.toInt()));
+            break;
+        case IPV4_DST:
             ip = (ModIPInstruction) i;
             ip4 = ip.ip().getIp4Address();
             oxm = factory().oxms().ipv4Dst(IPv4Address.of(ip4.toInt()));
             break;
-        case IP_SRC:
+        case IPV6_SRC:
             ip = (ModIPInstruction) i;
-            ip4 = ip.ip().getIp4Address();
-            oxm = factory().oxms().ipv4Src(IPv4Address.of(ip4.toInt()));
+            ip6 = ip.ip().getIp6Address();
+            oxm = factory().oxms().ipv6Src(IPv6Address.of(ip6.toOctets()));
+            break;
+        case IPV6_DST:
+            ip = (ModIPInstruction) i;
+            ip6 = ip.ip().getIp6Address();
+            oxm = factory().oxms().ipv6Dst(IPv6Address.of(ip6.toOctets()));
+            break;
+        case IPV6_FLABEL:
+            ModIPv6FlowLabelInstruction flowLabelInstruction =
+                (ModIPv6FlowLabelInstruction) i;
+            int flowLabel = flowLabelInstruction.flowLabel();
+            oxm = factory().oxms().ipv6Flabel(IPv6FlowLabel.of(flowLabel));
             break;
         case DEC_TTL:
             return factory().actions().decNwTtl();
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 191372c..07ddbec 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
@@ -16,6 +16,7 @@
 package org.onosproject.provider.of.group.impl;
 
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
 import org.onosproject.core.GroupId;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.TrafficTreatment;
@@ -39,6 +40,8 @@
 import org.projectfloodlight.openflow.types.CircuitSignalID;
 import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
+import org.projectfloodlight.openflow.types.IPv6FlowLabel;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFGroup;
 import org.projectfloodlight.openflow.types.OFPort;
@@ -284,17 +287,34 @@
         L3ModificationInstruction l3m = (L3ModificationInstruction) i;
         L3ModificationInstruction.ModIPInstruction ip;
         Ip4Address ip4;
+        Ip6Address ip6;
         OFOxm<?> oxm = null;
         switch (l3m.subtype()) {
-            case IP_DST:
+            case IPV4_SRC:
+                ip = (L3ModificationInstruction.ModIPInstruction) i;
+                ip4 = ip.ip().getIp4Address();
+                oxm = factory.oxms().ipv4Src(IPv4Address.of(ip4.toInt()));
+                break;
+            case IPV4_DST:
                 ip = (L3ModificationInstruction.ModIPInstruction) i;
                 ip4 = ip.ip().getIp4Address();
                 oxm = factory.oxms().ipv4Dst(IPv4Address.of(ip4.toInt()));
                 break;
-            case IP_SRC:
+            case IPV6_SRC:
                 ip = (L3ModificationInstruction.ModIPInstruction) i;
-                ip4 = ip.ip().getIp4Address();
-                oxm = factory.oxms().ipv4Src(IPv4Address.of(ip4.toInt()));
+                ip6 = ip.ip().getIp6Address();
+                oxm = factory.oxms().ipv6Src(IPv6Address.of(ip6.toOctets()));
+                break;
+            case IPV6_DST:
+                ip = (L3ModificationInstruction.ModIPInstruction) i;
+                ip6 = ip.ip().getIp6Address();
+                oxm = factory.oxms().ipv6Dst(IPv6Address.of(ip6.toOctets()));
+                break;
+            case IPV6_FLABEL:
+                L3ModificationInstruction.ModIPv6FlowLabelInstruction flowLabelInstruction =
+                    (L3ModificationInstruction.ModIPv6FlowLabelInstruction) i;
+                int flowLabel = flowLabelInstruction.flowLabel();
+                oxm = factory.oxms().ipv6Flabel(IPv6FlowLabel.of(flowLabel));
                 break;
             case DEC_TTL:
                 return factory.actions().decNwTtl();
diff --git a/web/api/src/main/java/org/onosproject/codec/impl/InstructionCodec.java b/web/api/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
index 913fc8b..3957beb 100644
--- a/web/api/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
+++ b/web/api/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
@@ -117,13 +117,22 @@
     private void encodeL3(ObjectNode result, L3ModificationInstruction instruction) {
         result.put("subtype", instruction.subtype().name());
         switch (instruction.subtype()) {
-            case IP_SRC:
-            case IP_DST:
+            case IPV4_SRC:
+            case IPV4_DST:
+            case IPV6_SRC:
+            case IPV6_DST:
                 final L3ModificationInstruction.ModIPInstruction modIPInstruction =
                         (L3ModificationInstruction.ModIPInstruction) instruction;
                 result.put("ip", modIPInstruction.ip().toString());
                 break;
 
+            case IPV6_FLABEL:
+                final L3ModificationInstruction.ModIPv6FlowLabelInstruction
+                    modFlowLabelInstruction =
+                        (L3ModificationInstruction.ModIPv6FlowLabelInstruction) instruction;
+                result.put("flowLabel", modFlowLabelInstruction.flowLabel());
+                break;
+
             default:
                 log.info("Cannot convert L3 subtype of {}", instruction.subtype());
                 break;
diff --git a/web/api/src/test/java/org/onosproject/codec/impl/InstructionCodecTest.java b/web/api/src/test/java/org/onosproject/codec/impl/InstructionCodecTest.java
index 8bf4439..923573e 100644
--- a/web/api/src/test/java/org/onosproject/codec/impl/InstructionCodecTest.java
+++ b/web/api/src/test/java/org/onosproject/codec/impl/InstructionCodecTest.java
@@ -17,8 +17,8 @@
 
 import org.junit.Before;
 import org.junit.Test;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.codec.CodecContext;
@@ -144,11 +144,11 @@
     }
 
     /**
-     * Tests the encoding of mod ip instructions.
+     * Tests the encoding of mod IPv4 src instructions.
      */
     @Test
-    public void modIPInstructionTest() {
-        final IpAddress ip = IpPrefix.valueOf("1.2.3.4/24").address();
+    public void modIPSrcInstructionTest() {
+        final Ip4Address ip = Ip4Address.valueOf("1.2.3.4");
         final L3ModificationInstruction.ModIPInstruction instruction =
                 (L3ModificationInstruction.ModIPInstruction)
                         Instructions.modL3Src(ip);
@@ -158,6 +158,62 @@
     }
 
     /**
+     * Tests the encoding of mod IPv4 dst instructions.
+     */
+    @Test
+    public void modIPDstInstructionTest() {
+        final Ip4Address ip = Ip4Address.valueOf("1.2.3.4");
+        final L3ModificationInstruction.ModIPInstruction instruction =
+                (L3ModificationInstruction.ModIPInstruction)
+                        Instructions.modL3Dst(ip);
+        final ObjectNode instructionJson =
+                instructionCodec.encode(instruction, context);
+        assertThat(instructionJson, matchesInstruction(instruction));
+    }
+
+    /**
+     * Tests the encoding of mod IPv6 src instructions.
+     */
+    @Test
+    public void modIPv6SrcInstructionTest() {
+        final Ip6Address ip = Ip6Address.valueOf("1111::2222");
+        final L3ModificationInstruction.ModIPInstruction instruction =
+                (L3ModificationInstruction.ModIPInstruction)
+                        Instructions.modL3IPv6Src(ip);
+        final ObjectNode instructionJson =
+                instructionCodec.encode(instruction, context);
+        assertThat(instructionJson, matchesInstruction(instruction));
+    }
+
+    /**
+     * Tests the encoding of mod IPv6 dst instructions.
+     */
+    @Test
+    public void modIPv6DstInstructionTest() {
+        final Ip6Address ip = Ip6Address.valueOf("1111::2222");
+        final L3ModificationInstruction.ModIPInstruction instruction =
+                (L3ModificationInstruction.ModIPInstruction)
+                        Instructions.modL3IPv6Dst(ip);
+        final ObjectNode instructionJson =
+                instructionCodec.encode(instruction, context);
+        assertThat(instructionJson, matchesInstruction(instruction));
+    }
+
+    /**
+     * Tests the encoding of mod IPv6 flow label instructions.
+     */
+    @Test
+    public void modIPv6FlowLabelInstructionTest() {
+        final int flowLabel = 0xfffff;
+        final L3ModificationInstruction.ModIPv6FlowLabelInstruction instruction =
+                (L3ModificationInstruction.ModIPv6FlowLabelInstruction)
+                        Instructions.modL3IPv6FlowLabel(flowLabel);
+        final ObjectNode instructionJson =
+                instructionCodec.encode(instruction, context);
+        assertThat(instructionJson, matchesInstruction(instruction));
+    }
+
+    /**
      * Tests the encoding of mod MPLS label instructions.
      */
     @Test
diff --git a/web/api/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java b/web/api/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java
index 0086255..72d3e41 100644
--- a/web/api/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java
+++ b/web/api/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java
@@ -41,6 +41,7 @@
      * Matches the contents of a push header instruction.
      *
      * @param instructionJson JSON instruction to match
+     * @param description Description object used for recording errors
      * @return true if contents match, false otherwise
      */
     private boolean matchPushHeaderInstruction(JsonNode instructionJson,
@@ -77,6 +78,7 @@
      * Matches the contents of an output instruction.
      *
      * @param instructionJson JSON instruction to match
+     * @param description Description object used for recording errors
      * @return true if contents match, false otherwise
      */
     private boolean matchOutputInstruction(JsonNode instructionJson,
@@ -102,6 +104,7 @@
      * Matches the contents of a mod lambda instruction.
      *
      * @param instructionJson JSON instruction to match
+     * @param description Description object used for recording errors
      * @return true if contents match, false otherwise
      */
     private boolean matchModLambdaInstruction(JsonNode instructionJson,
@@ -133,6 +136,7 @@
      * Matches the contents of a mod Ethernet instruction.
      *
      * @param instructionJson JSON instruction to match
+     * @param description Description object used for recording errors
      * @return true if contents match, false otherwise
      */
     private boolean matchModEtherInstruction(JsonNode instructionJson,
@@ -165,6 +169,7 @@
      * Matches the contents of a mod vlan id instruction.
      *
      * @param instructionJson JSON instruction to match
+     * @param description Description object used for recording errors
      * @return true if contents match, false otherwise
      */
     private boolean matchModVlanIdInstruction(JsonNode instructionJson,
@@ -197,6 +202,7 @@
      * Matches the contents of a mod vlan pcp instruction.
      *
      * @param instructionJson JSON instruction to match
+     * @param description Description object used for recording errors
      * @return true if contents match, false otherwise
      */
     private boolean matchModVlanPcpInstruction(JsonNode instructionJson,
@@ -229,6 +235,7 @@
      * Matches the contents of a mod ip instruction.
      *
      * @param instructionJson JSON instruction to match
+     * @param description Description object used for recording errors
      * @return true if contents match, false otherwise
      */
     private boolean matchModIpInstruction(JsonNode instructionJson,
@@ -258,9 +265,43 @@
     }
 
     /**
+     * Matches the contents of a mod IPv6 Flow Label instruction.
+     *
+     * @param instructionJson JSON instruction to match
+     * @param description Description object used for recording errors
+     * @return true if contents match, false otherwise
+     */
+    private boolean matchModIPv6FlowLabelInstruction(JsonNode instructionJson,
+                                                     Description description) {
+        ModIPv6FlowLabelInstruction instructionToMatch =
+                (ModIPv6FlowLabelInstruction) instruction;
+        final String jsonSubtype = instructionJson.get("subtype").textValue();
+        if (!instructionToMatch.subtype().name().equals(jsonSubtype)) {
+            description.appendText("subtype was " + jsonSubtype);
+            return false;
+        }
+
+        final String jsonType = instructionJson.get("type").textValue();
+        if (!instructionToMatch.type().name().equals(jsonType)) {
+            description.appendText("type was " + jsonType);
+            return false;
+        }
+
+        final int jsonFlowLabel = instructionJson.get("flowLabel").intValue();
+        final int flowLabel = instructionToMatch.flowLabel();
+        if (flowLabel != jsonFlowLabel) {
+            description.appendText("IPv6 flow label was " + jsonFlowLabel);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
      * Matches the contents of a mod MPLS label instruction.
      *
      * @param instructionJson JSON instruction to match
+     * @param description Description object used for recording errors
      * @return true if contents match, false otherwise
      */
     private boolean matchModMplsLabelInstruction(JsonNode instructionJson,
@@ -282,7 +323,7 @@
         final int jsonLabel = instructionJson.get("label").intValue();
         final int label = instructionToMatch.label();
         if (label != jsonLabel) {
-            description.appendText("ip was " + jsonLabel);
+            description.appendText("MPLS label was " + jsonLabel);
             return false;
         }
 
@@ -317,6 +358,9 @@
             return matchModVlanPcpInstruction(jsonInstruction, description);
         } else if (instruction instanceof ModIPInstruction) {
             return matchModIpInstruction(jsonInstruction, description);
+        } else if (instruction instanceof ModIPv6FlowLabelInstruction) {
+            return matchModIPv6FlowLabelInstruction(jsonInstruction,
+                                                    description);
         } else if (instruction instanceof ModMplsLabelInstruction) {
             return matchModMplsLabelInstruction(jsonInstruction, description);
         }