adding a criterion for inner vlans
used by olt to match on the inner vlan

Change-Id: I7671b68d9860d598395cba134a589ca23f264c7e
diff --git a/apps/olt/src/main/java/org/onosproject/olt/Olt.java b/apps/olt/src/main/java/org/onosproject/olt/Olt.java
index bdff1fa..fdf82a0 100644
--- a/apps/olt/src/main/java/org/onosproject/olt/Olt.java
+++ b/apps/olt/src/main/java/org/onosproject/olt/Olt.java
@@ -147,7 +147,13 @@
         }
 
         provisionVlans(olt.deviceId(), olt.uplink(), port.port(), vlan, olt.vlan(),
-                olt.defaultVlan());
+                       olt.defaultVlan());
+    }
+
+    @Override
+    public void removeSubscriber(ConnectPoint port) {
+        throw new UnsupportedOperationException();
+
     }
 
     private void provisionVlans(DeviceId deviceId, PortNumber uplinkPort,
@@ -166,6 +172,7 @@
         TrafficSelector downstream = DefaultTrafficSelector.builder()
                 .matchVlanId(deviceVlan)
                 .matchInPort(uplinkPort)
+                .matchInnerVlanId(subscriberVlan)
                 .build();
 
         TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder()
@@ -243,11 +250,6 @@
 
     }
 
