TUNNEL_ID field support in flow matching and actions

Change-Id: I886123a7c8d57dc4a3e12727ec8a9be4920da79e
diff --git a/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java b/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java
index 67e057e..09c6327 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java
@@ -294,6 +294,11 @@
         }
 
         @Override
+        public TrafficSelector.Builder matchTunnelId(long tunnelId) {
+            return add(Criteria.matchTunnelId(tunnelId));
+        }
+
+        @Override
         public Builder matchIPv6ExthdrFlags(short exthdrFlags) {
             return add(Criteria.matchIPv6ExthdrFlags(exthdrFlags));
         }
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 96abbe9..fe657e6 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
@@ -385,6 +385,11 @@
         }
 
         @Override
+        public Builder setTunnelId(long tunnelId) {
+            return add(Instructions.modTunnelId(tunnelId));
+        }
+
+        @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/TrafficSelector.java b/core/api/src/main/java/org/onosproject/net/flow/TrafficSelector.java
index 3b28093..6661cb6 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/TrafficSelector.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/TrafficSelector.java
@@ -301,6 +301,14 @@
         Builder matchMplsLabel(MplsLabel mplsLabel);
 
         /**
+         * Matches a tunnel id.
+         *
+         * @param tunnelId a tunnel id
+         * @return a selection builder
+         */
+        Builder matchTunnelId(long tunnelId);
+
+        /**
          * Matches on IPv6 Extension Header pseudo-field flags.
          *
          * @param exthdrFlags the IPv6 Extension Header pseudo-field flags
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 3802bf2..35ef6d3 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
@@ -311,6 +311,14 @@
         Builder writeMetadata(long value, long mask);
 
         /**
+         * Sets the tunnel id.
+         *
+         * @param tunnelId a tunnel id.
+         * @return a treatment builder.
+         */
+        Builder setTunnelId(long tunnelId);
+
+        /**
          * 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/criteria/Criteria.java b/core/api/src/main/java/org/onosproject/net/flow/criteria/Criteria.java
index 0347694..91221e2 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/criteria/Criteria.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/criteria/Criteria.java
@@ -356,6 +356,16 @@
     }
 
     /**
+     * Creates a match on Tunnel ID.
+     *
+     * @param tunnelId Tunnel ID (64 bits)
+     * @return match criterion
+     */
+    public static Criterion matchTunnelId(long tunnelId) {
+        return new TunnelIdCriterion(tunnelId);
+    }
+
+    /**
      * Creates a match on IPv6 Extension Header pseudo-field fiags.
      * Those are defined in Criterion.IPv6ExthdrFlags.
      *
diff --git a/core/api/src/main/java/org/onosproject/net/flow/criteria/TunnelIdCriterion.java b/core/api/src/main/java/org/onosproject/net/flow/criteria/TunnelIdCriterion.java
new file mode 100644
index 0000000..3362c73
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/flow/criteria/TunnelIdCriterion.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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.net.flow.criteria;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+/**
+ * Implementation of Tunnel ID criterion.
+ */
+public class TunnelIdCriterion implements Criterion {
+    private final long tunnelId;
+
+    /**
+     * Constructor.
+     *
+     * @param tunnelId a Tunnel ID to match(64 bits)
+     */
+    TunnelIdCriterion(long tunnelId) {
+        this.tunnelId = tunnelId;
+    }
+
+    @Override
+    public Type type() {
+        return Type.TUNNEL_ID;
+    }
+
+    /**
+     * Gets the Tunnel ID to match.
+     *
+     * @return the Tunnel ID to match (64 bits)
+     */
+    public long tunnelId() {
+        return tunnelId;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(type().toString())
+                .add("tunnelId", Long.toHexString(tunnelId))
+                .toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(type().ordinal(), tunnelId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof TunnelIdCriterion) {
+            TunnelIdCriterion that = (TunnelIdCriterion) obj;
+            return Objects.equals(tunnelId, that.tunnelId) &&
+                    Objects.equals(this.type(), that.type());
+        }
+        return false;
+    }
+}
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 e8101f1..0467a91 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
@@ -350,6 +350,17 @@
     }
 
     /**
+     * Creates a Tunnel ID modification.
+     *
+     * @param tunnelId the Tunnel ID to modify to
+     * @return a L2 modification
+     */
+    public static L2ModificationInstruction modTunnelId(long tunnelId) {
+        checkNotNull(tunnelId, "Tunnel id cannot be null");
+        return new L2ModificationInstruction.ModTunnelIdInstruction(tunnelId);
+    }
+
+    /**
      *  Drop instruction.
      */
     public static final class DropInstruction implements Instruction {
diff --git a/core/api/src/main/java/org/onosproject/net/flow/instructions/L2ModificationInstruction.java b/core/api/src/main/java/org/onosproject/net/flow/instructions/L2ModificationInstruction.java
index 2fd809f..bfb6300 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/instructions/L2ModificationInstruction.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/instructions/L2ModificationInstruction.java
@@ -81,7 +81,12 @@
         /**
          * VLAN Push modification.
          */
-        VLAN_PUSH
+        VLAN_PUSH,
+
+        /**
+         * Tunnle id modification.
+         */
+        TUNNEL_ID
     }
 
     // TODO: Create factory class 'Instructions' that will have various factory
