/* * Copyright 2016-present Open Networking Laboratory * * 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.drivers.microsemi; import static com.google.common.base.Preconditions.checkNotNull; import static org.slf4j.LoggerFactory.getLogger; import java.math.BigInteger; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.concurrent.Semaphore; import java.util.stream.Collectors; import org.onlab.packet.EthType; import org.onlab.packet.EthType.EtherType; import org.onlab.packet.IpPrefix; import org.onlab.packet.VlanId; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; import org.onosproject.drivers.microsemi.yang.MseaSaFilteringNetconfService; import org.onosproject.drivers.microsemi.yang.MseaUniEvcServiceNetconfService; import org.onosproject.drivers.microsemi.yang.UniSide; import org.onosproject.drivers.microsemi.yang.utils.CeVlanMapUtils; import org.onosproject.net.PortNumber; import org.onosproject.net.driver.AbstractHandlerBehaviour; import org.onosproject.net.flow.DefaultFlowEntry; import org.onosproject.net.flow.DefaultFlowRule; import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.FlowEntry; import org.onosproject.net.flow.FlowEntry.FlowEntryState; import org.onosproject.net.flow.FlowRule; import org.onosproject.net.flow.FlowRuleProgrammable; import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.flow.criteria.Criteria; import org.onosproject.net.flow.criteria.Criterion; import org.onosproject.net.flow.criteria.Criterion.Type; import org.onosproject.net.flow.criteria.PortCriterion; import org.onosproject.net.flow.criteria.VlanIdCriterion; import org.onosproject.net.flow.instructions.Instruction; import org.onosproject.net.flow.instructions.L2ModificationInstruction; import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanHeaderInstruction; import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction; import org.onosproject.net.meter.MeterId; import org.onosproject.netconf.NetconfController; import org.onosproject.netconf.NetconfException; import org.onosproject.netconf.NetconfSession; import org.onosproject.netconf.TargetConfig; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.MseaSaFiltering; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.MseaSaFilteringOpParam; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.mseasafiltering.DefaultSourceIpaddressFiltering; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.mseasafiltering.SourceIpaddressFiltering; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.mseasafiltering.sourceipaddressfiltering.DefaultInterfaceEth0; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.mseasafiltering.sourceipaddressfiltering.InterfaceEth0; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.mseasafiltering.sourceipaddressfiltering.interfaceeth0.DefaultSourceAddressRange; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.mseasafiltering.sourceipaddressfiltering.interfaceeth0.FilterAdminStateEnum; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.mseasafiltering.sourceipaddressfiltering.interfaceeth0.SourceAddressRange; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.types.rev20160229.mseatypes.Identifier45; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.types.rev20160229.mseatypes.ServiceListType; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.types.rev20160229.mseatypes.VlanIdType; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.MseaUniEvcService; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.MseaUniEvcServiceOpParam; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.DefaultMefServices; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.MefServices; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.DefaultFlowMapping; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.FlowMapping; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.TagManipulation; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation.DefaultTagOverwrite; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation.DefaultTagPop; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation.DefaultTagPush; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation.TagOverwrite; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation.TagPop; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation.TagPush; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation.tagpush.tagpush.PushTagTypeEnum; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.DefaultProfiles; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.DefaultUni; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.Profiles; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.Uni; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.profiles.BwpGroup; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.profiles.DefaultBwpGroup; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.CustomEvc; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.DefaultEvc; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.Evc; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.UniSideInterfaceAssignmentEnum; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.evc.DefaultEvcPerUni; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.evc.EvcPerUni; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.evc.evcperuni.CustomEvcPerUnic; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.evc.evcperuni.CustomEvcPerUnin; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.evc.evcperuni.DefaultEvcPerUnic.EvcPerUnicBuilder; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.evc.evcperuni.DefaultEvcPerUnin.EvcPerUninBuilder; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.evc.evcperuni.EvcPerUnic; import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.evc.evcperuni.EvcPerUnin; import org.slf4j.Logger; /** * An implementation of the FlowRuleProgrammable behaviour for the EA10000 device. * * This device is not a native Open Flow device. It has only a NETCONF interface for configuration * status retrieval and notifications. It supports only a small subset of OpenFlow rules.<br> * * The device supports only:<br> * 1) Open flow rules that blocks certain IP address ranges, but only those incoming on Port 0 * and has a limit of 10 such rules<br> * 2) Open flow rules that PUSH, POP and OVERWRITE VLAN tags on both ports. This can push and overwrite * both C-TAGs (0x8100) and S-TAGs (0x88a8). */ public class EA1000FlowRuleProgrammable extends AbstractHandlerBehaviour implements FlowRuleProgrammable { protected final Logger log = getLogger(getClass()); public static final String MICROSEMI_DRIVERS = "com.microsemi.drivers"; public static final int PRIORITY_DEFAULT = 50000; //To protect the NETCONF session from concurrent access across flow addition and removal static Semaphore sessionMutex = new Semaphore(1); /** * Get the flow entries that are present on the EA1000. * Since the EA1000 does not have any 'real' flow entries these are retrieved from 2 configuration * areas on the EA1000 NETCONF model - from SA filtering YANG model and from EVC UNI YANG model.<br> * The flow entries must match exactly the FlowRule entries in the ONOS store. If they are not an * exact match the device will be requested to remove those flows and the FlowRule will stay in a * PENDING_ADD state. * @return A collection of Flow Entries */ @Override public Collection<FlowEntry> getFlowEntries() { Collection<FlowEntry> flowEntryCollection = new HashSet<FlowEntry>(); UniSideInterfaceAssignmentEnum portAssignment = UniSideInterfaceAssignmentEnum.UNI_C_ON_HOST; NetconfController controller = checkNotNull(handler().get(NetconfController.class)); NetconfSession session = controller.getDevicesMap().get(handler().data().deviceId()).getSession(); CoreService coreService = checkNotNull(handler().get(CoreService.class)); ApplicationId appId = coreService.getAppId(MICROSEMI_DRIVERS); MseaSaFilteringNetconfService mseaSaFilteringService = (MseaSaFilteringNetconfService) checkNotNull(handler().get(MseaSaFilteringNetconfService.class)); MseaUniEvcServiceNetconfService mseaUniEvcServiceSvc = (MseaUniEvcServiceNetconfService) checkNotNull(handler().get(MseaUniEvcServiceNetconfService.class)); log.debug("getFlowEntries() called on EA1000FlowRuleProgrammable"); //First get the MseaSaFiltering rules SourceIpaddressFiltering.SourceIpaddressFilteringBuilder sipBuilder = new DefaultSourceIpaddressFiltering.SourceIpaddressFilteringBuilder(); MseaSaFilteringOpParam.MseaSaFilteringBuilder opBuilder = new MseaSaFilteringOpParam.MseaSaFilteringBuilder(); MseaSaFilteringOpParam mseaSaFilteringFilter = (MseaSaFilteringOpParam) opBuilder .sourceIpaddressFiltering(sipBuilder.build()) .build(); try { MseaSaFiltering saFilteringCurrent = mseaSaFilteringService.getMseaSaFiltering(mseaSaFilteringFilter, session); if (saFilteringCurrent != null) { flowEntryCollection.addAll( convertSaFilteringToFlowRules(saFilteringCurrent, appId)); } } catch (NetconfException e) { log.warn("Unexpected error on getFlowEntries", e); } //Then get the EVCs - there will be a flow entry per EVC Uni.UniBuilder uniBuilder = new DefaultUni.UniBuilder(); MefServices.MefServicesBuilder mefBuilder = new DefaultMefServices.MefServicesBuilder(); MefServices mefServices = mefBuilder.uni(uniBuilder.build()).build(); MseaUniEvcService.MseaUniEvcServiceBuilder evcUniBuilder = new MseaUniEvcServiceOpParam.MseaUniEvcServiceBuilder(); MseaUniEvcServiceOpParam mseaUniEvcServiceFilter = (MseaUniEvcServiceOpParam) evcUniBuilder.mefServices(mefServices).build(); try { MseaUniEvcService uniEvcCurrent = mseaUniEvcServiceSvc.getConfigMseaUniEvcService(mseaUniEvcServiceFilter, session, TargetConfig.RUNNING); flowEntryCollection.addAll( convertEvcUniToFlowRules(uniEvcCurrent, portAssignment)); } catch (NetconfException e) { log.warn("Unexpected error on getFlowEntries", e); } return flowEntryCollection; } /** * Apply the flow entries to the EA1000. * Since the EA1000 does not have any 'real' flow entries these are converted 2 configuration * areas on the EA1000 NETCONF model - to SA filtering YANG model and to EVC UNI YANG model.<br> * Only a subset of the possible OpenFlow rules are supported. Any rule that's not handled * will not be in the returned set. * * @param rules A collection of Flow Rules to be applied to the EA1000 * @return A collection of the Flow Rules that have been added. */ @Override public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) { Collection<FlowRule> frAdded = new HashSet<FlowRule>(); if (rules == null || rules.size() == 0) { return rules; } NetconfController controller = checkNotNull(handler().get(NetconfController.class)); NetconfSession session = controller.getDevicesMap().get(handler().data().deviceId()).getSession(); MseaSaFilteringNetconfService mseaSaFilteringService = (MseaSaFilteringNetconfService) checkNotNull(handler().get(MseaSaFilteringNetconfService.class)); MseaUniEvcServiceNetconfService mseaUniEvcServiceSvc = (MseaUniEvcServiceNetconfService) checkNotNull(handler().get(MseaUniEvcServiceNetconfService.class)); log.debug("applyFlowRules() called on EA1000FlowRuleProgrammable with {} rules.", rules.size()); // FIXME: Change this so it's dynamically driven UniSideInterfaceAssignmentEnum portAssignment = UniSideInterfaceAssignmentEnum.UNI_C_ON_HOST; List<SourceAddressRange> saRangeList = new ArrayList<SourceAddressRange>(); Map<Integer, Evc> evcMap = new HashMap<>(); //Retrieve the list of actual EVCs and the CeVlanMaps from device List<Evc> activeEvcs = new ArrayList<>(); try { sessionMutex.acquire(); MseaUniEvcService evcResponse = mseaUniEvcServiceSvc.getmseaUniEvcCeVlanMaps(session, TargetConfig.RUNNING); //There could be zero or more EVCs if (evcResponse != null && evcResponse.mefServices() != null && evcResponse.mefServices().uni() != null) { activeEvcs.addAll(evcResponse.mefServices().uni().evc()); } } catch (NetconfException | InterruptedException e1) { log.warn("Unexpected error on applyFlowRules", e1); } for (FlowRule fr : rules) { // IP SA Filtering can only apply to Port 0 optics if (fr.selector().getCriterion(Type.IPV4_SRC) != null && fr.selector().getCriterion(Type.IN_PORT) != null && ((PortCriterion) fr.selector().getCriterion(Type.IN_PORT)).port().toLong() == 0) { parseFrForSaRange(frAdded, saRangeList, fr); // EVCs will be defined by Flow Rules relating to VIDs } else if (fr.selector().getCriterion(Type.VLAN_VID) != null && fr.selector().getCriterion(Type.IN_PORT) != null) { //There could be many Flow Rules for one EVC depending on the ceVlanMap //Cannot build up the EVC until we know the details - the key is the tableID and port parseFrForEvcs(frAdded, evcMap, activeEvcs, portAssignment, fr); } else { log.info("Unexpected Flow Rule type applied: " + fr); } } //If there are IPv4 Flow Rules created commit them now through the //MseaSaFiltering service if (saRangeList.size() > 0) { try { mseaSaFilteringService.setMseaSaFiltering( buildSaFilteringObject(saRangeList), session, TargetConfig.RUNNING); } catch (NetconfException e) { log.error("Error applying Flow Rules to SA Filtering - will try again: " + e.getMessage()); sessionMutex.release(); return frAdded; } } //If there are EVC flow rules then populate the MseaUniEvc part of EA1000 if (evcMap.size() > 0) { List<Evc> evcList = evcMap.entrySet().stream() .map(x -> x.getValue()) .collect(Collectors.toList()); Uni.UniBuilder uniBuilder = new DefaultUni.UniBuilder(); URI deviceName = handler().data().deviceId().uri(); Uni uni = uniBuilder.name(new Identifier45("Uni-on-" + deviceName.getSchemeSpecificPart())).evc(evcList).build(); List<BwpGroup> bwpGroupList = new ArrayList<BwpGroup>(); BwpGroup.BwpGroupBuilder bwpGrpBuilder = new DefaultBwpGroup.BwpGroupBuilder(); bwpGroupList.add(bwpGrpBuilder.groupIndex((short) 0).build()); Profiles profiles = (new DefaultProfiles.ProfilesBuilder()).bwpGroup(bwpGroupList).build(); MefServices.MefServicesBuilder mefBuilder = new DefaultMefServices.MefServicesBuilder(); MefServices mefServices = mefBuilder.uni(uni).profiles(profiles).build(); MseaUniEvcService.MseaUniEvcServiceBuilder evcUniBuilder = new MseaUniEvcServiceOpParam.MseaUniEvcServiceBuilder(); MseaUniEvcServiceOpParam mseaUniEvcServiceFilter = (MseaUniEvcServiceOpParam) evcUniBuilder.mefServices(mefServices).build(); try { mseaUniEvcServiceSvc.setMseaUniEvcService(mseaUniEvcServiceFilter, session, TargetConfig.RUNNING); } catch (NetconfException e) { log.error("Error applying Flow Rules to EVC - will try again: " + e.getMessage()); sessionMutex.release(); return frAdded; } } sessionMutex.release(); return frAdded; } /** * Remove flow rules from the EA1000. * Since the EA1000 does not have any 'real' flow entries these are converted 2 configuration * areas on the EA1000 NETCONF model - to SA filtering YANG model and to EVC UNI YANG model. * * @param rulesToRemove A collection of Flow Rules to be removed to the EA1000 * @return A collection of the Flow Rules that have been removed. */ @Override public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rulesToRemove) { NetconfController controller = checkNotNull(handler().get(NetconfController.class)); NetconfSession session = controller.getDevicesMap().get(handler().data().deviceId()).getSession(); MseaSaFilteringNetconfService mseaSaFilteringService = (MseaSaFilteringNetconfService) checkNotNull(handler().get(MseaSaFilteringNetconfService.class)); MseaUniEvcServiceNetconfService mseaUniEvcServiceSvc = (MseaUniEvcServiceNetconfService) checkNotNull(handler().get(MseaUniEvcServiceNetconfService.class)); UniSideInterfaceAssignmentEnum portAssignment = UniSideInterfaceAssignmentEnum.UNI_C_ON_HOST; log.debug("removeFlowRules() called on EA1000FlowRuleProgrammable with {} rules.", rulesToRemove.size()); if (rulesToRemove.size() == 0) { return rulesToRemove; } //Retrieve the list of actual EVCs and the CeVlanMaps from device List<Evc> activeEvcs = new ArrayList<>(); try { sessionMutex.acquire(); MseaUniEvcService evcResponse = mseaUniEvcServiceSvc.getmseaUniEvcCeVlanMaps(session, TargetConfig.RUNNING); //There could be zero or more EVCs if (evcResponse != null && evcResponse.mefServices() != null && evcResponse.mefServices().uni() != null) { activeEvcs.addAll(evcResponse.mefServices().uni().evc()); } } catch (NetconfException | InterruptedException e1) { log.warn("Error on removeFlowRules.", e1); } List<SourceAddressRange> saRangeList = new ArrayList<SourceAddressRange>(); Map<Integer, String> ceVlanMapMap = new HashMap<>(); Map<Integer, List<Short>> flowIdMap = new HashMap<>(); Collection<FlowRule> rulesRemoved = new HashSet<FlowRule>(); for (FlowRule ruleToRemove : rulesToRemove) { // IP SA Filtering can only apply to Port 0 optics if (ruleToRemove.selector().getCriterion(Type.IPV4_SRC) != null && ruleToRemove.selector().getCriterion(Type.IN_PORT) != null && ((PortCriterion) ruleToRemove.selector().getCriterion(Type.IN_PORT)).port().toLong() == 0) { SourceAddressRange.SourceAddressRangeBuilder saBuilder = new DefaultSourceAddressRange.SourceAddressRangeBuilder(); SourceAddressRange sar = saBuilder .rangeId((short) ruleToRemove.tableId()) .yangSourceAddressRangeOpType(org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi. edge.assure.msea.sa.filtering.rev20160412.MseaSaFiltering.OnosYangOpType.DELETE) .build(); rulesRemoved.add(ruleToRemove); saRangeList.add(sar); } else if (ruleToRemove.selector().getCriterion(Type.VLAN_VID) != null && ruleToRemove.selector().getCriterion(Type.IN_PORT) != null) { PortNumber portNumber = ((PortCriterion) ruleToRemove.selector().getCriterion(Type.IN_PORT)).port(); VlanId vlanId = ((VlanIdCriterion) ruleToRemove.selector().getCriterion(Type.VLAN_VID)).vlanId(); int evcId = ruleToRemove.tableId(); int evcKey = (evcId << 2) + (int) portNumber.toLong(); String activeCeVlanMap = ""; //If this is one of many VLANs belonging to an EVC then we should only remove this VLAN // from the ceVlanMap and not the whole EVC if (!ceVlanMapMap.containsKey(evcKey)) { for (Evc activeEvc:activeEvcs) { if (activeEvc.evcIndex() == evcId) { if (Ea1000Port.fromNum(portNumber.toLong()).nOrC(portAssignment) == UniSide.CUSTOMER) { activeCeVlanMap = activeEvc.evcPerUni().evcPerUnic().ceVlanMap().string(); } else if (Ea1000Port.fromNum(portNumber.toLong()).nOrC(portAssignment) == UniSide.NETWORK) { activeCeVlanMap = activeEvc.evcPerUni().evcPerUnin().ceVlanMap().string(); } } } } ceVlanMapMap.put(evcKey, CeVlanMapUtils.removeFromCeVlanMap(activeCeVlanMap, vlanId.id())); if (!flowIdMap.containsKey(evcKey)) { flowIdMap.put(evcKey, new ArrayList<>()); } flowIdMap.get(evcKey).add(vlanId.id()); rulesRemoved.add(ruleToRemove); } else { log.info("Unexpected Flow Rule type removal: " + ruleToRemove); } } //If there are IPv4 Flow Rules created commit them now through the //MseaSaFiltering service if (saRangeList.size() > 0) { try { mseaSaFilteringService.setMseaSaFiltering( buildSaFilteringObject(saRangeList), session, TargetConfig.RUNNING); } catch (NetconfException e) { log.warn("Remove FlowRule on MseaSaFilteringService could not delete SARule - " + "it may already have been deleted: " + e.getMessage()); } } if (ceVlanMapMap.size() > 0) { try { mseaUniEvcServiceSvc.removeEvcUniFlowEntries(ceVlanMapMap, flowIdMap, session, TargetConfig.RUNNING, portAssignment); } catch (NetconfException e) { log.warn("Remove FlowRule on MseaUniEvcService could not delete EVC - " + "it may already have been deleted: " + e.getMessage()); } } sessionMutex.release(); return rulesRemoved; } /** * An internal method for extracting one EVC from a list and returning its ceVlanMap. * * @param evcList - the list of known EVCs * @param evcIndex - the index of the EVC we're looking for * @param side - the side of the UNI * @return - the CEVlanMap we're looking for */ private String getCeVlanMapForIdxFromEvcList(List<Evc> evcList, long evcIndex, UniSide side) { if (evcList != null && evcList.size() > 0) { for (Evc evc:evcList) { if (evc.evcIndex() == evcIndex && evc.evcPerUni() != null) { if (side == UniSide.CUSTOMER && evc.evcPerUni().evcPerUnic() != null && evc.evcPerUni().evcPerUnic().ceVlanMap() != null) { return evc.evcPerUni().evcPerUnic().ceVlanMap().string(); } else if (side == UniSide.NETWORK && evc.evcPerUni().evcPerUnin() != null && evc.evcPerUni().evcPerUnin().ceVlanMap() != null) { return evc.evcPerUni().evcPerUnin().ceVlanMap().string(); } } } } return ""; //The EVC required was not in the list } /** * An internal method to convert from a FlowRule to SARange. * * @param frList A collection of flow rules * @param saRangeList A list of SARanges * @param fr A flow rule */ private void parseFrForSaRange(Collection<FlowRule> frList, List<SourceAddressRange> saRangeList, FlowRule fr) { String ipAddrStr = fr.selector().getCriterion(Type.IPV4_SRC).toString().substring(9); log.debug("Applying IP address to " + ipAddrStr + " (on Port 0) to IP SA Filtering on EA1000 through NETCONF"); SourceAddressRange.SourceAddressRangeBuilder saBuilder = new DefaultSourceAddressRange.SourceAddressRangeBuilder(); SourceAddressRange sar = saBuilder .rangeId((short) fr.tableId()) .name("Flow:" + fr.id().toString()) .ipv4AddressPrefix(ipAddrStr) .build(); frList.add(fr); saRangeList.add(sar); } private void parseFrForEvcs(Collection<FlowRule> frList, Map<Integer, Evc> evcMap, List<Evc> activeEvcs, UniSideInterfaceAssignmentEnum portAssignment, FlowRule fr) { //There could be many Flow Rules for one EVC depending on the ceVlanMap //Cannot build up the EVC until we know the details - the key is the tableID and port Ea1000Port port = Ea1000Port.fromNum( ((PortCriterion) fr.selector().getCriterion(Type.IN_PORT)).port().toLong()); Integer evcKey = (fr.tableId() << 2) + port.portNum(); VlanId sourceVid = ((VlanIdCriterion) fr.selector().getCriterion(Type.VLAN_VID)).vlanId(); FlowMapping.FlowMappingBuilder fmBuilder = DefaultFlowMapping.builder() .ceVlanId(VlanIdType.of(sourceVid.id())) .flowId(BigInteger.valueOf(fr.id().value())); if (evcMap.containsKey(evcKey)) { //Is there an entry already for this EVC and port? //Replace ceVlanMap evcMap.put(evcKey, CustomEvc.builder(evcMap.get(evcKey)) .addToCeVlanMap(new ServiceListType(sourceVid.toString()), port.nOrC(portAssignment)) .addToFlowMapping(fmBuilder.build(), port.nOrC(portAssignment)) .build()); } else if (evcMap.containsKey((evcKey ^ 1))) { //Is there an entry for this EVC but the opposite port? TagManipulation tm = getTagManipulation(fr); if (port.nOrC(portAssignment) == UniSide.NETWORK) { EvcPerUnin epun = CustomEvcPerUnin.builder(evcMap.get(evcKey ^ 1).evcPerUni().evcPerUnin()) .addToCeVlanMap(new ServiceListType(sourceVid.toString())) .tagManipulation(tm) .addToFlowMapping(fmBuilder.build()) .ingressBwpGroupIndex(getMeterId(fr.treatment())) .build(); evcMap.put((evcKey ^ 1), CustomEvc.builder(evcMap.get((evcKey ^ 1))).addUniN(epun).build()); } else { EvcPerUnic epuc = CustomEvcPerUnic.builder(evcMap.get(evcKey ^ 1).evcPerUni().evcPerUnic()) .addToCeVlanMap(new ServiceListType(sourceVid.toString())) .tagManipulation(tm) .addToFlowMapping(fmBuilder.build()) .ingressBwpGroupIndex(getMeterId(fr.treatment())) .build(); evcMap.put((evcKey ^ 1), CustomEvc.builder(evcMap.get((evcKey ^ 1))).addUniC(epuc).build()); } } else { Evc.EvcBuilder evcBuilder = new DefaultEvc.EvcBuilder(); EvcPerUninBuilder epunBuilder = new CustomEvcPerUnin.EvcPerUninBuilder(); EvcPerUnicBuilder epucBuilder = new CustomEvcPerUnic.EvcPerUnicBuilder(); TagManipulation tm = getTagManipulation(fr); UniSide side = port.nOrC(portAssignment); String oldCeVlanMap = getCeVlanMapForIdxFromEvcList(activeEvcs, fr.tableId(), side); String newCeVlanMap = CeVlanMapUtils.addtoCeVlanMap(oldCeVlanMap, sourceVid.id()); String oppositeCeVlanMap = getCeVlanMapForIdxFromEvcList(activeEvcs, fr.tableId(), port.opposite().nOrC(portAssignment)); oppositeCeVlanMap = oppositeCeVlanMap.isEmpty() ? "0" : oppositeCeVlanMap; if (side == UniSide.NETWORK) { epunBuilder .ceVlanMap(new ServiceListType(newCeVlanMap)) .tagManipulation(tm) .addToFlowMapping(fmBuilder.build()) .ingressBwpGroupIndex(getMeterId(fr.treatment())); epucBuilder.ceVlanMap(new ServiceListType(oppositeCeVlanMap)); } else { epucBuilder .ceVlanMap(new ServiceListType(newCeVlanMap)) .tagManipulation(tm) .addToFlowMapping(fmBuilder.build()) .ingressBwpGroupIndex(getMeterId(fr.treatment())); epunBuilder.ceVlanMap(new ServiceListType(oppositeCeVlanMap)); } evcBuilder .evcIndex(fr.tableId()) .name(new Identifier45("EVC-" + String.valueOf(fr.tableId()))) .evcPerUni(new DefaultEvcPerUni.EvcPerUniBuilder() .evcPerUnin(epunBuilder.build()) .evcPerUnic(epucBuilder.build()) .build()); evcMap.put(evcKey, evcBuilder.build()); } frList.add(fr); } private MseaSaFilteringOpParam buildSaFilteringObject(List<SourceAddressRange> saRangeList) { InterfaceEth0.InterfaceEth0Builder ifBuilder = new DefaultInterfaceEth0.InterfaceEth0Builder(); for (SourceAddressRange sa:saRangeList) { ifBuilder = ifBuilder.addToSourceAddressRange(sa); } InterfaceEth0 saIf = ifBuilder.filterAdminState(FilterAdminStateEnum.BLACKLIST).build(); SourceIpaddressFiltering.SourceIpaddressFilteringBuilder saFilterBuilder = new DefaultSourceIpaddressFiltering.SourceIpaddressFilteringBuilder(); SourceIpaddressFiltering saFilter = saFilterBuilder.interfaceEth0(saIf).build(); MseaSaFilteringOpParam.MseaSaFilteringBuilder opBuilder = new MseaSaFilteringOpParam.MseaSaFilteringBuilder(); MseaSaFilteringOpParam mseaSaFiltering = (MseaSaFilteringOpParam) opBuilder.sourceIpaddressFiltering(saFilter).build(); return mseaSaFiltering; } private Collection<FlowEntry> convertSaFilteringToFlowRules( MseaSaFiltering saFilteringCurrent, ApplicationId appId) { Collection<FlowEntry> flowEntryCollection = new HashSet<FlowEntry>(); List<SourceAddressRange> saRangelist = saFilteringCurrent.sourceIpaddressFiltering().interfaceEth0().sourceAddressRange(); Criterion matchInPort = Criteria.matchInPort(PortNumber.portNumber(0)); TrafficSelector.Builder tsBuilder = DefaultTrafficSelector.builder(); for (SourceAddressRange sa:saRangelist) { Criterion matchIpSrc = Criteria.matchIPSrc(IpPrefix.valueOf(sa.ipv4AddressPrefix())); TrafficSelector selector = tsBuilder.add(matchIpSrc).add(matchInPort).build(); TrafficTreatment.Builder trBuilder = DefaultTrafficTreatment.builder(); TrafficTreatment treatment = trBuilder.drop().build(); FlowRule.Builder feBuilder = new DefaultFlowRule.Builder(); if (sa.name() != null && sa.name().startsWith("Flow:")) { String[] nameParts = sa.name().split(":"); Long cookie = Long.valueOf(nameParts[1], 16); feBuilder = feBuilder.withCookie(cookie); } else { feBuilder = feBuilder.fromApp(appId); } FlowRule fr = feBuilder .forDevice(handler().data().deviceId()) .withSelector(selector) .withTreatment(treatment) .forTable(sa.rangeId()) .makePermanent() .withPriority(PRIORITY_DEFAULT) .build(); flowEntryCollection.add(new DefaultFlowEntry(fr, FlowEntryState.ADDED, 0, 0, 0)); } return flowEntryCollection; } private Collection<FlowEntry> convertEvcUniToFlowRules( MseaUniEvcService uniEvcCurrent, UniSideInterfaceAssignmentEnum portAssignment) { Collection<FlowEntry> flowEntryCollection = new HashSet<FlowEntry>(); if (uniEvcCurrent == null || uniEvcCurrent.mefServices() == null || uniEvcCurrent.mefServices().uni() == null || uniEvcCurrent.mefServices().uni().evc() == null) { log.info("No EVC's found when getting flow rules"); return flowEntryCollection; } for (Evc evc:uniEvcCurrent.mefServices().uni().evc()) { FlowRule.Builder frBuilder = new DefaultFlowRule.Builder(); TrafficSelector.Builder tsBuilder = DefaultTrafficSelector.builder(); TrafficTreatment uniNTreatment = treatmentForUniSde(evc.evcPerUni(), true); //Depending on the ceVlanMap there may be multiple VLans and hence multiple flow entries Short[] vlanIdsUniN = CeVlanMapUtils.getVlanSet(ceVlanMapForUniSide(evc.evcPerUni(), true)); for (Short vlanId:vlanIdsUniN) { if (vlanId == 0) { continue; } Criterion uniNportCriterion = criterionPortForUniSide(portAssignment, true); TrafficSelector tsUniN = tsBuilder.matchVlanId(VlanId.vlanId(vlanId)).add(uniNportCriterion).build(); long flowId = getFlowIdForVlan(evc.evcPerUni().evcPerUnin().flowMapping(), vlanId); FlowRule frUniN = frBuilder .forDevice(handler().data().deviceId()) .withSelector(tsUniN) .withTreatment(uniNTreatment) .forTable(new Long(evc.evcIndex()).intValue()) //narrowing to int .makePermanent() .withPriority(PRIORITY_DEFAULT) .withCookie(flowId) .build(); flowEntryCollection.add(new DefaultFlowEntry(frUniN, FlowEntryState.ADDED, 0, 0, 0)); } TrafficTreatment uniCTreatment = treatmentForUniSde(evc.evcPerUni(), false); //Depending on the ceVlanMap there may be multiple VLans and hence multiple flow entries Short[] vlanIdsUniC = CeVlanMapUtils.getVlanSet(ceVlanMapForUniSide(evc.evcPerUni(), false)); if (vlanIdsUniC != null && vlanIdsUniC.length > 0) { for (Short vlanId:vlanIdsUniC) { if (vlanId == 0) { continue; } Criterion uniCportCriterion = criterionPortForUniSide(portAssignment, false); TrafficSelector tsUniC = tsBuilder.matchVlanId(VlanId.vlanId(vlanId)).add(uniCportCriterion).build(); long flowId = getFlowIdForVlan(evc.evcPerUni().evcPerUnic().flowMapping(), vlanId); FlowRule frUniC = frBuilder .forDevice(handler().data().deviceId()) .withSelector(tsUniC) .withTreatment(uniCTreatment) .forTable(new Long(evc.evcIndex()).intValue()) //narrowing to int .makePermanent() .withPriority(PRIORITY_DEFAULT) .withCookie(flowId) .build(); flowEntryCollection.add(new DefaultFlowEntry(frUniC, FlowEntryState.ADDED, 0, 0, 0)); } } } return flowEntryCollection; } private long getFlowIdForVlan(List<FlowMapping> fmList, Short vlanId) { if (fmList == null || vlanId == null) { log.warn("Flow Mapping list is null when reading EVCs"); return -1L; } for (FlowMapping fm:fmList) { if (fm.ceVlanId().uint16() == vlanId.intValue()) { return fm.flowId().longValue(); } } return 0L; } private String ceVlanMapForUniSide( EvcPerUni evcPerUni, boolean portN) { if (portN) { return evcPerUni.evcPerUnin().ceVlanMap().string(); } else { return evcPerUni.evcPerUnic().ceVlanMap().string(); } } private Criterion criterionPortForUniSide( UniSideInterfaceAssignmentEnum portAssignment, boolean portN) { boolean cOnOptics = (portAssignment == UniSideInterfaceAssignmentEnum.UNI_C_ON_OPTICS); int portNum = ((cOnOptics && portN) || (!cOnOptics && !portN)) ? 1 : 0; return Criteria.matchInPort(PortNumber.portNumber(portNum)); } private TrafficTreatment treatmentForUniSde( EvcPerUni evcPerUni, boolean portN) { TrafficTreatment.Builder trBuilder = DefaultTrafficTreatment.builder(); TagManipulation tm = null; short meterId = 0; if (portN) { tm = evcPerUni.evcPerUnin().tagManipulation(); meterId = (short) evcPerUni.evcPerUnin().ingressBwpGroupIndex(); } else { tm = evcPerUni.evcPerUnic().tagManipulation(); meterId = (short) evcPerUni.evcPerUnic().ingressBwpGroupIndex(); } if (meterId > 0L) { trBuilder = trBuilder.meter(MeterId.meterId((long) meterId)); // trBuilder = trBuilder.meter(MeterId.meterId(meterId)).transition(0); } if (tm == null) { return trBuilder.build(); //no tag manipulation found } if (tm.getClass().equals(DefaultTagPush.class)) { VlanId pushVlanNum = VlanId.vlanId((short) ((TagPush) tm).tagPush().outerTagVlan().uint16()); PushTagTypeEnum pushTagType = ((TagPush) tm).tagPush().pushTagType(); //Note - the order of elements below MUST match the order of the Treatment in the stored FlowRule // to be an exactMatch. See DefaultFlowRule.exactMatch() trBuilder = trBuilder .pushVlan(pushTagType.equals(PushTagTypeEnum.PUSHCTAG) ? EtherType.VLAN.ethType() : EtherType.QINQ.ethType()) .setVlanId(pushVlanNum).transition(Integer.valueOf(0)); } else if (tm.getClass().equals(DefaultTagPop.class)) { trBuilder = trBuilder.popVlan(); } else if (tm.getClass().equals(DefaultTagOverwrite.class)) { TagOverwrite to = (TagOverwrite) tm; VlanId ovrVlanNum = VlanId .vlanId((short) ( //There are 2 classes TagOverwrite - the other one is already imported to .tagOverwrite() .outerTagVlan() .uint16())); trBuilder = trBuilder.setVlanId(ovrVlanNum); } return trBuilder.build(); } private static TagManipulation getTagManipulation(FlowRule fr) { boolean isPop = false; boolean isPush = false; VlanId vlanId = null; EthType ethType = EtherType.VLAN.ethType(); //Default for (Instruction inst:fr.treatment().allInstructions()) { if (inst.type() == Instruction.Type.L2MODIFICATION) { L2ModificationInstruction l2Mod = (L2ModificationInstruction) inst; if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP) { isPop = true; } else if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) { isPush = true; ethType = ((ModVlanHeaderInstruction) l2Mod).ethernetType(); } else if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) { vlanId = ((ModVlanIdInstruction) l2Mod).vlanId(); } } } if (isPop) { //The should be no vlanId in this case TagPop.TagPopBuilder popBuilder = new DefaultTagPop.TagPopBuilder(); org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc .service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation .tagpop.TagPop.TagPopBuilder popInnerBuilder = new org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea .uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes .tagmanipulation.tagpop.DefaultTagPop.TagPopBuilder(); return popBuilder .tagPop(popInnerBuilder.build()) .build(); } else if (isPush && vlanId != null) { TagPush.TagPushBuilder pushBuilder = new DefaultTagPush.TagPushBuilder(); org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc .service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation .tagpush.TagPush.TagPushBuilder pushInnerBuilder = new org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea .uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes .tagmanipulation.tagpush.DefaultTagPush.TagPushBuilder(); return pushBuilder .tagPush(pushInnerBuilder .outerTagVlan(new VlanIdType(vlanId.id())) .pushTagType(ethType.equals(EtherType.VLAN.ethType()) ? PushTagTypeEnum.PUSHCTAG : PushTagTypeEnum.PUSHSTAG) .build()) .build(); } else if (vlanId != null) { //This is overwrite, as it has vlanId, but not push or pop TagOverwrite.TagOverwriteBuilder ovrBuilder = new DefaultTagOverwrite.TagOverwriteBuilder(); org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc .service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation .tagoverwrite.TagOverwrite.TagOverwriteBuilder ovrInnerBuilder = new org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea .uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes .tagmanipulation.tagoverwrite.DefaultTagOverwrite.TagOverwriteBuilder(); return ovrBuilder. tagOverwrite(ovrInnerBuilder .outerTagVlan(new VlanIdType(vlanId.id())) .build()) .build(); } return null; } private static long getMeterId(TrafficTreatment treatment) { return (treatment.metered() != null && treatment.metered().meterId() != null) ? treatment.metered().meterId().id() : 0L; } /** * An enumerated type that characterises the 2 port layout of the EA1000 device. * The device is in an SFP package and has only 2 ports, the HOST port which * plugs in to the chassis (Port 1) and the Optics Port on the rear (Port 0). */ public enum Ea1000Port { HOST(1), OPTICS(0); private int num = 0; private Ea1000Port(int num) { this.num = num; } /** * The numerical assignment of this port. * @return The port number */ public int portNum() { return num; } /** * Return the enumerated value from a port number. * @param num The port number * @return An enumerated value */ public static Ea1000Port fromNum(long num) { for (Ea1000Port a:Ea1000Port.values()) { if (a.num == num) { return a; } } return HOST; } /** * Get the port that the UNI-N is present on. * @param side The assignment of UNI-side to port * @return An enumerated value */ public static Ea1000Port uniNNum(UniSideInterfaceAssignmentEnum side) { if (side.equals(UniSideInterfaceAssignmentEnum.UNI_C_ON_HOST)) { return OPTICS; } else { return HOST; } } /** * Get the port that the UNI-C is present on. * @param side The assignment of UNI-side to port * @return An enumerated value */ public static Ea1000Port uniCNum(UniSideInterfaceAssignmentEnum side) { if (side.equals(UniSideInterfaceAssignmentEnum.UNI_C_ON_HOST)) { return HOST; } else { return OPTICS; } } /** * Get the port opposite the current port. * @return An enumerated value for the opposite side */ public Ea1000Port opposite() { if (this.equals(HOST)) { return OPTICS; } else { return HOST; } } /** * Evaluate which side of the UNI on the EA1000 device this port refers to. * @param side The assignment of UNI-side to port * @return An enumerated value representing the UniSide */ public UniSide nOrC(UniSideInterfaceAssignmentEnum side) { if ((this == HOST && side == UniSideInterfaceAssignmentEnum.UNI_C_ON_HOST) || (this == OPTICS && side == UniSideInterfaceAssignmentEnum.UNI_C_ON_OPTICS)) { return UniSide.CUSTOMER; } else { return UniSide.NETWORK; } } } }