| /* |
| * Copyright 2015-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.sfc.manager.impl; |
| |
| import static org.slf4j.LoggerFactory.getLogger; |
| |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Set; |
| import java.util.UUID; |
| |
| import org.apache.felix.scr.annotations.Activate; |
| import org.apache.felix.scr.annotations.Component; |
| import org.apache.felix.scr.annotations.Deactivate; |
| import org.apache.felix.scr.annotations.Reference; |
| import org.apache.felix.scr.annotations.ReferenceCardinality; |
| import org.apache.felix.scr.annotations.Service; |
| import org.onlab.packet.Ethernet; |
| import org.onlab.packet.IPv4; |
| import org.onlab.packet.IpAddress; |
| import org.onlab.packet.MacAddress; |
| import org.onlab.packet.TCP; |
| import org.onlab.packet.UDP; |
| import org.onlab.util.ItemNotFoundException; |
| import org.onlab.util.KryoNamespace; |
| import org.onosproject.core.ApplicationId; |
| import org.onosproject.core.CoreService; |
| import org.onosproject.core.IdGenerator; |
| import org.onosproject.net.ConnectPoint; |
| import org.onosproject.net.NshServicePathId; |
| import org.onosproject.net.PortNumber; |
| import org.onosproject.net.flow.DefaultTrafficTreatment; |
| import org.onosproject.net.flow.TrafficTreatment; |
| import org.onosproject.net.packet.DefaultOutboundPacket; |
| import org.onosproject.net.packet.OutboundPacket; |
| import org.onosproject.net.packet.PacketContext; |
| import org.onosproject.net.packet.PacketProcessor; |
| import org.onosproject.net.packet.PacketService; |
| import org.onosproject.sfc.installer.impl.SfcFlowRuleInstallerImpl; |
| import org.onosproject.sfc.manager.SfcService; |
| import org.onosproject.store.serializers.KryoNamespaces; |
| import org.onosproject.store.service.DistributedSet; |
| import org.onosproject.store.service.EventuallyConsistentMap; |
| import org.onosproject.store.service.Serializer; |
| import org.onosproject.store.service.StorageService; |
| import org.onosproject.store.service.WallClockTimestamp; |
| import org.onosproject.vtnrsc.DefaultFiveTuple; |
| import org.onosproject.vtnrsc.FiveTuple; |
| import org.onosproject.vtnrsc.FixedIp; |
| import org.onosproject.vtnrsc.FlowClassifier; |
| import org.onosproject.vtnrsc.FlowClassifierId; |
| import org.onosproject.vtnrsc.LoadBalanceId; |
| import org.onosproject.vtnrsc.PortChain; |
| import org.onosproject.vtnrsc.PortChainId; |
| import org.onosproject.vtnrsc.PortPair; |
| import org.onosproject.vtnrsc.PortPairGroup; |
| import org.onosproject.vtnrsc.PortPairGroupId; |
| import org.onosproject.vtnrsc.PortPairId; |
| import org.onosproject.vtnrsc.TenantId; |
| import org.onosproject.vtnrsc.VirtualPort; |
| import org.onosproject.vtnrsc.VirtualPortId; |
| import org.onosproject.vtnrsc.event.VtnRscEvent; |
| import org.onosproject.vtnrsc.event.VtnRscListener; |
| import org.onosproject.vtnrsc.flowclassifier.FlowClassifierService; |
| import org.onosproject.vtnrsc.portchain.PortChainService; |
| import org.onosproject.vtnrsc.portpairgroup.PortPairGroupService; |
| import org.onosproject.vtnrsc.service.VtnRscService; |
| import org.onosproject.vtnrsc.virtualport.VirtualPortService; |
| import org.slf4j.Logger; |
| |
| import com.google.common.collect.Lists; |
| |
| /** |
| * Provides implementation of SFC Service. |
| */ |
| @Component(immediate = true) |
| @Service |
| public class SfcManager implements SfcService { |
| |
| private final Logger log = getLogger(getClass()); |
| |
| private String nshSpiIdTopic = "nsh-spi-id"; |
| private static final String APP_ID = "org.onosproject.app.vtn"; |
| private static final int SFC_PRIORITY = 1000; |
| private static final int MAX_NSH_SPI_ID = 0x7FFFF; |
| private static final int MAX_LOAD_BALANCE_ID = 0x20; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected VtnRscService vtnRscService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected CoreService coreService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected PacketService packetService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected PortChainService portChainService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected PortPairGroupService portPairGroupService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected FlowClassifierService flowClassifierService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected VirtualPortService virtualPortService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected StorageService storageService; |
| |
| protected SfcPacketProcessor processor = new SfcPacketProcessor(); |
| |
| protected ApplicationId appId; |
| protected IdGenerator nshSpiIdGenerator; |
| protected EventuallyConsistentMap<PortChainId, Integer> nshSpiPortChainMap; |
| protected EventuallyConsistentMap<PortChainId, List<FiveTuple>> portChainFiveTupleMap; |
| protected DistributedSet<Integer> nshSpiIdFreeList; |
| |
| private final VtnRscListener vtnRscListener = new InnerVtnRscListener(); |
| |
| @Activate |
| public void activate() { |
| appId = coreService.registerApplication(APP_ID); |
| nshSpiIdGenerator = coreService.getIdGenerator(nshSpiIdTopic); |
| |
| vtnRscService.addListener(vtnRscListener); |
| |
| KryoNamespace.Builder serializer = KryoNamespace |
| .newBuilder() |
| .register(PortChainId.class, UUID.class, FiveTuple.class, IpAddress.class, PortNumber.class, |
| DefaultFiveTuple.class, IpAddress.Version.class, TenantId.class); |
| |
| nshSpiPortChainMap = storageService.<PortChainId, Integer>eventuallyConsistentMapBuilder() |
| .withName("nshSpiPortChainMap").withSerializer(serializer) |
| .withTimestampProvider((k, v) ->new WallClockTimestamp()).build(); |
| |
| portChainFiveTupleMap = storageService.<PortChainId, List<FiveTuple>>eventuallyConsistentMapBuilder() |
| .withName("portChainFiveTupleMap").withSerializer(serializer) |
| .withTimestampProvider((k, v) ->new WallClockTimestamp()).build(); |
| |
| nshSpiIdFreeList = storageService.<Integer>setBuilder() |
| .withName("nshSpiIdDeletedList") |
| .withSerializer(Serializer.using(KryoNamespaces.API)) |
| .build() |
| .asDistributedSet(); |
| |
| packetService.addProcessor(processor, PacketProcessor.director(SFC_PRIORITY)); |
| log.info("Started"); |
| } |
| |
| @Deactivate |
| public void deactivate() { |
| vtnRscService.removeListener(vtnRscListener); |
| packetService.removeProcessor(processor); |
| log.info("Stopped"); |
| } |
| |
| /* |
| * Handle events. |
| */ |
| private class InnerVtnRscListener implements VtnRscListener { |
| @Override |
| public void event(VtnRscEvent event) { |
| |
| if (VtnRscEvent.Type.PORT_PAIR_PUT == event.type()) { |
| PortPair portPair = event.subject().portPair(); |
| onPortPairCreated(portPair); |
| } else if (VtnRscEvent.Type.PORT_PAIR_DELETE == event.type()) { |
| PortPair portPair = event.subject().portPair(); |
| onPortPairDeleted(portPair); |
| } else if (VtnRscEvent.Type.PORT_PAIR_UPDATE == event.type()) { |
| PortPair portPair = event.subject().portPair(); |
| onPortPairDeleted(portPair); |
| onPortPairCreated(portPair); |
| } else if (VtnRscEvent.Type.PORT_PAIR_GROUP_PUT == event.type()) { |
| PortPairGroup portPairGroup = event.subject().portPairGroup(); |
| onPortPairGroupCreated(portPairGroup); |
| } else if (VtnRscEvent.Type.PORT_PAIR_GROUP_DELETE == event.type()) { |
| PortPairGroup portPairGroup = event.subject().portPairGroup(); |
| onPortPairGroupDeleted(portPairGroup); |
| } else if (VtnRscEvent.Type.PORT_PAIR_GROUP_UPDATE == event.type()) { |
| PortPairGroup portPairGroup = event.subject().portPairGroup(); |
| onPortPairGroupDeleted(portPairGroup); |
| onPortPairGroupCreated(portPairGroup); |
| } else if (VtnRscEvent.Type.FLOW_CLASSIFIER_PUT == event.type()) { |
| FlowClassifier flowClassifier = event.subject().flowClassifier(); |
| onFlowClassifierCreated(flowClassifier); |
| } else if (VtnRscEvent.Type.FLOW_CLASSIFIER_DELETE == event.type()) { |
| FlowClassifier flowClassifier = event.subject().flowClassifier(); |
| onFlowClassifierDeleted(flowClassifier); |
| } else if (VtnRscEvent.Type.FLOW_CLASSIFIER_UPDATE == event.type()) { |
| FlowClassifier flowClassifier = event.subject().flowClassifier(); |
| onFlowClassifierDeleted(flowClassifier); |
| onFlowClassifierCreated(flowClassifier); |
| } else if (VtnRscEvent.Type.PORT_CHAIN_PUT == event.type()) { |
| PortChain portChain = event.subject().portChain(); |
| if (portChain.oldPortChain() != null) { |
| onPortChainDeleted(portChain.oldPortChain()); |
| } |
| onPortChainCreated(portChain); |
| } else if (VtnRscEvent.Type.PORT_CHAIN_DELETE == event.type()) { |
| PortChain portChain = event.subject().portChain(); |
| onPortChainDeleted(portChain); |
| portChainFiveTupleMap.remove(portChain.portChainId()); |
| } else if (VtnRscEvent.Type.PORT_CHAIN_UPDATE == event.type()) { |
| PortChain portChain = event.subject().portChain(); |
| onPortChainDeleted(portChain); |
| onPortChainCreated(portChain); |
| } |
| } |
| } |
| |
| @Override |
| public void onPortPairCreated(PortPair portPair) { |
| log.debug("onPortPairCreated"); |
| // Do nothing |
| } |
| |
| @Override |
| public void onPortPairDeleted(PortPair portPair) { |
| log.debug("onPortPairDeleted"); |
| // Do nothing |
| } |
| |
| @Override |
| public void onPortPairGroupCreated(PortPairGroup portPairGroup) { |
| log.debug("onPortPairGroupCreated"); |
| // Do nothing |
| } |
| |
| @Override |
| public void onPortPairGroupDeleted(PortPairGroup portPairGroup) { |
| log.debug("onPortPairGroupDeleted"); |
| // Do nothing |
| } |
| |
| @Override |
| public void onFlowClassifierCreated(FlowClassifier flowClassifier) { |
| log.debug("onFlowClassifierCreated"); |
| // Do nothing |
| } |
| |
| @Override |
| public void onFlowClassifierDeleted(FlowClassifier flowClassifier) { |
| log.debug("onFlowClassifierDeleted"); |
| // Do nothing |
| } |
| |
| @Override |
| public void onPortChainCreated(PortChain portChain) { |
| NshServicePathId nshSpi; |
| log.info("On port chain created"); |
| |
| int spi = getNextNshSpi(); |
| if (spi > MAX_NSH_SPI_ID) { |
| log.error("Reached max limit of service path index." + "Failed to install SFC for port chain {}", |
| portChain.portChainId().toString()); |
| return; |
| } |
| nshSpi = NshServicePathId.of(spi); |
| nshSpiPortChainMap.put(portChain.portChainId(), new Integer(spi)); |
| if (!portChainFiveTupleMap.containsKey(portChain.portChainId())) { |
| portChainFiveTupleMap.put(portChain.portChainId(), Lists.newArrayList()); |
| } |
| // Install classifier rule to send the packet to controller |
| SfcFlowRuleInstallerImpl flowRuleInstaller = new SfcFlowRuleInstallerImpl(appId); |
| flowRuleInstaller.installFlowClassifier(portChain, nshSpi); |
| |
| // Install rules for already identified five tuples. |
| List<FiveTuple> list = portChainFiveTupleMap.get(portChain.portChainId()); |
| for (FiveTuple fiveTuple : list) { |
| LoadBalanceId id = loadBalanceSfc(portChain.portChainId(), fiveTuple); |
| // Get nsh service path index |
| nshSpi = NshServicePathId.of(getNshServicePathId(id, spi)); |
| // download the required flow rules for classifier and |
| // forwarding |
| flowRuleInstaller.installLoadBalancedFlowRules(portChain, fiveTuple, nshSpi); |
| } |
| } |
| |
| @Override |
| public void onPortChainDeleted(PortChain portChain) { |
| log.info("onPortChainDeleted"); |
| if (!nshSpiPortChainMap.containsKey(portChain.portChainId())) { |
| throw new ItemNotFoundException("Unable to find NSH SPI"); |
| } |
| |
| int nshSpiId = nshSpiPortChainMap.get(portChain.portChainId()); |
| // Uninstall classifier rules |
| SfcFlowRuleInstallerImpl flowRuleInstaller = new SfcFlowRuleInstallerImpl(appId); |
| flowRuleInstaller.unInstallFlowClassifier(portChain, NshServicePathId.of(nshSpiId)); |
| // remove from nshSpiPortChainMap and add to nshSpiIdFreeList |
| nshSpiPortChainMap.remove(portChain.portChainId()); |
| nshSpiIdFreeList.add(nshSpiId); |
| |
| // Uninstall load balanced classifier and forwarding rules. |
| NshServicePathId nshSpi; |
| LoadBalanceId id; |
| List<LoadBalanceId> processedIdList = Lists.newArrayList(); |
| Set<FiveTuple> fiveTupleSet = portChain.getLoadBalanceIdMapKeys(); |
| for (FiveTuple fiveTuple : fiveTupleSet) { |
| id = portChain.getLoadBalanceId(fiveTuple); |
| nshSpi = NshServicePathId.of(getNshServicePathId(id, nshSpiId)); |
| if (processedIdList.contains(id)) { |
| // Multiple five tuple can have single path. In this case only |
| // the classifier rule need to delete |
| flowRuleInstaller.unInstallLoadBalancedClassifierRules(portChain, fiveTuple, nshSpi); |
| continue; |
| } else { |
| processedIdList.add(id); |
| } |
| flowRuleInstaller.unInstallLoadBalancedFlowRules(portChain, fiveTuple, nshSpi); |
| } |
| |
| // Reset load for all the port pairs |
| List<PortPairGroupId> ppgIdlist = portChain.portPairGroups(); |
| ListIterator<PortPairGroupId> ppgIdListIterator = ppgIdlist.listIterator(); |
| while (ppgIdListIterator.hasNext()) { |
| PortPairGroupId portPairGroupId = ppgIdListIterator.next(); |
| PortPairGroup ppg = portPairGroupService.getPortPairGroup(portPairGroupId); |
| ppg.resetLoad(); |
| } |
| } |
| |
| /** |
| * Get next nsh service path identifier. |
| * |
| * @return value of service path identifier |
| */ |
| int getNextNshSpi() { |
| // If there is any free id use it. Otherwise generate new id. |
| if (nshSpiIdFreeList.isEmpty()) { |
| return (int) nshSpiIdGenerator.getNewId(); |
| } |
| Iterator<Integer> it = nshSpiIdFreeList.iterator(); |
| Integer value = it.next(); |
| nshSpiIdFreeList.remove(value); |
| return value; |
| } |
| |
| private class SfcPacketProcessor implements PacketProcessor { |
| |
| /** |
| * Check for given ip match with the fixed ips for the virtual port. |
| * |
| * @param vPortId virtual port id |
| * @param ip ip address to match |
| * @return true if the ip match with the fixed ips in virtual port false otherwise |
| */ |
| private boolean checkIpInVirtualPort(VirtualPortId vPortId, IpAddress ip) { |
| boolean found = false; |
| Set<FixedIp> ips = virtualPortService.getPort(vPortId).fixedIps(); |
| for (FixedIp fixedIp : ips) { |
| if (fixedIp.ip().equals(ip)) { |
| found = true; |
| break; |
| } |
| } |
| return found; |
| } |
| |
| /** |
| * Find the port chain for the received packet. |
| * |
| * @param fiveTuple five tuple info from the packet |
| * @return portChainId id of port chain |
| */ |
| private PortChainId findPortChainFromFiveTuple(FiveTuple fiveTuple) { |
| |
| PortChainId portChainId = null; |
| |
| Iterable<PortChain> portChains = portChainService.getPortChains(); |
| if (portChains == null) { |
| log.error("Could not retrive port chain list"); |
| } |
| |
| // Identify the port chain to which the packet belongs |
| for (final PortChain portChain : portChains) { |
| |
| if (!portChain.tenantId().equals(fiveTuple.tenantId())) { |
| continue; |
| } |
| |
| Iterable<FlowClassifierId> flowClassifiers = portChain.flowClassifiers(); |
| |
| // One port chain can have multiple flow classifiers. |
| for (final FlowClassifierId flowClassifierId : flowClassifiers) { |
| |
| FlowClassifier flowClassifier = flowClassifierService.getFlowClassifier(flowClassifierId); |
| boolean match = false; |
| // Check whether protocol is set in flow classifier |
| if (flowClassifier.protocol() != null) { |
| if ((flowClassifier.protocol().equalsIgnoreCase("TCP") |
| && fiveTuple.protocol() == IPv4.PROTOCOL_TCP) |
| || (flowClassifier.protocol().equalsIgnoreCase("UDP") |
| && fiveTuple.protocol() == IPv4.PROTOCOL_UDP) |
| || (flowClassifier.protocol().equalsIgnoreCase("ICMP") |
| && fiveTuple.protocol() == IPv4.PROTOCOL_ICMP)) { |
| match = true; |
| } else { |
| continue; |
| } |
| } |
| |
| // Check whether source ip prefix is set in flow classifier |
| if (flowClassifier.srcIpPrefix() != null) { |
| if (flowClassifier.srcIpPrefix().contains(fiveTuple.ipSrc())) { |
| match = true; |
| } else { |
| continue; |
| } |
| } |
| |
| // Check whether destination ip prefix is set in flow classifier |
| if (flowClassifier.dstIpPrefix() != null) { |
| if (flowClassifier.dstIpPrefix().contains(fiveTuple.ipDst())) { |
| match = true; |
| } else { |
| continue; |
| } |
| } |
| |
| // Check whether source port is set in flow classifier |
| if (fiveTuple.portSrc().toLong() >= flowClassifier.minSrcPortRange() || |
| fiveTuple.portSrc().toLong() <= flowClassifier.maxSrcPortRange()) { |
| match = true; |
| } else { |
| continue; |
| } |
| |
| // Check whether destination port is set in flow classifier |
| if (fiveTuple.portDst().toLong() >= flowClassifier.minSrcPortRange() || |
| fiveTuple.portDst().toLong() <= flowClassifier.maxSrcPortRange()) { |
| match = true; |
| } else { |
| continue; |
| } |
| |
| // Check whether neutron source port is set in flow classfier |
| if ((flowClassifier.srcPort() != null) && (!flowClassifier.srcPort().portId().isEmpty())) { |
| match = checkIpInVirtualPort(VirtualPortId.portId(flowClassifier.srcPort().portId()), |
| fiveTuple.ipSrc()); |
| if (!match) { |
| continue; |
| } |
| } |
| |
| // Check whether destination neutron destination port is set in flow classifier |
| if ((flowClassifier.dstPort() != null) && (!flowClassifier.dstPort().portId().isEmpty())) { |
| match = checkIpInVirtualPort(VirtualPortId.portId(flowClassifier.dstPort().portId()), |
| fiveTuple.ipDst()); |
| if (!match) { |
| continue; |
| } |
| } |
| |
| if (match) { |
| portChainId = portChain.portChainId(); |
| break; |
| } |
| } |
| } |
| return portChainId; |
| } |
| |
| /** |
| * Get the tenant id for the given mac address. |
| * |
| * @param mac mac address |
| * @return tenantId tenant id for the given mac address |
| */ |
| private TenantId getTenantId(MacAddress mac) { |
| Collection<VirtualPort> virtualPorts = virtualPortService.getPorts(); |
| for (VirtualPort virtualPort : virtualPorts) { |
| if (virtualPort.macAddress().equals(mac)) { |
| return virtualPort.tenantId(); |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public void process(PacketContext context) { |
| Ethernet packet = context.inPacket().parsed(); |
| if (packet == null || portChainService.getPortChainCount() == 0) { |
| return; |
| } |
| // get the five tuple parameters for the packet |
| short ethType = packet.getEtherType(); |
| IpAddress ipSrc = null; |
| IpAddress ipDst = null; |
| int portSrc = 0; |
| int portDst = 0; |
| byte protocol = 0; |
| MacAddress macSrc = packet.getSourceMAC(); |
| MacAddress macDst = packet.getDestinationMAC(); |
| TenantId tenantId = getTenantId(macSrc); |
| |
| if (ethType == Ethernet.TYPE_IPV4) { |
| IPv4 ipv4Packet = (IPv4) packet.getPayload(); |
| ipSrc = IpAddress.valueOf(ipv4Packet.getSourceAddress()); |
| ipDst = IpAddress.valueOf(ipv4Packet.getDestinationAddress()); |
| protocol = ipv4Packet.getProtocol(); |
| if (protocol == IPv4.PROTOCOL_TCP) { |
| TCP tcpPacket = (TCP) ipv4Packet.getPayload(); |
| portSrc = tcpPacket.getSourcePort(); |
| portDst = tcpPacket.getDestinationPort(); |
| } else if (protocol == IPv4.PROTOCOL_UDP) { |
| UDP udpPacket = (UDP) ipv4Packet.getPayload(); |
| portSrc = udpPacket.getSourcePort(); |
| portDst = udpPacket.getDestinationPort(); |
| } else if (protocol == IPv4.PROTOCOL_ICMP) { |
| // do nothing |
| } else { |
| // No need to process other packets received by controller. |
| return; |
| } |
| } else { |
| return; |
| } |
| |
| FiveTuple fiveTuple = DefaultFiveTuple.builder() |
| .setIpSrc(ipSrc) |
| .setIpDst(ipDst) |
| .setPortSrc(PortNumber.portNumber(portSrc)) |
| .setPortDst(PortNumber.portNumber(portDst)) |
| .setProtocol(protocol) |
| .setTenantId(tenantId) |
| .setMacSrc(macSrc) |
| .setMacDst(macDst) |
| .build(); |
| |
| PortChainId portChainId = findPortChainFromFiveTuple(fiveTuple); |
| |
| if (portChainId == null) { |
| return; |
| } |
| |
| // Once the 5 tuple and port chain are identified, give this input for load balancing |
| addToPortChainIdFiveTupleMap(portChainId, fiveTuple); |
| LoadBalanceId id = loadBalanceSfc(portChainId, fiveTuple); |
| // Get nsh service path index |
| NshServicePathId nshSpi; |
| PortChain portChain = portChainService.getPortChain(portChainId); |
| if (nshSpiPortChainMap.containsKey(portChain.portChainId())) { |
| int nshSpiId = nshSpiPortChainMap.get(portChain.portChainId()); |
| nshSpi = NshServicePathId.of(getNshServicePathId(id, nshSpiId)); |
| } else { |
| int nshSpiId = getNextNshSpi(); |
| if (nshSpiId > MAX_NSH_SPI_ID) { |
| log.error("Reached max limit of service path index." |
| + "Failed to install SFC for port chain {}", portChain.portChainId()); |
| return; |
| } |
| nshSpi = NshServicePathId.of(getNshServicePathId(id, nshSpiId)); |
| nshSpiPortChainMap.put(portChain.portChainId(), new Integer(nshSpiId)); |
| } |
| // download the required flow rules for classifier and forwarding |
| // install in OVS. |
| SfcFlowRuleInstallerImpl flowRuleInstaller = new SfcFlowRuleInstallerImpl(appId); |
| flowRuleInstaller.installLoadBalancedFlowRules(portChain, fiveTuple, nshSpi); |
| sendPacket(context); |
| } |
| |
| /** |
| * Send packet back to classifier. |
| * |
| * @param context packet context |
| */ |
| private void sendPacket(PacketContext context) { |
| |
| ConnectPoint sourcePoint = context.inPacket().receivedFrom(); |
| |
| TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(PortNumber.TABLE).build(); |
| OutboundPacket packet = new DefaultOutboundPacket(sourcePoint.deviceId(), treatment, context.inPacket() |
| .unparsed()); |
| packetService.emit(packet); |
| log.trace("Sending packet: {}", packet); |
| } |
| } |
| |
| /** |
| * Encapsulate 5 bit load balance id to nsh spi. |
| * |
| * @param id load balance identifier |
| * @param nshSpiId nsh service path index |
| * @return updated service path index |
| */ |
| protected int getNshServicePathId(LoadBalanceId id, int nshSpiId) { |
| int nshSpiNew = nshSpiId << 5; |
| nshSpiNew = nshSpiNew | id.loadBalanceId(); |
| return nshSpiNew; |
| } |
| |
| private void addToPortChainIdFiveTupleMap(PortChainId portChainId, FiveTuple fiveTuple) { |
| List<FiveTuple> list = portChainFiveTupleMap.get(portChainId); |
| list.add(fiveTuple); |
| portChainFiveTupleMap.put(portChainId, list); |
| } |
| |
| /** |
| * Find the load balanced path set it to port chain for the given five |
| * tuple. |
| * |
| * @param portChainId port chain id |
| * @param fiveTuple five tuple info |
| * @return load balance id |
| */ |
| private LoadBalanceId loadBalanceSfc(PortChainId portChainId, FiveTuple fiveTuple) { |
| |
| // Get the port chain |
| PortChain portChain = portChainService.getPortChain(portChainId); |
| List<PortPairId> loadBalancePath = Lists.newArrayList(); |
| LoadBalanceId id; |
| int paths = portChain.getLoadBalancePathSize(); |
| if (paths >= MAX_LOAD_BALANCE_ID) { |
| log.info("Max limit reached for load balance paths. " |
| + "Reusing the created path for port chain {} with five tuple {}", portChainId, fiveTuple); |
| id = LoadBalanceId.of((byte) ((paths + 1) % MAX_LOAD_BALANCE_ID)); |
| portChain.addLoadBalancePath(fiveTuple, id, portChain.getLoadBalancePath(id)); |
| } |
| |
| // Get the list of port pair groups from port chain |
| Iterable<PortPairGroupId> portPairGroups = portChain.portPairGroups(); |
| for (final PortPairGroupId portPairGroupId : portPairGroups) { |
| PortPairGroup portPairGroup = portPairGroupService.getPortPairGroup(portPairGroupId); |
| |
| // Get the list of port pair ids from port pair group. |
| Iterable<PortPairId> portPairs = portPairGroup.portPairs(); |
| int minLoad = 0xFFF; |
| PortPairId minLoadPortPairId = null; |
| for (final PortPairId portPairId : portPairs) { |
| int load = portPairGroup.getLoad(portPairId); |
| if (load == 0) { |
| minLoadPortPairId = portPairId; |
| break; |
| } else { |
| // Check the port pair which has min load. |
| if (load < minLoad) { |
| minLoad = load; |
| minLoadPortPairId = portPairId; |
| } |
| } |
| } |
| if (minLoadPortPairId != null) { |
| loadBalancePath.add(minLoadPortPairId); |
| portPairGroup.addLoad(minLoadPortPairId); |
| } |
| } |
| |
| // Check if the path already exists, if not create a new id |
| id = portChain.matchPath(loadBalancePath); |
| if (id == null) { |
| id = LoadBalanceId.of((byte) (paths + 1)); |
| } |
| |
| portChain.addLoadBalancePath(fiveTuple, id, loadBalancePath); |
| return id; |
| } |
| |
| } |