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