[ONOS-7129] Pipeliner for fabric pipeline

Change-Id: I86b44694e1251611359e8ddc8be2533a741230cc
diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipelinerTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipelinerTest.java
new file mode 100644
index 0000000..f3de7b7
--- /dev/null
+++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipelinerTest.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2017-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.pipeliner;
+
+import org.junit.Test;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TableId;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flowobjective.DefaultFilteringObjective;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionParam;
+import org.onosproject.pipelines.fabric.FabricConstants;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test cases for fabric.p4 pipeline filtering control block.
+ */
+public class FabricFilteringPipelinerTest extends FabricPipelinerTest {
+
+    /**
+     * Creates one rule for ingress_port_vlan table and 3 rules for
+     * fwd_classifier table (IPv4, IPv6 and MPLS unicast) when
+     * the condition is VLAN + MAC.
+     */
+    @Test
+    public void testRouterMacAndVlanFilter() {
+        FilteringObjective filteringObjective = buildFilteringObjective(ROUTER_MAC);
+        PipelinerTranslationResult result = pipeliner.pipelinerFilter.filter(filteringObjective);
+
+        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
+        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
+
+        assertTrue(groupsInstalled.isEmpty());
+
+        // in port vlan flow rule
+        FlowRule actualFlowRule = flowRulesInstalled.get(0);
+        FlowRule flowRuleExpected = buildExpectedVlanInPortRule(PORT_1,
+                                                                VlanId.NONE,
+                                                                VLAN_100,
+                                                                FabricConstants.TBL_INGRESS_PORT_VLAN_ID);
+        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
+
+        // forwarding classifier ipv4
+        actualFlowRule = flowRulesInstalled.get(1);
+        flowRuleExpected = buildExpectedFwdClassifierRule(PORT_1,
+                                                          ROUTER_MAC,
+                                                          Ethernet.TYPE_IPV4,
+                                                          FWD_IPV4_UNICAST);
+        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
+
+        // forwarding classifier ipv6
+        actualFlowRule = flowRulesInstalled.get(2);
+        flowRuleExpected = buildExpectedFwdClassifierRule(PORT_1,
+                                                          ROUTER_MAC,
+                                                          Ethernet.TYPE_IPV6,
+                                                          FWD_IPV6_UNICAST);
+        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
+
+        // forwarding classifier mpls
+        actualFlowRule = flowRulesInstalled.get(3);
+        flowRuleExpected = buildExpectedFwdClassifierRule(PORT_1,
+                                                          ROUTER_MAC,
+                                                          Ethernet.MPLS_UNICAST,
+                                                          FWD_MPLS);
+        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
+    }
+
+    /**
+     * Creates one rule for ingress_port_vlan table and one rule for
+     * fwd_classifier table (IPv4 multicast) when the condition is ipv4
+     * multicast mac address.
+     */
+    @Test
+    public void testIpv4MulticastFwdClass() {
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .pushVlan()
+                .setVlanId(VLAN_100)
+                .build();
+        FilteringObjective filteringObjective = DefaultFilteringObjective.builder()
+                .permit()
+                .withPriority(PRIORITY)
+                .withKey(Criteria.matchInPort(PORT_1))
+                .addCondition(Criteria.matchEthDst(MacAddress.IPV4_MULTICAST))
+                .addCondition(Criteria.matchVlanId(VlanId.NONE))
+                .withMeta(treatment)
+                .fromApp(APP_ID)
+                .makePermanent()
+                .add();
+        PipelinerTranslationResult result = pipeliner.pipelinerFilter.filter(filteringObjective);
+        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
+        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
+
+        assertTrue(groupsInstalled.isEmpty());
+
+        // in port vlan flow rule
+        FlowRule actualFlowRule = flowRulesInstalled.get(0);
+        FlowRule flowRuleExpected = buildExpectedVlanInPortRule(PORT_1,
+                                                                VlanId.NONE,
+                                                                VLAN_100,
+                                                                FabricConstants.TBL_INGRESS_PORT_VLAN_ID);
+        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
+
+        // forwarding classifier
+        actualFlowRule = flowRulesInstalled.get(1);
+        flowRuleExpected = buildExpectedFwdClassifierRule(PORT_1,
+                                                          MacAddress.IPV4_MULTICAST,
+                                                          Ethernet.TYPE_IPV4,
+                                                          FWD_IPV4_MULTICAST);
+        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
+    }
+
+    /**
+     * Creates one rule for ingress_port_vlan table and one rule for
+     * fwd_classifier table (IPv6 multicast) when the condition is ipv6
+     * multicast mac address.
+     */
+    @Test
+    public void testIpv6MulticastFwdClass() {
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .pushVlan()
+                .setVlanId(VLAN_100)
+                .build();
+        FilteringObjective filteringObjective = DefaultFilteringObjective.builder()
+                .permit()
+                .withPriority(PRIORITY)
+                .withKey(Criteria.matchInPort(PORT_1))
+                .addCondition(Criteria.matchEthDst(MacAddress.IPV6_MULTICAST))
+                .addCondition(Criteria.matchVlanId(VlanId.NONE))
+                .withMeta(treatment)
+                .fromApp(APP_ID)
+                .makePermanent()
+                .add();
+        PipelinerTranslationResult result = pipeliner.pipelinerFilter.filter(filteringObjective);
+        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
+        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
+
+        assertTrue(groupsInstalled.isEmpty());
+
+        // in port vlan flow rule
+        FlowRule actualFlowRule = flowRulesInstalled.get(0);
+        FlowRule flowRuleExpected = buildExpectedVlanInPortRule(PORT_1,
+                                                                VlanId.NONE,
+                                                                VLAN_100,
+                                                                FabricConstants.TBL_INGRESS_PORT_VLAN_ID);
+        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
+
+        // forwarding classifier
+        actualFlowRule = flowRulesInstalled.get(1);
+        flowRuleExpected = buildExpectedFwdClassifierRule(PORT_1,
+                                                          MacAddress.IPV6_MULTICAST,
+                                                          Ethernet.TYPE_IPV6,
+                                                          FWD_IPV6_MULTICAST);
+        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
+    }
+
+    /**
+     * Creates only one rule for ingress_port_vlan table if there is no condition
+     * of destination mac address.
+     * The packet will be handled by bridging table by default.
+     */
+    @Test
+    public void testFwdBridging() {
+        FilteringObjective filteringObjective = buildFilteringObjective(null);
+        PipelinerTranslationResult result = pipeliner.pipelinerFilter.filter(filteringObjective);
+        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
+        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
+
+        assertTrue(groupsInstalled.isEmpty());
+
+        // in port vlan flow rule
+        FlowRule actualFlowRule = flowRulesInstalled.get(0);
+        FlowRule flowRuleExpected = buildExpectedVlanInPortRule(PORT_1,
+                                                                VlanId.NONE,
+                                                                VLAN_100,
+                                                                FabricConstants.TBL_INGRESS_PORT_VLAN_ID);
+        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
+
+        // No rules in forwarding classifier, will do default action: set fwd type to bridging
+    }
+
+    /**
+     * We supports only PERMIT type of filtering objective.
+     */
+    @Test
+    public void testUnsupportedObjective() {
+        FilteringObjective filteringObjective = DefaultFilteringObjective.builder()
+                .deny()
+                .withKey(Criteria.matchInPort(PORT_1))
+                .addCondition(Criteria.matchVlanId(VLAN_100))
+                .fromApp(APP_ID)
+                .makePermanent()
+                .add();
+
+        PipelinerTranslationResult result = pipeliner.pipelinerFilter.filter(filteringObjective);
+        pipeliner.pipelinerFilter.filter(filteringObjective);
+
+        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
+        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
+
+        assertTrue(flowRulesInstalled.isEmpty());
+        assertTrue(groupsInstalled.isEmpty());
+
+        assertTrue(result.error().isPresent());
+        ObjectiveError error = result.error().get();
+        assertEquals(ObjectiveError.UNSUPPORTED, error);
+    }
+
+    /**
+     * Incorrect filtering key or filtering conditions test.
+     */
+    @Test
+    public void badParamTest() {
+        // Filtering objective should contains filtering key
+        FilteringObjective filteringObjective = DefaultFilteringObjective.builder()
+                .permit()
+                .addCondition(Criteria.matchVlanId(VLAN_100))
+                .fromApp(APP_ID)
+                .makePermanent()
+                .add();
+
+        PipelinerTranslationResult result = pipeliner.pipelinerFilter.filter(filteringObjective);
+        pipeliner.pipelinerFilter.filter(filteringObjective);
+
+        assertTrue(result.error().isPresent());
+        ObjectiveError error = result.error().get();
+        assertEquals(ObjectiveError.BADPARAMS, error);
+
+        // Filtering objective should use in_port as key
+        filteringObjective = DefaultFilteringObjective.builder()
+                .permit()
+                .withKey(Criteria.matchEthDst(ROUTER_MAC))
+                .addCondition(Criteria.matchVlanId(VLAN_100))
+                .withMeta(DefaultTrafficTreatment.emptyTreatment())
+                .fromApp(APP_ID)
+                .makePermanent()
+                .add();
+
+        result = pipeliner.pipelinerFilter.filter(filteringObjective);
+        pipeliner.pipelinerFilter.filter(filteringObjective);
+
+        assertTrue(result.error().isPresent());
+        error = result.error().get();
+        assertEquals(ObjectiveError.BADPARAMS, error);
+    }
+
+    /* Utilities */
+
+    private FilteringObjective buildFilteringObjective(MacAddress dstMac) {
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .pushVlan()
+                .setVlanId(VLAN_100)
+                .build();
+        DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder()
+                .permit()
+                .withPriority(PRIORITY)
+                .withKey(Criteria.matchInPort(PORT_1));
+        if (dstMac != null) {
+            builder.addCondition(Criteria.matchEthDst(dstMac));
+        }
+
+        builder.addCondition(Criteria.matchVlanId(VlanId.NONE))
+                .withMeta(treatment)
+                .fromApp(APP_ID)
+                .makePermanent();
+        return builder.add();
+    }
+
+    private FlowRule buildExpectedVlanInPortRule(PortNumber inPort, VlanId vlanId,
+                                                 VlanId internalVlan,
+                                                 TableId tableId) {
+
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder()
+                .matchInPort(inPort);
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+        if (vlanId == null || vlanId.equals(VlanId.NONE)) {
+            selector.matchPi(VLAN_INVALID);
+            treatment.pushVlan();
+            treatment.setVlanId(internalVlan);
+        } else {
+            selector.matchPi(VLAN_VALID);
+            selector.matchVlanId(vlanId);
+        }
+
+        return DefaultFlowRule.builder()
+                .withPriority(PRIORITY)
+                .withSelector(selector.build())
+                .withTreatment(treatment.build())
+                .fromApp(APP_ID)
+                .forDevice(DEVICE_ID)
+                .makePermanent()
+                .forTable(tableId)
+                .build();
+    }
+
+    private FlowRule buildExpectedFwdClassifierRule(PortNumber inPort,
+                                                    MacAddress dstMac,
+                                                    short ethType,
+                                                    byte fwdClass) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthDst(dstMac)
+                .matchInPort(inPort)
+                .matchEthType(ethType)
+                .build();
+        PiActionParam classParam = new PiActionParam(FabricConstants.ACT_PRM_FWD_TYPE_ID,
+                                                     ImmutableByteSequence.copyFrom(fwdClass));
+        PiAction fwdClassifierAction = PiAction.builder()
+                .withId(FabricConstants.ACT_SET_FORWARDING_TYPE_ID)
+                .withParameter(classParam)
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .piTableAction(fwdClassifierAction)
+                .build();
+
+        return DefaultFlowRule.builder()
+                .withPriority(PRIORITY)
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .fromApp(APP_ID)
+                .forDevice(DEVICE_ID)
+                .makePermanent()
+                .forTable(FabricConstants.TBL_FWD_CLASSIFIER_ID)
+                .build();
+    }
+}
diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricForwardingPipelineTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricForwardingPipelineTest.java
new file mode 100644
index 0000000..d580827
--- /dev/null
+++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricForwardingPipelineTest.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2017-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.pipeliner;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.TpPort;
+import org.onlab.packet.UDP;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.group.GroupDescription;
+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.FabricConstants;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test cases for fabric.p4 pipeline forwarding control block.
+ */
+public class FabricForwardingPipelineTest extends FabricPipelinerTest {
+
+    /**
+     * Test versatile flag of forwarding objective with ARP match.
+     */
+    @Test
+    public void testAclArp() {
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .wipeDeferred()
+                .punt()
+                .build();
+        // ARP
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_ARP)
+                .build();
+        ForwardingObjective fwd = DefaultForwardingObjective.builder()
+                .withSelector(selector)
+                .withPriority(PRIORITY)
+                .fromApp(APP_ID)
+                .makePermanent()
+                .withFlag(ForwardingObjective.Flag.VERSATILE)
+                .withTreatment(treatment)
+                .add();
+
+        PipelinerTranslationResult result = pipeliner.pipelinerForward.forward(fwd);
+
+        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
+        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
+        assertEquals(1, flowRulesInstalled.size());
+        assertTrue(groupsInstalled.isEmpty());
+
+        FlowRule actualFlowRule = flowRulesInstalled.get(0);
+        FlowRule expectedFlowRule = DefaultFlowRule.builder()
+                .forDevice(DEVICE_ID)
+                .forTable(FabricConstants.TBL_ACL_ID)
+                .withPriority(PRIORITY)
+                .makePermanent()
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .fromApp(APP_ID)
+                .build();
+
+        assertTrue(expectedFlowRule.exactMatch(actualFlowRule));
+    }
+
+    /**
+     * Test versatile flag of forwarding objective with DHCP match.
+     */
+    @Test
+    public void testAclDhcp() {
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .wipeDeferred()
+                .punt()
+                .build();
+        // DHCP
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPProtocol(IPv4.PROTOCOL_UDP)
+                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
+                .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
+                .build();
+        ForwardingObjective fwd = DefaultForwardingObjective.builder()
+                .withSelector(selector)
+                .withPriority(PRIORITY)
+                .fromApp(APP_ID)
+                .makePermanent()
+                .withFlag(ForwardingObjective.Flag.VERSATILE)
+                .withTreatment(treatment)
+                .add();
+
+        PipelinerTranslationResult result = pipeliner.pipelinerForward.forward(fwd);
+
+        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
+        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
+        assertEquals(1, flowRulesInstalled.size());
+        assertTrue(groupsInstalled.isEmpty());
+
+        FlowRule actualFlowRule = flowRulesInstalled.get(0);
+        FlowRule expectedFlowRule = DefaultFlowRule.builder()
+                .forDevice(DEVICE_ID)
+                .forTable(FabricConstants.TBL_ACL_ID)
+                .withPriority(PRIORITY)
+                .makePermanent()
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .fromApp(APP_ID)
+                .build();
+
+        assertTrue(expectedFlowRule.exactMatch(actualFlowRule));
+    }
+
+    /**
+     * Test programming L2 unicast rule to bridging table.
+     */
+    @Test
+    public void testL2Unicast() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchVlanId(VLAN_100)
+                .matchEthDst(HOST_MAC)
+                .build();
+        testSpecificForward(FabricConstants.TBL_BRIDGING_ID, selector, selector, NEXT_ID_1);
+    }
+
+    @Test
+    public void testL2Broadcast() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchVlanId(VLAN_100)
+                .build();
+        testSpecificForward(FabricConstants.TBL_BRIDGING_ID, selector, selector, NEXT_ID_1);
+    }
+
+    @Test
+    @Ignore
+    public void testIPv4Unicast() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(IPV4_UNICAST_ADDR)
+                .build();
+        TrafficSelector expectedSelector = DefaultTrafficSelector.builder()
+                .matchIPDst(IPV4_UNICAST_ADDR)
+                .build();
+        testSpecificForward(FabricConstants.TBL_UNICAST_V4_ID, expectedSelector, selector, NEXT_ID_1);
+    }
+
+    @Test
+    @Ignore
+    public void testIPv4Multicast() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchVlanId(VLAN_100)
+                .matchIPDst(IPV4_MCAST_ADDR)
+                .build();
+        TrafficSelector expectedSelector = DefaultTrafficSelector.builder()
+                .matchIPDst(IPV4_MCAST_ADDR)
+                .build();
+        testSpecificForward(FabricConstants.TBL_MULTICAST_V4_ID, expectedSelector, selector, NEXT_ID_1);
+    }
+
+    @Test
+    @Ignore
+    public void testIPv6Unicast() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV6)
+                .matchIPDst(IPV6_UNICAST_ADDR)
+                .build();
+        TrafficSelector expectedSelector = DefaultTrafficSelector.builder()
+                .matchIPDst(IPV6_UNICAST_ADDR)
+                .build();
+        testSpecificForward(FabricConstants.TBL_UNICAST_V6_ID, expectedSelector, selector, NEXT_ID_1);
+    }
+
+    @Test
+    @Ignore
+    public void testIPv6Multicast() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV6)
+                .matchVlanId(VLAN_100)
+                .matchIPDst(IPV6_MCAST_ADDR)
+                .build();
+        TrafficSelector expectedSelector = DefaultTrafficSelector.builder()
+                .matchIPDst(IPV6_MCAST_ADDR)
+                .build();
+        testSpecificForward(FabricConstants.TBL_MULTICAST_V6_ID, expectedSelector, selector, NEXT_ID_1);
+    }
+
+    @Test
+    @Ignore
+    public void testMpls() {
+
+    }
+
+    private void testSpecificForward(PiTableId expectedTableId, TrafficSelector expectedSelector,
+                                     TrafficSelector selector, Integer nextId) {
+        ForwardingObjective fwd = DefaultForwardingObjective.builder()
+                .withSelector(selector)
+                .withPriority(PRIORITY)
+                .fromApp(APP_ID)
+                .makePermanent()
+                .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                .nextStep(nextId)
+                .add();
+
+        PipelinerTranslationResult result = pipeliner.pipelinerForward.forward(fwd);
+
+        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
+        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
+        assertEquals(1, flowRulesInstalled.size());
+        assertTrue(groupsInstalled.isEmpty());
+
+        FlowRule actualFlowRule = flowRulesInstalled.get(0);
+        PiActionParam nextIdParam = new PiActionParam(FabricConstants.ACT_PRM_NEXT_ID_ID,
+                                                      ImmutableByteSequence.copyFrom(nextId.byteValue()));
+        PiAction setNextIdAction = PiAction.builder()
+                .withId(FabricConstants.ACT_SET_NEXT_ID_ID)
+                .withParameter(nextIdParam)
+                .build();
+        TrafficTreatment setNextIdTreatment = DefaultTrafficTreatment.builder()
+                .piTableAction(setNextIdAction)
+                .build();
+
+        FlowRule expectedFlowRule = DefaultFlowRule.builder()
+                .forDevice(DEVICE_ID)
+                .forTable(expectedTableId)
+                .withPriority(PRIORITY)
+                .makePermanent()
+                .withSelector(expectedSelector)
+                .withTreatment(setNextIdTreatment)
+                .fromApp(APP_ID)
+                .build();
+
+        assertTrue(expectedFlowRule.exactMatch(actualFlowRule));
+    }
+}
diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipelinerTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipelinerTest.java
new file mode 100644
index 0000000..107752c
--- /dev/null
+++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipelinerTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2017-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.pipeliner;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.PiCriterion;
+import org.onosproject.net.flowobjective.DefaultNextObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionParam;
+import org.onosproject.pipelines.fabric.FabricConstants;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test cases for fabric.p4 pipeline next control block.
+ */
+public class FabricNextPipelinerTest extends FabricPipelinerTest {
+
+    /**
+     * Test program output rule for Simple table.
+     */
+    @Test
+    public void testSimpleOutput() {
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(PORT_1)
+                .build();
+        testSimple(treatment);
+    }
+
+    /**
+     * Test program set vlan and output rule for Simple table.
+     */
+    @Test
+    public void testSimpleOutputWithVlanTranslation() {
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setVlanId(VLAN_100)
+                .setOutput(PORT_1)
+                .build();
+        testSimple(treatment);
+    }
+
+    private void testSimple(TrafficTreatment treatment) {
+        NextObjective nextObjective = DefaultNextObjective.builder()
+                .withId(NEXT_ID_1)
+                .withPriority(PRIORITY)
+                .addTreatment(treatment)
+                .withType(NextObjective.Type.SIMPLE)
+                .makePermanent()
+                .fromApp(APP_ID)
+                .add();
+
+        PipelinerTranslationResult result = pipeliner.pipelinerNext.next(nextObjective);
+
+        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
+        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
+        assertEquals(2, flowRulesInstalled.size());
+        assertTrue(groupsInstalled.isEmpty());
+
+        FlowRule actualFlowRule;
+        FlowRule expectedFlowRule;
+
+        // Next id mapping table
+        actualFlowRule = flowRulesInstalled.get(0);
+        byte[] nextIdVal = new byte[]{NEXT_ID_1.byteValue()};
+        PiCriterion nextIdCriterion = PiCriterion.builder()
+                .matchExact(FabricConstants.HF_FABRIC_METADATA_NEXT_ID_ID, nextIdVal)
+                .build();
+        TrafficSelector nextIdSelector = DefaultTrafficSelector.builder()
+                .matchPi(nextIdCriterion)
+                .build();
+        PiActionParam setNextToSimpleParam = new PiActionParam(FabricConstants.ACT_PRM_NEXT_TYPE_ID,
+                                                               ImmutableByteSequence.copyFrom(NEXT_TYPE_SIMPLE));
+        PiAction setNextToSimpleAction = PiAction.builder()
+                .withId(FabricConstants.ACT_SET_NEXT_TYPE_ID)
+                .withParameter(setNextToSimpleParam)
+                .build();
+        TrafficTreatment setNextTypeTreatment = DefaultTrafficTreatment.builder()
+                .piTableAction(setNextToSimpleAction)
+                .build();
+        expectedFlowRule = DefaultFlowRule.builder()
+                .forDevice(DEVICE_ID)
+                .fromApp(APP_ID)
+                .makePermanent()
+                // FIXME: currently next objective doesn't support priority, set priority to zero
+                .withPriority(0)
+                .forTable(FabricConstants.TBL_NEXT_ID_MAPPING_ID)
+                .withSelector(nextIdSelector)
+                .withTreatment(setNextTypeTreatment)
+                .build();
+        assertTrue(expectedFlowRule.exactMatch(actualFlowRule));
+
+        // Simple table
+        actualFlowRule = flowRulesInstalled.get(1);
+        expectedFlowRule = DefaultFlowRule.builder()
+                .forDevice(DEVICE_ID)
+                .fromApp(APP_ID)
+                .makePermanent()
+                // FIXME: currently next objective doesn't support priority, ignore this
+                .withPriority(0)
+                .forTable(FabricConstants.TBL_SIMPLE_ID)
+                .withSelector(nextIdSelector)
+                .withTreatment(treatment)
+                .build();
+        assertTrue(expectedFlowRule.exactMatch(actualFlowRule));
+    }
+
+    /**
+     * Test program ecmp output group for Hashed table.
+     */
+    @Test
+    @Ignore
+    public void testHashedOutput() {
+
+    }
+
+    /**
+     * Test program output group for Broadcast table.
+     */
+    @Test
+    @Ignore
+    public void testBroadcastOutput() {
+
+    }
+}
diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipelinerTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipelinerTest.java
new file mode 100644
index 0000000..30610ed
--- /dev/null
+++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipelinerTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2017-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.pipeliner;
+
+import org.junit.Before;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
+import org.onosproject.TestApplicationId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.PipelinerContext;
+import org.onosproject.net.flow.criteria.PiCriterion;
+import org.onosproject.pipelines.fabric.FabricConstants;
+
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+
+public abstract class FabricPipelinerTest {
+    static final ApplicationId APP_ID = TestApplicationId.create("FabricPipelinerTest");
+    static final DeviceId DEVICE_ID = DeviceId.deviceId("device:bmv2:11");
+    static final int PRIORITY = 100;
+    static final PortNumber PORT_1 = PortNumber.portNumber(1);
+    static final VlanId VLAN_100 = VlanId.vlanId("100");
+    static final MacAddress HOST_MAC = MacAddress.valueOf("00:00:00:00:00:01");
+    static final MacAddress ROUTER_MAC = MacAddress.valueOf("00:00:00:00:02:01");
+    static final IpPrefix IPV4_UNICAST_ADDR = IpPrefix.valueOf("10.0.0.1/32");
+    static final IpPrefix IPV4_MCAST_ADDR = IpPrefix.valueOf("224.0.0.1/32");
+    static final IpPrefix IPV6_UNICAST_ADDR = IpPrefix.valueOf("2000::1/32");
+    static final IpPrefix IPV6_MCAST_ADDR = IpPrefix.valueOf("ff00::1/32");
+    static final MplsLabel MPLS_10 = MplsLabel.mplsLabel(10);
+    static final Integer NEXT_ID_1 = 1;
+
+    // Forwarding types
+    static final byte FWD_BRIDGING = 0;
+    static final byte FWD_MPLS = 1;
+    static final byte FWD_IPV4_UNICAST = 2;
+    static final byte FWD_IPV4_MULTICAST = 3;
+    static final byte FWD_IPV6_UNICAST = 4;
+    static final byte FWD_IPV6_MULTICAST = 5;
+
+    // Next types
+    static final byte NEXT_TYPE_SIMPLE = 0;
+    static final byte NEXT_TYPE_HASHED = 1;
+    static final byte NEXT_TYPE_BROADCAST = 2;
+    static final byte NEXT_TYPE_PUNT = 3;
+
+    static final PiCriterion VLAN_VALID = PiCriterion.builder()
+            .matchExact(FabricConstants.HF_VLAN_TAG_IS_VALID_ID, new byte[]{1})
+            .build();
+    static final PiCriterion VLAN_INVALID = PiCriterion.builder()
+            .matchExact(FabricConstants.HF_VLAN_TAG_IS_VALID_ID, new byte[]{0})
+            .build();
+
+    FabricPipeliner pipeliner;
+
+    @Before
+    public void setup() {
+        pipeliner = new FabricPipeliner();
+
+        ServiceDirectory serviceDirectory = createNiceMock(ServiceDirectory.class);
+        PipelinerContext pipelinerContext = createNiceMock(PipelinerContext.class);
+        expect(pipelinerContext.directory()).andReturn(serviceDirectory).anyTimes();
+        replay(serviceDirectory, pipelinerContext);
+
+        pipeliner.init(DEVICE_ID, pipelinerContext);
+    }
+}
diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingFunctionTypeTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingFunctionTypeTest.java
new file mode 100644
index 0000000..9c68cd6
--- /dev/null
+++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingFunctionTypeTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2017-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.pipeliner;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
+import org.onosproject.TestApplicationId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Unit tests for Forwarding class.
+ */
+public class ForwardingFunctionTypeTest {
+    private static final ApplicationId APP_ID = TestApplicationId.create("ForwardingFunctionTypeTest");
+    private static final VlanId VLAN_100 = VlanId.vlanId((short) 100);
+    private static final MacAddress MAC_ADDR = MacAddress.valueOf("00:00:00:00:00:01");
+    private static final IpPrefix IPV4_UNICAST_ADDR = IpPrefix.valueOf("10.0.0.1/32");
+    private static final IpPrefix IPV4_MCAST_ADDR = IpPrefix.valueOf("224.0.0.1/32");
+    private static final IpPrefix IPV6_UNICAST_ADDR = IpPrefix.valueOf("2000::1/32");
+    private static final IpPrefix IPV6_MCAST_ADDR = IpPrefix.valueOf("ff00::1/32");
+    private static final MplsLabel MPLS_10 = MplsLabel.mplsLabel(10);
+    private TrafficSelector selector;
+
+    /**
+     * Match Vlan + EthDst.
+     */
+    @Test
+    public void testL2Unicast() {
+        selector = DefaultTrafficSelector.builder()
+                .matchVlanId(VLAN_100)
+                .matchEthDst(MAC_ADDR)
+                .build();
+        testFft(selector, ForwardingFunctionType.L2_UNICAST);
+    }
+
+    @Test
+    public void testL2Broadcast() {
+        selector = DefaultTrafficSelector.builder()
+                .matchVlanId(VLAN_100)
+                .build();
+        testFft(selector, ForwardingFunctionType.L2_BROADCAST);
+    }
+
+    @Test
+    @Ignore
+    public void testIpv4Unicast() {
+        selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(IPV4_UNICAST_ADDR)
+                .build();
+        testFft(selector, ForwardingFunctionType.IPV4_UNICAST);
+    }
+
+    @Test
+    @Ignore
+    public void testIpv4Multicast() {
+        selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(IPV4_MCAST_ADDR)
+                .build();
+        testFft(selector, ForwardingFunctionType.IPV4_MULTICAST);
+    }
+
+    @Test
+    @Ignore
+    public void testIpv6Unicast() {
+        selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV6)
+                .matchIPDst(IPV6_UNICAST_ADDR)
+                .build();
+        testFft(selector, ForwardingFunctionType.IPV6_UNICAST);
+    }
+
+    @Test
+    @Ignore
+    public void testIpv6Multicast() {
+        selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV6)
+                .matchIPDst(IPV6_MCAST_ADDR)
+                .build();
+        testFft(selector, ForwardingFunctionType.IPV4_MULTICAST);
+    }
+
+    @Test
+    @Ignore
+    public void testMplsUnicast() {
+        selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.MPLS_UNICAST)
+                .matchMplsLabel(MPLS_10)
+                .matchMplsBos(true)
+                .build();
+        testFft(selector, ForwardingFunctionType.MPLS);
+    }
+
+    private void testFft(TrafficSelector selector, ForwardingFunctionType expectedFft) {
+        ForwardingObjective fwd = DefaultForwardingObjective.builder()
+                .withSelector(selector)
+                .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                .nextStep(0)
+                .fromApp(APP_ID)
+                .add();
+        assertEquals(expectedFft,
+                     ForwardingFunctionType.getForwardingFunctionType(fwd));
+    }
+}