blob: 3c76e89f4f1d1d905297b6c91248d7d11159ee69 [file] [log] [blame]
/*
* Copyright 2016-present Open Networking Foundation
*
* 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();
}
}