-    @Override
-    public void removeSubscriber(ConnectPoint port) {
-        throw new UnsupportedOperationException("Not yet implemented");
-    }
-
     private class InternalDeviceListener implements DeviceListener {
         @Override
         public void event(DeviceEvent event) {
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 0525d8f..779bccd 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
@@ -194,6 +194,16 @@
         }
 
         @Override
+        public Builder matchInnerVlanId(VlanId vlanId) {
+            return add(Criteria.matchInnerVlanId(vlanId));
+        }
+
+        @Override
+        public Builder matchInnerVlanPcp(byte vlanPcp) {
+            return add(Criteria.matchInnerVlanPcp(vlanPcp));
+        }
+
+        @Override
         public Builder matchIPDscp(byte ipDscp) {
             return add(Criteria.matchIPDscp(ipDscp));
         }
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 0d055ad..b45dc45 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
@@ -129,6 +129,22 @@
         Builder matchVlanPcp(byte vlanPcp);
 
         /**
+         * Matches the inner vlan id.
+         *
+         * @param vlanId a vlan id
+         * @return a selection builder
+         */
+        Builder matchInnerVlanId(VlanId vlanId);
+
+        /**
+         * Matches a vlan priority.
+         *
+         * @param vlanPcp a vlan priority
+         * @return a selection builder
+         */
+        Builder matchInnerVlanPcp(byte vlanPcp);
+
+        /**
          * Matches an IP DSCP (6 bits in ToS field).
          *
          * @param ipDscp an IP DSCP value
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 c94b1e0..0c26b89 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
@@ -127,6 +127,16 @@
     }
 
     /**
+     * Creates a match on the inner VLAN ID field using the specified value.
+     *
+     * @param vlanId vlan id value
+     * @return match criterion
+     */
+    public static Criterion matchInnerVlanId(VlanId vlanId) {
+        return new VlanIdCriterion(vlanId, Type.INNER_VLAN_VID);
+    }
+
+    /**
      * Creates a match on VLAN PCP field using the specified value.
      *
      * @param vlanPcp vlan pcp value (3 bits)
@@ -137,6 +147,16 @@
     }
 
     /**
+     * Creates a match on the inner VLAN PCP field using the specified value.
+     *
+     * @param vlanPcp vlan pcp value (3 bits)
+     * @return match criterion
+     */
+    public static Criterion matchInnerVlanPcp(byte vlanPcp) {
+        return new VlanPcpCriterion(vlanPcp, Type.INNER_VLAN_PCP);
+    }
+
+    /**
      * Creates a match on IP DSCP field using the specified value.
      *
      * @param ipDscp ip dscp value (6 bits)
diff --git a/core/api/src/main/java/org/onosproject/net/flow/criteria/Criterion.java b/core/api/src/main/java/org/onosproject/net/flow/criteria/Criterion.java
index 17557b9..e6b4939 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/criteria/Criterion.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/criteria/Criterion.java
@@ -50,6 +50,20 @@
         /** VLAN priority. */
         VLAN_PCP,
 
+        /**
+         * Inner VLAN id.
+         *
+         * Note: Some drivers may not support this.
+         */
+        INNER_VLAN_VID,
+
+        /**
+         * Inner VLAN pcp.
+         *
+         * Note: Some drivers may not support this.
+         */
+        INNER_VLAN_PCP,
+
         /** IP DSCP (6 bits in ToS field). */
         IP_DSCP,
 
diff --git a/core/api/src/main/java/org/onosproject/net/flow/criteria/VlanIdCriterion.java b/core/api/src/main/java/org/onosproject/net/flow/criteria/VlanIdCriterion.java
index c73edb1..757e8ce 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/criteria/VlanIdCriterion.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/criteria/VlanIdCriterion.java
@@ -20,12 +20,14 @@
 import java.util.Objects;
 
 import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
 
 /**
  * Implementation of VLAN ID criterion.
  */
 public final class VlanIdCriterion implements Criterion {
     private final VlanId vlanId;
+    private final Type type;
 
     /**
      * Constructor.
@@ -34,11 +36,26 @@
      */
     VlanIdCriterion(VlanId vlanId) {
         this.vlanId = vlanId;
+        this.type = Type.VLAN_VID;
+    }
+
+    /**
+     * Constructs a vlan criterion with a specific type.
+     *
+     * @param vlanId a vlan id to match
+     * @param type a criterion type (only INNER_VLAN_VID and VLAN_ID are supported)
+     */
+    VlanIdCriterion(VlanId vlanId, Type type) {
+        checkArgument(
+                type == Type.INNER_VLAN_VID || type == Type.VLAN_VID,
+                "Type can only be inner vlan or vlan");
+        this.vlanId = vlanId;
+        this.type = type;
     }
 
     @Override
     public Type type() {
-        return Type.VLAN_VID;
+        return type;
     }
 
     /**
diff --git a/core/api/src/main/java/org/onosproject/net/flow/criteria/VlanPcpCriterion.java b/core/api/src/main/java/org/onosproject/net/flow/criteria/VlanPcpCriterion.java
index 0c83e14..99607c8 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/criteria/VlanPcpCriterion.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/criteria/VlanPcpCriterion.java
@@ -18,6 +18,7 @@
 import java.util.Objects;
 
 import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
 
 /**
  * Implementation of VLAN priority criterion (3 bits).
@@ -25,6 +26,7 @@
 public final class VlanPcpCriterion implements Criterion {
     private static final byte MASK = 0x7;
     private final byte vlanPcp;             // VLAN pcp value: 3 bits
+    private final Type type;
 
     /**
      * Constructor.
@@ -33,11 +35,26 @@
      */
     VlanPcpCriterion(byte vlanPcp) {
         this.vlanPcp = (byte) (vlanPcp & MASK);
+        this.type = Type.VLAN_PCP;
+    }
+
+    /**
+     * Constructs a vlan priority criterion with a specific type.
+     *
+     * @param vlanPcp the VLAN priority to match (3 bits)
+     * @param type a criterion type (only INNER_VLAN_PCP and VLAN_PCP are supported)
+     */
+    VlanPcpCriterion(byte vlanPcp, Type type) {
+        checkArgument(
+                type == Type.INNER_VLAN_PCP || type == Type.VLAN_PCP,
+                "Type can only be inner vlan or vlan");
+        this.vlanPcp = (byte) (vlanPcp & MASK);
+        this.type = type;
     }
 
     @Override
     public Type type() {
-        return Type.VLAN_PCP;
+        return type;
     }
 
     /**
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
index 975503b..1d7c47e 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/CriterionCodec.java
@@ -38,6 +38,8 @@
     protected static final String METADATA = "metadata";
 
     protected static final String VLAN_ID = "vlanId";
+    protected static final String INNER_VLAN_ID = "innerVlanId";
+    protected static final String INNER_PRIORITY = "innerPriority";
     protected static final String PRIORITY = "priority";
     protected static final String IP_DSCP = "ipDscp";
     protected static final String IP_ECN = "ipEcn";
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java
index 88cc332..cd28afc 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java
@@ -72,6 +72,8 @@
         decoderMap.put(Criterion.Type.ETH_TYPE.name(), new EthTypeDecoder());
         decoderMap.put(Criterion.Type.VLAN_VID.name(), new VlanVidDecoder());
         decoderMap.put(Criterion.Type.VLAN_PCP.name(), new VlanPcpDecoder());
+        decoderMap.put(Criterion.Type.INNER_VLAN_VID.name(), new InnerVlanVidDecoder());
+        decoderMap.put(Criterion.Type.INNER_VLAN_PCP.name(), new InnerVlanPcpDecoder());
         decoderMap.put(Criterion.Type.IP_DSCP.name(), new IpDscpDecoder());
         decoderMap.put(Criterion.Type.IP_ECN.name(), new IpEcnDecoder());
         decoderMap.put(Criterion.Type.IP_PROTO.name(), new IpProtoDecoder());
@@ -139,7 +141,8 @@
         @Override
         public Criterion decodeCriterion(ObjectNode json) {
             PortNumber port = PortNumber.portNumber(nullIsIllegal(json.get(CriterionCodec.PORT),
-                    CriterionCodec.PORT + MISSING_MEMBER_MESSAGE).asLong());
+                                                                  CriterionCodec.PORT +
+                                                                          MISSING_MEMBER_MESSAGE).asLong());
 
             return Criteria.matchInPort(port);
         }
@@ -149,7 +152,8 @@
         @Override
         public Criterion decodeCriterion(ObjectNode json) {
             PortNumber port = PortNumber.portNumber(nullIsIllegal(json.get(CriterionCodec.PORT),
-                    CriterionCodec.PORT + MISSING_MEMBER_MESSAGE).asLong());
+                                                                  CriterionCodec.PORT +
+                                                                          MISSING_MEMBER_MESSAGE).asLong());
 
             return Criteria.matchInPhyPort(port);
         }
@@ -179,12 +183,34 @@
         @Override
         public Criterion decodeCriterion(ObjectNode json) {
             byte priority = (byte) nullIsIllegal(json.get(CriterionCodec.PRIORITY),
-                    CriterionCodec.VLAN_ID + MISSING_MEMBER_MESSAGE).asInt();
+                    CriterionCodec.PRIORITY + MISSING_MEMBER_MESSAGE).asInt();
 
             return Criteria.matchVlanPcp(priority);
         }
     }
 
+    private class InnerVlanVidDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            short vlanId = (short) nullIsIllegal(json.get(CriterionCodec.INNER_VLAN_ID),
+                                                 CriterionCodec.INNER_VLAN_ID +
+                                                         MISSING_MEMBER_MESSAGE).asInt();
+
+            return Criteria.matchInnerVlanId(VlanId.vlanId(vlanId));
+        }
+    }
+
+    private class InnerVlanPcpDecoder implements CriterionDecoder {
+        @Override
+        public Criterion decodeCriterion(ObjectNode json) {
+            byte priority = (byte) nullIsIllegal(json.get(CriterionCodec.INNER_PRIORITY),
+                                                 CriterionCodec.INNER_PRIORITY +
+                                                         MISSING_MEMBER_MESSAGE).asInt();
+
+            return Criteria.matchInnerVlanPcp(priority);
+        }
+    }
+
     private class IpDscpDecoder implements CriterionDecoder {
         @Override
         public Criterion decodeCriterion(ObjectNode json) {
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java
index 1852ee2..2130293 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java
@@ -84,6 +84,8 @@
         formatMap.put(Criterion.Type.ETH_TYPE, new FormatEthType());
         formatMap.put(Criterion.Type.VLAN_VID, new FormatVlanVid());
         formatMap.put(Criterion.Type.VLAN_PCP, new FormatVlanPcp());
+        formatMap.put(Criterion.Type.INNER_VLAN_VID, new FormatInnerVlanVid());
+        formatMap.put(Criterion.Type.INNER_VLAN_PCP, new FormatInnerVlanPcp());
         formatMap.put(Criterion.Type.IP_DSCP, new FormatIpDscp());
         formatMap.put(Criterion.Type.IP_ECN, new FormatIpEcn());
         formatMap.put(Criterion.Type.IP_PROTO, new FormatIpProto());
@@ -194,6 +196,24 @@
         }
     }
 
+    private static class FormatInnerVlanVid implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final VlanIdCriterion vlanIdCriterion =
+                    (VlanIdCriterion) criterion;
+            return root.put(CriterionCodec.INNER_VLAN_ID, vlanIdCriterion.vlanId().toShort());
+        }
+    }
+
+    private static class FormatInnerVlanPcp implements CriterionTypeFormatter {
+        @Override
+        public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
+            final VlanPcpCriterion vlanPcpCriterion =
+                    (VlanPcpCriterion) criterion;
+            return root.put(CriterionCodec.INNER_PRIORITY, vlanPcpCriterion.priority());
+        }
+    }
+
     private static class FormatIpDscp implements CriterionTypeFormatter {
         @Override
         public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) {
diff --git a/drivers/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java b/drivers/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java
index 37980f1..28d6071 100644
--- a/drivers/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java
+++ b/drivers/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java
@@ -214,24 +214,34 @@
 
         Pair<Instruction, Instruction> popAndRewrite = vlanOps.remove(0);
 
-        FlowRule.Builder inner = DefaultFlowRule.builder()
+        TrafficSelector selector = fwd.selector();
+
+        Criterion outerVlan = selector.getCriterion(Criterion.Type.VLAN_VID);
+        Criterion innerVlan = selector.getCriterion(Criterion.Type.INNER_VLAN_VID);
+        Criterion inport = selector.getCriterion(Criterion.Type.IN_PORT);
+
+        if (outerVlan == null || innerVlan == null || inport == null) {
+            log.error("Forwarding objective is underspecified: {}", fwd);
+            fail(fwd, ObjectiveError.BADPARAMS);
+            return;
+        }
+
+        FlowRule.Builder outer = DefaultFlowRule.builder()
                 .forDevice(deviceId)
                 .fromApp(appId)
                 .makePermanent()
                 .withPriority(fwd.priority())
-                .withSelector(fwd.selector())
+                .withSelector(buildSelector(inport, outerVlan))
                 .withTreatment(buildTreatment(popAndRewrite.getLeft(),
                                               Instructions.transition(QQ_TABLE)));
-        PortCriterion inPort = (PortCriterion)
-                fwd.selector().getCriterion(Criterion.Type.IN_PORT);
 
-        FlowRule.Builder outer = DefaultFlowRule.builder()
+        FlowRule.Builder inner = DefaultFlowRule.builder()
                 .forDevice(deviceId)
                 .fromApp(appId)
                 .forTable(QQ_TABLE)
                 .makePermanent()
                 .withPriority(fwd.priority())
-                .withSelector(buildSelector(inPort))
+                .withSelector(buildSelector(inport, innerVlan))
                 .withTreatment(buildTreatment(popAndRewrite.getRight(),
                                               output));