@@ -401,4 +406,50 @@
             return false;
         }
     }
+
+    /**
+     * Represents a Tunnel id modification.
+     */
+    public static final class ModTunnelIdInstruction
+            extends L2ModificationInstruction {
+
+        private final long tunnelId;
+
+        ModTunnelIdInstruction(long tunnelId) {
+            this.tunnelId = tunnelId;
+        }
+
+        public long tunnelId() {
+            return this.tunnelId;
+        }
+
+        @Override
+        public L2SubType subtype() {
+            return L2SubType.TUNNEL_ID;
+        }
+
+        @Override
+        public String toString() {
+            return toStringHelper(subtype().toString())
+                    .add("id", Long.toHexString(tunnelId))
+                    .toString();
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(type(), subtype(), tunnelId);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof ModTunnelIdInstruction) {
+                ModTunnelIdInstruction that = (ModTunnelIdInstruction) obj;
+                return  Objects.equals(tunnelId, that.tunnelId);
+            }
+            return false;
+        }
+    }
 }
diff --git a/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java b/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java
index 4733cb9..de60b69 100644
--- a/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java
+++ b/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java
@@ -198,6 +198,12 @@
     Criterion sameAsMatchMpls1 = Criteria.matchMplsLabel(mpls1);
     Criterion matchMpls2 = Criteria.matchMplsLabel(mpls2);
 
+    long tunnelId1 = 1;
+    long tunnelId2 = 2;
+    Criterion matchTunnelId1 = Criteria.matchTunnelId(tunnelId1);
+    Criterion sameAsMatchTunnelId1 = Criteria.matchTunnelId(tunnelId1);
+    Criterion matchTunnelId2 = Criteria.matchTunnelId(tunnelId2);
+
     int ipv6ExthdrFlags1 =
         Criterion.IPv6ExthdrFlags.NONEXT.getValue() |
         Criterion.IPv6ExthdrFlags.ESP.getValue() |
@@ -992,6 +998,33 @@
                 .testEquals();
     }
 
