| /* |
| * 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.installer.impl; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_ENCAP_ETH_TYPE; |
| import static org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_NSH_SI; |
| import static org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_NSH_SPI; |
| import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_ENCAP_ETH_DST; |
| import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_ENCAP_ETH_SRC; |
| import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_NSH_MDTYPE; |
| import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_NSH_NP; |
| import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_POP_NSH; |
| import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_PUSH_NSH; |
| import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_RESUBMIT_TABLE; |
| import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH1; |
| import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH2; |
| import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH3; |
| import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH4; |
| import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_SI; |
| import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_SPI; |
| import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST; |
| import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_TUN_GPE_NP; |
| import static org.slf4j.LoggerFactory.getLogger; |
| |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Set; |
| |
| import org.onlab.osgi.DefaultServiceDirectory; |
| import org.onlab.osgi.ServiceDirectory; |
| import org.onlab.packet.Ethernet; |
| import org.onlab.packet.IPv4; |
| import org.onlab.packet.Ip4Address; |
| import org.onlab.packet.IpPrefix; |
| import org.onlab.packet.MacAddress; |
| import org.onlab.packet.TpPort; |
| import org.onosproject.core.ApplicationId; |
| import org.onosproject.net.AnnotationKeys; |
| import org.onosproject.net.ConnectPoint; |
| import org.onosproject.net.Device; |
| import org.onosproject.net.DeviceId; |
| import org.onosproject.net.Host; |
| import org.onosproject.net.HostId; |
| import org.onosproject.net.NshContextHeader; |
| import org.onosproject.net.NshServiceIndex; |
| import org.onosproject.net.NshServicePathId; |
| import org.onosproject.net.Port; |
| import org.onosproject.net.PortNumber; |
| import org.onosproject.net.behaviour.BridgeConfig; |
| import org.onosproject.net.behaviour.ExtensionSelectorResolver; |
| import org.onosproject.net.behaviour.ExtensionTreatmentResolver; |
| import org.onosproject.net.device.DeviceService; |
| import org.onosproject.net.driver.DriverHandler; |
| import org.onosproject.net.driver.DriverService; |
| import org.onosproject.net.flow.DefaultTrafficSelector; |
| import org.onosproject.net.flow.DefaultTrafficTreatment; |
| 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.ExtensionSelector; |
| import org.onosproject.net.flow.instructions.ExtensionTreatment; |
| import org.onosproject.net.flow.instructions.ExtensionTreatmentType; |
| import org.onosproject.net.flow.instructions.Instructions; |
| import org.onosproject.net.flowobjective.DefaultForwardingObjective; |
| import org.onosproject.net.flowobjective.FlowObjectiveService; |
| import org.onosproject.net.flowobjective.ForwardingObjective; |
| import org.onosproject.net.flowobjective.ForwardingObjective.Flag; |
| import org.onosproject.net.flowobjective.Objective; |
| import org.onosproject.net.host.HostService; |
| import org.onosproject.sfc.installer.SfcFlowRuleInstallerService; |
| import org.onosproject.vtnrsc.FiveTuple; |
| import org.onosproject.vtnrsc.FlowClassifier; |
| import org.onosproject.vtnrsc.FlowClassifierId; |
| import org.onosproject.vtnrsc.PortChain; |
| import org.onosproject.vtnrsc.PortPair; |
| import org.onosproject.vtnrsc.PortPairGroup; |
| import org.onosproject.vtnrsc.PortPairGroupId; |
| import org.onosproject.vtnrsc.PortPairId; |
| import org.onosproject.vtnrsc.SegmentationId; |
| import org.onosproject.vtnrsc.VirtualPort; |
| import org.onosproject.vtnrsc.VirtualPortId; |
| import org.onosproject.vtnrsc.flowclassifier.FlowClassifierService; |
| import org.onosproject.vtnrsc.portpair.PortPairService; |
| import org.onosproject.vtnrsc.portpairgroup.PortPairGroupService; |
| import org.onosproject.vtnrsc.service.VtnRscService; |
| import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService; |
| import org.onosproject.vtnrsc.virtualport.VirtualPortService; |
| import org.slf4j.Logger; |
| |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Sets; |
| |
| /** |
| * Provides flow classifier installer implementation. |
| */ |
| public class SfcFlowRuleInstallerImpl implements SfcFlowRuleInstallerService { |
| private final Logger log = getLogger(getClass()); |
| |
| protected VirtualPortService virtualPortService; |
| protected VtnRscService vtnRscService; |
| protected PortPairService portPairService; |
| protected PortPairGroupService portPairGroupService; |
| protected FlowClassifierService flowClassifierService; |
| protected DriverService driverService; |
| protected DeviceService deviceService; |
| protected HostService hostService; |
| protected TenantNetworkService tenantNetworkService; |
| protected FlowObjectiveService flowObjectiveService; |
| protected ApplicationId appId; |
| |
| private static final String PORT_CHAIN_NOT_NULL = "Port-Chain cannot be null"; |
| private static final int FLOW_CLASSIFIER_PRIORITY = 0xC738; |
| private static final int DEFAULT_FORWARDER_PRIORITY = 0xD6D8; |
| private static final int ENCAP_OUTPUT_PRIORITY = 0x64; |
| private static final int TUNNEL_SEND_PRIORITY = 0xC8; |
| private static final String SWITCH_CHANNEL_ID = "channelId"; |
| private static final int ENCAP_OUTPUT_TABLE = 4; |
| private static final int TUNNEL_SEND_TABLE = 7; |
| private static final short ENCAP_ETH_TYPE = (short) 0x894f; |
| private static final String DEFAULT_IP = "0.0.0.0"; |
| private static final String VXLANPORT_HEAD = "vxlan-0.0.0.0"; |
| |
| /* Port chain params */ |
| private short nshSi; |
| List<DeviceId> classifierList; |
| List<DeviceId> forwarderList; |
| |
| /** |
| * Default constructor. |
| */ |
| public SfcFlowRuleInstallerImpl() { |
| } |
| |
| /** |
| * Explicit constructor. |
| * |
| * @param appId application id. |
| */ |
| public SfcFlowRuleInstallerImpl(ApplicationId appId) { |
| this.appId = checkNotNull(appId, "ApplicationId can not be null"); |
| ServiceDirectory serviceDirectory = new DefaultServiceDirectory(); |
| this.flowObjectiveService = serviceDirectory.get(FlowObjectiveService.class); |
| this.driverService = serviceDirectory.get(DriverService.class); |
| this.deviceService = serviceDirectory.get(DeviceService.class); |
| this.hostService = serviceDirectory.get(HostService.class); |
| this.virtualPortService = serviceDirectory.get(VirtualPortService.class); |
| this.vtnRscService = serviceDirectory.get(VtnRscService.class); |
| this.portPairService = serviceDirectory.get(PortPairService.class); |
| this.portPairGroupService = serviceDirectory.get(PortPairGroupService.class); |
| this.flowClassifierService = serviceDirectory.get(FlowClassifierService.class); |
| this.tenantNetworkService = serviceDirectory.get(TenantNetworkService.class); |
| nshSi = 0xff; |
| } |
| |
| @Override |
| public ConnectPoint installFlowClassifier(PortChain portChain, NshServicePathId nshSpiId) { |
| checkNotNull(portChain, PORT_CHAIN_NOT_NULL); |
| // Get the portPairGroup |
| List<PortPairGroupId> llPortPairGroupIdList = portChain.portPairGroups(); |
| ListIterator<PortPairGroupId> portPairGroupIdListIterator = llPortPairGroupIdList.listIterator(); |
| PortPairGroupId portPairGroupId = portPairGroupIdListIterator.next(); |
| PortPairGroup portPairGroup = portPairGroupService.getPortPairGroup(portPairGroupId); |
| List<PortPairId> llPortPairIdList = portPairGroup.portPairs(); |
| |
| // Get port pair |
| ListIterator<PortPairId> portPairListIterator = llPortPairIdList.listIterator(); |
| PortPairId portPairId = portPairListIterator.next(); |
| PortPair portPair = portPairService.getPortPair(portPairId); |
| |
| return installSfcClassifierRules(portChain, portPair, nshSpiId, null, Objective.Operation.ADD); |
| } |
| |
| @Override |
| public ConnectPoint unInstallFlowClassifier(PortChain portChain, NshServicePathId nshSpiId) { |
| checkNotNull(portChain, PORT_CHAIN_NOT_NULL); |
| // Get the portPairGroup |
| List<PortPairGroupId> llPortPairGroupIdList = portChain.portPairGroups(); |
| ListIterator<PortPairGroupId> portPairGroupIdListIterator = llPortPairGroupIdList.listIterator(); |
| PortPairGroupId portPairGroupId = portPairGroupIdListIterator.next(); |
| PortPairGroup portPairGroup = portPairGroupService.getPortPairGroup(portPairGroupId); |
| List<PortPairId> llPortPairIdList = portPairGroup.portPairs(); |
| |
| // Get port pair |
| ListIterator<PortPairId> portPairListIterator = llPortPairIdList.listIterator(); |
| PortPairId portPairId = portPairListIterator.next(); |
| PortPair portPair = portPairService.getPortPair(portPairId); |
| |
| return installSfcClassifierRules(portChain, portPair, nshSpiId, null, Objective.Operation.REMOVE); |
| } |
| |
| @Override |
| public ConnectPoint installLoadBalancedFlowRules(PortChain portChain, FiveTuple fiveTuple, |
| NshServicePathId nshSpiId) { |
| checkNotNull(portChain, PORT_CHAIN_NOT_NULL); |
| |
| return installSfcFlowRules(portChain, fiveTuple, nshSpiId, Objective.Operation.ADD); |
| } |
| |
| @Override |
| public ConnectPoint unInstallLoadBalancedFlowRules(PortChain portChain, FiveTuple fiveTuple, |
| NshServicePathId nshSpiId) { |
| checkNotNull(portChain, PORT_CHAIN_NOT_NULL); |
| return installSfcFlowRules(portChain, fiveTuple, nshSpiId, Objective.Operation.REMOVE); |
| } |
| |
| public ConnectPoint installSfcFlowRules(PortChain portChain, FiveTuple fiveTuple, NshServicePathId nshSpiId, |
| Objective.Operation type) { |
| checkNotNull(portChain, PORT_CHAIN_NOT_NULL); |
| |
| classifierList = Lists.newArrayList(); |
| forwarderList = Lists.newArrayList(); |
| |
| // Get the load balanced path |
| List<PortPairId> portPairs = portChain.getLoadBalancePath(fiveTuple); |
| |
| // Get the first port pair |
| ListIterator<PortPairId> portPairListIterator = portPairs.listIterator(); |
| PortPairId portPairId = portPairListIterator.next(); |
| PortPair currentPortPair = portPairService.getPortPair(portPairId); |
| |
| ConnectPoint connectPoint = installSfcClassifierRules(portChain, currentPortPair, nshSpiId, fiveTuple, type); |
| |
| log.info("Installing encap and output for first port pair"); |
| |
| installSfcEncapOutputRule(currentPortPair, nshSpiId, type); |
| |
| PortPair nextPortPair; |
| while (portPairListIterator.hasNext()) { |
| portPairId = portPairListIterator.next(); |
| nextPortPair = portPairService.getPortPair(portPairId); |
| installSfcForwardRule(currentPortPair, nextPortPair, nshSpiId, type); |
| installSfcEncapOutputRule(nextPortPair, nshSpiId, type); |
| currentPortPair = nextPortPair; |
| } |
| installSfcEndRule(currentPortPair, nshSpiId, type); |
| |
| if (type.equals(Objective.Operation.ADD)) { |
| portChain.addSfcClassifiers(portChain.getLoadBalanceId(fiveTuple), classifierList); |
| portChain.addSfcForwarders(portChain.getLoadBalanceId(fiveTuple), forwarderList); |
| } else { |
| portChain.removeSfcClassifiers(portChain.getLoadBalanceId(fiveTuple), classifierList); |
| portChain.removeSfcForwarders(portChain.getLoadBalanceId(fiveTuple), forwarderList); |
| } |
| return connectPoint; |
| } |
| |
| public void installSfcTunnelReceiveRule(DeviceId deviceId, NshServicePathId nshSpiId, Objective.Operation type) { |
| |
| DriverHandler handler = driverService.createHandler(deviceId); |
| ExtensionSelectorResolver selectorResolver = handler.behaviour(ExtensionSelectorResolver.class); |
| ExtensionSelector nshSpiSelector = selectorResolver.getExtensionSelector(NICIRA_MATCH_NSH_SPI.type()); |
| ExtensionSelector nshSiSelector = selectorResolver.getExtensionSelector(NICIRA_MATCH_NSH_SI.type()); |
| |
| try { |
| nshSpiSelector.setPropertyValue("nshSpi", nshSpiId); |
| } catch (Exception e) { |
| log.error("Failed to set extension selector to match Nsh Spi Id for end rule {}", e.getMessage()); |
| } |
| try { |
| nshSiSelector.setPropertyValue("nshSi", NshServiceIndex.of(nshSi)); |
| } catch (Exception e) { |
| log.error("Failed to set extension selector to match Nsh Si Id for end rule {}", e.getMessage()); |
| } |
| |
| TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); |
| selector.extension(nshSpiSelector, deviceId); |
| selector.extension(nshSiSelector, deviceId); |
| |
| TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); |
| treatment.transition(ENCAP_OUTPUT_TABLE); |
| |
| sendSfcRule(selector, treatment, deviceId, type, DEFAULT_FORWARDER_PRIORITY); |
| } |
| |
| public void installSfcTunnelSendRule(DeviceId deviceId, NshServicePathId nshSpiId, Objective.Operation type) { |
| |
| // Prepare selector with nsp, nsi and inport from egress of port pair |
| DriverHandler handler = driverService.createHandler(deviceId); |
| ExtensionSelectorResolver selectorResolver = handler.behaviour(ExtensionSelectorResolver.class); |
| ExtensionSelector nshSpiSelector = selectorResolver.getExtensionSelector(NICIRA_MATCH_NSH_SPI.type()); |
| ExtensionSelector nshSiSelector = selectorResolver.getExtensionSelector(NICIRA_MATCH_NSH_SI.type()); |
| ExtensionSelector encapEthTypeSelector = selectorResolver.getExtensionSelector(NICIRA_MATCH_ENCAP_ETH_TYPE |
| .type()); |
| try { |
| nshSpiSelector.setPropertyValue("nshSpi", nshSpiId); |
| } catch (Exception e) { |
| log.error("Failed to set extension selector to match Nsh Spi Id for end rule {}", e.getMessage()); |
| } |
| try { |
| nshSiSelector.setPropertyValue("nshSi", NshServiceIndex.of(nshSi)); |
| } catch (Exception e) { |
| log.error("Failed to set extension selector to match Nsh Si Id for end rule {}", e.getMessage()); |
| } |
| try { |
| encapEthTypeSelector.setPropertyValue("encapEthType", ENCAP_ETH_TYPE); |
| } catch (Exception e) { |
| log.error("Failed to set extension selector to match encapEthType {}", deviceId); |
| } |
| |
| TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); |
| selector.extension(nshSpiSelector, deviceId); |
| selector.extension(nshSiSelector, deviceId); |
| // selector.extension(encapEthTypeSelector, deviceId); |
| |
| ExtensionTreatmentResolver treatmentResolver = handler.behaviour(ExtensionTreatmentResolver.class); |
| ExtensionTreatment tunGpeNpTreatment = treatmentResolver.getExtensionInstruction(NICIRA_TUN_GPE_NP.type()); |
| try { |
| tunGpeNpTreatment.setPropertyValue("tunGpeNp", ((byte) 4)); |
| } catch (Exception e) { |
| log.error("Failed to get extension instruction to set tunGpeNp {}", deviceId); |
| } |
| |
| ExtensionTreatment moveC1ToC1 = treatmentResolver |
| .getExtensionInstruction(ExtensionTreatmentType.ExtensionTreatmentTypes |
| .NICIRA_MOV_NSH_C1_TO_C1.type()); |
| |
| ExtensionTreatment moveC2ToC2 = treatmentResolver |
| .getExtensionInstruction(ExtensionTreatmentType.ExtensionTreatmentTypes |
| .NICIRA_MOV_NSH_C2_TO_C2.type()); |
| |
| ExtensionTreatment moveC3ToC3 = treatmentResolver |
| .getExtensionInstruction(ExtensionTreatmentType.ExtensionTreatmentTypes |
| .NICIRA_MOV_NSH_C3_TO_C3.type()); |
| |
| ExtensionTreatment moveC4ToC4 = treatmentResolver |
| .getExtensionInstruction(ExtensionTreatmentType.ExtensionTreatmentTypes |
| .NICIRA_MOV_NSH_C4_TO_C4.type()); |
| |
| ExtensionTreatment moveTunIpv4DstToTunIpv4Dst = treatmentResolver |
| .getExtensionInstruction(ExtensionTreatmentType.ExtensionTreatmentTypes |
| .NICIRA_MOV_TUN_IPV4_DST_TO_TUN_IPV4_DST.type()); |
| |
| ExtensionTreatment moveTunIdToTunId = treatmentResolver |
| .getExtensionInstruction(ExtensionTreatmentType.ExtensionTreatmentTypes |
| .NICIRA_MOV_TUN_ID_TO_TUN_ID.type()); |
| |
| TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); |
| treatment.extension(tunGpeNpTreatment, deviceId); |
| treatment.extension(moveC1ToC1, deviceId); |
| treatment.extension(moveC2ToC2, deviceId); |
| treatment.extension(moveC3ToC3, deviceId); |
| treatment.extension(moveC4ToC4, deviceId); |
| treatment.extension(moveTunIpv4DstToTunIpv4Dst, deviceId); |
| treatment.extension(moveTunIdToTunId, deviceId); |
| |
| Iterable<Device> devices = deviceService.getAvailableDevices(); |
| DeviceId localControllerId = getControllerId(deviceService.getDevice(deviceId), devices); |
| DriverHandler controllerHandler = driverService.createHandler(localControllerId); |
| |
| BridgeConfig bridgeConfig = controllerHandler.behaviour(BridgeConfig.class); |
| Set<PortNumber> ports = bridgeConfig.getPortNumbers(); |
| String tunnelName = "vxlan-" + DEFAULT_IP; |
| ports.stream() |
| .filter(p ->p.name().equalsIgnoreCase(tunnelName)) |
| .forEach(p -> { |
| treatment.setOutput(p); |
| sendSfcRule(selector, treatment, deviceId, type, TUNNEL_SEND_PRIORITY); |
| }); |
| } |
| |
| public void installSfcEndRule(PortPair portPair, NshServicePathId nshSpiId, Objective.Operation type) { |
| DeviceId deviceId = vtnRscService.getSfToSffMaping(VirtualPortId.portId(portPair.egress())); |
| MacAddress srcMacAddress = virtualPortService.getPort(VirtualPortId.portId(portPair.egress())).macAddress(); |
| Host host = hostService.getHost(HostId.hostId(srcMacAddress)); |
| PortNumber port = host.location().port(); |
| |
| // Prepare selector with nsp, nsi and inport from egress of port pair |
| DriverHandler handler = driverService.createHandler(deviceId); |
| ExtensionSelectorResolver selectorResolver = handler.behaviour(ExtensionSelectorResolver.class); |
| ExtensionSelector nshSpiSelector = selectorResolver.getExtensionSelector(NICIRA_MATCH_NSH_SPI.type()); |
| ExtensionSelector nshSiSelector = selectorResolver.getExtensionSelector(NICIRA_MATCH_NSH_SI.type()); |
| ExtensionSelector encapEthTypeSelector = selectorResolver.getExtensionSelector(NICIRA_MATCH_ENCAP_ETH_TYPE |
| .type()); |
| try { |
| nshSpiSelector.setPropertyValue("nshSpi", nshSpiId); |
| } catch (Exception e) { |
| log.error("Failed to set extension selector to match Nsh Spi Id for end rule {}", e.getMessage()); |
| } |
| // Decrement the SI |
| nshSi = (short) (nshSi - 1); |
| try { |
| nshSiSelector.setPropertyValue("nshSi", NshServiceIndex.of(nshSi)); |
| } catch (Exception e) { |
| log.error("Failed to set extension selector to match Nsh Si Id for end rule {}", e.getMessage()); |
| } |
| try { |
| encapEthTypeSelector.setPropertyValue("encapEthType", ENCAP_ETH_TYPE); |
| } catch (Exception e) { |
| log.error("Failed to set extension selector to match encapEthType {}", deviceId); |
| } |
| TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); |
| selector.extension(encapEthTypeSelector, deviceId); |
| selector.extension(nshSpiSelector, deviceId); |
| selector.extension(nshSiSelector, deviceId); |
| selector.matchInPort(port); |
| |
| // Set treatment to pop nsh header, set tunnel id and resubmit to table |
| // 0. |
| ExtensionTreatmentResolver treatmentResolver = handler.behaviour(ExtensionTreatmentResolver.class); |
| ExtensionTreatment popNshTreatment = treatmentResolver.getExtensionInstruction(NICIRA_POP_NSH.type()); |
| |
| TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); |
| treatment.extension(popNshTreatment, deviceId); |
| |
| VirtualPort virtualPort = virtualPortService.getPort(VirtualPortId.portId(portPair.ingress())); |
| SegmentationId segmentationId = tenantNetworkService.getNetwork(virtualPort.networkId()).segmentationId(); |
| treatment.add(Instructions.modTunnelId(Long.parseLong(segmentationId.toString()))); |
| |
| ExtensionTreatment resubmitTableTreatment = treatmentResolver.getExtensionInstruction(NICIRA_RESUBMIT_TABLE |
| .type()); |
| |
| PortNumber vxlanPortNumber = getVxlanPortNumber(deviceId); |
| |
| try { |
| resubmitTableTreatment.setPropertyValue("inPort", vxlanPortNumber); |
| } catch (Exception e) { |
| log.error("Failed to set extension treatment for resubmit table in port {}", deviceId); |
| } |
| try { |
| resubmitTableTreatment.setPropertyValue("table", ((short) 0)); |
| } catch (Exception e) { |
| log.error("Failed to set extension treatment for resubmit table {}", deviceId); |
| } |
| treatment.extension(resubmitTableTreatment, deviceId); |
| |
| sendSfcRule(selector, treatment, deviceId, type, DEFAULT_FORWARDER_PRIORITY); |
| } |
| |
| public void installSfcForwardRule(PortPair portPair, PortPair nextPortPair, NshServicePathId nshSpiId, |
| Objective.Operation type) { |
| DeviceId deviceId = vtnRscService.getSfToSffMaping(VirtualPortId.portId(portPair.egress())); |
| MacAddress srcMacAddress = virtualPortService.getPort(VirtualPortId.portId(portPair.egress())).macAddress(); |
| Host host = hostService.getHost(HostId.hostId(srcMacAddress)); |
| PortNumber port = host.location().port(); |
| |
| DriverHandler handler = driverService.createHandler(deviceId); |
| ExtensionSelectorResolver resolver = handler.behaviour(ExtensionSelectorResolver.class); |
| |
| // Prepare selector with nsp, nsi and inport from egress of port pair |
| ExtensionSelector nshSpiSelector = resolver.getExtensionSelector(NICIRA_MATCH_NSH_SPI.type()); |
| ExtensionSelector nshSiSelector = resolver.getExtensionSelector(NICIRA_MATCH_NSH_SI.type()); |
| try { |
| nshSpiSelector.setPropertyValue("nshSpi", nshSpiId); |
| } catch (Exception e) { |
| log.error("Failed to set extension selector to match Nsh Spi Id for forward rule {}", e.getMessage()); |
| } |
| // Decrement the SI |
| nshSi = (short) (nshSi - 1); |
| try { |
| nshSiSelector.setPropertyValue("nshSi", NshServiceIndex.of(nshSi)); |
| } catch (Exception e) { |
| log.error("Failed to set extension selector to match Nsh Si Id for forward rule {}", e.getMessage()); |
| } |
| TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); |
| selector.extension(nshSpiSelector, deviceId); |
| selector.extension(nshSiSelector, deviceId); |
| selector.matchInPort(port); |
| |
| DeviceId nextDeviceId = vtnRscService.getSfToSffMaping(VirtualPortId.portId(nextPortPair.ingress())); |
| if (deviceId.equals(nextDeviceId)) { |
| |
| // Treatment with transition |
| TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); |
| treatment.transition(ENCAP_OUTPUT_TABLE); |
| |
| sendSfcRule(selector, treatment, deviceId, type, DEFAULT_FORWARDER_PRIORITY); |
| } else { |
| // Treatment with with transition to send on tunnel |
| ExtensionTreatmentResolver treatmentResolver = handler.behaviour(ExtensionTreatmentResolver.class); |
| ExtensionTreatment moveC2ToTunId = treatmentResolver |
| .getExtensionInstruction(ExtensionTreatmentType.ExtensionTreatmentTypes |
| .NICIRA_MOV_NSH_C2_TO_TUN_ID.type()); |
| |
| Device remoteDevice = deviceService.getDevice(nextDeviceId); |
| String url = remoteDevice.annotations().value(SWITCH_CHANNEL_ID); |
| String remoteControllerIp = url.substring(0, url.lastIndexOf(":")); |
| if (remoteControllerIp == null) { |
| log.error("Can't find remote controller of device: {}", nextDeviceId.toString()); |
| return; |
| } |
| |
| ExtensionTreatment tunnelDsttreatment = treatmentResolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST |
| .type()); |
| try { |
| tunnelDsttreatment.setPropertyValue("tunnelDst", Ip4Address.valueOf(remoteControllerIp)); |
| } catch (Exception e) { |
| log.error("Failed to get extension instruction to set tunnel dst {}", deviceId); |
| } |
| |
| TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); |
| treatment.extension(moveC2ToTunId, deviceId); |
| treatment.extension(tunnelDsttreatment, deviceId); |
| treatment.transition(TUNNEL_SEND_TABLE); |
| |
| sendSfcRule(selector, treatment, deviceId, type, DEFAULT_FORWARDER_PRIORITY); |
| |
| installSfcTunnelSendRule(deviceId, nshSpiId, type); |
| installSfcTunnelReceiveRule(nextDeviceId, nshSpiId, type); |
| } |
| } |
| |
| public void installSfcEncapOutputRule(PortPair portPair, NshServicePathId nshSpiId, Objective.Operation type) { |
| |
| DeviceId deviceId = vtnRscService.getSfToSffMaping(VirtualPortId.portId(portPair.ingress())); |
| MacAddress srcMacAddress = virtualPortService.getPort(VirtualPortId.portId(portPair.ingress())).macAddress(); |
| Host host = hostService.getHost(HostId.hostId(srcMacAddress)); |
| PortNumber port = host.location().port(); |
| |
| DriverHandler handler = driverService.createHandler(deviceId); |
| ExtensionSelectorResolver resolver = handler.behaviour(ExtensionSelectorResolver.class); |
| |
| // Prepare selector with nsp, nsi and encap eth type |
| ExtensionSelector nshSpiSelector = resolver.getExtensionSelector(NICIRA_MATCH_NSH_SPI.type()); |
| ExtensionSelector nshSiSelector = resolver.getExtensionSelector(NICIRA_MATCH_NSH_SI.type()); |
| ExtensionSelector nshEncapEthTypeSelector = resolver.getExtensionSelector(NICIRA_MATCH_ENCAP_ETH_TYPE.type()); |
| |
| try { |
| nshSpiSelector.setPropertyValue("nshSpi", nshSpiId); |
| } catch (Exception e) { |
| log.error("Failed to set extension selector to match Nsh Spi Id for encap rule {}", e.getMessage()); |
| } |
| try { |
| nshSiSelector.setPropertyValue("nshSi", NshServiceIndex.of(nshSi)); |
| } catch (Exception e) { |
| log.error("Failed to set extension selector to match Nsh Si Id for encap rule {}", e.getMessage()); |
| } |
| try { |
| nshEncapEthTypeSelector.setPropertyValue("encapEthType", ENCAP_ETH_TYPE); |
| } catch (Exception e) { |
| log.error("Failed to set extension selector to match Nsh Si Id {}", deviceId); |
| } |
| TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); |
| selector.extension(nshSpiSelector, deviceId); |
| selector.extension(nshSiSelector, deviceId); |
| |
| ExtensionTreatmentResolver treatmentResolver = handler.behaviour(ExtensionTreatmentResolver.class); |
| ExtensionTreatment encapEthSrcTreatment = treatmentResolver |
| .getExtensionInstruction(NICIRA_ENCAP_ETH_SRC.type()); |
| ExtensionTreatment encapEthDstTreatment = treatmentResolver |
| .getExtensionInstruction(NICIRA_ENCAP_ETH_DST.type()); |
| |
| try { |
| encapEthDstTreatment.setPropertyValue("encapEthDst", srcMacAddress); |
| } catch (Exception e) { |
| log.error("Failed to set extension treatment to set encap eth dst {}", deviceId); |
| } |
| // TODO: move from packet source mac address |
| try { |
| encapEthSrcTreatment.setPropertyValue("encapEthSrc", srcMacAddress); |
| } catch (Exception e) { |
| log.error("Failed to set extension treatment to set encap eth src {}", deviceId); |
| } |
| |
| TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); |
| treatment.extension(encapEthSrcTreatment, deviceId); |
| treatment.extension(encapEthDstTreatment, deviceId); |
| treatment.setOutput(port); |
| |
| sendSfcRule(selector, treatment, deviceId, type, ENCAP_OUTPUT_PRIORITY); |
| forwarderList.add(deviceId); |
| } |
| |
| public ConnectPoint installSfcClassifierRules(PortChain portChain, PortPair portPair, NshServicePathId nshSpiId, |
| FiveTuple fiveTuple, Objective.Operation type) { |
| |
| DeviceId deviceIdfromPortPair = vtnRscService.getSfToSffMaping(VirtualPortId.portId(portPair.ingress())); |
| MacAddress srcMacAddress = virtualPortService.getPort(VirtualPortId.portId(portPair.ingress())).macAddress(); |
| VirtualPort virtualPort = virtualPortService.getPort(VirtualPortId.portId(portPair.ingress())); |
| Host host = hostService.getHost(HostId.hostId(srcMacAddress)); |
| PortNumber port = host.location().port(); |
| |
| DeviceId deviceId = deviceIdfromPortPair; |
| |
| // get flow classifiers |
| List<FlowClassifierId> llFlowClassifierList = portChain.flowClassifiers(); |
| ListIterator<FlowClassifierId> flowClassifierListIterator = llFlowClassifierList.listIterator(); |
| |
| while (flowClassifierListIterator.hasNext()) { |
| FlowClassifierId flowclassifierId = flowClassifierListIterator.next(); |
| FlowClassifier flowClassifier = flowClassifierService.getFlowClassifier(flowclassifierId); |
| |
| if ((flowClassifier.srcPort() != null) && (!flowClassifier.srcPort().portId().isEmpty())) { |
| deviceId = vtnRscService.getSfToSffMaping(flowClassifier.srcPort()); |
| } |
| |
| // Build Traffic selector. |
| TrafficSelector.Builder selector = packClassifierSelector(flowClassifier, fiveTuple); |
| |
| if (fiveTuple == null) { |
| // Send the packet to controller |
| log.info("Downloading rule to send packet to controller"); |
| TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); |
| treatment.setOutput(PortNumber.CONTROLLER); |
| sendSfcRule(selector, treatment, deviceId, type, FLOW_CLASSIFIER_PRIORITY); |
| continue; |
| } |
| |
| if (deviceId != null && !deviceId.equals(deviceIdfromPortPair)) { |
| // First SF is in another device. Set tunnel ipv4 destination to |
| // treatment |
| Device remoteDevice = deviceService.getDevice(deviceIdfromPortPair); |
| String url = remoteDevice.annotations().value(SWITCH_CHANNEL_ID); |
| String remoteControllerIp = url.substring(0, url.lastIndexOf(":")); |
| if (remoteControllerIp == null) { |
| log.error("Can't find remote controller of device: {}", deviceIdfromPortPair.toString()); |
| return null; |
| } |
| |
| DriverHandler handler = driverService.createHandler(deviceId); |
| ExtensionTreatmentResolver resolver = handler.behaviour(ExtensionTreatmentResolver.class); |
| ExtensionTreatment tunnelDsttreatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type()); |
| try { |
| tunnelDsttreatment.setPropertyValue("tunnelDst", Ip4Address.valueOf(remoteControllerIp)); |
| } catch (Exception e) { |
| log.error("Failed to get extension instruction to set tunnel dst {}", deviceId); |
| } |
| |
| TrafficTreatment.Builder treatment = packClassifierTreatment(deviceId, virtualPort, port, |
| nshSpiId, flowClassifier); |
| treatment.extension(tunnelDsttreatment, deviceId); |
| treatment.transition(TUNNEL_SEND_TABLE); |
| sendSfcRule(selector, treatment, deviceId, type, flowClassifier.priority()); |
| classifierList.add(deviceIdfromPortPair); |
| |
| installSfcTunnelSendRule(deviceId, nshSpiId, type); |
| installSfcTunnelReceiveRule(deviceIdfromPortPair, nshSpiId, type); |
| |
| } else { |
| // classifier and port pair are in the same OVS. So directly |
| // send packet to first port pair |
| TrafficTreatment.Builder treatment = packClassifierTreatment(deviceIdfromPortPair, virtualPort, port, |
| nshSpiId, flowClassifier); |
| treatment.transition(ENCAP_OUTPUT_TABLE); |
| sendSfcRule(selector, treatment, deviceIdfromPortPair, type, flowClassifier.priority()); |
| classifierList.add(deviceIdfromPortPair); |
| } |
| } |
| |
| return host.location(); |
| } |
| |
| /** |
| * Pack Traffic selector. |
| * |
| * @param flowClassifier flow-classifier |
| * @param fiveTuple five tuple info for the packet |
| * @return traffic selector |
| */ |
| public TrafficSelector.Builder packClassifierSelector(FlowClassifier flowClassifier, FiveTuple fiveTuple) { |
| |
| TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); |
| |
| if ((flowClassifier.srcIpPrefix() != null) && (flowClassifier.srcIpPrefix().prefixLength() != 0)) { |
| selector.matchIPSrc(flowClassifier.srcIpPrefix()); |
| } else if (fiveTuple != null && fiveTuple.ipSrc() != null) { |
| selector.matchIPSrc(IpPrefix.valueOf(fiveTuple.ipSrc(), 24)); |
| } |
| |
| if ((flowClassifier.dstIpPrefix() != null) && (flowClassifier.dstIpPrefix().prefixLength() != 0)) { |
| selector.matchIPDst(flowClassifier.dstIpPrefix()); |
| } else if (fiveTuple != null && fiveTuple.ipDst() != null) { |
| selector.matchIPDst(IpPrefix.valueOf(fiveTuple.ipDst(), 24)); |
| } |
| |
| if ((flowClassifier.protocol() != null) && (!flowClassifier.protocol().isEmpty())) { |
| if (flowClassifier.protocol().equalsIgnoreCase("TCP")) { |
| selector.add(Criteria.matchIPProtocol(IPv4.PROTOCOL_TCP)); |
| } else if (flowClassifier.protocol().equalsIgnoreCase("UDP")) { |
| selector.add(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP)); |
| } else if (flowClassifier.protocol().equalsIgnoreCase("ICMP")) { |
| selector.add(Criteria.matchIPProtocol(IPv4.PROTOCOL_ICMP)); |
| } |
| } else if (fiveTuple != null && fiveTuple.protocol() != 0) { |
| selector.add(Criteria.matchIPProtocol(fiveTuple.protocol())); |
| } |
| |
| if (((flowClassifier.etherType() != null) && (!flowClassifier.etherType().isEmpty())) |
| && (flowClassifier.etherType().equals("IPv4") || flowClassifier.etherType().equals("IPv6"))) { |
| if (flowClassifier.etherType().equals("IPv4")) { |
| selector.matchEthType(Ethernet.TYPE_IPV4); |
| } else { |
| selector.matchEthType(Ethernet.TYPE_IPV6); |
| } |
| } |
| |
| if ((flowClassifier.srcPort() != null) && (!flowClassifier.srcPort().portId().isEmpty())) { |
| VirtualPortId vPortId = VirtualPortId.portId(flowClassifier.srcPort().portId()); |
| MacAddress macAddress = virtualPortService.getPort(vPortId).macAddress(); |
| Host host = hostService.getHost(HostId.hostId(macAddress)); |
| selector.matchInPort(host.location().port()); |
| } |
| |
| // Take the port information from five tuple only when the protocol is |
| // TCP. |
| if (fiveTuple != null && fiveTuple.protocol() == IPv4.PROTOCOL_TCP) { |
| selector.matchTcpSrc(TpPort.tpPort((int) fiveTuple.portSrc().toLong())); |
| selector.matchTcpDst(TpPort.tpPort((int) fiveTuple.portDst().toLong())); |
| } else { |
| // For udp packets take the port information from flow classifier |
| List<TpPort> srcPortRange = new LinkedList<>(); |
| List<TpPort> dstPortRange = new LinkedList<>(); |
| if ((flowClassifier.minSrcPortRange() != 0) && flowClassifier.maxSrcPortRange() != 0 |
| && flowClassifier.minDstPortRange() != 0 && flowClassifier.maxDstPortRange() != 0) { |
| |
| for (int port = flowClassifier.minSrcPortRange(); port <= flowClassifier.maxSrcPortRange(); port++) { |
| srcPortRange.add(TpPort.tpPort(port)); |
| } |
| for (int port = flowClassifier.minDstPortRange(); port <= flowClassifier.maxDstPortRange(); port++) { |
| dstPortRange.add(TpPort.tpPort(port)); |
| } |
| } |
| |
| for (TpPort inPort : srcPortRange) { |
| selector.matchUdpSrc(inPort); |
| } |
| for (TpPort outPort : dstPortRange) { |
| selector.matchUdpDst(outPort); |
| } |
| } |
| return selector; |
| } |
| |
| /** |
| * Pack traffic treatment. |
| * |
| * @param deviceId device id |
| * @param virtualPort virtual port |
| * @param port port number |
| * @param nshSpi nsh spi |
| * @param flowClassifier flow-classifier |
| * @return traffic treatment |
| */ |
| public TrafficTreatment.Builder packClassifierTreatment(DeviceId deviceId, VirtualPort virtualPort, |
| PortNumber port, NshServicePathId nshSpi, FlowClassifier flowClassifier) { |
| |
| TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder(); |
| |
| // set tunnel id |
| SegmentationId segmentationId = tenantNetworkService.getNetwork(virtualPort.networkId()).segmentationId(); |
| treatmentBuilder.add(Instructions.modTunnelId(Long.parseLong(segmentationId.toString()))); |
| |
| // Set all NSH header fields |
| DriverHandler handler = driverService.createHandler(deviceId); |
| ExtensionTreatmentResolver resolver = handler.behaviour(ExtensionTreatmentResolver.class); |
| ExtensionTreatment nspIdTreatment = resolver.getExtensionInstruction(NICIRA_SET_NSH_SPI.type()); |
| ExtensionTreatment nsiIdTreatment = resolver.getExtensionInstruction(NICIRA_SET_NSH_SI.type()); |
| ExtensionTreatment pushNshTreatment = resolver.getExtensionInstruction(NICIRA_PUSH_NSH.type()); |
| |
| ExtensionTreatment nshCh1Treatment = resolver.getExtensionInstruction(NICIRA_SET_NSH_CH1.type()); |
| ExtensionTreatment nshCh2Treatment = resolver.getExtensionInstruction(NICIRA_SET_NSH_CH2.type()); |
| ExtensionTreatment nshCh3Treatment = resolver.getExtensionInstruction(NICIRA_SET_NSH_CH3.type()); |
| ExtensionTreatment nshCh4Treatment = resolver.getExtensionInstruction(NICIRA_SET_NSH_CH4.type()); |
| ExtensionTreatment nshMdTypeTreatment = resolver.getExtensionInstruction(NICIRA_NSH_MDTYPE.type()); |
| ExtensionTreatment nshNpTreatment = resolver.getExtensionInstruction(NICIRA_NSH_NP.type()); |
| |
| try { |
| nshMdTypeTreatment.setPropertyValue("nshMdType", ((byte) 1)); |
| } catch (Exception e) { |
| log.error("Failed to get extension instruction to set nshMdType {}", deviceId); |
| } |
| try { |
| nshNpTreatment.setPropertyValue("nshNp", ((byte) 3)); |
| } catch (Exception e) { |
| log.error("Failed to get extension instruction to set nshNp {}", deviceId); |
| } |
| try { |
| nspIdTreatment.setPropertyValue("nshSpi", nshSpi); |
| } catch (Exception e) { |
| log.error("Failed to get extension instruction to set Nsh Spi Id {}", deviceId); |
| } |
| try { |
| nsiIdTreatment.setPropertyValue("nshSi", NshServiceIndex.of(nshSi)); |
| } catch (Exception e) { |
| log.error("Failed to get extension instruction to set Nsh Si Id {}", deviceId); |
| } |
| try { |
| nshCh1Treatment.setPropertyValue("nshCh", NshContextHeader.of(1)); |
| } catch (Exception e) { |
| log.error("Failed to get extension instruction to set NshCh1 {}", deviceId); |
| } |
| try { |
| nshCh2Treatment.setPropertyValue("nshCh", NshContextHeader.of(Integer.parseInt(segmentationId.toString()))); |
| } catch (Exception e) { |
| log.error("Failed to get extension instruction to set NshCh2 {}", deviceId); |
| } |
| try { |
| nshCh3Treatment.setPropertyValue("nshCh", NshContextHeader.of(3)); |
| } catch (Exception e) { |
| log.error("Failed to get extension instruction to set NshCh3 {}", deviceId); |
| } |
| try { |
| nshCh4Treatment.setPropertyValue("nshCh", NshContextHeader.of(4)); |
| } catch (Exception e) { |
| log.error("Failed to get extension instruction to set NshCh4 {}", deviceId); |
| } |
| treatmentBuilder.extension(pushNshTreatment, deviceId); |
| treatmentBuilder.extension(nshMdTypeTreatment, deviceId); |
| treatmentBuilder.extension(nshNpTreatment, deviceId); |
| treatmentBuilder.extension(nspIdTreatment, deviceId); |
| treatmentBuilder.extension(nsiIdTreatment, deviceId); |
| treatmentBuilder.extension(nshCh1Treatment, deviceId); |
| treatmentBuilder.extension(nshCh2Treatment, deviceId); |
| treatmentBuilder.extension(nshCh3Treatment, deviceId); |
| treatmentBuilder.extension(nshCh4Treatment, deviceId); |
| |
| return treatmentBuilder; |
| } |
| |
| /** |
| * Get the ControllerId from the device . |
| * |
| * @param device Device |
| * @param devices Devices |
| * @return Controller Id |
| */ |
| public DeviceId getControllerId(Device device, Iterable<Device> devices) { |
| for (Device d : devices) { |
| if (d.type() == Device.Type.CONTROLLER && d.id().toString() |
| .contains(getControllerIpOfSwitch(device))) { |
| return d.id(); |
| } |
| } |
| log.info("Can not find controller for device : {}", device.id()); |
| return null; |
| } |
| |
| /** |
| * Get the ControllerIp from the device . |
| * |
| * @param device Device |
| * @return Controller Ip |
| */ |
| public String getControllerIpOfSwitch(Device device) { |
| String url = device.annotations().value(SWITCH_CHANNEL_ID); |
| return url.substring(0, url.lastIndexOf(":")); |
| } |
| |
| /** |
| * Send service-function-forwarder to OVS. |
| * |
| * @param selector traffic selector |
| * @param treatment traffic treatment |
| * @param deviceId device id |
| * @param type operation type |
| * @param priority priority of classifier |
| */ |
| public void sendSfcRule(TrafficSelector.Builder selector, TrafficTreatment.Builder treatment, DeviceId deviceId, |
| Objective.Operation type, int priority) { |
| |
| log.info("Sending sfc flow rule. Selector {}, Treatment {}", selector.toString(), |
| treatment.toString()); |
| ForwardingObjective.Builder objective = DefaultForwardingObjective.builder().withTreatment(treatment.build()) |
| .withSelector(selector.build()).fromApp(appId).makePermanent().withFlag(Flag.VERSATILE) |
| .withPriority(priority); |
| |
| if (type.equals(Objective.Operation.ADD)) { |
| log.debug("flowClassifierRules-->ADD"); |
| flowObjectiveService.forward(deviceId, objective.add()); |
| } else { |
| log.debug("flowClassifierRules-->REMOVE"); |
| flowObjectiveService.forward(deviceId, objective.remove()); |
| } |
| } |
| |
| private PortNumber getVxlanPortNumber(DeviceId deviceId) { |
| Iterable<Port> ports = deviceService.getPorts(deviceId); |
| Port vxlanPort = Sets.newHashSet(ports).stream() |
| .filter(p ->!p.number().equals(PortNumber.LOCAL)) |
| .filter(p ->p.annotations().value(AnnotationKeys.PORT_NAME) |
| .startsWith(VXLANPORT_HEAD)) |
| .findFirst().get(); |
| return vxlanPort.number(); |
| } |
| } |