blob: dbae14c75a8a9c0671119593f583ea7c61e32be3 [file] [log] [blame]
/*
* 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.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_SI;
import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_SPI;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import org.onlab.osgi.DefaultServiceDirectory;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.TpPort;
import org.onlab.packet.VlanId;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.NshServiceIndex;
import org.onosproject.net.NshServicePathId;
import org.onosproject.net.PortNumber;
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.instructions.ExtensionTreatment;
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.FlowClassifierInstallerService;
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.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.virtualport.VirtualPortService;
import org.slf4j.Logger;
/**
* Provides flow classifier installer implementation.
*/
public class FlowClassifierInstallerImpl implements FlowClassifierInstallerService {
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 FlowObjectiveService flowObjectiveService;
protected ApplicationId appId;
private static final String DRIVER_NAME = "onosfw";
private static final String FLOW_CLASSIFIER_NOT_NULL = "Flow-Classifier cannot be null";
private static final String FLOW_CLASSIFIER_ID_NOT_NULL = "Flow-Classifier-Id cannot be null";
private static final String PORT_CHAIN_NOT_NULL = "Port-Chain cannot be null";
private static final int NULL = 0;
private static final int FLOW_CLASSIFIER_PRIORITY = 0x7fff;
private static final short NSH_SI_ID = 0xff;
/**
* Default constructor.
*/
public FlowClassifierInstallerImpl() {
}
/**
* Explicit constructor.
*
* @param appId application id.
*/
public FlowClassifierInstallerImpl(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);
}
@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 processFlowClassifier(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 processFlowClassifier(portChain, portPair, nshSpiId, null, Objective.Operation.REMOVE);
}
@Override
public ConnectPoint installLoadBalancedFlowClassifier(PortChain portChain, FiveTuple fiveTuple,
NshServicePathId nshSpiId) {
checkNotNull(portChain, PORT_CHAIN_NOT_NULL);
// 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 portPair = portPairService.getPortPair(portPairId);
return processFlowClassifier(portChain, portPair, nshSpiId, fiveTuple, Objective.Operation.ADD);
}
@Override
public ConnectPoint unInstallLoadBalancedFlowClassifier(PortChain portChain, FiveTuple fiveTuple,
NshServicePathId nshSpiId) {
checkNotNull(portChain, PORT_CHAIN_NOT_NULL);
// 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 portPair = portPairService.getPortPair(portPairId);
return processFlowClassifier(portChain, portPair, nshSpiId, fiveTuple, Objective.Operation.REMOVE);
}
public ConnectPoint processFlowClassifier(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();
Host host = hostService.getHost(HostId.hostId(srcMacAddress));
PortNumber port = host.location().port();
DeviceId deviceId = deviceIdfromPortPair;
// Vxlan tunnel port for NSH header(Vxlan + NSH).
TpPort nshDstPort = TpPort.tpPort(6633);
FlowClassifierInstallerService flowclassifierinstallerService;
// 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 = packTrafficSelector(flowClassifier, fiveTuple);
if (fiveTuple == null) {
// Send the packet to controller
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
treatment.setOutput(PortNumber.CONTROLLER);
sendServiceFunctionClassifier(selector, treatment, deviceId, type);
} else if (deviceId.equals(deviceIdfromPortPair)) {
// classifier and source device are in the same OVS. So directly send packet to first port pair
TrafficTreatment.Builder treatment = packTrafficTreatment(deviceId, port, nshDstPort,
nshSpiId, flowClassifier, true);
// Build forwarding objective and send to OVS.
sendServiceFunctionClassifier(selector, treatment, deviceId, type);
} else {
// classifier and source device are not in the same OVS. Send packet on vlan Tunnel
TrafficTreatment.Builder treatment = packTrafficTreatment(deviceId, port, nshDstPort,
nshSpiId, flowClassifier, false);
// Build forwarding objective and send to OVS.
sendServiceFunctionClassifier(selector, treatment, deviceId, type);
// At the other device get the packet from vlan and send to first port pair
TrafficSelector.Builder selectorDst = DefaultTrafficSelector.builder();
selectorDst.matchVlanId((VlanId.vlanId(Short.parseShort((vtnRscService
.getL3vni(flowClassifier.tenantId()).toString())))));
TrafficTreatment.Builder treatmentDst = DefaultTrafficTreatment.builder();
Host hostDst = hostService.getHost(HostId.hostId(srcMacAddress));
treatmentDst.setOutput(hostDst.location().port());
sendServiceFunctionClassifier(selectorDst, treatmentDst, deviceIdfromPortPair, type);
}
}
return host.location();
}
/**
* Pack Traffic selector.
*
* @param flowClassifier flow-classifier
* @param fiveTuple five tuple info for the packet
* @return traffic selector
*/
public TrafficSelector.Builder packTrafficSelector(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 (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 port port number
* @param nshDstPort vxlan tunnel port for nsh header
* @param nshSpi nsh spi
* @param flowClassifier flow-classifier
* @param isSameOvs whether the next service function is in same ovs
* @return traffic treatment
*/
public TrafficTreatment.Builder packTrafficTreatment(DeviceId deviceId, PortNumber port,
TpPort nshDstPort, NshServicePathId nshSpi,
FlowClassifier flowClassifier, boolean isSameOvs) {
TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
if (isSameOvs) {
treatmentBuilder.setOutput(port);
} else {
treatmentBuilder.setVlanId((VlanId.vlanId(Short.parseShort((vtnRscService
.getL3vni(flowClassifier.tenantId()).toString())))));
treatmentBuilder.setUdpDst(nshDstPort);
}
// Set NSH
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());
treatmentBuilder.extension(nspIdTreatment, deviceId);
treatmentBuilder.extension(nsiIdTreatment, 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(NSH_SI_ID));
} catch (Exception e) {
log.error("Failed to get extension instruction to set Nsh Si Id {}", deviceId);
}
return treatmentBuilder;
}
/**
* Send service-function-forwarder to OVS.
*
* @param selector traffic selector
* @param treatment traffic treatment
* @param deviceId device id
* @param type operation type
*/
public void sendServiceFunctionClassifier(TrafficSelector.Builder selector, TrafficTreatment.Builder treatment,
DeviceId deviceId, Objective.Operation type) {
log.info("Sending flow to service function classifier. Selector {}, Treatment {}",
selector.toString(), treatment.toString());
ForwardingObjective.Builder objective = DefaultForwardingObjective.builder().withTreatment(treatment.build())
.withSelector(selector.build()).fromApp(appId).makePermanent().withFlag(Flag.VERSATILE)
.withPriority(FLOW_CLASSIFIER_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());
}
}
}