[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();
+ }
+}