/* * 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.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); } @Override public ConnectPoint unInstallLoadBalancedClassifierRules(PortChain portChain, FiveTuple fiveTuple, NshServicePathId nshSpiId) { checkNotNull(portChain, PORT_CHAIN_NOT_NULL); List<PortPairId> portPairs = portChain.getLoadBalancePath(fiveTuple); // Get the first port pair ListIterator<PortPairId> portPairListIterator = portPairs.listIterator(); PortPairId portPairId = portPairListIterator.next(); PortPair portPair = portPairService.getPortPair(portPairId); return installSfcClassifierRules(portChain, portPair, nshSpiId, fiveTuple, 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); 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()); selector.matchInPort(PortNumber.CONTROLLER); sendSfcRule(selector, treatment, deviceId, type, flowClassifier.priority()); classifierList.add(deviceId); 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()); selector.matchInPort(PortNumber.CONTROLLER); sendSfcRule(selector, treatment, deviceId, 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 ("TCP".equalsIgnoreCase(flowClassifier.protocol())) { selector.add(Criteria.matchIPProtocol(IPv4.PROTOCOL_TCP)); } else if ("UDP".equalsIgnoreCase(flowClassifier.protocol())) { selector.add(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP)); } else if ("ICMP".equalsIgnoreCase(flowClassifier.protocol())) { 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())) && ("IPv4".equals(flowClassifier.etherType()) || "IPv6".equals(flowClassifier.etherType()))) { if ("IPv4".equals(flowClassifier.etherType())) { 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(); } }