[SDFAB-522] Fix port type for pair ports

Additionally standardize the usage of the 64 bits carried in the metadata instruction.
Implements unit tests for different metadata configurations

Change-Id: I3382657c81876ec2eb0749f06f0fb9e96b2c0e6e
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
index 6f92cad..5267572 100644
--- 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
@@ -16,6 +16,10 @@
 
 package org.onosproject.pipelines.fabric.impl.behaviour;
 
+import com.google.common.collect.ImmutableMap;
+
+import java.util.Map;
+
 /**
  * Constant values.
  */
@@ -48,6 +52,63 @@
     public static final int DEFAULT_TC = 0;
     public static final byte DEFAULT_QFI = (byte) 0x00;
 
+    //////////////////////////////////////////////////////////////////////////////
+    // 64 .... 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 //
+    //  X      X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X X X X X 1 1 1 1 1 //
+    //////////////////////////////////////////////////////////////////////////////
+    // Metadata instruction is used as 8 byte sequence to carry up to 64 metadata
+
+    // FIXME We are assuming SR as the only app programming this meta.
+    // SDFAB-530 to get rid of this limitation
+
+    /**
+     * SR is setting this metadata when a double tagged filtering objective is removed
+     * and no other hosts is sharing the same input port. Thus, termination mac entries
+     * can be removed together with the vlan table entries.
+     *
+     * See org.onosproject.segmentrouting.RoutingRulePopulator#buildDoubleTaggedFilteringObj()
+     * See org.onosproject.segmentrouting.RoutingRulePopulator#processDoubleTaggedFilter()
+     */
+    public static final long CLEANUP_DOUBLE_TAGGED_HOST_ENTRIES = 1;
+
+    /**
+     * SR is setting this metadata when an interface config update has been performed
+     * and thus termination mac entries should not be removed.
+     *
+     * See org.onosproject.segmentrouting.RoutingRulePopulator#processSinglePortFiltersInternal
+     */
+    public static final long INTERFACE_CONFIG_UPDATE = 1L << 1;
+
+    /**
+     * SR is setting this metadata to signal the driver when the config is for the pair port,
+     * i.e. ports connecting two leaves.
+     *
+     *  See org.onosproject.segmentrouting.RoutingRulePopulator#portType
+     */
+    public static final long PAIR_PORT = 1L << 2;
+
+    /**
+     * SR is setting this metadata to signal the driver when the config is for an edge port,
+     * i.e. ports facing an host.
+     *
+     * See org.onosproject.segmentrouting.policy.impl.PolicyManager#trafficMatchFwdObjective
+     * See org.onosproject.segmentrouting.RoutingRulePopulator#portType
+     */
+    public static final long EDGE_PORT = 1L << 3;
+
+    /**
+     * SR is setting this metadata to signal the driver when the config is for an infra port,
+     * i.e. ports connecting a leaf with a spine.
+     */
+    public static final long INFRA_PORT = 1L << 4;
+
+    public static final long METADATA_MASK = 0x1FL;
+
+    public static final Map<Long, Byte> METADATA_TO_PORT_TYPE = ImmutableMap.<Long, Byte>builder()
+            .put(PAIR_PORT, PORT_TYPE_INFRA)
+            .put(EDGE_PORT, PORT_TYPE_EDGE)
+            .put(INFRA_PORT, PORT_TYPE_INFRA)
+            .build();
 
     // hide default constructor
     private Constants() {
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 4496f19..5d89f99 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
@@ -20,11 +20,15 @@
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.MetadataCriterion;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction;
 import org.onosproject.net.flowobjective.DefaultNextTreatment;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
 import org.onosproject.net.flowobjective.NextTreatment;
+import org.onosproject.net.flowobjective.Objective;
 import org.onosproject.net.pi.model.PiPipelineInterpreter;
 import org.onosproject.net.pi.model.PiTableId;
 
@@ -34,6 +38,11 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static java.lang.String.format;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.EDGE_PORT;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.INFRA_PORT;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.METADATA_MASK;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.METADATA_TO_PORT_TYPE;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.PAIR_PORT;
 
 /**
  * Utility class with methods common to fabric pipeconf operations.
@@ -123,4 +132,87 @@
         throw new PiPipelineInterpreter.PiInterpreterException(format(
                 "Invalid treatment for table '%s', %s: %s", tableId, explanation, treatment));
     }
+
+    /**
+     * Port type metadata conversion.
+     *
+     * @param obj the objective
+     * @return the port type associated to the metadata, null otherwise
+     */
+    public static Byte portType(Objective obj) {
+        Byte portType = null;
+        if (isSrMetadataSet(obj, PAIR_PORT) && METADATA_TO_PORT_TYPE.containsKey(PAIR_PORT)) {
+            portType = METADATA_TO_PORT_TYPE.get(PAIR_PORT);
+        } else if (isSrMetadataSet(obj, EDGE_PORT) && METADATA_TO_PORT_TYPE.containsKey(EDGE_PORT)) {
+            portType = METADATA_TO_PORT_TYPE.get(EDGE_PORT);
+        } else if (isSrMetadataSet(obj, INFRA_PORT) && METADATA_TO_PORT_TYPE.containsKey(INFRA_PORT)) {
+            portType = METADATA_TO_PORT_TYPE.get(INFRA_PORT);
+        }
+        return portType;
+    }
+
+    /**
+     * Check metadata passed from SegmentRouting app.
+     *
+     * @param obj the objective containing the metadata
+     * @return true if the objective contains valid metadata, false otherwise
+     */
+    public static boolean isValidSrMetadata(Objective obj) {
+        long meta = 0;
+        if (obj instanceof FilteringObjective) {
+            FilteringObjective filtObj = (FilteringObjective) obj;
+            if (filtObj.meta() == null) {
+                return true;
+            }
+            Instructions.MetadataInstruction metaIns = filtObj.meta().writeMetadata();
+            if (metaIns == null) {
+                return true;
+            }
+            meta = metaIns.metadata() & metaIns.metadataMask();
+        } else if (obj instanceof ForwardingObjective) {
+            ForwardingObjective fwdObj = (ForwardingObjective) obj;
+            if (fwdObj.meta() == null) {
+                return true;
+            }
+            MetadataCriterion metaCrit = (MetadataCriterion) fwdObj.meta().getCriterion(Criterion.Type.METADATA);
+            if (metaCrit == null) {
+                return true;
+            }
+            meta = metaCrit.metadata();
+        }
+        return meta != 0 && ((meta ^ METADATA_MASK) <= METADATA_MASK);
+    }
+
+    /**
+     * Verify if a given flag has been set into the metadata.
+     *
+     * @param obj the objective containing the metadata
+     * @param flag the flag to verify
+     * @return true if the flag is set, false otherwise
+     */
+    public static boolean isSrMetadataSet(Objective obj, long flag) {
+        long meta = 0;
+        if (obj instanceof FilteringObjective) {
+            FilteringObjective filtObj = (FilteringObjective) obj;
+            if (filtObj.meta() == null) {
+                return false;
+            }
+            Instructions.MetadataInstruction metaIns = filtObj.meta().writeMetadata();
+            if (metaIns == null) {
+                return false;
+            }
+            meta = metaIns.metadata() & metaIns.metadataMask();
+        } else if (obj instanceof ForwardingObjective) {
+            ForwardingObjective fwdObj = (ForwardingObjective) obj;
+            if (fwdObj.meta() == null) {
+                return false;
+            }
+            MetadataCriterion metaCrit = (MetadataCriterion) fwdObj.meta().getCriterion(Criterion.Type.METADATA);
+            if (metaCrit == null) {
+                return false;
+            }
+            meta = metaCrit.metadata();
+        }
+        return (meta & flag) == flag;
+    }
 }
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 bfdcc2b..387230b 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
@@ -32,7 +32,6 @@
 import org.onosproject.net.flow.criteria.PiCriterion;
 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.L2SubType;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
 import org.onosproject.net.flowobjective.FilteringObjective;
