[ONOS-7129] Pipeliner for fabric pipeline
Change-Id: I86b44694e1251611359e8ddc8be2533a741230cc
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipeliner.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipeliner.java
new file mode 100644
index 0000000..69ec0ab
--- /dev/null
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipeliner.java
@@ -0,0 +1,244 @@
+/*
+ * 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 com.google.common.collect.Sets;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.DeviceId;
+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.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.EthCriterion;
+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.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionParam;
+import org.onosproject.pipelines.fabric.FabricConstants;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Handling filtering objective for fabric pipeliner.
+ */
+public class FabricFilteringPipeliner {
+ private static final Logger log = getLogger(FabricFilteringPipeliner.class);
+ // Forwarding types
+ private static final byte FWD_BRIDGING = 0;
+ private static final byte FWD_MPLS = 1;
+ private static final byte FWD_IPV4_UNICAST = 2;
+ private static final byte FWD_IPV4_MULTICAST = 3;
+ private static final byte FWD_IPV6_UNICAST = 4;
+ private static final byte FWD_IPV6_MULTICAST = 5;
+ private static final PiCriterion VLAN_VALID = PiCriterion.builder()
+ .matchExact(FabricConstants.HF_VLAN_TAG_IS_VALID_ID, new byte[]{1})
+ .build();
+ private static final PiCriterion VLAN_INVALID = PiCriterion.builder()
+ .matchExact(FabricConstants.HF_VLAN_TAG_IS_VALID_ID, new byte[]{0})
+ .build();
+
+ protected DeviceId deviceId;
+
+ public FabricFilteringPipeliner(DeviceId deviceId) {
+ this.deviceId = deviceId;
+ }
+
+ /**
+ * Translates filtering objective to flows and groups.
+ *
+ * @param filterObjective the filtering objective
+ * @return translation result, contains flows, groups or error it generated
+ */
+ public PipelinerTranslationResult filter(FilteringObjective filterObjective) {
+ PipelinerTranslationResult.Builder resultBuilder = PipelinerTranslationResult.builder();
+ // maps selector and treatment from filtering objective to filtering
+ // control block.
+
+ if (filterObjective.type() == FilteringObjective.Type.DENY) {
+ log.warn("Unsupported filtering objective type {}", filterObjective.type());
+ resultBuilder.setError(ObjectiveError.UNSUPPORTED);
+ return resultBuilder.build();
+ }
+
+ if (filterObjective.key() == null ||
+ filterObjective.key().type() != Criterion.Type.IN_PORT) {
+ log.warn("Unsupported filter key {}", filterObjective.key());
+ resultBuilder.setError(ObjectiveError.BADPARAMS);
+ return resultBuilder.build();
+ }
+ PortCriterion inPortCriterion = (PortCriterion) filterObjective.key();
+ VlanIdCriterion vlanCriterion = filterObjective.conditions().stream()
+ .filter(criterion -> criterion.type() == Criterion.Type.VLAN_VID)
+ .map(criterion -> (VlanIdCriterion) criterion)
+ .findFirst()
+ .orElse(null);
+ EthCriterion ethDstCriterion = filterObjective.conditions().stream()
+ .filter(criterion -> criterion.type() == Criterion.Type.ETH_DST)
+ .map(criterion -> (EthCriterion) criterion)
+ .findFirst()
+ .orElse(null);
+
+ FlowRule inPortVlanTableRule = createInPortVlanTable(inPortCriterion, vlanCriterion,
+ filterObjective);
+ Collection<FlowRule> fwdClassifierRules = createFwdClassifierRules(inPortCriterion, ethDstCriterion,
+ filterObjective);
+
+ resultBuilder.addFlowRule(inPortVlanTableRule);
+ fwdClassifierRules.forEach(resultBuilder::addFlowRule);
+ return resultBuilder.build();
+ }
+
+ private FlowRule createInPortVlanTable(Criterion inPortCriterion,
+ VlanIdCriterion vlanCriterion,
+ FilteringObjective filterObjective) {
+ Criterion vlanIsVlalidCriterion;
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder()
+ .add(inPortCriterion);
+
+ VlanId vlanId = null;
+ if (vlanCriterion != null) {
+ vlanId = vlanCriterion.vlanId();
+ }
+
+ vlanIsVlalidCriterion = VLAN_VALID;
+ if (vlanId == null || vlanId.equals(VlanId.NONE)) {
+ // untag vlan, match in port only
+ vlanIsVlalidCriterion = VLAN_INVALID;
+ }
+
+ selector.add(vlanIsVlalidCriterion);
+
+ // TODO: check if this treatment is valid or not
+ TrafficTreatment treatment = filterObjective.meta();
+ if (treatment == null) {
+ treatment = DefaultTrafficTreatment.emptyTreatment();
+ }
+
+ return DefaultFlowRule.builder()
+ .fromApp(filterObjective.appId())
+ .withPriority(filterObjective.priority())
+ .withSelector(selector.build())
+ .withTreatment(treatment)
+ .withPriority(filterObjective.priority())
+ .forTable(FabricConstants.TBL_INGRESS_PORT_VLAN_ID)
+ .forDevice(deviceId)
+ .makePermanent()
+ .build();
+ }
+
+ private Collection<FlowRule> createFwdClassifierRules(PortCriterion inPortCriterion,
+ EthCriterion ethDstCriterion,
+ FilteringObjective filterObjective) {
+ Collection<FlowRule> flowRules = Sets.newHashSet();
+ if (ethDstCriterion == null) {
+ // Bridging table, do nothing
+ return flowRules;
+ }
+ PortNumber port = inPortCriterion.port();
+ MacAddress dstMac = ethDstCriterion.mac();
+ if (dstMac.isMulticast()) {
+ flowRules.add(createMulticastFwdClassifierRule(port, dstMac, filterObjective));
+ return flowRules;
+ }
+
+ flowRules.addAll(createIpFwdClassifierRules(port, dstMac, filterObjective));
+ flowRules.add(createMplsFwdClassifierRule(port, dstMac, filterObjective));
+ return flowRules;
+ }
+
+ private FlowRule createMulticastFwdClassifierRule(PortNumber inPort, MacAddress dstMac,
+ FilteringObjective filterObjective) {
+ TrafficTreatment treatment;
+ short ethType;
+ if (dstMac.equals(MacAddress.IPV4_MULTICAST)) {
+ // Ipv4 multicast
+ treatment = createFwdClassifierTreatment(FWD_IPV4_MULTICAST);
+ ethType = Ethernet.TYPE_IPV4;
+ } else {
+ // IPv6 multicast
+ treatment = createFwdClassifierTreatment(FWD_IPV6_MULTICAST);
+ ethType = Ethernet.TYPE_IPV6;
+ }
+ return createFwdClassifierRule(inPort, ethType, dstMac, treatment, filterObjective);
+ }
+
+ private Collection<FlowRule> createIpFwdClassifierRules(PortNumber inPort,
+ MacAddress dstMac,
+ FilteringObjective filterObjective) {
+ Collection<FlowRule> flowRules = Sets.newHashSet();
+ TrafficTreatment treatment;
+ treatment = createFwdClassifierTreatment(FWD_IPV4_UNICAST);
+ flowRules.add(createFwdClassifierRule(inPort, Ethernet.TYPE_IPV4, dstMac, treatment, filterObjective));
+ treatment = createFwdClassifierTreatment(FWD_IPV6_UNICAST);
+ flowRules.add(createFwdClassifierRule(inPort, Ethernet.TYPE_IPV6, dstMac, treatment, filterObjective));
+ return flowRules;
+ }
+
+ private FlowRule createMplsFwdClassifierRule(PortNumber inPort,
+ MacAddress dstMac,
+ FilteringObjective filterObjective) {
+ TrafficTreatment treatment = createFwdClassifierTreatment(FWD_MPLS);
+ return createFwdClassifierRule(inPort, Ethernet.MPLS_UNICAST, dstMac, treatment, filterObjective);
+ }
+
+ private FlowRule createFwdClassifierRule(PortNumber inPort,
+ short ethType,
+ MacAddress dstMac,
+ TrafficTreatment treatment,
+ FilteringObjective filterObjective) {
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder()
+ .matchInPort(inPort)
+ .matchEthDst(dstMac)
+ .matchEthType(ethType);
+
+ return DefaultFlowRule.builder()
+ .withSelector(selector.build())
+ .withTreatment(treatment)
+ .fromApp(filterObjective.appId())
+ .withPriority(filterObjective.priority())
+ .forDevice(deviceId)
+ .makePermanent()
+ .forTable(FabricConstants.TBL_FWD_CLASSIFIER_ID)
+ .build();
+ }
+
+ private TrafficTreatment createFwdClassifierTreatment(byte fwdType) {
+ PiActionParam param = new PiActionParam(FabricConstants.ACT_PRM_FWD_TYPE_ID,
+ ImmutableByteSequence.copyFrom(fwdType));
+ PiAction action = PiAction.builder()
+ .withId(FabricConstants.ACT_SET_FORWARDING_TYPE_ID)
+ .withParameter(param)
+ .build();
+ return DefaultTrafficTreatment.builder()
+ .piTableAction(action)
+ .build();
+
+ }
+}