[VOL-4577] : Update ONOS olt-app for adding FTTB DPU Management, ANCP traffic & trap rules

Change-Id: Ibb8aad6e68e8bd3b5f5824f0b04f4c5bc2f84a9e
diff --git a/impl/src/main/java/org/opencord/olt/impl/OltFlowServiceUtils.java b/impl/src/main/java/org/opencord/olt/impl/OltFlowServiceUtils.java
new file mode 100644
index 0000000..7099b1b
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/impl/OltFlowServiceUtils.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2021-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.opencord.olt.impl;
+
+import org.onlab.packet.EthType;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.EthTypeCriterion;
+import org.onosproject.net.flow.criteria.IPProtocolCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.criteria.UdpPortCriterion;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.meter.MeterId;
+import org.opencord.sadis.UniTagInformation;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.onosproject.net.flow.instructions.Instruction.Type.L2MODIFICATION;
+import static org.opencord.olt.impl.OltFlowService.EAPOL_DEFAULT_VLAN;
+import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID_DEFAULT;
+
+/**
+ * Utility class for Flow service utility methods.
+ */
+public final class OltFlowServiceUtils {
+
+    public static final int NONE_TP_ID = -1;
+
+    private OltFlowServiceUtils() {
+    }
+
+    /**
+     * Constructs and returns the metadata from cVlan, techProfileId and upstreamOltMeterId.
+     *
+     * @param cVlan                 the customer vlan
+     * @param techProfileId         the technology profile
+     * @param upstreamOltMeterId    the upstream olt meter id
+     * @return Metadata
+     */
+    public static Long createTechProfValueForWriteMetadata(VlanId cVlan, int techProfileId,
+                                                           MeterId upstreamOltMeterId) {
+        Long writeMetadata;
+
+        if (cVlan == null || VlanId.NONE.equals(cVlan)) {
+            writeMetadata = (long) techProfileId << 32;
+        } else {
+            writeMetadata = ((long) (cVlan.id()) << 48 | (long) techProfileId << 32);
+        }
+        if (upstreamOltMeterId == null) {
+            return writeMetadata;
+        } else {
+            return writeMetadata | upstreamOltMeterId.id();
+        }
+    }
+
+    /**
+     * Converts FlowRuleEvent.Type to OltFlowService.OltFlowsStatus.
+     *
+     * @param type FlowRuleEvent type
+     * @return OltFlowService.OltFlowsStatus
+     */
+    public static OltFlowService.OltFlowsStatus flowRuleStatusToOltFlowStatus(FlowRuleEvent.Type type) {
+        switch (type) {
+            case RULE_ADD_REQUESTED:
+                return OltFlowService.OltFlowsStatus.PENDING_ADD;
+            case RULE_ADDED:
+                return OltFlowService.OltFlowsStatus.ADDED;
+            case RULE_REMOVE_REQUESTED:
+                return OltFlowService.OltFlowsStatus.PENDING_REMOVE;
+            case RULE_REMOVED:
+                return OltFlowService.OltFlowsStatus.REMOVED;
+            default:
+                return OltFlowService.OltFlowsStatus.NONE;
+        }
+    }
+
+    /**
+     * Checks if the configured Mac address is valid for a UniTagInformation.
+     *
+     * @param tagInformation UniTagInformation
+     * @return true if the mac address is valid
+     */
+    public static boolean isMacAddressValid(UniTagInformation tagInformation) {
+        return tagInformation.getConfiguredMacAddress() != null &&
+                !tagInformation.getConfiguredMacAddress().trim().equals("") &&
+                !MacAddress.NONE.equals(MacAddress.valueOf(tagInformation.getConfiguredMacAddress()));
+    }
+
+    /**
+     * Returns true if the flow is a DHCP flow.
+     * Matches both upstream and downstream flows.
+     *
+     * @param flowRule The FlowRule to evaluate
+     * @return boolean
+     */
+    public static boolean isDhcpFlow(FlowRule flowRule) {
+        IPProtocolCriterion ipCriterion = (IPProtocolCriterion) flowRule.selector()
+                .getCriterion(Criterion.Type.IP_PROTO);
+        if (ipCriterion == null) {
+            return false;
+        }
+
+        UdpPortCriterion src = (UdpPortCriterion) flowRule.selector().getCriterion(Criterion.Type.UDP_SRC);
+
+        if (src == null) {
+            return false;
+        }
+        return ipCriterion.protocol() == IPv4.PROTOCOL_UDP &&
+                (src.udpPort().toInt() == 68 || src.udpPort().toInt() == 67);
+    }
+
+    /**
+     * Returns true if the flow is a Pppoe flow.
+     *
+     * @param flowRule The FlowRule to evaluate
+     * @return boolean
+     */
+    public static boolean isPppoeFlow(FlowRule flowRule) {
+        EthTypeCriterion ethTypeCriterion = (EthTypeCriterion) flowRule.selector()
+                .getCriterion(Criterion.Type.ETH_TYPE);
+
+        if (ethTypeCriterion == null) {
+            return false;
+        }
+        return EthType.EtherType.PPPoED.ethType().equals(ethTypeCriterion.ethType());
+    }
+
+    /**
+     * Return true if the flow is a Data flow.
+     * @param flowRule The FlowRule to evaluate
+     * @return boolean
+     */
+    public static boolean isDataFlow(FlowRule flowRule) {
+        // we consider subscriber flows the one that matches on VLAN_VID
+        // method is valid only because it's the last check after EAPOL and DHCP.
+        // this matches mcast flows as well, if we want to avoid that we can
+        // filter out the elements that have groups in the treatment or
+        // mcastIp in the selector
+        // IPV4_DST:224.0.0.22/32
+        // treatment=[immediate=[GROUP:0x1]]
+
+        return flowRule.selector().getCriterion(Criterion.Type.VLAN_VID) != null;
+    }
+
+    /**
+     * Extracts and returns inPort selector from the FlowRule.
+     *
+     * @param flowRule The FlowRule to evaluate
+     * @return PortNumber
+     */
+    public static PortNumber getPortNumberFromFlowRule(FlowRule flowRule) {
+        PortCriterion inPort = (PortCriterion) flowRule.selector().getCriterion(Criterion.Type.IN_PORT);
+        if (inPort != null) {
+            return inPort.port();
+        }
+        return null;
+    }
+
+    /**
+     * Constructs and returns the metadata from innerVlan, techProfileId and egressPort.
+     *
+     * @param innerVlan         inner vlan tag
+     * @param techProfileId     technology profile
+     * @param egressPort        outport
+     * @return Metadata
+     */
+    public static Long createMetadata(VlanId innerVlan, int techProfileId, PortNumber egressPort) {
+        if (techProfileId == NONE_TP_ID) {
+            techProfileId = DEFAULT_TP_ID_DEFAULT;
+        }
+
+        Long writeMetadata = (long) techProfileId << 32 | egressPort.toLong();
+
+        if (innerVlan != null && !VlanId.NONE.equals(innerVlan)) {
+            writeMetadata |= (long) (innerVlan.id()) << 48;
+        }
+
+        return writeMetadata;
+    }
+
+    /***
+     * Checks if the FlowRule is default eapol.
+     * @param flowRule FlowRule to check.
+     * @return true if FlowRule is default eapol.
+     */
+    public static boolean isDefaultEapolFlow(FlowRule flowRule) {
+        EthTypeCriterion c = (EthTypeCriterion) flowRule.selector().getCriterion(Criterion.Type.ETH_TYPE);
+        if (c == null) {
+            return false;
+        }
+        if (c.ethType().equals(EthType.EtherType.EAPOL.ethType())) {
+            AtomicBoolean isDefault = new AtomicBoolean(false);
+            flowRule.treatment().allInstructions().forEach(instruction -> {
+                if (instruction.type() == L2MODIFICATION) {
+                    L2ModificationInstruction modificationInstruction = (L2ModificationInstruction) instruction;
+                    if (modificationInstruction.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
+                        L2ModificationInstruction.ModVlanIdInstruction vlanInstruction =
+                                (L2ModificationInstruction.ModVlanIdInstruction) modificationInstruction;
+                        if (vlanInstruction.vlanId().id().equals(EAPOL_DEFAULT_VLAN)) {
+                            isDefault.set(true);
+                            return;
+                        }
+                    }
+                }
+            });
+            return isDefault.get();
+        }
+        return false;
+    }
+
+    /***
+     * Checks if the FlowRule is Subscriber eapol.
+     * @param flowRule Flow Rule
+     * @return true if FlowRule is Subscriber eapol.
+     */
+    public static boolean isSubscriberEapolFlow(FlowRule flowRule) {
+        EthTypeCriterion c = (EthTypeCriterion) flowRule.selector().getCriterion(Criterion.Type.ETH_TYPE);
+        if (c == null) {
+            return false;
+        }
+        if (c.ethType().equals(EthType.EtherType.EAPOL.ethType())) {
+            AtomicBoolean isSubscriber = new AtomicBoolean(false);
+            flowRule.treatment().allInstructions().forEach(instruction -> {
+                if (instruction.type() == L2MODIFICATION) {
+                    L2ModificationInstruction modificationInstruction = (L2ModificationInstruction) instruction;
+                    if (modificationInstruction.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
+                        L2ModificationInstruction.ModVlanIdInstruction vlanInstruction =
+                                (L2ModificationInstruction.ModVlanIdInstruction) modificationInstruction;
+                        if (!vlanInstruction.vlanId().id().equals(EAPOL_DEFAULT_VLAN)) {
+                            isSubscriber.set(true);
+                            return;
+                        }
+                    }
+                }
+            });
+            return isSubscriber.get();
+        }
+        return false;
+    }
+}