/* * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.netvirt.cloudservicechain.utils; import com.google.common.base.Optional; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.genius.mdsalutil.ActionInfo; import org.opendaylight.genius.mdsalutil.MDSALUtil; import org.opendaylight.genius.mdsalutil.MatchInfo; import org.opendaylight.genius.mdsalutil.MetaDataUtil; import org.opendaylight.genius.mdsalutil.NwConstants; import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad; import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager; import org.opendaylight.genius.mdsalutil.matches.MatchMetadata; import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId; import org.opendaylight.genius.utils.ServiceIndex; import org.opendaylight.netvirt.cloudservicechain.CloudServiceChainConstants; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev160711.ElanServiceChainState; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev160711.elan.to.pseudo.port.data.list.ElanToPseudoPortData; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev160711.elan.to.pseudo.port.data.list.ElanToPseudoPortDataBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev160711.elan.to.pseudo.port.data.list.ElanToPseudoPortDataKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanDpnInterfaces; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesList; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesListKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class ElanServiceChainUtils { private static final Logger LOG = LoggerFactory.getLogger(ElanServiceChainUtils.class); private ElanServiceChainUtils() { } public static InstanceIdentifier getElanInstanceConfigDataPath(String elanInstanceName) { return InstanceIdentifier.builder(ElanInstances.class).child(ElanInstance.class, new ElanInstanceKey(elanInstanceName)).build(); } public static Optional getElanInstanceByName(DataBroker broker, String elanName) { return MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, getElanInstanceConfigDataPath(elanName)); } public static Collection getElanDpnsByName(DataBroker broker, String elanInstanceName) { InstanceIdentifier elanDpnIfacesIid = InstanceIdentifier.builder(ElanDpnInterfaces.class) .child(ElanDpnInterfacesList.class,new ElanDpnInterfacesListKey(elanInstanceName)) .build(); Optional elanDpnIfacesOpc = MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, elanDpnIfacesIid); if (!elanDpnIfacesOpc.isPresent()) { LOG.warn("Could not find and DpnInterface for elan {}", elanInstanceName); return Collections.emptySet(); } return elanDpnIfacesOpc.get().getDpnInterfaces().stream().map(DpnInterfaces::getDpId).collect( Collectors.toSet()); } public static BigInteger getElanMetadataLabel(long elanTag) { return BigInteger.valueOf(elanTag).shiftLeft(24); } /** * This flow is in charge of handling packets coming from ExtTunnelTable * that must be redirected to the SCF Pipeline. * + Matches on lportTag=ElanPseudoLportTag + SI=1 * + Sets scfTag and sends to the DlSubsFilter table. * * @param dpnId Dpn Id where the flow must be installed * @param elanLportTag the Elan Pseudo Lport Id in Dataplane * @param elanTag the Elan Id in the Dataplane * @param addOrRemove States if the flow must be added or removed */ public static void programLPortDispatcherToScf(IMdsalApiManager mdsalManager, BigInteger dpnId, long elanTag, int elanLportTag, short tableId, long scfTag, int addOrRemove) { LOG.info("L2-ServiceChaining: programLPortDispatcherToScf dpId={} elanLportTag={} scfTag={} addOrRemove={} ", dpnId, elanLportTag, scfTag, addOrRemove); String flowRef = buildLportDispToScfFlowRef(elanLportTag, scfTag); if (addOrRemove == NwConstants.ADD_FLOW) { int instructionKey = 0; List instructions = new ArrayList<>(); List actionsInfos = new ArrayList<>(); actionsInfos.add(new ActionRegLoad(NxmNxReg2.class, 0, 31, scfTag)); instructions.add(MDSALUtil.buildApplyActionsInstruction(MDSALUtil .buildActions(actionsInfos),instructionKey++)); instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(tableId, instructionKey++)); List matches = Collections.singletonList( new MatchMetadata( MetaDataUtil.getMetaDataForLPortDispatcher(elanLportTag, ServiceIndex.getIndex(NwConstants.SCF_SERVICE_NAME, NwConstants.SCF_SERVICE_INDEX)), MetaDataUtil.getMetaDataMaskForLPortDispatcher())); Flow flow = MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef, CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY, flowRef, 0, 0, CloudServiceChainConstants.COOKIE_LPORT_DISPATCHER_BASE.add(BigInteger.valueOf(elanTag)), matches, instructions); mdsalManager.installFlow(dpnId, flow); } else { Flow flow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE) .setId(new FlowId(flowRef)).build(); mdsalManager.removeFlow(dpnId, flow); } } /** * This flow is in charge of handling packets coming from the SCF Pipeline * when there is no matching ServiceChain. *
    *
  • Matches on ElanPseudoPortTag and SI=3 (ELAN)
  • *
  • Sets elanTag and sends to DMAC table
  • *
* @param dpnId Dpn Id where the flow must be installed * @param elanLportTag the Elan Pseudo Lport Id to be used in the Dataplane * @param elanTag the Elan Id to be used in the Dataplane * @param addOrRemove States if the flow must be added or removed */ public static void programLPortDispatcherFromScf(IMdsalApiManager mdsalManager, BigInteger dpnId, int elanLportTag, long elanTag, int addOrRemove) { LOG.info("L2-ServiceChaining: programLPortDispatcherFromScf dpId={} elanLportTag={} elanTag={} addOrRemove={} ", dpnId, elanLportTag, elanTag, addOrRemove); String flowRef = buildLportDispFromScfFlowRef(elanTag, elanLportTag); if (addOrRemove == NwConstants.ADD_FLOW) { List matches = Collections.singletonList( new MatchMetadata(MetaDataUtil.getMetaDataForLPortDispatcher(elanLportTag, ServiceIndex.getIndex(NwConstants.ELAN_SERVICE_NAME, NwConstants.ELAN_SERVICE_INDEX)), MetaDataUtil.getMetaDataMaskForLPortDispatcher())); int instructionKey = 0; List instructions = Arrays.asList( // BigInter.ONE is for setting also the Split-Horizon flag since it could have been cleared // while going through the SCF Pipeline MDSALUtil.buildAndGetWriteMetadaInstruction(getElanMetadataLabel(elanTag).or(BigInteger.ONE), MetaDataUtil.METADATA_MASK_SERVICE.or(BigInteger.ONE), instructionKey++), MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.ELAN_SMAC_TABLE, instructionKey)); Flow flow = MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef, CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY, flowRef, 0, 0, CloudServiceChainConstants.COOKIE_LPORT_DISPATCHER_BASE.add(BigInteger.valueOf(elanTag)), matches, instructions); mdsalManager.installFlow(dpnId, flow); } else { Flow flow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE) .setId(new FlowId(flowRef)).build(); mdsalManager.removeFlow(dpnId, flow); } } /** * This flow is in charge of receiving packets from the TOR and sending * them to the SCF Pipeline by setting the LportTag of ElanPseudoPort. * Note that ELAN already has a flow in this table that redirects packets * to the ELAN Pipeline. However, the flow for the SCF Pipeline will have * higher priority, and will only be present when there is a ServiceChain * using this ElanPseudoPort. *
    *
  • Matches on the VNI *
  • Sets SI=1 and ElanPseudoPort tag in the Metadata and sends to * LPortDispatcher via table 80. *
* @param dpnId Dpn Id where the flow must be installed * @param elanLportTag the Elan Pseudo Lport Id to be used in the Dataplane * @param vni the VNI to which the Elan is related * @param elanTag the Elan Id to be used in the Dataplane * @param addOrRemove States if the flow must be added or removed */ public static void programExternalTunnelTable(IMdsalApiManager mdsalManager, BigInteger dpnId, int elanLportTag, long vni, int elanTag, int addOrRemove) { LOG.info("L2-ServiceChaining: programExternalTunnelTable dpId={} vni={} elanLportTag={} addOrRemove={} ", dpnId, vni, elanLportTag, addOrRemove); String flowRef = buildExtTunnelTblToLportDispFlowRef(vni, elanLportTag); if (addOrRemove == NwConstants.ADD_FLOW) { List matches = Collections.singletonList(new MatchTunnelId(BigInteger.valueOf(vni))); List instructions = buildSetLportTagAndGotoLportDispInstructions(elanLportTag); Flow flow = MDSALUtil.buildFlowNew(NwConstants.EXTERNAL_TUNNEL_TABLE, flowRef, CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY, flowRef, 0, 0, NwConstants.TUNNEL_TABLE_COOKIE.add(BigInteger.valueOf(elanTag)), matches, instructions); mdsalManager.installFlow(dpnId, flow); } else { Flow flow = new FlowBuilder().setTableId(NwConstants.EXTERNAL_TUNNEL_TABLE).setId(new FlowId(flowRef)).build(); mdsalManager.removeFlow(dpnId, flow); } } /** * Builds a List of Instructions that set the ElanPseudoPort Tag in * metadata and sends to LPortDispatcher table (via Table 80). * * @param lportTag Dataplane identifier of the ElanPseudoPort * * @return the List of Instructions */ public static List buildSetLportTagAndGotoLportDispInstructions(int lportTag) { int instructionKey = 0; BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(lportTag, ServiceIndex.getIndex(NwConstants.SCF_SERVICE_NAME, NwConstants.SCF_SERVICE_INDEX)); List result = Arrays.asList(MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, MetaDataUtil.getMetaDataMaskForLPortDispatcher(), ++instructionKey), MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.L3_INTERFACE_TABLE, ++instructionKey)); return result; } private static String buildExtTunnelTblToLportDispFlowRef(long vni, int elanLportTag) { return CloudServiceChainConstants.L2_FLOWID_PREFIX + vni + NwConstants.FLOWID_SEPARATOR + elanLportTag; } private static String buildLportDispToScfFlowRef(int elanLportTag, long scfTag) { return CloudServiceChainConstants.ELAN_TO_SCF_L2_FLOWID_PREFIX + elanLportTag + NwConstants.FLOWID_SEPARATOR + scfTag; } private static String buildLportDispFromScfFlowRef(long elanTag, int elanLportTag) { return CloudServiceChainConstants.SCF_TO_ELAN_L2_FLOWID_PREFIX + elanTag + NwConstants.FLOWID_SEPARATOR + elanLportTag; } /** * Stores the relation between ElanLport and scfTag. * * @param broker dataBroker service reference * @param elanInstanceName Name of the ELAN. Typically its UUID * @param lportTag Dataplane identifier of the ElanPseudoPort * @param scfTag Dataplane identifier of the SCF * @param addOrRemove States if flows must be added or removed */ public static void updateElanToLportTagMap(final DataBroker broker, final String elanInstanceName, final int lportTag, final long scfTag, final int addOrRemove) { Long portTag = Long.valueOf(lportTag); ElanToPseudoPortDataKey key = new ElanToPseudoPortDataKey(portTag, scfTag); InstanceIdentifier path = InstanceIdentifier.builder(ElanInstances.class) .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName)) .augmentation(ElanServiceChainState.class) .child(ElanToPseudoPortData.class, key).build(); if (addOrRemove == NwConstants.ADD_FLOW) { ElanToPseudoPortData newValue = new ElanToPseudoPortDataBuilder().withKey(key).setElanLportTag(portTag) .setScfTag(scfTag).build(); MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, path, newValue); } else { MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, path); } } /** * Read from ElanToLportTagMap the PsuedoLogicalPort related with a given elan. * * @param broker dataBroker service reference * @param elanInstanceName the name of the Elan * @return the ElanToPseudoPortData object or Optional.absent() if it * cannot be found */ public static Optional getElanServiceChainState(final DataBroker broker, final String elanInstanceName) { InstanceIdentifier path = InstanceIdentifier.builder(ElanInstances.class) .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName)) .augmentation(ElanServiceChainState.class).build(); Optional elanServiceChainStateOpc = MDSALUtil.read(broker,LogicalDatastoreType.CONFIGURATION, path); return elanServiceChainStateOpc; } }