[SDFAB-102] Backport changes required for policies to fabric.p4 (Redirect)

Change-Id: I357c908d31abad9c3f8d74723d937ea948e54808
diff --git a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/Constants.java b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/Constants.java
new file mode 100644
index 0000000..f55a825
--- /dev/null
+++ b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/Constants.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.pipelines.fabric.impl.behaviour;
+
+/**
+ * Constant values.
+ */
+public final class Constants {
+
+    // Used with is_infra_port metadata
+    public static final byte[] ONE = new byte[]{1};
+    public static final byte[] ZERO = new byte[]{0};
+
+    public static final long PORT_TYPE_MASK = 0x3;
+    public static final byte PORT_TYPE_EDGE = 0x1;
+    public static final byte PORT_TYPE_INFRA = 0x2;
+    public static final byte PORT_TYPE_INTERNAL = 0x3;
+
+    // Forwarding types from P4 program (not exposed in P4Info).
+    public static final byte FWD_MPLS = 1;
+    public static final byte FWD_IPV4_ROUTING = 2;
+    public static final byte FWD_IPV6_ROUTING = 4;
+
+    public static final short ETH_TYPE_EXACT_MASK = (short) 0xFFFF;
+
+    public static final int DEFAULT_VLAN = 4094;
+    public static final int DEFAULT_PW_TRANSPORT_VLAN = 4090;
+
+    // hide default constructor
+    private Constants() {
+    }
+}
\ No newline at end of file
diff --git a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/FabricCapabilities.java b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/FabricCapabilities.java
index fbb709a..93605e2 100644
--- a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/FabricCapabilities.java
+++ b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/FabricCapabilities.java
@@ -81,9 +81,9 @@
 
     public boolean supportDoubleVlanTerm() {
         if (pipeconf.pipelineModel()
-                .table(FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN).isPresent()) {
-            return pipeconf.pipelineModel().table(FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN)
-                    .get().action(FabricConstants.FABRIC_INGRESS_NEXT_SET_DOUBLE_VLAN)
+                .table(FabricConstants.FABRIC_INGRESS_PRE_NEXT_NEXT_VLAN).isPresent()) {
+            return pipeconf.pipelineModel().table(FabricConstants.FABRIC_INGRESS_PRE_NEXT_NEXT_VLAN)
+                    .get().action(FabricConstants.FABRIC_INGRESS_PRE_NEXT_SET_DOUBLE_VLAN)
                     .isPresent();
         }
         return false;
diff --git a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/FabricInterpreter.java b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/FabricInterpreter.java
index 4ed412d..1a7b2ba 100644
--- a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/FabricInterpreter.java
+++ b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/FabricInterpreter.java
@@ -68,18 +68,17 @@
     public static final byte[] ZERO = new byte[]{0};
 
     // Group tables by control block.
-    private static final Set<PiTableId> FILTERING_CTRL_TBLS = ImmutableSet.of(
-            FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN,
-            FabricConstants.FABRIC_INGRESS_FILTERING_FWD_CLASSIFIER);
     private static final Set<PiTableId> FORWARDING_CTRL_TBLS = ImmutableSet.of(
             FabricConstants.FABRIC_INGRESS_FORWARDING_MPLS,
             FabricConstants.FABRIC_INGRESS_FORWARDING_ROUTING_V4,
             FabricConstants.FABRIC_INGRESS_FORWARDING_ROUTING_V6,
             FabricConstants.FABRIC_INGRESS_FORWARDING_BRIDGING);
+    private static final Set<PiTableId> PRE_NEXT_CTRL_TBLS  = ImmutableSet.of(
+            FabricConstants.FABRIC_INGRESS_PRE_NEXT_NEXT_MPLS,
+            FabricConstants.FABRIC_INGRESS_PRE_NEXT_NEXT_VLAN);
     private static final Set<PiTableId> ACL_CTRL_TBLS = ImmutableSet.of(
             FabricConstants.FABRIC_INGRESS_ACL_ACL);
     private static final Set<PiTableId> NEXT_CTRL_TBLS = ImmutableSet.of(
-            FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN,
             FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE,
             FabricConstants.FABRIC_INGRESS_NEXT_HASHED,
             FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT);
@@ -166,10 +165,10 @@
     @Override
     public PiAction mapTreatment(TrafficTreatment treatment, PiTableId piTableId)
             throws PiInterpreterException {
-        if (FILTERING_CTRL_TBLS.contains(piTableId)) {
-            return treatmentInterpreter.mapFilteringTreatment(treatment, piTableId);
-        } else if (FORWARDING_CTRL_TBLS.contains(piTableId)) {
+        if (FORWARDING_CTRL_TBLS.contains(piTableId)) {
             return treatmentInterpreter.mapForwardingTreatment(treatment, piTableId);
+        } else if (PRE_NEXT_CTRL_TBLS.contains(piTableId)) {
+            return treatmentInterpreter.mapPreNextTreatment(treatment, piTableId);
         } else if (ACL_CTRL_TBLS.contains(piTableId)) {
             return treatmentInterpreter.mapAclTreatment(treatment, piTableId);
         } else if (NEXT_CTRL_TBLS.contains(piTableId)) {
diff --git a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/FabricTreatmentInterpreter.java b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/FabricTreatmentInterpreter.java
index fe2d490..bdf3eaf 100644
--- a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/FabricTreatmentInterpreter.java
+++ b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/FabricTreatmentInterpreter.java
@@ -41,13 +41,14 @@
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.ETH_DST;
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.ETH_SRC;
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.MPLS_LABEL;
-import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.MPLS_PUSH;
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_ID;
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_POP;
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_PUSH;
 import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.instruction;
 import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.l2Instruction;
+import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.l2InstructionOrFail;
 import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.l2Instructions;
+import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.treatmentException;
 
 /**
  * Treatment translation logic.
@@ -57,8 +58,6 @@
     private final FabricCapabilities capabilities;
     private static final ImmutableMap<PiTableId, PiActionId> NOP_ACTIONS =
             ImmutableMap.<PiTableId, PiActionId>builder()
-                    .put(FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN,
-                         FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT)
                     .put(FabricConstants.FABRIC_INGRESS_FORWARDING_ROUTING_V4,
                          FabricConstants.FABRIC_INGRESS_FORWARDING_NOP_ROUTING_V4)
                     .put(FabricConstants.FABRIC_INGRESS_ACL_ACL,
@@ -72,34 +71,10 @@
         this.capabilities = capabilities;
     }
 
-    static PiAction mapFilteringTreatment(TrafficTreatment treatment, PiTableId tableId)
-            throws PiInterpreterException {
-
-        if (!tableId.equals(FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN)) {
-            // Mapping for other tables of the filtering block must be handled
-            // in the pipeliner.
-            tableException(tableId);
-        }
-
-        // VLAN_POP action is equivalent to the permit action (VLANs pop is done anyway)
-        if (isFilteringNoAction(treatment) || isFilteringPopAction(treatment)) {
-            // Permit action if table is ingress_port_vlan;
-            return nop(tableId);
-        }
-
-        final ModVlanIdInstruction setVlanInst = (ModVlanIdInstruction) l2InstructionOrFail(
-                treatment, VLAN_ID, tableId);
-        return PiAction.builder()
-                .withId(FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT_WITH_INTERNAL_VLAN)
-                .withParameter(new PiActionParam(
-                        FabricConstants.VLAN_ID, setVlanInst.vlanId().toShort()))
-                .build();
-    }
-
 
     static PiAction mapForwardingTreatment(TrafficTreatment treatment, PiTableId tableId)
             throws PiInterpreterException {
-        if (isForwardingNoAction(treatment)) {
+        if (isNoAction(treatment)) {
             return nop(tableId);
         }
         treatmentException(
@@ -108,35 +83,58 @@
         return null;
     }
 
-    PiAction mapNextTreatment(TrafficTreatment treatment, PiTableId tableId)
+    PiAction mapPreNextTreatment(TrafficTreatment treatment, PiTableId tableId)
             throws PiInterpreterException {
-        if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN) {
+        if (tableId == FabricConstants.FABRIC_INGRESS_PRE_NEXT_NEXT_MPLS) {
+            return mapNextMplsTreatment(treatment, tableId);
+        } else if (tableId == FabricConstants.FABRIC_INGRESS_PRE_NEXT_NEXT_VLAN) {
             return mapNextVlanTreatment(treatment, tableId);
-        } else if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_HASHED) {
-            return mapNextHashedOrSimpleTreatment(treatment, tableId, false);
-        } else if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE) {
-            return mapNextHashedOrSimpleTreatment(treatment, tableId, true);
-        } else if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT) {
-            return mapNextXconnect(treatment, tableId);
         }
         throw new PiInterpreterException(format(
                 "Treatment mapping not supported for table '%s'", tableId));
     }
 
+    PiAction mapNextTreatment(TrafficTreatment treatment, PiTableId tableId)
+            throws PiInterpreterException {
+        if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_HASHED) {
+            return mapNextHashedOrSimpleTreatment(treatment, tableId, false);
+        } else if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE) {
+           return mapNextHashedOrSimpleTreatment(treatment, tableId, true);
+        } else if (tableId == FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT) {
+           return mapNextXconnect(treatment, tableId);
+        }
+        throw new PiInterpreterException(format(
+                "Treatment mapping not supported for table '%s'", tableId));
+    }
+
+    private static PiAction mapNextMplsTreatment(TrafficTreatment treatment, PiTableId tableId)
+            throws PiInterpreterException {
+        final ModMplsLabelInstruction mplsLabel = (ModMplsLabelInstruction) l2Instruction(
+                treatment, MPLS_LABEL);
+        if (mplsLabel != null) {
+            return PiAction.builder()
+                    .withParameter(new PiActionParam(FabricConstants.LABEL, mplsLabel.label().toInt()))
+                    .withId(FabricConstants.FABRIC_INGRESS_PRE_NEXT_SET_MPLS_LABEL)
+                    .build();
+        }
+        throw new PiInterpreterException("There is no MPLS instruction");
+    }
+
     private PiAction mapNextVlanTreatment(TrafficTreatment treatment, PiTableId tableId)
             throws PiInterpreterException {
         final List<ModVlanIdInstruction> modVlanIdInst = l2InstructionsOrFail(treatment, VLAN_ID, tableId)
                 .stream().map(i -> (ModVlanIdInstruction) i).collect(Collectors.toList());
         if (modVlanIdInst.size() == 1) {
-            return PiAction.builder().withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_VLAN)
+            return PiAction.builder().withId(FabricConstants.FABRIC_INGRESS_PRE_NEXT_SET_VLAN)
                     .withParameter(new PiActionParam(
                             FabricConstants.VLAN_ID,
                             modVlanIdInst.get(0).vlanId().toShort()))
                     .build();
         }
+        // next_vlan has been moved to pre_next
         if (modVlanIdInst.size() == 2 && capabilities.supportDoubleVlanTerm()) {
             return PiAction.builder()
-                    .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_DOUBLE_VLAN)
+                    .withId(FabricConstants.FABRIC_INGRESS_PRE_NEXT_SET_DOUBLE_VLAN)
                     .withParameter(new PiActionParam(
                             FabricConstants.INNER_VLAN_ID,
                             modVlanIdInst.get(0).vlanId().toShort()))
@@ -151,20 +149,15 @@
     private static PiAction mapNextHashedOrSimpleTreatment(
             TrafficTreatment treatment, PiTableId tableId, boolean simple)
             throws PiInterpreterException {
-        // Provide mapping for output_hashed, routing_hashed, and
-        // mpls_routing_hashed. multicast_hashed can only be invoked with
-        // PiAction, hence no mapping. outPort required for all actions. Presence
-        // of other instructions will determine which action to map to.
+        // Provide mapping for output_hashed and routing_hashed; multicast_hashed
+        // can only be invoked with PiAction, hence no mapping. outPort required for
+        // all actions. Presence of other instructions will determine which action to map to.
         final PortNumber outPort = ((OutputInstruction) instructionOrFail(
                 treatment, OUTPUT, tableId)).port();
         final ModEtherInstruction ethDst = (ModEtherInstruction) l2Instruction(
                 treatment, ETH_DST);
         final ModEtherInstruction ethSrc = (ModEtherInstruction) l2Instruction(
                 treatment, ETH_SRC);
-        final Instruction mplsPush = l2Instruction(
-                treatment, MPLS_PUSH);
-        final ModMplsLabelInstruction mplsLabel = (ModMplsLabelInstruction) l2Instruction(
-                treatment, MPLS_LABEL);
 
         final PiAction.Builder actionBuilder = PiAction.builder()
                 .withParameter(new PiActionParam(FabricConstants.PORT_NUM, outPort.toLong()));
@@ -174,20 +167,11 @@
                     FabricConstants.SMAC, ethSrc.mac().toBytes()));
             actionBuilder.withParameter(new PiActionParam(
                     FabricConstants.DMAC, ethDst.mac().toBytes()));
-            if (mplsLabel != null) {
-                // mpls_routing_hashed
-                return actionBuilder
-                        .withParameter(new PiActionParam(FabricConstants.LABEL, mplsLabel.label().toInt()))
-                        .withId(simple ? FabricConstants.FABRIC_INGRESS_NEXT_MPLS_ROUTING_SIMPLE
-                                        : FabricConstants.FABRIC_INGRESS_NEXT_MPLS_ROUTING_HASHED)
-                        .build();
-            } else {
-                // routing_hashed
-                return actionBuilder
-                        .withId(simple ? FabricConstants.FABRIC_INGRESS_NEXT_ROUTING_SIMPLE
-                                        : FabricConstants.FABRIC_INGRESS_NEXT_ROUTING_HASHED)
-                        .build();
-            }
+            // routing_hashed
+            return actionBuilder
+                    .withId(simple ? FabricConstants.FABRIC_INGRESS_NEXT_ROUTING_SIMPLE
+                                : FabricConstants.FABRIC_INGRESS_NEXT_ROUTING_HASHED)
+                    .build();
         } else {
             // output_hashed
             return actionBuilder
@@ -214,7 +198,7 @@
         if (isDrop(treatment)) {
                 return drop(tableId);
         }
-        if (isForwardingNoAction(treatment)) {
+        if (isNoAction(treatment)) {
                 return nop(tableId);
         }
         treatmentException(
@@ -256,15 +240,8 @@
         return PiAction.builder().withId(FabricConstants.FABRIC_INGRESS_ACL_DROP).build();
     }
 
-    // NOTE: we use clearDeferred to signal when there are no more ports associated to a given vlan
-    private static boolean isFilteringNoAction(TrafficTreatment treatment) {
-        return treatment.equals(DefaultTrafficTreatment.emptyTreatment()) ||
-                (treatment.allInstructions().isEmpty()) ||
-                (treatment.allInstructions().size() == 1 && treatment.writeMetadata() != null);
-    }
-
     // NOTE: clearDeferred is used by the routing application to implement ACL drop and route black-holing
-    private static boolean isForwardingNoAction(TrafficTreatment treatment) {
+    private static boolean isNoAction(TrafficTreatment treatment) {
         return treatment.equals(DefaultTrafficTreatment.emptyTreatment()) ||
                 (treatment.allInstructions().isEmpty() && !treatment.clearedDeferred()) ||
                 (treatment.allInstructions().size() == 1 && treatment.writeMetadata() != null);
@@ -274,21 +251,6 @@
         return treatment.allInstructions().isEmpty() && treatment.clearedDeferred();
     }
 
-    private static boolean isFilteringPopAction(TrafficTreatment treatment) {
-        return l2Instruction(treatment, VLAN_POP) != null;
-    }
-
-    private static Instruction l2InstructionOrFail(
-            TrafficTreatment treatment,
-            L2ModificationInstruction.L2SubType subType, PiTableId tableId)
-            throws PiInterpreterException {
-        final Instruction inst = l2Instruction(treatment, subType);
-        if (inst == null) {
-            treatmentException(tableId, treatment, format("missing %s instruction", subType));
-        }
-        return inst;
-    }
-
     private static List<L2ModificationInstruction> l2InstructionsOrFail(
             TrafficTreatment treatment,
             L2ModificationInstruction.L2SubType subType, PiTableId tableId)
@@ -309,16 +271,4 @@
         }
         return inst;
     }
-
-    private static void tableException(PiTableId tableId)
-            throws PiInterpreterException {
-        throw new PiInterpreterException(format("Table '%s' not supported", tableId));
-    }
-
-    private static void treatmentException(
-            PiTableId tableId, TrafficTreatment treatment, String explanation)
-            throws PiInterpreterException {
-        throw new PiInterpreterException(format(
-                "Invalid treatment for table '%s', %s: %s", tableId, explanation, treatment));
-    }
 }
diff --git a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/FabricUtils.java b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/FabricUtils.java
index 655f88d..4496f19 100644
--- a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/FabricUtils.java
+++ b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/FabricUtils.java
@@ -25,6 +25,8 @@
 import org.onosproject.net.flow.instructions.L2ModificationInstruction;
 import org.onosproject.net.flowobjective.DefaultNextTreatment;
 import org.onosproject.net.flowobjective.NextTreatment;
+import org.onosproject.net.pi.model.PiPipelineInterpreter;
+import org.onosproject.net.pi.model.PiTableId;
 
 import java.util.Collection;
 import java.util.List;
@@ -62,11 +64,10 @@
                             format("%s criterion cannot be null", type));
     }
 
-    public static Instructions.OutputInstruction instruction(TrafficTreatment treatment, Instruction.Type type) {
+    public static Instruction instruction(TrafficTreatment treatment, Instruction.Type type) {
         return treatment.allInstructions()
                 .stream()
                 .filter(inst -> inst.type() == type)
-                .map(inst -> (Instructions.OutputInstruction) inst)
                 .findFirst().orElse(null);
     }
 
@@ -79,6 +80,17 @@
                 .findFirst().orElse(null);
     }
 
+    public static Instruction l2InstructionOrFail(
+            TrafficTreatment treatment,
+            L2ModificationInstruction.L2SubType subType, PiTableId tableId)
+            throws PiPipelineInterpreter.PiInterpreterException {
+        final Instruction inst = l2Instruction(treatment, subType);
+        if (inst == null) {
+            treatmentException(tableId, treatment, format("missing %s instruction", subType));
+        }
+        return inst;
+    }
+
     public static List<L2ModificationInstruction> l2Instructions(
             TrafficTreatment treatment, L2ModificationInstruction.L2SubType subType) {
         return treatment.allInstructions().stream()
@@ -89,7 +101,7 @@
     }
 
     public static Instructions.OutputInstruction outputInstruction(TrafficTreatment treatment) {
-        return instruction(treatment, Instruction.Type.OUTPUT);
+        return (Instructions.OutputInstruction) instruction(treatment, Instruction.Type.OUTPUT);
     }
 
     public static PortNumber outputPort(TrafficTreatment treatment) {
@@ -104,4 +116,11 @@
         }
         return null;
     }
+
+    public static void treatmentException(
+            PiTableId tableId, TrafficTreatment treatment, String explanation)
+            throws PiPipelineInterpreter.PiInterpreterException {
+        throw new PiPipelineInterpreter.PiInterpreterException(format(
+                "Invalid treatment for table '%s', %s: %s", tableId, explanation, treatment));
+    }
 }
diff --git a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/FabricPipeliner.java b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/FabricPipeliner.java
index 744cd9d..3105e66 100644
--- a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/FabricPipeliner.java
+++ b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/FabricPipeliner.java
@@ -80,7 +80,8 @@
 import static org.onosproject.pipelines.fabric.impl.behaviour.FabricInterpreter.ONE;
 import static org.onosproject.pipelines.fabric.impl.behaviour.FabricInterpreter.ZERO;
 import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.outputPort;
-import static org.onosproject.pipelines.fabric.impl.behaviour.pipeliner.FilteringObjectiveTranslator.FWD_IPV4_ROUTING;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.FWD_IPV4_ROUTING;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.PORT_TYPE_INTERNAL;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -368,6 +369,7 @@
                         .withId(vlanValid ? FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT
                                 : FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT_WITH_INTERNAL_VLAN)
                         .withParameter(new PiActionParam(FabricConstants.VLAN_ID, vlanId))
+                        .withParameter(new PiActionParam(FabricConstants.PORT_TYPE, PORT_TYPE_INTERNAL))
                         .build())
                 .build();
         return DefaultFlowRule.builder()
diff --git a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/FilteringObjectiveTranslator.java b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/FilteringObjectiveTranslator.java
index 39d100e..bfdcc2b 100644
--- a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/FilteringObjectiveTranslator.java
+++ b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/FilteringObjectiveTranslator.java
@@ -33,10 +33,12 @@
 import org.onosproject.net.flow.criteria.PortCriterion;
 import org.onosproject.net.flow.criteria.VlanIdCriterion;
 import org.onosproject.net.flow.instructions.Instructions;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
 import org.onosproject.net.flowobjective.FilteringObjective;
 import org.onosproject.net.flowobjective.Objective;
 import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.pi.model.PiTableId;
 import org.onosproject.net.pi.runtime.PiAction;
 import org.onosproject.net.pi.runtime.PiActionParam;
 import org.onosproject.pipelines.fabric.impl.behaviour.FabricCapabilities;
@@ -49,7 +51,23 @@
 import static java.lang.String.format;
 import static org.onosproject.net.flow.criteria.Criterion.Type.INNER_VLAN_VID;
 import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
+import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_ID;
+import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_POP;
+import static org.onosproject.net.pi.model.PiPipelineInterpreter.PiInterpreterException;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.DEFAULT_VLAN;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.DEFAULT_PW_TRANSPORT_VLAN;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.ETH_TYPE_EXACT_MASK;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.FWD_MPLS;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.FWD_IPV4_ROUTING;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.FWD_IPV6_ROUTING;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.ONE;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.PORT_TYPE_EDGE;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.PORT_TYPE_INFRA;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.ZERO;
+import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.l2InstructionOrFail;
 import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.criterion;
+import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.l2Instruction;
+
 
 /**
  * ObjectiveTranslator implementation for FilteringObjective.
@@ -57,16 +75,6 @@
 class FilteringObjectiveTranslator
         extends AbstractObjectiveTranslator<FilteringObjective> {
 
-    // Forwarding types from fabric.p4.
-    static final byte FWD_MPLS = 1;
-    static final byte FWD_IPV4_ROUTING = 2;
-    static final byte FWD_IPV6_ROUTING = 3;
-
-    private static final byte[] ONE = new byte[]{1};
-    private static final byte[] ZERO = new byte[]{0};
-
-    private static final short ETH_TYPE_EXACT_MASK = (short) 0xFFFF;
-
     private static final PiAction DENY = PiAction.builder()
             .withId(FabricConstants.FABRIC_INGRESS_FILTERING_DENY)
             .build();
@@ -168,7 +176,7 @@
 
     private boolean isDoubleTagged(FilteringObjective obj) {
         return obj.meta() != null &&
-                FabricUtils.l2Instruction(obj.meta(), L2ModificationInstruction.L2SubType.VLAN_POP) != null &&
+                FabricUtils.l2Instruction(obj.meta(), L2SubType.VLAN_POP) != null &&
                 FabricUtils.criterion(obj.conditions(), VLAN_VID) != null &&
                 FabricUtils.criterion(obj.conditions(), INNER_VLAN_VID) != null;
     }
@@ -206,18 +214,66 @@
             selector.add(innerVlanCriterion);
         }
 
-        final TrafficTreatment treatment;
+        final TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
         if (obj.type().equals(FilteringObjective.Type.DENY)) {
-            treatment = DefaultTrafficTreatment.builder()
-                    .piTableAction(DENY)
-                    .build();
+            treatmentBuilder.piTableAction(DENY);
         } else {
-            treatment = obj.meta() == null
-                    ? DefaultTrafficTreatment.emptyTreatment() : obj.meta();
+            byte portType = PORT_TYPE_EDGE;
+            if (!innerVlanValid && outerVlanValid &&
+                    outerVlanCriterion.vlanId().toShort() == DEFAULT_PW_TRANSPORT_VLAN) {
+                portType = PORT_TYPE_INFRA;
+            } else if (obj.meta() != null) {
+                ModVlanIdInstruction modVlanIdInstruction = (ModVlanIdInstruction) l2Instruction(obj.meta(), VLAN_ID);
+                if (modVlanIdInstruction != null && modVlanIdInstruction.vlanId().toShort() == DEFAULT_VLAN) {
+                    portType = PORT_TYPE_INFRA;
+                }
+            }
+            try {
+                treatmentBuilder.piTableAction(mapFilteringTreatment(obj.meta(),
+                        FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN, portType));
+            } catch (PiInterpreterException ex) {
+                throw new FabricPipelinerException(format("Unable to map treatment for table '%s': %s",
+                        FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN,
+                        ex.getMessage()), ObjectiveError.UNSUPPORTED);
+            }
         }
         resultBuilder.addFlowRule(flowRule(
                 obj, FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN,
-                selector.build(), treatment));
+                selector.build(), treatmentBuilder.build()));
+    }
+
+    private PiAction mapFilteringTreatment(TrafficTreatment treatment, PiTableId tableId, byte portType)
+            throws PiInterpreterException {
+        if (treatment == null) {
+            treatment = DefaultTrafficTreatment.emptyTreatment();
+        }
+        // VLAN_POP action is equivalent to the permit action (VLANs pop is done anyway)
+        if (isFilteringNoAction(treatment) || isFilteringPopAction(treatment)) {
+            // Permit action if table is ingress_port_vlan;
+            return PiAction.builder()
+                    .withId(FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT)
+                    .withParameter(new PiActionParam(FabricConstants.PORT_TYPE, portType))
+                    .build();
+        }
+
+        final ModVlanIdInstruction setVlanInst = (ModVlanIdInstruction) l2InstructionOrFail(
+                treatment, VLAN_ID, tableId);
+        return PiAction.builder()
+                .withId(FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT_WITH_INTERNAL_VLAN)
+                .withParameter(new PiActionParam(FabricConstants.VLAN_ID, setVlanInst.vlanId().toShort()))
+                .withParameter(new PiActionParam(FabricConstants.PORT_TYPE, portType))
+                .build();
+    }
+
+    // NOTE: we use clearDeferred to signal when there are no more ports associated to a given vlan
+    private static boolean isFilteringNoAction(TrafficTreatment treatment) {
+        return treatment.equals(DefaultTrafficTreatment.emptyTreatment()) ||
+                (treatment.allInstructions().isEmpty()) ||
+                (treatment.allInstructions().size() == 1 && treatment.writeMetadata() != null);
+    }
+
+    private boolean isFilteringPopAction(TrafficTreatment treatment) {
+        return l2Instruction(treatment, VLAN_POP) != null;
     }
 
     private void fwdClassifierRules(
diff --git a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/ForwardingObjectiveTranslator.java b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/ForwardingObjectiveTranslator.java
index 0ab5beb..d527228 100644
--- a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/ForwardingObjectiveTranslator.java
+++ b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/ForwardingObjectiveTranslator.java
@@ -32,7 +32,9 @@
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.criteria.EthCriterion;
 import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.MetadataCriterion;
 import org.onosproject.net.flow.criteria.MplsCriterion;
+import org.onosproject.net.flow.criteria.PiCriterion;
 import org.onosproject.net.flow.criteria.VlanIdCriterion;
 import org.onosproject.net.flowobjective.ForwardingObjective;
 import org.onosproject.net.flowobjective.Objective;
@@ -59,6 +61,9 @@
 import static org.onosproject.net.group.DefaultGroupBucket.createCloneGroupBucket;
 import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.criterionNotNull;
 import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.outputPort;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.PORT_TYPE_MASK;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.PORT_TYPE_EDGE;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.PORT_TYPE_INFRA;
 
 
 /**
@@ -103,6 +108,8 @@
                  FabricConstants.FABRIC_INGRESS_FORWARDING_SET_NEXT_ID_ROUTING_V6)
             .put(FabricConstants.FABRIC_INGRESS_FORWARDING_MPLS,
                  FabricConstants.FABRIC_INGRESS_FORWARDING_POP_MPLS_AND_NEXT)
+            .put(FabricConstants.FABRIC_INGRESS_ACL_ACL,
+                 FabricConstants.FABRIC_INGRESS_ACL_SET_NEXT_ID_ACL)
             .build();
 
     ForwardingObjectiveTranslator(DeviceId deviceId, FabricCapabilities capabilities) {
@@ -290,8 +297,22 @@
                 return;
             }
         }
+        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder(obj.selector());
+        // Meta are used to signal the port type which can be edge or infra
+        if (obj.meta() != null && obj.meta().getCriterion(Criterion.Type.METADATA) != null) {
+            long portType = ((MetadataCriterion) obj.meta().getCriterion(Criterion.Type.METADATA)).metadata();
+            if (portType == PORT_TYPE_EDGE || portType == PORT_TYPE_INFRA) {
+                selectorBuilder.matchPi(PiCriterion.builder()
+                        .matchTernary(FabricConstants.HDR_PORT_TYPE, portType, PORT_TYPE_MASK)
+                        .build());
+            } else {
+                throw new FabricPipelinerException(format("Port type '%s' is not allowed for table '%s'",
+                        portType, FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN),
+                        ObjectiveError.UNSUPPORTED);
+            }
+        }
         resultBuilder.addFlowRule(flowRule(
-                obj, FabricConstants.FABRIC_INGRESS_ACL_ACL, obj.selector()));
+                obj, FabricConstants.FABRIC_INGRESS_ACL_ACL, selectorBuilder.build()));
     }
 
     private DefaultGroupDescription createCloneGroup(
diff --git a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/NextObjectiveTranslator.java b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/NextObjectiveTranslator.java
index 8ecffb2..47b7ef6 100644
--- a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/NextObjectiveTranslator.java
+++ b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/NextObjectiveTranslator.java
@@ -17,6 +17,7 @@
 package org.onosproject.pipelines.fabric.impl.behaviour.pipeliner;
 
 import com.google.common.collect.Lists;
+import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
@@ -28,6 +29,7 @@
 import org.onosproject.net.flow.criteria.PiCriterion;
 import org.onosproject.net.flow.criteria.VlanIdCriterion;
 import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
 import org.onosproject.net.flowobjective.DefaultNextTreatment;
 import org.onosproject.net.flowobjective.NextObjective;
@@ -57,6 +59,7 @@
 import java.util.stream.Collectors;
 
 import static java.lang.String.format;
+import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.MPLS_LABEL;
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_ID;
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_POP;
 import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.criterion;
@@ -103,13 +106,55 @@
         }
 
         if (!isGroupModifyOp(obj)) {
-            // Generate next VLAN rules.
+            // Generate next MPLS and VLAN rules.
+            nextMpls(obj, resultBuilder);
             nextVlan(obj, resultBuilder);
         }
 
         return resultBuilder.build();
     }
 
+    private void nextMpls(NextObjective obj,
+                          ObjectiveTranslation.Builder resultBuilder)
+            throws FabricPipelinerException {
+        // Next objective can contain only one mpls push and one mpls label
+        // instruction. Pipeliner does not support other configurations.
+
+        final List<List<ModMplsLabelInstruction>> mplsInstructions = defaultNextTreatments(
+                obj.nextTreatments(), false).stream()
+                .map(defaultNextTreatment -> l2Instructions(defaultNextTreatment.treatment(), MPLS_LABEL)
+                        .stream().map(v -> (ModMplsLabelInstruction) v)
+                        .collect(Collectors.toList()))
+                .filter(l -> !l.isEmpty())
+                .collect(Collectors.toList());
+
+        if (mplsInstructions.isEmpty()) {
+            // No need to apply next mpls table
+            return;
+        }
+
+        // We expect one mpls label for each treatment and the label has to be the same
+        final Set<MplsLabel> mplsLabels = mplsInstructions.stream()
+                .flatMap(Collection::stream)
+                .map(ModMplsLabelInstruction::label)
+                .collect(Collectors.toSet());
+        if (obj.nextTreatments().size() != mplsInstructions.size() ||
+                mplsLabels.size() != 1) {
+            throw new FabricPipelinerException(
+                    "Inconsistent MPLS_LABEL instructions, cannot process " +
+                            "next_mpls rule. It is required that all " +
+                            "treatments have the same MPLS_LABEL instructions.");
+        }
+        final TrafficSelector selector = nextIdSelector(obj.id());
+        final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setMpls(mplsLabels.iterator().next())
+                .build();
+
+        resultBuilder.addFlowRule(flowRule(
+                obj, FabricConstants.FABRIC_INGRESS_PRE_NEXT_NEXT_MPLS,
+                selector, treatment));
+    }
+
     private void nextVlan(NextObjective obj,
                           ObjectiveTranslation.Builder resultBuilder)
             throws FabricPipelinerException {
@@ -169,7 +214,7 @@
         final TrafficTreatment treatment = treatmentBuilder.build();
 
         resultBuilder.addFlowRule(flowRule(
-                obj, FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN,
+                obj, FabricConstants.FABRIC_INGRESS_PRE_NEXT_NEXT_VLAN,
                 selector, treatment));
     }
 
@@ -258,7 +303,7 @@
             if (obj.meta() != null && obj.meta().getCriterion(Criterion.Type.VLAN_VID) != null) {
                 egressVlan(outPort, obj, popVlanInst, resultBuilder);
             } else {
-                log.warn("NextObjective {} is trying to program {} without {} information",
+                log.debug("NextObjective {} is trying to program {} without {} information",
                         obj, FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN,
                         obj.meta() == null ? "metadata" : "vlanId");
             }