blob: 3f2d3b9400dbcf8dba5e114067e63e29d5f398e5 [file] [log] [blame]
/*
* Copyright 2021-2023 Open Networking Foundation (ONF) and the ONF Contributors
*
* 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.olt.OltFlowsStatus;
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 OltFlowsStatus flowRuleStatusToOltFlowStatus(FlowRuleEvent.Type type) {
switch (type) {
case RULE_ADD_REQUESTED:
return OltFlowsStatus.PENDING_ADD;
case RULE_ADDED:
return OltFlowsStatus.ADDED;
case RULE_REMOVE_REQUESTED:
return OltFlowsStatus.PENDING_REMOVE;
case RULE_REMOVED:
return OltFlowsStatus.REMOVED;
default:
return 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;
}
}