+    // TunnelIdCriterion class
+
+    /**
+     * Test the matchTunnelId method.
+     */
+    @Test
+    public void testMatchTunnelIdMethod() {
+        Criterion matchTunnelId = Criteria.matchTunnelId(tunnelId1);
+        TunnelIdCriterion tunnelIdCriterion =
+                checkAndConvert(matchTunnelId,
+                                Criterion.Type.TUNNEL_ID,
+                                TunnelIdCriterion.class);
+        assertThat(tunnelIdCriterion.tunnelId(), is(equalTo(tunnelId1)));
+
+    }
+
+    /**
+     * Test the equals() method of the TunnelIdCriterion class.
+     */
+    @Test
+    public void testTunnelIdCriterionEquals() {
+       new EqualsTester()
+               .addEqualityGroup(matchTunnelId1, sameAsMatchTunnelId1)
+               .addEqualityGroup(matchTunnelId2)
+               .testEquals();
+    }
+
     // IPv6ExthdrFlagsCriterion class
 
     /**
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 b81c5ad..b68e1d6 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
@@ -119,6 +119,7 @@
 import org.onosproject.net.flow.criteria.PortCriterion;
 import org.onosproject.net.flow.criteria.SctpPortCriterion;
 import org.onosproject.net.flow.criteria.TcpPortCriterion;
+import org.onosproject.net.flow.criteria.TunnelIdCriterion;
 import org.onosproject.net.flow.criteria.UdpPortCriterion;
 import org.onosproject.net.flow.criteria.VlanIdCriterion;
 import org.onosproject.net.flow.criteria.VlanPcpCriterion;
@@ -320,6 +321,7 @@
                     IPv6NDTargetAddressCriterion.class,
                     IPv6NDLinkLayerAddressCriterion.class,
                     MplsCriterion.class,
+                    TunnelIdCriterion.class,
                     IPv6ExthdrFlagsCriterion.class,
                     LambdaCriterion.class,
                     IndexedLambdaCriterion.class,
@@ -346,6 +348,7 @@
                     L2ModificationInstruction.PopVlanInstruction.class,
                     L2ModificationInstruction.ModMplsLabelInstruction.class,
                     L2ModificationInstruction.ModMplsTtlInstruction.class,
+                    L2ModificationInstruction.ModTunnelIdInstruction.class,
                     L3ModificationInstruction.class,
                     L3ModificationInstruction.L3SubType.class,
                     L3ModificationInstruction.ModIPInstruction.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 4605564..6760462 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
@@ -72,6 +72,7 @@
 import org.projectfloodlight.openflow.types.Masked;
 import org.projectfloodlight.openflow.types.OFVlanVidMatch;
 import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.U64;
 import org.projectfloodlight.openflow.types.U8;
 import org.projectfloodlight.openflow.types.VlanPcp;
 import org.slf4j.Logger;
@@ -402,6 +403,11 @@
             OFOxm<U32> labelId = (OFOxm<U32>) oxm;
             builder.setMpls(MplsLabel.mplsLabel((int) labelId.getValue().getValue()));
             break;
+        case TUNNEL_ID:
+            @SuppressWarnings("unchecked")
+            OFOxm<U64> tunnelId = (OFOxm<U64>) oxm;
+            builder.setTunnelId(tunnelId.getValue().getValue());
+            break;
         case ARP_OP:
         case ARP_SHA:
         case ARP_SPA:
@@ -451,7 +457,6 @@
         case SCTP_SRC:
         case TCP_DST:
         case TCP_SRC:
-        case TUNNEL_ID:
         case UDP_DST:
         case UDP_SRC:
         default:
@@ -640,7 +645,7 @@
                 break;
             case IPV6_EXTHDR:
                 builder.matchIPv6ExthdrFlags((short) match.get(MatchField.IPV6_EXTHDR)
-                                            .getValue());
+                        .getValue());
                 break;
             case OCH_SIGID:
                 CircuitSignalID sigId = match.get(MatchField.OCH_SIGID);
@@ -653,13 +658,16 @@
                 U8 sigType = match.get(MatchField.OCH_SIGTYPE);
                 builder.add(matchOchSignalType(lookupOchSignalType((byte) sigType.getValue())));
                 break;
+            case TUNNEL_ID:
+                long tunnelId = match.get(MatchField.TUNNEL_ID).getValue();
+                builder.matchTunnelId(tunnelId);
+                break;
             case ARP_OP:
             case ARP_SHA:
             case ARP_SPA:
             case ARP_THA:
             case ARP_TPA:
             case MPLS_TC:
-            case TUNNEL_ID:
             default:
                 log.warn("Match type {} not yet implemented.", field.id);
             }
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
index d34fd5e..d9d02ff 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
@@ -44,6 +44,7 @@
 import org.onosproject.net.flow.criteria.PortCriterion;
 import org.onosproject.net.flow.criteria.SctpPortCriterion;
 import org.onosproject.net.flow.criteria.TcpPortCriterion;
+import org.onosproject.net.flow.criteria.TunnelIdCriterion;
 import org.onosproject.net.flow.criteria.UdpPortCriterion;
 import org.onosproject.net.flow.criteria.VlanIdCriterion;
 import org.onosproject.net.flow.criteria.VlanPcpCriterion;
@@ -72,6 +73,7 @@
 import org.projectfloodlight.openflow.types.TransportPort;
 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 org.projectfloodlight.openflow.types.VlanPcp;
 import org.projectfloodlight.openflow.types.VlanVid;
@@ -392,6 +394,11 @@
                 byte signalType = OpenFlowValueMapper.lookupOchSignalType(sc.signalType());
                 mBuilder.setExact(MatchField.OCH_SIGTYPE, U8.of(signalType));
                 break;
+            case TUNNEL_ID:
+                TunnelIdCriterion tunnelId = (TunnelIdCriterion) c;
+                mBuilder.setExact(MatchField.TUNNEL_ID,
+                                  U64.of(tunnelId.tunnelId()));
+                break;
             case ARP_OP:
             case ARP_SHA:
             case ARP_SPA:
@@ -400,7 +407,6 @@
             case MPLS_BOS:
             case MPLS_TC:
             case PBB_ISID:
-            case TUNNEL_ID:
             default:
                 log.warn("Match type {} not yet implemented.", c.type());
             }
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 63fc63a..7bbc375 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
@@ -35,6 +35,7 @@
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanPcpInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.PushHeaderInstructions;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModTunnelIdInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction;
@@ -336,6 +337,10 @@
                 PushHeaderInstructions pushVlanInstruction = (PushHeaderInstructions) l2m;
                 return factory().actions().pushVlan(
                         EthType.of(pushVlanInstruction.ethernetType().toShort()));
+            case TUNNEL_ID:
+                ModTunnelIdInstruction tunnelId = (ModTunnelIdInstruction) l2m;
+                oxm = factory().oxms().tunnelId(U64.of(tunnelId.tunnelId()));
+                break;
             default:
                 log.warn("Unimplemented action type {}.", l2m.subtype());
                 break;