MPLS label selector and treatment

Change-Id: Id1fba1e04155e6d97de4c8fd95573641537f1b7a
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java
index 673773a..2730c14 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java
@@ -184,6 +184,11 @@
         }
 
         @Override
+        public Builder matchMplsLabel(Integer mplsLabel) {
+            return add(Criteria.matchMplsLabel(mplsLabel));
+        }
+
+        @Override
         public Builder matchLambda(Short lambda) {
             return add(Criteria.matchLambda(lambda));
         }
@@ -191,7 +196,6 @@
         @Override
         public Builder matchOpticalSignalType(Short signalType) {
             return add(Criteria.matchOpticalSignalType(signalType));
-
         }
 
         @Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java
index a63ae13..54c0972 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java
@@ -192,6 +192,22 @@
         }
 
         @Override
+        public Builder pushMpls() {
+            return add(Instructions.pushMpls());
+        }
+
+        @Override
+        public Builder popMpls() {
+            return add(Instructions.popMpls());
+        }
+
+
+        @Override
+        public Builder setMpls(Integer mplsLabel) {
+            return add(Instructions.modMplsLabel(mplsLabel));
+        }
+
+        @Override
         public Builder setLambda(short lambda) {
             return add(Instructions.modL0Lambda(lambda));
         }
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/TrafficSelector.java b/core/api/src/main/java/org/onlab/onos/net/flow/TrafficSelector.java
index ac75147..daf5268 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/TrafficSelector.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/TrafficSelector.java
@@ -146,6 +146,14 @@
          */
         public Builder matchTcpDst(Short tcpPort);
 
+
+        /**
+         * Matches on a MPLS label .
+         * @param mplsLabel a MPLS label.
+         * @return a selection builder
+         */
+        public Builder matchMplsLabel(Integer mplsLabel);
+
         /**
          * Matches an optical signal ID or lambda.
          *
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/TrafficTreatment.java b/core/api/src/main/java/org/onlab/onos/net/flow/TrafficTreatment.java
index bbecae0..3ac2c44 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/TrafficTreatment.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/TrafficTreatment.java
@@ -110,6 +110,25 @@
         public Builder setIpDst(IpAddress addr);
 
         /**
+         * Push MPLS ether type.
+         * @return a treatment builder.
+         */
+        public Builder pushMpls();
+
+        /**
+         * Pops MPLS ether type.
+         * @return a treatment builder.
+         */
+        public Builder popMpls();
+
+        /**
+         * Sets the mpls label.
+         * @param mplsLabel MPLS label.
+         * @return a treatment builder.
+         */
+        public Builder setMpls(Integer mplsLabel);
+
+        /**
          * Sets the optical channel ID or lambda.
          *
          * @param lambda optical channel ID
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/criteria/Criteria.java b/core/api/src/main/java/org/onlab/onos/net/flow/criteria/Criteria.java
index 61fe54d..83ade8b 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/criteria/Criteria.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/criteria/Criteria.java
@@ -149,6 +149,16 @@
     }
 
     /**
+     * Creates a match on MPLS label.
+     * @param mplsLabel MPLS label
+     * @return match criterion
+     */
+
+    public static Criterion matchMplsLabel(Integer mplsLabel) {
+        return new MplsCriterion(mplsLabel);
+    }
+
+    /**
      * Creates a match on lambda field using the specified value.
      *
      * @param lambda lamda to match on
@@ -541,6 +551,52 @@
         }
     }
 
+    public static final class MplsCriterion implements Criterion {
+
+        private final Integer mplsLabel;
+
+        public MplsCriterion(Integer mplsLabel) {
+            this.mplsLabel = mplsLabel;
+        }
+
+        @Override
+        public Type type() {
+            return Type.MPLS_LABEL;
+        }
+
+        public Integer label() {
+            return mplsLabel;
+        }
+
+        @Override
+        public String toString() {
+            return toStringHelper(type().toString())
+                    .add("mpls", mplsLabel.intValue()).toString();
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mplsLabel, type());
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof MplsCriterion) {
+                MplsCriterion that = (MplsCriterion) obj;
+                return Objects.equals(mplsLabel, that.mplsLabel) &&
+                        Objects.equals(this.type(), that.type());
+
+
+            }
+            return false;
+        }
+
+    }
+
+
     public static final class LambdaCriterion implements Criterion {
 
         private final short lambda;
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java
index 7dc0f8d..c0fe386 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/Instructions.java
@@ -17,6 +17,7 @@
 
 import static com.google.common.base.MoreObjects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.onos.net.flow.instructions.L2ModificationInstruction.*;
 
 import java.util.Objects;
 
@@ -27,6 +28,8 @@
 import org.onlab.onos.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
 import org.onlab.onos.net.flow.instructions.L3ModificationInstruction.L3SubType;
 import org.onlab.onos.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
+
+import org.onlab.packet.Ethernet;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
@@ -87,7 +90,7 @@
      */
     public static L2ModificationInstruction modL2Dst(MacAddress addr) {
         checkNotNull(addr, "Dst l2 address cannot be null");
-        return new L2ModificationInstruction.ModEtherInstruction(L2SubType.ETH_DST, addr);
+        return new ModEtherInstruction(L2SubType.ETH_DST, addr);
     }
 
     /**
@@ -97,7 +100,7 @@
      */
     public static L2ModificationInstruction modVlanId(VlanId vlanId) {
         checkNotNull(vlanId, "VLAN id cannot be null");
-        return new L2ModificationInstruction.ModVlanIdInstruction(vlanId);
+        return new ModVlanIdInstruction(vlanId);
     }
 
     /**
@@ -107,10 +110,19 @@
      */
     public static L2ModificationInstruction modVlanPcp(Byte vlanPcp) {
         checkNotNull(vlanPcp, "VLAN Pcp cannot be null");
-        return new L2ModificationInstruction.ModVlanPcpInstruction(vlanPcp);
+        return new ModVlanPcpInstruction(vlanPcp);
     }
 
     /**
+     * Creates a MPLS label modification.
+     * @param mplsLabel to set.
+     * @return a L2 Modification
+     */
+    public static L2ModificationInstruction modMplsLabel(Integer mplsLabel) {
+        checkNotNull(mplsLabel, "MPLS label cannot be null");
+        return new ModMplsLabelInstruction(mplsLabel);
+    }
+    /**
      * Creates a L3 src modification.
      * @param addr the ip address to modify to.
      * @return a L3 modification
@@ -130,6 +142,23 @@
         return new ModIPInstruction(L3SubType.IP_DST, addr);
     }
 
+    /**
+     * Creates a mpls header instruction.
+     * @return a L2 modification.
+     */
+    public static Instruction pushMpls() {
+        return new PushHeaderInstructions(L2SubType.MPLS_PUSH,
+                                          new Ethernet().setEtherType(Ethernet.MPLS_UNICAST));
+    }
+
+    /**
+     * Creates a mpls header instruction.
+     * @return a L2 modification.
+     */
+    public static Instruction popMpls() {
+        return new PushHeaderInstructions(L2SubType.MPLS_POP,
+                                          new Ethernet().setEtherType(Ethernet.MPLS_UNICAST));
+    }
 
     /*
      *  Output instructions
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java
index 20eaf6e..9b027d5 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/instructions/L2ModificationInstruction.java
@@ -19,6 +19,7 @@
 
 import java.util.Objects;
 
+import org.onlab.packet.Ethernet;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 
@@ -49,7 +50,22 @@
         /**
          * VLAN priority modification.
          */