@@ -54,21 +53,21 @@
 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.CLEANUP_DOUBLE_TAGGED_HOST_ENTRIES;
 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.INTERFACE_CONFIG_UPDATE;
 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.isSrMetadataSet;
+import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.isValidSrMetadata;
+import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.portType;
 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.
  */
@@ -79,8 +78,6 @@
             .withId(FabricConstants.FABRIC_INGRESS_FILTERING_DENY)
             .build();
 
-    private static final int INTERFACE_CONFIG_UPDATE = 2;
-
     FilteringObjectiveTranslator(DeviceId deviceId, FabricCapabilities capabilities) {
         super(deviceId, capabilities);
     }
@@ -98,6 +95,12 @@
                     ObjectiveError.BADPARAMS);
         }
 
+        if (!isValidSrMetadata(obj)) {
+            throw new FabricPipelinerException(
+                    format("Unsupported metadata configuration: metadata=%s", obj.meta()),
+                    ObjectiveError.BADPARAMS);
+        }
+
         final PortCriterion inPort = (PortCriterion) obj.key();
 
         final VlanIdCriterion outerVlan = (VlanIdCriterion) criterion(
@@ -134,44 +137,9 @@
         //     AND it is a port REMOVE event OR
         // - it refers to double tagged traffic
         //     and SR is triggering the removal of forwarding classifier rules.
-        return (obj.op() == Objective.Operation.ADD && !isInterfaceConfigUpdate(obj)) ||
-                (!isDoubleTagged(obj) && !isInterfaceConfigUpdate(obj)) ||
-                (isDoubleTagged(obj) && isLastDoubleTaggedForPort(obj));
-    }
-
-    /**
-     * Check if the given filtering objective is triggered by a interface config change.
-     *
-     * @param obj Filtering objective to check.
-     * @return True if SR is signaling to not remove the forwarding classifier rule,
-     * false otherwise.
-     */
-    private boolean isInterfaceConfigUpdate(FilteringObjective obj) {
-        if (obj.meta() == null) {
-            return false;
-        }
-        Instructions.MetadataInstruction meta = obj.meta().writeMetadata();
-        // SR is setting this metadata when an interface config update has
-        // been performed and thus fwd classifier rules should not be removed
-        return (meta != null && (meta.metadata() & meta.metadataMask()) == INTERFACE_CONFIG_UPDATE);
-    }
-
-    /**
-     * Check if the given filtering objective is the last filtering objective
-     * for a double-tagged host for a specific port.
-     * <p>
-     * {@see org.onosproject.segmentrouting.RoutingRulePopulator#buildDoubleTaggedFilteringObj()}
-     * {@see org.onosproject.segmentrouting.RoutingRulePopulator#processDoubleTaggedFilter()}
-     *
-     * @param obj Filtering objective to check.
-     * @return True if SR is signaling to remove the forwarding classifier rule,
-     * false otherwise.
-     */
-    private boolean isLastDoubleTaggedForPort(FilteringObjective obj) {
-        Instructions.MetadataInstruction meta = obj.meta().writeMetadata();
-        // SR is setting this metadata when a double tagged filtering objective
-        // is removed and no other hosts is sharing the same input port.
-        return (meta != null && (meta.metadata() & meta.metadataMask()) == 1);
+        return (obj.op() == Objective.Operation.ADD && !isSrMetadataSet(obj, INTERFACE_CONFIG_UPDATE)) ||
+                (!isDoubleTagged(obj) && !isSrMetadataSet(obj, INTERFACE_CONFIG_UPDATE)) ||
+                (isDoubleTagged(obj) && isSrMetadataSet(obj, CLEANUP_DOUBLE_TAGGED_HOST_ENTRIES));
     }
 
     private boolean isDoubleTagged(FilteringObjective obj) {
@@ -218,15 +186,12 @@
         if (obj.type().equals(FilteringObjective.Type.DENY)) {
             treatmentBuilder.piTableAction(DENY);
         } else {
-            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;
-                }
+            // FIXME SDFAB-52 to complete the work on metadata
+            Byte portType = portType(obj);
+            if (portType == null) {
+                throw new FabricPipelinerException(
+                        format("Unsupported port_type configuration: metadata=%s", obj.meta()),
+                        ObjectiveError.BADPARAMS);
             }
             try {
                 treatmentBuilder.piTableAction(mapFilteringTreatment(obj.meta(),
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 d527228..e0c7ec1 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,6 @@
 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;
@@ -59,6 +58,10 @@
 
 import static java.lang.String.format;
 import static org.onosproject.net.group.DefaultGroupBucket.createCloneGroupBucket;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.PAIR_PORT;
+import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.isSrMetadataSet;
+import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.isValidSrMetadata;
+import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.portType;
 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;
@@ -119,6 +122,13 @@
     @Override
     public ObjectiveTranslation doTranslate(ForwardingObjective obj)
             throws FabricPipelinerException {
+
+        if (!isValidSrMetadata(obj)) {
+            throw new FabricPipelinerException(
+                    format("Unsupported metadata configuration: metadata=%s", obj.meta()),
+                    ObjectiveError.BADPARAMS);
+        }
+
         final ObjectiveTranslation.Builder resultBuilder =
                 ObjectiveTranslation.builder();
         switch (obj.flag()) {
@@ -299,11 +309,11 @@
         }
         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();
+        Byte portType = portType(obj);
+        if (portType != null && !isSrMetadataSet(obj, PAIR_PORT)) {
             if (portType == PORT_TYPE_EDGE || portType == PORT_TYPE_INFRA) {
                 selectorBuilder.matchPi(PiCriterion.builder()
-                        .matchTernary(FabricConstants.HDR_PORT_TYPE, portType, PORT_TYPE_MASK)
+                        .matchTernary(FabricConstants.HDR_PORT_TYPE, (long) portType, PORT_TYPE_MASK)
                         .build());
             } else {
                 throw new FabricPipelinerException(format("Port type '%s' is not allowed for table '%s'",
diff --git a/pipelines/fabric/impl/src/test/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/FilteringObjectiveTranslatorTest.java b/pipelines/fabric/impl/src/test/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/FilteringObjectiveTranslatorTest.java
index 73039b5..5e126cc 100644
--- a/pipelines/fabric/impl/src/test/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/FilteringObjectiveTranslatorTest.java
+++ b/pipelines/fabric/impl/src/test/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/FilteringObjectiveTranslatorTest.java
@@ -46,11 +46,14 @@
 import static org.junit.Assert.assertEquals;
 import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.DEFAULT_PW_TRANSPORT_VLAN;
 import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.DEFAULT_VLAN;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.EDGE_PORT;
 import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.ETH_TYPE_EXACT_MASK;
 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.FWD_MPLS;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.INFRA_PORT;
 import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.ONE;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.PAIR_PORT;
 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;
@@ -75,7 +78,7 @@
      */
     @Test
     public void testRouterMacAndVlanFilter() throws FabricPipelinerException {
-        FilteringObjective filteringObjective = buildFilteringObjective(ROUTER_MAC);
+        FilteringObjective filteringObjective = buildFilteringObjective(ROUTER_MAC, EDGE_PORT);
         ObjectiveTranslation actualTranslation = translator.translate(filteringObjective);
         Collection<FlowRule> expectedFlowRules = Lists.newArrayList();
         // in port vlan flow rule
@@ -137,6 +140,7 @@
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .pushVlan()
                 .setVlanId(VLAN_100)
+                .writeMetadata(EDGE_PORT, 0xffffffffffffffffL)
                 .build();
         FilteringObjective filteringObjective = DefaultFilteringObjective.builder()
                 .permit()
@@ -182,6 +186,7 @@
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .pushVlan()
                 .setVlanId(VLAN_100)
+                .writeMetadata(EDGE_PORT, 0xffffffffffffffffL)
                 .build();
         FilteringObjective filteringObjective = DefaultFilteringObjective.builder()
                 .permit()
@@ -223,7 +228,7 @@
      */
     @Test
     public void testFwdBridging() throws Exception {
-        FilteringObjective filteringObjective = buildFilteringObjective(null);
+        FilteringObjective filteringObjective = buildFilteringObjective(null, EDGE_PORT);
         ObjectiveTranslation actualTranslation = translator.translate(filteringObjective);
 
         // in port vlan flow rule
@@ -300,8 +305,9 @@
                 .withPriority(PRIORITY)
                 .fromApp(APP_ID)
                 .withMeta(DefaultTrafficTreatment.builder()
-                                  .popVlan()
-                                  .build())
+                        .popVlan()
+                        .writeMetadata(EDGE_PORT, 0xffffffffffffffffL)
+                        .build())
                 .permit()
                 .add();
         ObjectiveTranslation actualTranslation = translator.translate(filteringObjective);
@@ -369,7 +375,7 @@
                 .withPriority(PRIORITY)
                 .fromApp(APP_ID)
                 .withMeta(DefaultTrafficTreatment.builder()
-                        .writeMetadata(2, 0xffffffffffffffffL)
+                        .writeMetadata(10, 0xffffffffffffffffL)
                         .build())
                 .permit()
                 .add();
@@ -392,7 +398,7 @@
                 .withMeta(DefaultTrafficTreatment.builder()
                         .pushVlan()
                         .setVlanId(VLAN_200)
-                        .writeMetadata(2, 0xffffffffffffffffL)
+                        .writeMetadata(10, 0xffffffffffffffffL)
                         .build())
                 .permit()
                 .add();
@@ -418,7 +424,10 @@
                 .addCondition(Criteria.matchVlanId(VLAN_100))
                 .withPriority(PRIORITY)
                 .fromApp(APP_ID)
-                .withMeta(DefaultTrafficTreatment.builder().wipeDeferred().build())
+                .withMeta(DefaultTrafficTreatment.builder()
+                        .writeMetadata(EDGE_PORT, 0xffffffffffffffffL)
+                        .wipeDeferred()
+                        .build())
                 .permit()
                 .add();
         ObjectiveTranslation actualTranslation = translator.translate(filteringObjective);
@@ -464,6 +473,9 @@
                 .addCondition(Criteria.matchEthDst(ROUTER_MAC))
                 .addCondition(Criteria.matchVlanId(VlanId.vlanId((short) DEFAULT_PW_TRANSPORT_VLAN)))
                 .withPriority(PRIORITY)
+                .withMeta(DefaultTrafficTreatment.builder()
+                        .writeMetadata(INFRA_PORT, 0xffffffffffffffffL)
+                        .build())
                 .fromApp(APP_ID)
                 .permit()
                 .add();
@@ -505,6 +517,7 @@
                 .withMeta(DefaultTrafficTreatment.builder()
                         .pushVlan()
                         .setVlanId(VlanId.vlanId((short) DEFAULT_VLAN))
+                        .writeMetadata(INFRA_PORT, 0xffffffffffffffffL)
                         .build())
                 .permit()
                 .add();
@@ -536,6 +549,74 @@
         assertEquals(expectedTranslation, actualTranslation);
     }
 
+    /**
+     * Test is pair port scenarios for filtering objective.
+     */
+    @Test
+    public void testIsPairPort() throws FabricPipelinerException {
+        // Only pair port flag
+        FilteringObjective filteringObjective = DefaultFilteringObjective.builder()
+                .withKey(Criteria.matchInPort(PORT_1))
+                .addCondition(Criteria.matchEthDst(ROUTER_MAC))
+                .addCondition(Criteria.matchVlanId(VLAN_100))
+                .withPriority(PRIORITY)
+                .withMeta(DefaultTrafficTreatment.builder()
+                        .writeMetadata(PAIR_PORT, 0xffffffffffffffffL)
+                        .build())
+                .fromApp(APP_ID)
+                .permit()
+                .add();
+        ObjectiveTranslation actualTranslation = translator.translate(filteringObjective);
+
+        Collection<FlowRule> expectedFlowRules = Lists.newArrayList();
+        expectedFlowRules.add(buildExpectedVlanInPortRule(
+                PORT_1, VLAN_100, null, VlanId.NONE,
+                PORT_TYPE_INFRA, FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN));
+        expectedFlowRules.addAll(buildExpectedFwdClassifierRule(
+                PORT_1,
+                ROUTER_MAC,
+                null,
+                Ethernet.TYPE_IPV4,
+                FWD_IPV4_ROUTING));
+        expectedFlowRules.addAll(buildExpectedFwdClassifierRule(
+                PORT_1,
+                ROUTER_MAC,
+                null,
+                Ethernet.TYPE_IPV6,
+                FWD_IPV6_ROUTING));
+        expectedFlowRules.addAll(buildExpectedFwdClassifierRule(
+                PORT_1,
+                ROUTER_MAC,
+                null,
+                Ethernet.MPLS_UNICAST,
+                FWD_MPLS));
+
+        ObjectiveTranslation expectedTranslation = buildExpectedTranslation(expectedFlowRules);
+        assertEquals(expectedTranslation, actualTranslation);
+
+        // Pair port and config update flags
+        filteringObjective = DefaultFilteringObjective.builder()
+                .withKey(Criteria.matchInPort(PORT_1))
+                .addCondition(Criteria.matchEthDst(ROUTER_MAC))
+                .addCondition(Criteria.matchVlanId(VLAN_100))
+                .withPriority(PRIORITY)
+                .fromApp(APP_ID)
+                .withMeta(DefaultTrafficTreatment.builder()
+                        .writeMetadata(6, 0xffffffffffffffffL)
+                        .build())
+                .permit()
+                .add();
+
+        actualTranslation = translator.translate(filteringObjective);
+        expectedFlowRules = Lists.newArrayList();
+        // Ingress port vlan rule
+        expectedFlowRules.add(buildExpectedVlanInPortRule(
+                PORT_1, VLAN_100, null, VlanId.NONE, PORT_TYPE_INFRA,
+                FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN));
+        expectedTranslation = buildExpectedTranslation(expectedFlowRules);
+        assertEquals(expectedTranslation, actualTranslation);
+    }
+
     /* Utilities */
 
     private void assertError(ObjectiveError error, ObjectiveTranslation actualTranslation) {
@@ -543,10 +624,11 @@
         assertEquals(expectedTranslation, actualTranslation);
     }
 
-    private FilteringObjective buildFilteringObjective(MacAddress dstMac) {
+    private FilteringObjective buildFilteringObjective(MacAddress dstMac, long portType) {
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .pushVlan()
                 .setVlanId(VLAN_100)
+                .writeMetadata(portType, 0xffffffffffffffffL)
                 .build();
         DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder()
                 .permit()
diff --git a/pipelines/fabric/impl/src/test/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/ForwardingObjectiveTranslatorTest.java b/pipelines/fabric/impl/src/test/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/ForwardingObjectiveTranslatorTest.java
index dfae0df..94517bf 100644
--- a/pipelines/fabric/impl/src/test/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/ForwardingObjectiveTranslatorTest.java
+++ b/pipelines/fabric/impl/src/test/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/ForwardingObjectiveTranslatorTest.java
@@ -53,6 +53,8 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.EDGE_PORT;
+import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.INFRA_PORT;
 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.PORT_TYPE_MASK;
@@ -246,7 +248,7 @@
                 .matchIPDst(IPV4_UNICAST_ADDR)
                 .build();
         TrafficSelector metaSelector = DefaultTrafficSelector.builder()
-                .matchMetadata(PORT_TYPE_EDGE)
+                .matchMetadata(EDGE_PORT)
                 .build();
         ForwardingObjective fwd = DefaultForwardingObjective.builder()
                 .withSelector(selector)
@@ -291,7 +293,7 @@
         assertTrue(expectedFlowRule.exactMatch(actualFlowRule));
 
         metaSelector = DefaultTrafficSelector.builder()
-                .matchMetadata(PORT_TYPE_INFRA)
+                .matchMetadata(INFRA_PORT)
                 .build();
         fwd = DefaultForwardingObjective.builder()
                 .withSelector(selector)