| /* |
| * 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_FABRICINGRESS_FILTERING_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(); |
| } |
| } |