-        VLAN_PCP
+        VLAN_PCP,
+
+        /**
+         * MPLS Label modification.
+         */
+        MPLS_LABEL,
+
+        /**
+         * MPLS Push modification.
+         */
+        MPLS_PUSH,
+
+        /**
+         * MPLS Pop modification.
+         */
+        MPLS_POP
     }
 
     // TODO: Create factory class 'Instructions' that will have various factory
@@ -114,6 +130,53 @@
 
     }
 
+    public static final class PushHeaderInstructions extends
+            L2ModificationInstruction {
+
+        private final L2SubType subtype;
+        private final Ethernet ethernetType;
+
+        public PushHeaderInstructions(L2SubType subType, Ethernet ethernetType) {
+            this.subtype = subType;
+            this.ethernetType = ethernetType;
+        }
+
+        public Ethernet ethernetType() {
+            return ethernetType;
+        }
+
+        @Override
+        public L2SubType subtype() {
+            return this.subtype;
+        }
+
+        @Override
+        public String toString() {
+            return toStringHelper(subtype().toString()).toString();
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(type(), subtype);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof PushHeaderInstructions) {
+                PushHeaderInstructions that = (PushHeaderInstructions) obj;
+                return  Objects.equals(this.type(), that.type()) &&
+                        Objects.equals(subtype, that.subtype);
+
+            }
+            return false;
+        }
+    }
+
+
+
     /**
      * Represents a VLAN id modification instruction.
      */
@@ -212,4 +275,51 @@
     }
 
 
+    /**
+     * Represents a MPLS label modification.
+     */
+    public static final class ModMplsLabelInstruction extends
+    L2ModificationInstruction {
+
+        private final Integer mplsLabel;
+
+        public ModMplsLabelInstruction(Integer mplsLabel) {
+            this.mplsLabel = mplsLabel;
+        }
+
+        public Integer label() {
+            return mplsLabel;
+        }
+
+        @Override
+        public L2SubType subtype() {
+            return L2SubType.MPLS_LABEL;
+        }
+
+        @Override
+        public String toString() {
+            return toStringHelper(type().toString())
+                    .add("mpls", mplsLabel.intValue()).toString();
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mplsLabel);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof ModMplsLabelInstruction) {
+                ModMplsLabelInstruction that = (ModMplsLabelInstruction) obj;
+                return Objects.equals(mplsLabel, that.mplsLabel) &&
+                        Objects.equals(this.type(), that.type());
+
+
+            }
+            return false;
+        }
+    }
 }