sangho
Committed by Gerrit Code Review

ONOS-686, 687, 1344 : The first commit for the Segment Routing application

 - ICMP/ARP/IP handlers are implemented as a part of the application for now
 - Default routing and link add/failure/recovery are also supprted
 - Temporary NetworkConfigHandler, which is hardcoded to support only 6 router FISH topology, is used for test
 - Some fixes on GroupHanlder app to support transit routers
 - Supports multi-instance (tested with two instances)

Change-Id: Idfa67903e59e1c4cac4da430f89cd4c50e821420
Showing 19 changed files with 2512 additions and 448 deletions
......@@ -152,11 +152,13 @@ public class DefaultGroupHandler {
* @param newLink new neighbor link
*/
public void linkUp(Link newLink) {
if (newLink.type() != Link.Type.DIRECT) {
log.warn("linkUp: unknown link type");
return;
}
if (!newLink.src().deviceId().equals(deviceId)) {
log.warn("linkUp: deviceId{} doesn't match with link src{}",
deviceId,
......@@ -307,13 +309,11 @@ public class DefaultGroupHandler {
List<Integer> nsSegmentIds = new ArrayList<Integer>();
// Add one entry for "no label" (-1) to the list if
// dpid list has not more than one node/neighbor as
// there will never be a case a packet going to more than one
// neighbor without a label at an edge router
if (neighbors.size() == 1) {
nsSegmentIds.add(-1);
}
// Always pair up with no edge label
//If (neighbors.size() == 1) {
nsSegmentIds.add(-1);
//}
// Filter out SegmentIds matching with the
// nodes in the combo
for (Integer sId : allSegmentIds) {
......@@ -405,7 +405,8 @@ public class DefaultGroupHandler {
}
}
protected GroupKey getGroupKey(Object obj) {
public GroupKey getGroupKey(Object obj) {
return new DefaultGroupKey(kryo.build().serialize(obj));
}
}
......
......@@ -90,6 +90,7 @@ public class DefaultGroupHandlerApp {
public void activate() {
appId = coreService.registerApplication("org.onosproject.defaultgrouphandler");
log.info("DefaultGroupHandlerApp Activating");
deviceService.addListener(deviceListener);
linkService.addListener(linkListener);
for (Device device: deviceService.getDevices()) {
......@@ -111,6 +112,7 @@ public class DefaultGroupHandlerApp {
device.id());
}
}
log.info("Activated");
}
......
......@@ -138,7 +138,8 @@ public class DefaultTransitGroupHandler extends DefaultGroupHandler {
Set<DeviceId> updatedNeighbors) {
Set<Set<DeviceId>> powerSet = getPowerSetOfNeighbors(updatedNeighbors);
Set<DeviceId> tmp = updatedNeighbors;
Set<DeviceId> tmp = new HashSet<DeviceId>();
tmp.addAll(updatedNeighbors);
tmp.remove(impactedNeighbor);
Set<Set<DeviceId>> tmpPowerSet = getPowerSetOfNeighbors(tmp);
......
......@@ -45,6 +45,8 @@
<module>reactive-routing</module>
<module>bgprouter</module>
<module>test</module>
<module>grouphandler</module>
<module>segmentrouting</module>
</modules>
<properties>
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2014 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.
--><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>onos-apps</artifactId>
<groupId>org.onosproject</groupId>
<version>1.2.0-SNAPSHOT</version>
</parent>
<artifactId>onos-app-segmentrouting</artifactId>
<packaging>bundle</packaging>
<description>ONOS OSGi bundle archetype</description>
<url>http://onosproject.org</url>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-grouphandler</artifactId>
<version>1.2.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
/*
* Copyright 2015 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.segmentrouting;
import org.onlab.packet.ARP;
import org.onlab.packet.Ethernet;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.HostId;
import org.onosproject.net.packet.OutboundPacket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import static com.google.common.base.Preconditions.checkNotNull;
public class ArpHandler {
private static Logger log = LoggerFactory.getLogger(ArpHandler.class);
private SegmentRoutingManager srManager;
private NetworkConfigHandler config;
/**
* Creates an ArpHandler object.
*
* @param srManager SegmentRoutingManager object
*/
public ArpHandler(SegmentRoutingManager srManager) {
this.srManager = srManager;
this.config = checkNotNull(srManager.networkConfigHandler);
}
/**
* Processes incoming ARP packets.
* If it is an ARP request to router itself or known hosts,
* then it sends ARP response.
* If it is an ARP request to unknown hosts in its own subnet,
* then it flood the ARP request to the ports.
* If it is an ARP response, then set a flow rule for the host
* and forward any IP packets to the host in the packet buffer to the host.
*
* @param pkt incoming packet
*/
public void processPacketIn(InboundPacket pkt) {
Ethernet ethernet = pkt.parsed();
ARP arp = (ARP) ethernet.getPayload();
ConnectPoint connectPoint = pkt.receivedFrom();
PortNumber inPort = connectPoint.port();
DeviceId deviceId = connectPoint.deviceId();
byte[] senderMacAddressByte = arp.getSenderHardwareAddress();
Ip4Address hostIpAddress = Ip4Address.valueOf(arp.getSenderProtocolAddress());
srManager.routingRulePopulator.populateIpRuleForHost(deviceId, hostIpAddress, MacAddress.
valueOf(senderMacAddressByte), inPort);
if (arp.getOpCode() == ARP.OP_REQUEST) {
handleArpRequest(deviceId, connectPoint, ethernet);
} else {
srManager.ipHandler.forwardPackets(deviceId, hostIpAddress);
}
}
private void handleArpRequest(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
ARP arpRequest = (ARP) payload.getPayload();
HostId targetHostId = HostId.hostId(MacAddress.valueOf(
arpRequest.getTargetHardwareAddress()));
// ARP request for router
if (isArpReqForRouter(deviceId, arpRequest)) {
Ip4Address targetAddress = Ip4Address.valueOf(arpRequest.getTargetProtocolAddress());
sendArpResponse(arpRequest, config.getRouterMac(targetAddress));
// ARP request for known hosts
} else if (srManager.hostService.getHost(targetHostId) != null) {
MacAddress targetMac = srManager.hostService.getHost(targetHostId).mac();
sendArpResponse(arpRequest, targetMac);
// ARP request for unknown host in the subnet
} else if (isArpReqForSubnet(deviceId, arpRequest)) {
flood(payload, inPort);
}
}
private boolean isArpReqForRouter(DeviceId deviceId, ARP arpRequest) {
Ip4Address gatewayIpAddress = config.getGatewayIpAddress(deviceId);
if (gatewayIpAddress != null) {
Ip4Address targetProtocolAddress = Ip4Address.valueOf(arpRequest
.getTargetProtocolAddress());
if (gatewayIpAddress.equals(targetProtocolAddress)) {
return true;
}
}
return false;
}
private boolean isArpReqForSubnet(DeviceId deviceId, ARP arpRequest) {
String subnetInfo = config.getSubnetInfo(deviceId);
if (subnetInfo != null) {
IpPrefix prefix = IpPrefix.valueOf(subnetInfo);
if (prefix.contains(Ip4Address.valueOf(arpRequest.getTargetProtocolAddress()))) {
return true;
}
}
return false;
}
/**
* Sends an APR request for the target IP address to all ports except in-port.
*
* @param deviceId Switch device ID
* @param targetAddress target IP address for ARP
* @param inPort in-port
*/
public void sendArpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
byte[] senderMacAddress = config.getRouterMacAddress(deviceId).toBytes();
byte[] senderIpAddress = config.getRouterIpAddress(deviceId)
.getIp4Prefix().address().toOctets();
ARP arpRequest = new ARP();
arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
.setProtocolType(ARP.PROTO_TYPE_IP)
.setHardwareAddressLength(
(byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
.setOpCode(ARP.OP_REQUEST)
.setSenderHardwareAddress(senderMacAddress)
.setTargetHardwareAddress(MacAddress.ZERO.toBytes())
.setSenderProtocolAddress(senderIpAddress)
.setTargetProtocolAddress(targetAddress.toOctets());
Ethernet eth = new Ethernet();
eth.setDestinationMACAddress(MacAddress.BROADCAST.toBytes())
.setSourceMACAddress(senderMacAddress)
.setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
flood(eth, inPort);
}
private void sendArpResponse(ARP arpRequest, MacAddress targetMac) {
ARP arpReply = new ARP();
arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
.setProtocolType(ARP.PROTO_TYPE_IP)
.setHardwareAddressLength(
(byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
.setOpCode(ARP.OP_REPLY)
.setSenderHardwareAddress(targetMac.toBytes())
.setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
.setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
.setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
Ethernet eth = new Ethernet();
eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
.setSourceMACAddress(targetMac.toBytes())
.setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
HostId dstId = HostId.hostId(MacAddress.valueOf(
arpReply.getTargetHardwareAddress()));
Host dst = srManager.hostService.getHost(dstId);
if (dst == null) {
log.warn("Cannot send ARP response to unknown device");
return;
}
TrafficTreatment treatment = DefaultTrafficTreatment.builder().
setOutput(dst.location().port()).build();
OutboundPacket packet = new DefaultOutboundPacket(dst.location().deviceId(),
treatment, ByteBuffer.wrap(eth.serialize()));
srManager.packetService.emit(packet);
}
private void flood(Ethernet request, ConnectPoint inPort) {
TrafficTreatment.Builder builder;
ByteBuffer buf = ByteBuffer.wrap(request.serialize());
for (Port port: srManager.deviceService.getPorts(inPort.deviceId())) {
if (!port.number().equals(inPort.port()) &&
port.number().toLong() > 0) {
builder = DefaultTrafficTreatment.builder();
builder.setOutput(port.number());
srManager.packetService.emit(new DefaultOutboundPacket(inPort.deviceId(),
builder.build(), buf));
}
}
}
}
/*
* Copyright 2015 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.segmentrouting;
import org.onlab.packet.IpPrefix;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.flow.FlowRule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
public class DefaultRoutingHandler {
private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class);
private SegmentRoutingManager srManager;
private RoutingRulePopulator rulePopulator;
private NetworkConfigHandler config;
private Status populationStatus;
/**
* Represents the default routing population status.
*/
public enum Status {
// population process is not started yet.
IDLE,
// population process started.
STARTED,
// population process was aborted due to errors, mostly for groups not found.
ABORTED,
// population process was finished successfully.
SUCCEEDED
}
/**
* Creates a DefaultRoutingHandler object.
*
* @param srManager SegmentRoutingManager object
*/
public DefaultRoutingHandler(SegmentRoutingManager srManager) {
this.srManager = srManager;
this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
this.config = checkNotNull(srManager.networkConfigHandler);
this.populationStatus = Status.IDLE;
}
/**
* Populates all routing rules to all connected routers, including default
* routing rules, adjacency rules, and policy rules if any.
*
* @return true if it succeeds in populating all rules, otherwise false
*/
public boolean populateAllRoutingRules() {
populationStatus = Status.STARTED;
log.info("Starts to populate routing rules");
for (Device sw : srManager.deviceService.getDevices()) {
if (srManager.mastershipService.
getLocalRole(sw.id()) != MastershipRole.MASTER) {
continue;
}
ECMPShortestPathGraph ecmpSPG = new ECMPShortestPathGraph(sw.id(), srManager);
if (!populateEcmpRoutingRules(sw, ecmpSPG)) {
populationStatus = Status.ABORTED;
log.debug("Abort routing rule population");
return false;
}
// TODO: Set adjacency routing rule for all switches
}
populationStatus = Status.SUCCEEDED;
log.info("Completes routing rule population");
return true;
}
private boolean populateEcmpRoutingRules(Device sw,
ECMPShortestPathGraph ecmpSPG) {
HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
ecmpSPG.getAllLearnedSwitchesAndVia();
for (Integer itrIdx : switchVia.keySet()) {
HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
switchVia.get(itrIdx);
for (DeviceId targetSw : swViaMap.keySet()) {
DeviceId destSw = sw.id();
Set<DeviceId> nextHops = new HashSet<>();
for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
if (via.isEmpty()) {
nextHops.add(destSw);
} else {
nextHops.add(via.get(0));
}
}
if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops)) {
return false;
}
}
}
return true;
}
private boolean populateEcmpRoutingRulePartial(DeviceId targetSw, DeviceId destSw,
Set<DeviceId> nextHops) {
boolean result;
if (nextHops.isEmpty()) {
nextHops.add(destSw);
}
// If both target switch and dest switch are edge routers, then set IP rule
// for both subnet and router IP.
if (config.isEdgeRouter(targetSw) && config.isEdgeRouter(destSw)) {
String subnets = config.getSubnetInfo(destSw);
result = rulePopulator.populateIpRuleForSubnet(targetSw, subnets, destSw, nextHops);
if (!result) {
return false;
}
IpPrefix routerIp = config.getRouterIpAddress(destSw);
result = rulePopulator.populateIpRuleForRouter(targetSw, routerIp, destSw, nextHops);
if (!result) {
return false;
}
// If the target switch is an edge router, then set IP rules for the router IP.
} else if (config.isEdgeRouter(targetSw)) {
IpPrefix routerIp = config.getRouterIpAddress(destSw);
result = rulePopulator.populateIpRuleForRouter(targetSw, routerIp, destSw, nextHops);
if (!result) {
return false;
}
// If the target switch is an transit router, then set MPLS rules only.
} else if (config.isTransitRouter(targetSw)) {
result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops);
if (!result) {
return false;
}
} else {
log.warn("The switch {} is neither an edge router nor a transit router.", targetSw);
return false;
}
return true;
}
/**
* Populates table miss entries for all tables, and pipeline rules for
* VLAN and TACM tables.
*
* @param deviceId Switch ID to set the rules
*/
public void populateTtpRules(DeviceId deviceId) {
rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.VLAN,
true, false, false, FlowRule.Type.DEFAULT);
rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.ETHER,
true, false, false, FlowRule.Type.DEFAULT);
rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.IP,
false, true, true, FlowRule.Type.ACL);
rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.MPLS,
false, true, true, FlowRule.Type.ACL);
rulePopulator.populateTableMissEntry(deviceId, FlowRule.Type.ACL,
false, false, false, FlowRule.Type.DEFAULT);
rulePopulator.populateTableVlan(deviceId);
rulePopulator.populateTableTMac(deviceId);
}
/**
* Start the flow rule population process if it was never started.
* The process finishes successfully when all flow rules are set and
* stops with ABORTED status when any groups required for flows is not
* set yet.
*/
public void startPopulationProcess() {
synchronized (populationStatus) {
if (populationStatus == Status.IDLE ||
populationStatus == Status.SUCCEEDED) {
populationStatus = Status.STARTED;
populateAllRoutingRules();
}
}
}
/**
* Resume the flow rule population process if it was aborted for any reason.
* Mostly the process is aborted when the groups required are not set yet.
*/
public void resumePopulationProcess() {
synchronized (populationStatus) {
if (populationStatus == Status.ABORTED) {
populationStatus = Status.STARTED;
// TODO: we need to restart from the point aborted instead of restarting.
populateAllRoutingRules();
}
}
}
}
package org.onosproject.segmentrouting;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.MacAddress;
import org.onosproject.grouphandler.DeviceProperties;
import org.onosproject.net.DeviceId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DeviceConfiguration implements DeviceProperties {
private static final Logger log = LoggerFactory
.getLogger(DeviceConfiguration.class);
private final List<Integer> allSegmentIds =
Arrays.asList(101, 102, 103, 104, 105, 106);
private HashMap<DeviceId, Integer> deviceSegmentIdMap =
new HashMap<DeviceId, Integer>() {
{
put(DeviceId.deviceId("of:0000000000000001"), 101);
put(DeviceId.deviceId("of:0000000000000002"), 102);
put(DeviceId.deviceId("of:0000000000000003"), 103);
put(DeviceId.deviceId("of:0000000000000004"), 104);
put(DeviceId.deviceId("of:0000000000000005"), 105);
put(DeviceId.deviceId("of:0000000000000006"), 106);
}
};
private final HashMap<DeviceId, MacAddress> deviceMacMap =
new HashMap<DeviceId, MacAddress>() {
{
put(DeviceId.deviceId("of:0000000000000001"),
MacAddress.valueOf("00:00:00:00:00:01"));
put(DeviceId.deviceId("of:0000000000000002"),
MacAddress.valueOf("00:00:00:00:00:02"));
put(DeviceId.deviceId("of:0000000000000003"),
MacAddress.valueOf("00:00:00:00:00:03"));
put(DeviceId.deviceId("of:0000000000000004"),
MacAddress.valueOf("00:00:00:00:00:04"));
put(DeviceId.deviceId("of:0000000000000005"),
MacAddress.valueOf("00:00:00:00:00:05"));
put(DeviceId.deviceId("of:0000000000000006"),
MacAddress.valueOf("00:00:00:00:00:06"));
}
};
private final HashMap<DeviceId, Ip4Address> deviceIpMap =
new HashMap<DeviceId, Ip4Address>() {
{
put(DeviceId.deviceId("of:0000000000000001"),
Ip4Address.valueOf("192.168.0.1"));
put(DeviceId.deviceId("of:0000000000000002"),
Ip4Address.valueOf("192.168.0.2"));
put(DeviceId.deviceId("of:0000000000000003"),
Ip4Address.valueOf("192.168.0.3"));
put(DeviceId.deviceId("of:0000000000000004"),
Ip4Address.valueOf("192.168.0.4"));
put(DeviceId.deviceId("of:0000000000000005"),
Ip4Address.valueOf("192.168.0.5"));
put(DeviceId.deviceId("of:0000000000000006"),
Ip4Address.valueOf("192.168.0.6"));
}
};
@Override
public int getSegmentId(DeviceId deviceId) {
if (deviceSegmentIdMap.get(deviceId) != null) {
log.debug("getSegmentId for device{} is {}",
deviceId,
deviceSegmentIdMap.get(deviceId));
return deviceSegmentIdMap.get(deviceId);
} else {
throw new IllegalStateException();
}
}
@Override
public MacAddress getDeviceMac(DeviceId deviceId) {
if (deviceMacMap.get(deviceId) != null) {
log.debug("getDeviceMac for device{} is {}",
deviceId,
deviceMacMap.get(deviceId));
return deviceMacMap.get(deviceId);
} else {
throw new IllegalStateException();
}
}
@Override
public boolean isEdgeDevice(DeviceId deviceId) {
if (deviceId.equals(DeviceId.deviceId("of:0000000000000001"))
|| deviceId.equals(DeviceId.deviceId("of:0000000000000006"))) {
return true;
}
return false;
}
@Override
public List<Integer> getAllDeviceSegmentIds() {
return allSegmentIds;
}
/**
* Returns Segment ID for the router with the MAC address given.
*
* @param targetMac Mac address for the router
* @return Segment ID for the router with the MAC address
*/
public int getSegmentId(MacAddress targetMac) {
for (Map.Entry<DeviceId, MacAddress> entry: deviceMacMap.entrySet()) {
if (entry.getValue().equals(targetMac)) {
return deviceSegmentIdMap.get(entry.getKey());
}
}
return -1;
}
/**
* Returns Segment ID for the router withe IP address given.
*
* @param targetAddress IP address of the router
* @return Segment ID for the router with the IP address
*/
public int getSegmentId(Ip4Address targetAddress) {
for (Map.Entry<DeviceId, Ip4Address> entry: deviceIpMap.entrySet()) {
if (entry.getValue().equals(targetAddress)) {
return deviceSegmentIdMap.get(entry.getKey());
}
}
return -1;
}
/**
* Returns Router IP address for the router with the device ID given.
*
* @param deviceId device ID of the router
* @return IP address of the router
*/
public Ip4Address getRouterIp(DeviceId deviceId) {
if (deviceIpMap.get(deviceId) != null) {
log.debug("getDeviceIp for device{} is {}",
deviceId,
deviceIpMap.get(deviceId));
return deviceIpMap.get(deviceId);
} else {
throw new IllegalStateException();
}
}
/**
* Returns the Device ID of the router with the Segment ID given.
*
* @param sid Segment ID of the router
* @return Device ID of the router
*/
public DeviceId getDeviceId(int sid) {
for (Map.Entry<DeviceId, Integer> entry: deviceSegmentIdMap.entrySet()) {
if (entry.getValue() == sid) {
return entry.getKey();
}
}
return null;
}
/**
* Returns the Device ID of the router with the IP address given.
*
* @param ipAddress IP address of the router
* @return Device ID of the router
*/
public DeviceId getDeviceId(Ip4Address ipAddress) {
for (Map.Entry<DeviceId, Ip4Address> entry: deviceIpMap.entrySet()) {
if (entry.getValue().equals(ipAddress)) {
return entry.getKey();
}
}
return null;
}
}
/*
* Copyright 2015 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.segmentrouting;
import org.onosproject.net.DefaultLink;
import org.onosproject.net.DefaultPath;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.Path;
import org.onosproject.net.provider.ProviderId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
/**
* This class creates bandwidth constrained breadth first tree and returns paths
* from root Device to leaf Devicees which satisfies the bandwidth condition. If
* bandwidth parameter is not specified, the normal breadth first tree will be
* calculated. The paths are snapshot paths at the point of the class
* instantiation.
*/
public class ECMPShortestPathGraph {
LinkedList<DeviceId> deviceQueue = new LinkedList<>();
LinkedList<Integer> distanceQueue = new LinkedList<>();
HashMap<DeviceId, Integer> deviceSearched = new HashMap<>();
HashMap<DeviceId, ArrayList<Link>> upstreamLinks = new HashMap<>();
HashMap<DeviceId, ArrayList<Path>> paths = new HashMap<>();
HashMap<Integer, ArrayList<DeviceId>> distanceDeviceMap = new HashMap<>();
DeviceId rootDevice;
private SegmentRoutingManager srManager;
private static final Logger log = LoggerFactory
.getLogger(ECMPShortestPathGraph.class);
/**
* Constructor.
*
* @param rootDevice root of the BFS tree
* @param linkListToAvoid link list to avoid
* @param deviceIdListToAvoid device list to avoid
*/
public ECMPShortestPathGraph(DeviceId rootDevice, List<String> deviceIdListToAvoid,
List<Link> linkListToAvoid) {
this.rootDevice = rootDevice;
calcECMPShortestPathGraph(deviceIdListToAvoid, linkListToAvoid);
}
/**
* Constructor.
*
* @param rootDevice root of the BFS tree
* @param srManager SegmentRoutingManager object
*/
public ECMPShortestPathGraph(DeviceId rootDevice, SegmentRoutingManager srManager) {
this.rootDevice = rootDevice;
this.srManager = srManager;
calcECMPShortestPathGraph();
}
/**
* Calculates the BFS tree using any provided constraints and Intents.
*/
private void calcECMPShortestPathGraph() {
deviceQueue.add(rootDevice);
int currDistance = 0;
distanceQueue.add(currDistance);
deviceSearched.put(rootDevice, currDistance);
while (!deviceQueue.isEmpty()) {
DeviceId sw = deviceQueue.poll();
DeviceId prevSw = null;
currDistance = distanceQueue.poll();
for (Link link : srManager.linkService.getDeviceEgressLinks(sw)) {
DeviceId reachedDevice = link.dst().deviceId();
if ((prevSw != null)
&& (prevSw.equals(reachedDevice))) {
/* Ignore LAG links between the same set of Devicees */
continue;
} else {
prevSw = reachedDevice;
}
Integer distance = deviceSearched.get(reachedDevice);
if ((distance != null) && (distance.intValue() < (currDistance + 1))) {
continue;
}
if (distance == null) {
/* First time visiting this Device node */
deviceQueue.add(reachedDevice);
distanceQueue.add(currDistance + 1);
deviceSearched.put(reachedDevice, currDistance + 1);
ArrayList<DeviceId> distanceSwArray = distanceDeviceMap
.get(currDistance + 1);
if (distanceSwArray == null) {
distanceSwArray = new ArrayList<DeviceId>();
distanceSwArray.add(reachedDevice);
distanceDeviceMap.put(currDistance + 1, distanceSwArray);
} else {
distanceSwArray.add(reachedDevice);
}
}
ArrayList<Link> upstreamLinkArray =
upstreamLinks.get(reachedDevice);
if (upstreamLinkArray == null) {
upstreamLinkArray = new ArrayList<Link>();
upstreamLinkArray.add(copyDefaultLink(link));
//upstreamLinkArray.add(link);
upstreamLinks.put(reachedDevice, upstreamLinkArray);
} else {
/* ECMP links */
upstreamLinkArray.add(copyDefaultLink(link));
}
}
}
}
/**
* Calculates the BFS tree using any provided constraints and Intents.
*/
private void calcECMPShortestPathGraph(List<String> deviceIdListToAvoid, List<Link> linksToAvoid) {
deviceQueue.add(rootDevice);
int currDistance = 0;
distanceQueue.add(currDistance);
deviceSearched.put(rootDevice, currDistance);
boolean foundLinkToAvoid = false;
while (!deviceQueue.isEmpty()) {
DeviceId sw = deviceQueue.poll();
DeviceId prevSw = null;
currDistance = distanceQueue.poll();
for (Link link : srManager.linkService.getDeviceEgressLinks(sw)) {
for (Link linkToAvoid: linksToAvoid) {
// TODO: equls should work
//if (link.equals(linkToAvoid)) {
if (linkContains(link, linksToAvoid)) {
foundLinkToAvoid = true;
break;
}
}
if (foundLinkToAvoid) {
foundLinkToAvoid = false;
continue;
}
DeviceId reachedDevice = link.dst().deviceId();
if (deviceIdListToAvoid.contains(reachedDevice.toString())) {
continue;
}
if ((prevSw != null)
&& (prevSw.equals(reachedDevice))) {
/* Ignore LAG links between the same set of Devicees */
continue;
} else {
prevSw = reachedDevice;
}
Integer distance = deviceSearched.get(reachedDevice);
if ((distance != null) && (distance.intValue() < (currDistance + 1))) {
continue;
}
if (distance == null) {
/* First time visiting this Device node */
deviceQueue.add(reachedDevice);
distanceQueue.add(currDistance + 1);
deviceSearched.put(reachedDevice, currDistance + 1);
ArrayList<DeviceId> distanceSwArray = distanceDeviceMap
.get(currDistance + 1);
if (distanceSwArray == null) {
distanceSwArray = new ArrayList<DeviceId>();
distanceSwArray.add(reachedDevice);
distanceDeviceMap.put(currDistance + 1, distanceSwArray);
} else {
distanceSwArray.add(reachedDevice);
}
}
ArrayList<Link> upstreamLinkArray =
upstreamLinks.get(reachedDevice);
if (upstreamLinkArray == null) {
upstreamLinkArray = new ArrayList<Link>();
upstreamLinkArray.add(copyDefaultLink(link));
upstreamLinks.put(reachedDevice, upstreamLinkArray);
} else {
/* ECMP links */
upstreamLinkArray.add(copyDefaultLink(link));
}
}
}
}
private boolean linkContains(Link link, List<Link> links) {
DeviceId srcDevice1 = link.src().deviceId();
DeviceId dstDevice1 = link.dst().deviceId();
long srcPort1 = link.src().port().toLong();
long dstPort1 = link.dst().port().toLong();
for (Link link2: links) {
DeviceId srcDevice2 = link2.src().deviceId();
DeviceId dstDevice2 = link2.dst().deviceId();
long srcPort2 = link2.src().port().toLong();
long dstPort2 = link2.dst().port().toLong();
if (srcDevice1.toString().equals(srcDevice2.toString())
&& dstDevice1.toString().equals(dstDevice2.toString())
&& srcPort1 == srcPort2 && dstPort1 == dstPort2) {
return true;
}
}
return false;
}
private void getDFSPaths(DeviceId dstDeviceDeviceId, Path path, ArrayList<Path> paths) {
DeviceId rootDeviceDeviceId = rootDevice;
for (Link upstreamLink : upstreamLinks.get(dstDeviceDeviceId)) {
/* Deep clone the path object */
Path sofarPath;
ArrayList<Link> sofarLinks = new ArrayList<Link>();
if (path != null && !path.links().isEmpty()) {
sofarLinks.addAll(path.links());
}
sofarLinks.add(upstreamLink);
sofarPath = new DefaultPath(ProviderId.NONE, sofarLinks, 0);
if (upstreamLink.src().deviceId().equals(rootDeviceDeviceId)) {
paths.add(sofarPath);
return;
} else {
getDFSPaths(upstreamLink.src().deviceId(), sofarPath, paths);
}
}
}
/**
* Return root Device for the graph.
*
* @return root Device
*/
public DeviceId getRootDevice() {
return rootDevice;
}
/**
* Return the computed ECMP paths from the root Device to a given Device in
* the network.
*
* @param targetDevice the target Device
* @return the list of ECMP Paths from the root Device to the target Device
*/
public ArrayList<Path> getECMPPaths(DeviceId targetDevice) {
ArrayList<Path> pathArray = paths.get(targetDevice);
if (pathArray == null && deviceSearched.containsKey(
targetDevice)) {
pathArray = new ArrayList<>();
DeviceId sw = targetDevice;
getDFSPaths(sw, null, pathArray);
paths.put(targetDevice, pathArray);
}
return pathArray;
}
/**
* Return the complete info of the computed ECMP paths for each Device
* learned in multiple iterations from the root Device.
*
* @return the hash table of Devicees learned in multiple Dijkstra
* iterations and corresponding ECMP paths to it from the root
* Device
*/
public HashMap<Integer, HashMap<DeviceId,
ArrayList<Path>>> getCompleteLearnedDeviceesAndPaths() {
HashMap<Integer, HashMap<DeviceId, ArrayList<Path>>> pathGraph = new
HashMap<Integer, HashMap<DeviceId, ArrayList<Path>>>();
for (Integer itrIndx : distanceDeviceMap.keySet()) {
HashMap<DeviceId, ArrayList<Path>> swMap = new
HashMap<DeviceId, ArrayList<Path>>();
for (DeviceId sw : distanceDeviceMap.get(itrIndx)) {
swMap.put(sw, getECMPPaths(sw));
}
pathGraph.put(itrIndx, swMap);
}
return pathGraph;
}
/**
* Return the complete info of the computed ECMP paths for each Device
* learned in multiple iterations from the root Device.
*
* @return the hash table of Devicees learned in multiple Dijkstra
* iterations and corresponding ECMP paths in terms of Devicees to
* be traversed to it from the root Device
*/
public HashMap<Integer, HashMap<DeviceId,
ArrayList<ArrayList<DeviceId>>>> getAllLearnedSwitchesAndVia() {
HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> deviceViaMap =
new HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>>();
for (Integer itrIndx : distanceDeviceMap.keySet()) {
HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swMap =
new HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>();
for (DeviceId sw : distanceDeviceMap.get(itrIndx)) {
ArrayList<ArrayList<DeviceId>> swViaArray = new ArrayList<>();
for (Path path : getECMPPaths(sw)) {
ArrayList<DeviceId> swVia = new ArrayList<>();
for (Link link : path.links()) {
if (link.src().deviceId().equals(rootDevice)) {
/* No need to add the root Device again in
* the Via list
*/
continue;
}
swVia.add(link.src().deviceId());
}
swViaArray.add(swVia);
}
swMap.put(sw, swViaArray);
}
deviceViaMap.put(itrIndx, swMap);
}
return deviceViaMap;
}
private Link copyDefaultLink(Link link) {
DefaultLink src = (DefaultLink) link;
DefaultLink defaultLink = new DefaultLink(src.providerId(), src.src(),
src.dst(), src.type(), src.annotations());
return defaultLink;
}
@Override
public String toString() {
StringBuilder sBuilder = new StringBuilder();
for (Device device: srManager.deviceService.getDevices()) {
if (device.id() != rootDevice) {
sBuilder.append("Paths from" + rootDevice + " to " + device.id() + "\r\n");
ArrayList<Path> paths = getECMPPaths(device.id());
if (paths != null) {
for (Path path : paths) {
for (Link link : path.links()) {
sBuilder.append(" : " + link.src() + " -> " + link.dst());
}
}
}
}
}
return sBuilder.toString();
}
}
/*
* Copyright 2015 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.segmentrouting;
import java.nio.ByteBuffer;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ICMP;
import org.onlab.packet.IPv4;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MPLS;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkNotNull;
public class IcmpHandler {
private static Logger log = LoggerFactory.getLogger(IcmpHandler.class);
private SegmentRoutingManager srManager;
private NetworkConfigHandler config;
/**
* Creates an IcmpHandler object.
*
* @param srManager SegmentRoutingManager object
*/
public IcmpHandler(SegmentRoutingManager srManager) {
this.srManager = srManager;
this.config = checkNotNull(srManager.networkConfigHandler);
}
/**
* Process incoming ICMP packet.
* If it is an ICMP request to router or known host, then sends an ICMP response.
* If it is an ICMP packet to known host and forward the packet to the host.
* If it is an ICMP packet to unknown host in a subnet, then sends an ARP request
* to the subnet.
*
* @param pkt
*/
public void processPacketIn(InboundPacket pkt) {
Ethernet ethernet = pkt.parsed();
IPv4 ipv4 = (IPv4) ethernet.getPayload();
ConnectPoint connectPoint = pkt.receivedFrom();
DeviceId deviceId = connectPoint.deviceId();
Ip4Address destinationAddress =
Ip4Address.valueOf(ipv4.getDestinationAddress());
Ip4Address gatewayIpAddress = config.getGatewayIpAddress(deviceId);
IpPrefix routerIpPrefix = config.getRouterIpAddress(deviceId);
Ip4Address routerIpAddress = routerIpPrefix.getIp4Prefix().address();
// ICMP to the router IP or gateway IP
if (((ICMP) ipv4.getPayload()).getIcmpType() == ICMP.TYPE_ECHO_REQUEST &&
(destinationAddress.equals(routerIpAddress) ||
gatewayIpAddress.equals(destinationAddress))) {
sendICMPResponse(ethernet, connectPoint);
// TODO: do we need to set the flow rule again ??
// ICMP for any known host
} else if (!srManager.hostService.getHostsByIp(destinationAddress).isEmpty()) {
srManager.ipHandler.forwardPackets(deviceId, destinationAddress);
// ICMP for an unknown host in the subnet of the router
} else if (config.inSameSubnet(deviceId, destinationAddress)) {
srManager.arpHandler.sendArpRequest(deviceId, destinationAddress, connectPoint);
// ICMP for an unknown host
} else {
log.debug("ICMP request for unknown host {} ", destinationAddress);
// Do nothing
}
}
private void sendICMPResponse(Ethernet icmpRequest, ConnectPoint outport) {
Ethernet icmpReplyEth = new Ethernet();
IPv4 icmpRequestIpv4 = (IPv4) icmpRequest.getPayload();
IPv4 icmpReplyIpv4 = new IPv4();
int destAddress = icmpRequestIpv4.getDestinationAddress();
icmpReplyIpv4.setDestinationAddress(icmpRequestIpv4.getSourceAddress());
icmpReplyIpv4.setSourceAddress(destAddress);
icmpReplyIpv4.setTtl((byte) 64);
icmpReplyIpv4.setChecksum((short) 0);
ICMP icmpReply = (ICMP) icmpRequestIpv4.getPayload().clone();
icmpReply.setIcmpType(ICMP.TYPE_ECHO_REPLY);
icmpReply.setIcmpCode(ICMP.SUBTYPE_ECHO_REPLY);
icmpReply.setChecksum((short) 0);
icmpReplyIpv4.setPayload(icmpReply);
icmpReplyEth.setPayload(icmpReplyIpv4);
icmpReplyEth.setEtherType(Ethernet.TYPE_IPV4);
icmpReplyEth.setDestinationMACAddress(icmpRequest.getSourceMACAddress());
icmpReplyEth.setSourceMACAddress(icmpRequest.getDestinationMACAddress());
icmpReplyEth.setVlanID(icmpRequest.getVlanID());
Ip4Address destIpAddress = Ip4Address.valueOf(icmpReplyIpv4.getDestinationAddress());
Ip4Address destRouterAddress = config.getDestinationRouterAddress(destIpAddress);
int sid = config.getMplsId(destRouterAddress);
if (sid < 0) {
log.warn("Cannot find the Segment ID for {}", destAddress);
return;
}
sendPacketOut(outport, icmpReplyEth, sid);
}
private void sendPacketOut(ConnectPoint outport, Ethernet payload, int sid) {
IPv4 ipPacket = (IPv4) payload.getPayload();
Ip4Address destIpAddress = Ip4Address.valueOf(ipPacket.getDestinationAddress());
if (sid == -1 || config.getMplsId(payload.getDestinationMAC()) == sid ||
config.inSameSubnet(outport.deviceId(), destIpAddress)) {
TrafficTreatment treatment = DefaultTrafficTreatment.builder().
setOutput(outport.port()).build();
OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
treatment, ByteBuffer.wrap(payload.serialize()));
srManager.packetService.emit(packet);
} else {
log.warn("Send a MPLS packet as a ICMP response");
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(outport.port())
.build();
payload.setEtherType(Ethernet.MPLS_UNICAST);
MPLS mplsPkt = new MPLS();
mplsPkt.setLabel(sid);
mplsPkt.setTtl(((IPv4) payload.getPayload()).getTtl());
mplsPkt.setPayload(payload.getPayload());
payload.setPayload(mplsPkt);
OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
treatment, ByteBuffer.wrap(payload.serialize()));
srManager.packetService.emit(packet);
}
}
}
/*
* Copyright 2015 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.segmentrouting;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
import org.onlab.packet.Ip4Address;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import static com.google.common.base.Preconditions.checkNotNull;
public class IpHandler {
private static Logger log = LoggerFactory.getLogger(IpHandler.class);
private SegmentRoutingManager srManager;
private NetworkConfigHandler config;
private ConcurrentHashMap<Ip4Address, ConcurrentLinkedQueue<IPv4>> ipPacketQueue;
/**
* Creates an IpHandler object.
*
* @param srManager SegmentRoutingManager object
*/
public IpHandler(SegmentRoutingManager srManager) {
this.srManager = srManager;
this.config = checkNotNull(srManager.networkConfigHandler);
ipPacketQueue = new ConcurrentHashMap<Ip4Address, ConcurrentLinkedQueue<IPv4>>();
}
/**
* Processes incoming IP packets.
*
* If it is an IP packet for known host, then forward it to the host.
* If it is an IP packet for unknown host in subnet, then send an ARP request
* to the subnet.
*
* @param pkt incoming packet
*/
public void processPacketIn(InboundPacket pkt) {
Ethernet ethernet = pkt.parsed();
IPv4 ipv4 = (IPv4) ethernet.getPayload();
ConnectPoint connectPoint = pkt.receivedFrom();
DeviceId deviceId = connectPoint.deviceId();
Ip4Address destinationAddress =
Ip4Address.valueOf(ipv4.getDestinationAddress());
// IP packet for know hosts
if (!srManager.hostService.getHostsByIp(destinationAddress).isEmpty()) {
forwardPackets(deviceId, destinationAddress);
// IP packet for unknown host in the subnet of the router
} else if (config.inSameSubnet(deviceId, destinationAddress)) {
srManager.arpHandler.sendArpRequest(deviceId, destinationAddress, connectPoint);
// IP packets for unknown host
} else {
log.debug("ICMP request for unknown host {} which is not in the subnet",
destinationAddress);
// Do nothing
}
}
/**
* Adds the IP packet to a buffer.
* The packets are forwarded to corresponding destination when the destination
* MAC address is known via ARP response.
*
* @param ipPacket IP packet to add to the buffer
*/
public void addToPacketBuffer(IPv4 ipPacket) {
// Better not buffer TPC packets due to out-of-order packet transfer
if (ipPacket.getProtocol() == IPv4.PROTOCOL_TCP) {
return;
}
Ip4Address destIpAddress = Ip4Address.valueOf(ipPacket.getDestinationAddress());
if (ipPacketQueue.get(destIpAddress) == null) {
ConcurrentLinkedQueue<IPv4> queue = new ConcurrentLinkedQueue<IPv4>();
queue.add(ipPacket);
ipPacketQueue.put(destIpAddress, queue);
} else {
ipPacketQueue.get(destIpAddress).add(ipPacket);
}
}
/**
* Forwards IP packets in the buffer to the destination IP address.
* It is called when the controller finds the destination MAC address
* via ARP responsees.
*
* @param deviceId switch device ID
* @param destIpAddress destination IP address
*/
public void forwardPackets(DeviceId deviceId, Ip4Address destIpAddress) {
for (IPv4 ipPacket : ipPacketQueue.get(destIpAddress)) {
Ip4Address destAddress = Ip4Address.valueOf(ipPacket.getDestinationAddress());
if (ipPacket != null && config.inSameSubnet(deviceId, destAddress)) {
ipPacket.setTtl((byte) (ipPacket.getTtl() - 1));
ipPacket.setChecksum((short) 0);
for (Host dest: srManager.hostService.getHostsByIp(destIpAddress)) {
Ethernet eth = new Ethernet();
eth.setDestinationMACAddress(dest.mac());
eth.setSourceMACAddress(config.getRouterMacAddress(
deviceId));
eth.setEtherType(Ethernet.TYPE_IPV4);
eth.setPayload(ipPacket);
TrafficTreatment treatment = DefaultTrafficTreatment.builder().
setOutput(dest.location().port()).build();
OutboundPacket packet = new DefaultOutboundPacket(deviceId,
treatment, ByteBuffer.wrap(eth.serialize()));
srManager.packetService.emit(packet);
}
}
}
}
}
/*
* Copyright 2015 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.segmentrouting;
import com.google.common.collect.Lists;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.PortNumber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.util.List;
import java.util.Set;
/**
* This class is temporary class and used only for test.
* It will be replaced with "real" Network Config Manager.
*/
public class NetworkConfigHandler {
private static Logger log = LoggerFactory.getLogger(NetworkConfigHandler.class);
private SegmentRoutingManager srManager;
private DeviceConfiguration deviceConfig = new DeviceConfiguration();
public NetworkConfigHandler(SegmentRoutingManager srManager) {
this.srManager = srManager;
}
public Ip4Address getGatewayIpAddress(DeviceId deviceId) {
if (deviceId.uri().equals(URI.create("of:0000000000000001"))) {
return Ip4Address.valueOf("10.0.1.128");
} else if (deviceId.uri().equals(URI.create("of:0000000000000006"))) {
return Ip4Address.valueOf("7.7.7.128");
}
log.warn("No gateway Ip address was found for {}", deviceId);
return Ip4Address.valueOf("0.0.0.0");
}
public IpPrefix getRouterIpAddress(DeviceId deviceId) {
return IpPrefix.valueOf(deviceConfig.getRouterIp(deviceId), 32);
}
public MacAddress getRouterMacAddress(DeviceId deviceId) {
return deviceConfig.getDeviceMac(deviceId);
}
public boolean inSameSubnet(DeviceId deviceId, Ip4Address destIp) {
String subnetInfo = getSubnetInfo(deviceId);
if (subnetInfo == null) {
return false;
}
IpPrefix prefix = IpPrefix.valueOf(subnetInfo);
if (prefix.contains(destIp)) {
return true;
}
return false;
}
public boolean inSameSubnet(Ip4Address address, int sid) {
DeviceId deviceId = deviceConfig.getDeviceId(sid);
if (deviceId == null) {
log.warn("Cannot find a device for SID {}", sid);
return false;
}
String subnetInfo = getSubnetInfo(deviceId);
if (subnetInfo == null) {
log.warn("Cannot find the subnet info for {}", deviceId);
return false;
}
Ip4Prefix subnet = Ip4Prefix.valueOf(subnetInfo);
if (subnet.contains(address)) {
return true;
}
return false;
}
public String getSubnetInfo(DeviceId deviceId) {
// TODO : supports multiple subnet
if (deviceId.uri().equals(URI.create("of:0000000000000001"))) {
return "10.0.1.1/24";
} else if (deviceId.uri().equals(URI.create("of:0000000000000006"))) {
return "7.7.7.7/24";
} else {
log.error("Switch {} is not an edge router", deviceId);
return null;
}
}
public int getMplsId(DeviceId deviceId) {
return deviceConfig.getSegmentId(deviceId);
}
public int getMplsId(MacAddress mac) {
return deviceConfig.getSegmentId(mac);
}
public int getMplsId(Ip4Address address) {
return deviceConfig.getSegmentId(address);
}
public boolean isEcmpNotSupportedInTransit(DeviceId deviceId) {
return false;
}
public boolean isTransitRouter(DeviceId deviceId) {
return true;
}
public boolean isEdgeRouter(DeviceId deviceId) {
if (deviceId.uri().equals(URI.create("of:0000000000000001"))
|| deviceId.uri().equals(URI.create("of:0000000000000006"))) {
return true;
}
return false;
}
private List<PortNumber> getPortsToNeighbors(DeviceId deviceId, List<DeviceId> fwdSws) {
List<PortNumber> portNumbers = Lists.newArrayList();
Set<Link> links = srManager.linkService.getDeviceEgressLinks(deviceId);
for (Link link: links) {
for (DeviceId swId: fwdSws) {
if (link.dst().deviceId().equals(swId)) {
portNumbers.add(link.src().port());
break;
}
}
}
return portNumbers;
}
public List<PortNumber> getPortsToDevice(DeviceId deviceId) {
List<PortNumber> portNumbers = Lists.newArrayList();
Set<Link> links = srManager.linkService.getDeviceEgressLinks(deviceId);
for (Link link: links) {
if (link.dst().deviceId().equals(deviceId)) {
portNumbers.add(link.src().port());
}
}
return portNumbers;
}
public Ip4Address getDestinationRouterAddress(Ip4Address destIpAddress) {
// TODO: need to check the subnet info
if (destIpAddress.toString().equals("10.0.1.1")) {
return Ip4Address.valueOf("192.168.0.1");
} else if (destIpAddress.toString().equals("7.7.7.7")) {
return Ip4Address.valueOf("192.168.0.6");
} else {
log.warn("No router was found for {}", destIpAddress);
return null;
}
}
public DeviceId getDeviceId(Ip4Address ip4Address) {
return deviceConfig.getDeviceId(ip4Address);
}
public MacAddress getRouterMac(Ip4Address targetAddress) {
if (targetAddress.toString().equals("10.0.1.128")) {
return MacAddress.valueOf("00:00:00:00:00:01");
} else if (targetAddress.toString().equals("7.7.7.128")) {
return MacAddress.valueOf("00:00:00:00:00:06");
} else {
log.warn("Cannot find a router for {}", targetAddress);
return null;
}
}
}
/*
* Copyright 2015 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.segmentrouting;
import org.onlab.packet.Ethernet;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.MplsLabel;
import org.onosproject.grouphandler.NeighborSet;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.group.DefaultGroupKey;
import org.onosproject.net.group.Group;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
public class RoutingRulePopulator {
private static final Logger log = LoggerFactory.getLogger(RoutingRulePopulator.class);
private SegmentRoutingManager srManager;
private NetworkConfigHandler config;
/**
* Creates a RoutingRulePopulator object.
*
* @param srManager
*/
public RoutingRulePopulator(SegmentRoutingManager srManager) {
this.srManager = srManager;
this.config = checkNotNull(srManager.networkConfigHandler);
}
/**
* Populates IP flow rules for specific hosts directly connected to the switch.
*
* @param deviceId switch ID to set the rules
* @param hostIp host IP address
* @param hostMac host MAC address
* @param outPort port where the host is connected
*/
public void populateIpRuleForHost(DeviceId deviceId, Ip4Address hostIp,
MacAddress hostMac, PortNumber outPort) {
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
sbuilder.matchIPDst(IpPrefix.valueOf(hostIp, 32));
sbuilder.matchEthType(Ethernet.TYPE_IPV4);
tbuilder.setEthDst(hostMac)
.setEthSrc(config.getRouterMacAddress(deviceId))
.setOutput(outPort);
TrafficTreatment treatment = tbuilder.build();
TrafficSelector selector = sbuilder.build();
FlowRule f = new DefaultFlowRule(deviceId, selector, treatment, 100,
srManager.appId, 600, false, FlowRule.Type.IP);
srManager.flowRuleService.applyFlowRules(f);
log.debug("Flow rule {} is set to switch {}", f, deviceId);
}
/**
* Populates IP flow rules for the subnets of the destination router.
*
* @param deviceId switch ID to set the rules
* @param subnetInfo subnet information
* @param destSw destination switch ID
* @param nextHops next hop switch ID list
* @return true if all rules are set successfully, false otherwise
*/
public boolean populateIpRuleForSubnet(DeviceId deviceId, String subnetInfo,
DeviceId destSw, Set<DeviceId> nextHops) {
List<IpPrefix> subnets = extractSubnet(subnetInfo);
for (IpPrefix subnet: subnets) {
if (!populateIpRuleForRouter(deviceId, subnet, destSw, nextHops)) {
return false;
}
}
return true;
}
/**
* Populates IP flow rules for the router IP address.
*
* @param deviceId device ID to set the rules
* @param ipPrefix the IP address of the destination router
* @param destSw device ID of the destination router
* @param nextHops next hop switch ID list
* @return true if all rules are set successfully, false otherwise
*/
public boolean populateIpRuleForRouter(DeviceId deviceId, IpPrefix ipPrefix,
DeviceId destSw, Set<DeviceId> nextHops) {
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
sbuilder.matchIPDst(ipPrefix);
sbuilder.matchEthType(Ethernet.TYPE_IPV4);
NeighborSet ns = null;
//If the next hop is the same as the final destination, then MPLS label is not set.
if (nextHops.size() == 1 && nextHops.toArray()[0].equals(destSw)) {
tbuilder.decNwTtl();
ns = new NeighborSet(nextHops);
} else {
tbuilder.copyTtlOut();
ns = new NeighborSet(nextHops, config.getMplsId(destSw));
}
DefaultGroupKey groupKey = (DefaultGroupKey) srManager.getGroupKey(ns);
if (groupKey == null) {
log.warn("Group key is not found for ns {}", ns);
return false;
}
Group group = srManager.groupService.getGroup(deviceId, groupKey);
if (group != null) {
tbuilder.group(group.id());
} else {
log.warn("No group found for NeighborSet {} from {} to {}",
ns, deviceId, destSw);
return false;
}
TrafficTreatment treatment = tbuilder.build();
TrafficSelector selector = sbuilder.build();
FlowRule f = new DefaultFlowRule(deviceId, selector, treatment, 100,
srManager.appId, 600, false, FlowRule.Type.IP);
srManager.flowRuleService.applyFlowRules(f);
log.debug("IP flow rule {} is set to switch {}", f, deviceId);
return true;
}
/**
* Populates MPLS flow rules to all transit routers.
*
* @param deviceId device ID of the switch to set the rules
* @param destSwId destination switch device ID
* @param nextHops next hops switch ID list
* @return true if all rules are set successfully, false otherwise
*/
public boolean populateMplsRule(DeviceId deviceId, DeviceId destSwId, Set<DeviceId> nextHops) {
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
Collection<TrafficTreatment> treatments = new ArrayList<>();
// TODO Handle the case of Bos == false
sbuilder.matchMplsLabel(MplsLabel.mplsLabel(config.getMplsId(destSwId)));
sbuilder.matchEthType(Ethernet.MPLS_UNICAST);
//If the next hop is the destination router, do PHP
if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) {
TrafficTreatment treatmentBos =
getMplsTreatment(deviceId, destSwId, nextHops, true, true);
TrafficTreatment treatment =
getMplsTreatment(deviceId, destSwId, nextHops, true, false);
if (treatmentBos != null) {
treatments.add(treatmentBos);
} else {
log.warn("Failed to set MPLS rules.");
return false;
}
} else {
TrafficTreatment treatmentBos =
getMplsTreatment(deviceId, destSwId, nextHops, false, true);
TrafficTreatment treatment =
getMplsTreatment(deviceId, destSwId, nextHops, false, false);
if (treatmentBos != null) {
treatments.add(treatmentBos);
} else {
log.warn("Failed to set MPLS rules.");
return false;
}
}
TrafficSelector selector = sbuilder.build();
for (TrafficTreatment treatment: treatments) {
FlowRule f = new DefaultFlowRule(deviceId, selector, treatment, 100,
srManager.appId, 600, false, FlowRule.Type.MPLS);
srManager.flowRuleService.applyFlowRules(f);
log.debug("MPLS rule {} is set to {}", f, deviceId);
}
return true;
}
private TrafficTreatment getMplsTreatment(DeviceId deviceId, DeviceId destSw,
Set<DeviceId> nextHops,
boolean phpRequired, boolean isBos) {
TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
if (phpRequired) {
tbuilder.copyTtlIn();
if (isBos) {
tbuilder.popMpls(Ethernet.TYPE_IPV4)
.decNwTtl();
} else {
tbuilder.popMpls(Ethernet.MPLS_UNICAST)
.decMplsTtl();
}
} else {
tbuilder.decMplsTtl();
}
if (config.isEcmpNotSupportedInTransit(deviceId)
&& config.isTransitRouter(deviceId)) {
Link link = selectOneLink(deviceId, nextHops);
if (link == null) {
log.warn("No link from {} to {}", deviceId, nextHops);
return null;
}
tbuilder.setEthSrc(config.getRouterMacAddress(deviceId))
.setEthDst(config.getRouterMacAddress(link.dst().deviceId()))
.setOutput(link.src().port());
} else {
NeighborSet ns = new NeighborSet(nextHops);
DefaultGroupKey groupKey = (DefaultGroupKey) srManager.getGroupKey(ns);
if (groupKey == null) {
log.warn("Group key is not found for ns {}", ns);
return null;
}
Group group = srManager.groupService.getGroup(deviceId, groupKey);
if (group != null) {
tbuilder.group(group.id());
} else {
log.warn("No group found for ns {} key {} in {}", ns,
srManager.getGroupKey(ns), deviceId);
return null;
}
}
return tbuilder.build();
}
/**
* Populates VLAN flows rules.
* All packets are forwarded to TMAC table.
*
* @param deviceId switch ID to set the rules
*/
public void populateTableVlan(DeviceId deviceId) {
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
tbuilder.transition(FlowRule.Type.ETHER);
TrafficTreatment treatment = tbuilder.build();
TrafficSelector selector = sbuilder.build();
FlowRule f = new DefaultFlowRule(deviceId, selector, treatment, 100,
srManager.appId, 600, false, FlowRule.Type.VLAN);
srManager.flowRuleService.applyFlowRules(f);
log.debug("Vlan flow rule {} is set to switch {}", f, deviceId);
}
/**
* Populates TMAC table rules.
* IP packets are forwarded to IP table.
* MPLS packets are forwarded to MPLS table.
*
* @param deviceId switch ID to set the rules
*/
public void populateTableTMac(DeviceId deviceId) {
// flow rule for IP packets
TrafficSelector selectorIp = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchEthDst(config.getRouterMacAddress(deviceId))
.build();
TrafficTreatment treatmentIp = DefaultTrafficTreatment.builder()
.transition(FlowRule.Type.IP)
.build();
FlowRule flowIp = new DefaultFlowRule(deviceId, selectorIp, treatmentIp, 100,
srManager.appId, 600, false, FlowRule.Type.ETHER);
srManager.flowRuleService.applyFlowRules(flowIp);
// flow rule for MPLS packets
TrafficSelector selectorMpls = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.MPLS_UNICAST)
.matchEthDst(config.getRouterMacAddress(deviceId))
.build();
TrafficTreatment treatmentMpls = DefaultTrafficTreatment.builder()
.transition(FlowRule.Type.MPLS)
.build();
FlowRule flowMpls = new DefaultFlowRule(deviceId, selectorMpls, treatmentMpls, 100,
srManager.appId, 600, false, FlowRule.Type.ETHER);
srManager.flowRuleService.applyFlowRules(flowMpls);
}
/**
* Populates a table miss entry.
*
* @param deviceId switch ID to set rules
* @param tableToAdd table to set the rules
* @param toControllerNow flag to send packets to controller immediately
* @param toControllerWrite flag to send packets to controller at the end of pipeline
* @param toTable flag to send packets to a specific table
* @param tableToSend table type to send packets when the toTable flag is set
*/
public void populateTableMissEntry(DeviceId deviceId, FlowRule.Type tableToAdd, boolean toControllerNow,
boolean toControllerWrite,
boolean toTable, FlowRule.Type tableToSend) {
// TODO: Change arguments to EnumSet
TrafficSelector selector = DefaultTrafficSelector.builder()
.build();
TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
if (toControllerNow) {
tBuilder.setOutput(PortNumber.CONTROLLER);
}
if (toControllerWrite) {
tBuilder.deferred().setOutput(PortNumber.CONTROLLER);
}
if (toTable) {
tBuilder.transition(tableToSend);
}
FlowRule flow = new DefaultFlowRule(deviceId, selector, tBuilder.build(), 0,
srManager.appId, 600, false, tableToAdd);
srManager.flowRuleService.applyFlowRules(flow);
}
private List<IpPrefix> extractSubnet(String subnetInfo) {
List<IpPrefix> subnetIpPrefixes = new ArrayList<>();
// TODO: refactoring required depending on the format of the subnet info
IpPrefix prefix = IpPrefix.valueOf(subnetInfo);
if (prefix == null) {
log.error("Wrong ip prefix type {}", subnetInfo);
} else {
subnetIpPrefixes.add(prefix);
}
return subnetIpPrefixes;
}
private Link selectOneLink(DeviceId srcId, Set<DeviceId> destIds) {
Set<Link> links = srManager.linkService.getDeviceEgressLinks(srcId);
DeviceId destId = (DeviceId) destIds.toArray()[0];
for (Link link: links) {
if (link.dst().deviceId().equals(destId)) {
return link;
}
}
return null;
}
}
/*
* Copyright 2015 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.segmentrouting;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.event.Event;
import org.onosproject.grouphandler.DefaultGroupHandler;
import org.onosproject.grouphandler.NeighborSet;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.Port;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.group.Group;
import org.onosproject.net.group.GroupEvent;
import org.onosproject.net.group.GroupKey;
import org.onosproject.net.group.GroupListener;
import org.onosproject.net.group.GroupService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.link.LinkEvent;
import org.onosproject.net.link.LinkListener;
import org.onosproject.net.link.LinkService;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.PacketContext;
import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketService;
import org.onosproject.net.topology.TopologyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@SuppressWarnings("ALL")
@Component(immediate = true)
public class SegmentRoutingManager {
private static Logger log = LoggerFactory.getLogger(SegmentRoutingManager.class);
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TopologyService topologyService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PacketService packetService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentService intentService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostService hostService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowRuleService flowRuleService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkService linkService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected GroupService groupService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MastershipService mastershipService;
protected NetworkConfigHandler networkConfigHandler = new NetworkConfigHandler(this);
protected ArpHandler arpHandler = new ArpHandler(this);
protected IcmpHandler icmpHandler = new IcmpHandler(this);
protected IpHandler ipHandler = new IpHandler(this);
protected RoutingRulePopulator routingRulePopulator = new RoutingRulePopulator(this);
protected ApplicationId appId;
private DefaultRoutingHandler defaultRoutingHandler = new DefaultRoutingHandler(this);
private DeviceConfiguration deviceConfiguration = new DeviceConfiguration();
private InternalPacketProcessor processor = new InternalPacketProcessor();
private InternalEventHandler eventHandler = new InternalEventHandler();
private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
private static ScheduledFuture<?> eventHandlerFuture = null;
private ConcurrentLinkedQueue<Event> eventQueue = new ConcurrentLinkedQueue<Event>();
private Map<DeviceId, DefaultGroupHandler> groupHandlerMap
= new ConcurrentHashMap<DeviceId, DefaultGroupHandler>();
private static int numOfEvents = 0;
private static int numOfHandlerExecution = 0;
private static int numOfHandlerScheduled = 0;
@Activate
protected void activate() {
appId = coreService.registerApplication("org.onosproject.segmentrouting");
packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
linkService.addListener(new InternalLinkListener());
groupService.addListener(new InternalGroupListener());
deviceService.addListener(new InternalDeviceListener());
for (Device device: deviceService.getDevices()) {
if (mastershipService.
getLocalRole(device.id()) == MastershipRole.MASTER) {
DefaultGroupHandler groupHandler =
DefaultGroupHandler.createGroupHandler(device.id(),
appId, deviceConfiguration, linkService, groupService);
groupHandler.createGroups();
groupHandlerMap.put(device.id(), groupHandler);
log.debug("Initiating default group handling for {}", device.id());
defaultRoutingHandler.startPopulationProcess();
} else {
log.debug("Activate: Local role {} "
+ "is not MASTER for device {}",
mastershipService.
getLocalRole(device.id()),
device.id());
}
}
log.info("Started");
}
@Deactivate
protected void deactivate() {
packetService.removeProcessor(processor);
processor = null;
log.info("Stopped");
}
/**
* Returns the GrouopKey object for the device and the NighborSet given.
*
* @param ns NeightborSet object for the GroupKey
* @return GroupKey object for the NeighborSet
*/
public GroupKey getGroupKey(NeighborSet ns) {
for (DefaultGroupHandler groupHandler: groupHandlerMap.values()) {
return groupHandler.getGroupKey(ns);
}
return null;
}
private class InternalPacketProcessor implements PacketProcessor {
@Override
public void process(PacketContext context) {
if (context.isHandled()) {
return;
}
InboundPacket pkt = context.inPacket();
Ethernet ethernet = pkt.parsed();
if (ethernet.getEtherType() == Ethernet.TYPE_ARP) {
arpHandler.processPacketIn(pkt);
} else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
IPv4 ipPacket = (IPv4) ethernet.getPayload();
ipHandler.addToPacketBuffer(ipPacket);
if (ipPacket.getProtocol() == IPv4.PROTOCOL_ICMP) {
icmpHandler.processPacketIn(pkt);
} else {
ipHandler.processPacketIn(pkt);
}
}
}
}
private class InternalLinkListener implements LinkListener {
@Override
public void event(LinkEvent event) {
if (event.type() == LinkEvent.Type.LINK_ADDED ||
event.type() == LinkEvent.Type.LINK_REMOVED) {
scheduleEventHandlerIfNotScheduled(event);
}
}
}
private class InternalDeviceListener implements DeviceListener {
@Override
public void event(DeviceEvent event) {
if (mastershipService.
getLocalRole(event.subject().id()) != MastershipRole.MASTER) {
log.debug("Local role {} is not MASTER for device {}",
mastershipService.
getLocalRole(event.subject().id()),
event.subject().id());
return;
}
switch (event.type()) {
case DEVICE_ADDED:
case PORT_REMOVED:
scheduleEventHandlerIfNotScheduled(event);
break;
default:
}
}
}
private class InternalGroupListener implements GroupListener {
@Override
public void event(GroupEvent event) {
switch (event.type()) {
case GROUP_ADDED:
scheduleEventHandlerIfNotScheduled(event);
break;
case GROUP_ADD_REQUESTED:
log.info("Group add requested");
break;
case GROUP_UPDATED:
break;
default:
log.warn("Unhandled group event type: {}", event.type());
}
}
}
private void scheduleEventHandlerIfNotScheduled(Event event) {
eventQueue.add(event);
numOfEvents++;
if (eventHandlerFuture == null ||
eventHandlerFuture.isDone()) {
eventHandlerFuture = executorService.schedule(eventHandler,
100, TimeUnit.MILLISECONDS);
numOfHandlerScheduled++;
}
log.trace("numOfEvents {}, numOfEventHanlderScheduled {}", numOfEvents,
numOfHandlerScheduled);
}
private class InternalEventHandler implements Runnable {
public void run() {
numOfHandlerExecution++;
while (!eventQueue.isEmpty()) {
Event event = eventQueue.poll();
if (event.type() == LinkEvent.Type.LINK_ADDED) {
processLinkAdded((Link) event.subject());
} else if (event.type() == LinkEvent.Type.LINK_REMOVED) {
processLinkRemoved((Link) event.subject());
} else if (event.type() == GroupEvent.Type.GROUP_ADDED) {
processGroupAdded((Group) event.subject());
} else if (event.type() == DeviceEvent.Type.DEVICE_ADDED) {
processDeviceAdded((Device) event.subject());
} else if (event.type() == DeviceEvent.Type.PORT_REMOVED) {
processPortRemoved((Device) event.subject(),
((DeviceEvent) event).port());
} else {
log.warn("Unhandled event type: {}", event.type());
}
}
log.debug("numOfHandlerExecution {} numOfEventHanlderScheduled {} numOfEvents {}",
numOfHandlerExecution, numOfHandlerScheduled, numOfEvents);
}
}
private void processLinkAdded(Link link) {
log.debug("A new link {} was added", link.toString());
if (mastershipService.
getLocalRole(link.src().deviceId()) == MastershipRole.MASTER) {
DefaultGroupHandler groupHandler =
groupHandlerMap.get(link.src().deviceId());
if (groupHandler != null) {
groupHandler.linkUp(link);
}
}
defaultRoutingHandler.startPopulationProcess();
}
private void processLinkRemoved(Link link) {
log.debug("A link {} was removed", link.toString());
defaultRoutingHandler.startPopulationProcess();
}
private void processGroupAdded(Group group) {
log.debug("A new group with ID {} was added", group.id());
defaultRoutingHandler.resumePopulationProcess();
}
private void processDeviceAdded(Device device) {
log.debug("A new device with ID {} was added", device.id());
defaultRoutingHandler.populateTtpRules(device.id());
DefaultGroupHandler dgh = DefaultGroupHandler.createGroupHandler(
device.id(), appId, new DeviceConfiguration(), linkService, groupService);
dgh.createGroups();
groupHandlerMap.put(device.id(), dgh);
}
private void processPortRemoved(Device device, Port port) {
log.debug("Port {} was removed", port.toString());
DefaultGroupHandler groupHandler =
groupHandlerMap.get(device.id());
if (groupHandler != null) {
groupHandler.portDown(port.number());
}
}
}
......@@ -345,7 +345,8 @@ public final class KryoNamespaces {
MplsLabelResourceAllocation.class,
MplsLabelResourceRequest.class,
MplsLabel.class,
org.onlab.packet.MplsLabel.class
org.onlab.packet.MplsLabel.class,
org.onlab.packet.MPLS.class
)
.build();
......
......@@ -285,4 +285,18 @@
<bundle>mvn:org.onosproject/onos-app-calendar/@ONOS-VERSION</bundle>
</feature>
<feature name="onos-app-grouphandler" version="@FEATURE-VERSION"
description="Group Handler Sample App">
<feature>onos-api</feature>
<bundle>mvn:org.onosproject/onos-app-grouphandler/@ONOS-VERSION</bundle>
</feature>
<feature name="onos-app-segmentrouting" version="@FEATURE-VERSION"
description="Segment routing application">
<feature>onos-api</feature>
<bundle>mvn:org.onosproject/onos-app-segmentrouting/@ONOS-VERSION</bundle>
<bundle>mvn:org.onosproject/onos-app-grouphandler/@ONOS-VERSION</bundle>
</feature>
</features>
......
......@@ -70,7 +70,7 @@ public final class DriverManager implements OpenFlowSwitchDriverFactory {
if (vendor.startsWith("Stanford University, Ericsson Research and CPqD Research")
&&
hw.startsWith("OpenFlow 1.3 Reference Userspace Switch")) {
return new OFSwitchImplCPqD13(dpid, desc);
return new OFSwitchImplSpringOpenTTP(dpid, desc);
}
if (hw.startsWith("Open vSwitch")) {
......
......@@ -15,43 +15,21 @@
*/
package org.onosproject.openflow.drivers;
import com.google.common.collect.Lists;
import org.onosproject.openflow.controller.Dpid;
import org.onosproject.openflow.controller.RoleState;
import org.onosproject.openflow.controller.OpenFlowSwitch.TableType;
import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch;
import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeAlreadyStarted;
import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeCompleted;
import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted;
import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
import org.projectfloodlight.openflow.protocol.OFErrorMsg;
import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
import org.projectfloodlight.openflow.protocol.OFGroupFeaturesStatsReply;
import org.projectfloodlight.openflow.protocol.OFMatchV3;
import org.projectfloodlight.openflow.protocol.OFOxmList;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.OFStatsReply;
import org.projectfloodlight.openflow.protocol.OFFlowMod;
import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
import org.projectfloodlight.openflow.protocol.OFFactory;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFType;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthType;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmInPort;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmVlanVid;
import org.projectfloodlight.openflow.types.EthType;
import org.projectfloodlight.openflow.types.MacAddress;
import org.projectfloodlight.openflow.types.OFBufferId;
import org.projectfloodlight.openflow.types.OFPort;
import org.projectfloodlight.openflow.types.OFVlanVidMatch;
import org.projectfloodlight.openflow.protocol.instruction.OFInstructionGotoTable;
import org.projectfloodlight.openflow.types.TableId;
import org.projectfloodlight.openflow.types.U32;
import org.projectfloodlight.openflow.util.HexString;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
......@@ -63,8 +41,6 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch {
private final AtomicBoolean driverHandshakeComplete;
private AtomicBoolean haltStateMachine;
private DriverState driverState;
/* Default table ID - compatible with CpqD switch */
private static final int TABLE_VLAN = 0;
private static final int TABLE_TMAC = 1;
......@@ -72,14 +48,6 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch {
private static final int TABLE_MPLS = 3;
private static final int TABLE_ACL = 5;
private static final long TEST_FLOW_REMOVED_MASK = 0xf;
private static final long TEST_PACKET_IN_MASK = 0x7;
private static final long TEST_PORT_STATUS_MASK = 0x7;
private static final int OFPCML_NO_BUFFER = 0xffff;
private long barrierXidToWaitFor = -1;
/* Set the default values. These variables will get
* overwritten based on the switch vendor type
*/
......@@ -89,17 +57,10 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch {
protected int mplsTableId = TABLE_MPLS;
protected int aclTableId = TABLE_ACL;
/* priority values for OF message */
private static final short MAX_PRIORITY = (short) 0xffff;
private static final short PRIORITY_MULTIPLIER = (short) 2046;
private static final short MIN_PRIORITY = 0x0;
protected OFSwitchImplSpringOpenTTP(Dpid dpid, OFDescStatsReply desc) {
super(dpid);
driverHandshakeComplete = new AtomicBoolean(false);
haltStateMachine = new AtomicBoolean(false);
driverState = DriverState.INIT;
setSwitchDescription(desc);
}
......@@ -117,6 +78,7 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch {
return null;
}
@Override
public void startDriverHandshake() {
log.debug("Starting driver handshake for sw {}", getStringId());
......@@ -126,12 +88,9 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch {
startDriverHandshakeCalled = true;
factory = this.factory();
try {
nextDriverState();
} catch (IOException e) {
log.error("Error {} during driver handshake for sw {}", e.getCause(),
getStringId());
}
driverHandshakeComplete.set(true);
log.debug("Driver handshake is complete");
}
@Override
......@@ -142,6 +101,8 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch {
return driverHandshakeComplete.get();
}
@Override
public void processDriverHandshakeMessage(OFMessage m) {
if (!startDriverHandshakeCalled) {
......@@ -150,13 +111,9 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch {
if (driverHandshakeComplete.get()) {
throw new SwitchDriverSubHandshakeCompleted(m);
}
try {
processOFMessage(m);
} catch (IOException e) {
log.error("Error generated when processing OFMessage {}", e.getCause());
}
}
@Override
public void write(OFMessage msg) {
this.channel.write(Collections.singletonList(msg));
......@@ -168,384 +125,51 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch {
}
@Override
public void transformAndSendMsg(OFMessage m, TableType tableType) {
if (m.getType() == OFType.FLOW_MOD) {
OFFlowMod flowMod = (OFFlowMod) m;
public void transformAndSendMsg(OFMessage msg, TableType type) {
if (msg.getType() == OFType.FLOW_MOD) {
OFFlowMod flowMod = (OFFlowMod) msg;
OFFlowMod.Builder builder = flowMod.createBuilder();
builder.setTableId(getTableId(tableType));
OFFlowMod newFlowMod = builder.build();
if (role == RoleState.MASTER) {
this.write(newFlowMod);
}
} else {
if (role == RoleState.MASTER) {
this.write(m);
}
}
}
/*
* Driver handshake state machine
*/
enum DriverState {
INIT,
SET_TABLE_MISS_ENTRIES,
SET_TABLE_VLAN_TMAC,
AUDIT_GROUPS,
SET_GROUPS,
VERIFY_GROUPS,
SET_ADJACENCY_LABELS,
EXIT
}
protected void nextDriverState() throws IOException {
DriverState currentState = driverState;
if (haltStateMachine.get()) {
return;
}
switch (currentState) {
case INIT:
driverState = DriverState.SET_TABLE_MISS_ENTRIES;
setTableMissEntries();
sendHandshakeBarrier();
break;
case SET_TABLE_MISS_ENTRIES:
driverState = DriverState.SET_TABLE_VLAN_TMAC;
/* TODO: read network configuration
boolean isConfigured = getNetworkConfig();
if (!isConfigured) {
return; // this will result in a handshake timeout
List<OFInstruction> instructions = flowMod.getInstructions();
List<OFInstruction> newInstructions = Lists.newArrayList();
for (OFInstruction i : instructions) {
if (i instanceof OFInstructionGotoTable) {
OFInstructionGotoTable gotoTable = (OFInstructionGotoTable) i;
TableType tid = TableType.values()[gotoTable.getTableId().getValue()];
newInstructions.add(
gotoTable.createBuilder()
.setTableId(getTableId(tid)).build());
} else {
newInstructions.add(i);
}
*/
populateTableVlan();
populateTableTMac();
sendHandshakeBarrier();
break;
case SET_TABLE_VLAN_TMAC:
driverState = DriverState.EXIT;
driverHandshakeComplete.set(true);
log.debug("Driver handshake is complete");
break;
case EXIT:
default:
driverState = DriverState.EXIT;
log.error("Driver handshake has exited for sw: {}", getStringId());
}
}
private void processStatsReply(OFStatsReply sr) {
switch (sr.getStatsType()) {
case AGGREGATE:
break;
case DESC:
break;
case EXPERIMENTER:
break;
case FLOW:
break;
case GROUP_DESC:
processGroupDesc((OFGroupDescStatsReply) sr);
break;
case GROUP_FEATURES:
processGroupFeatures((OFGroupFeaturesStatsReply) sr);
break;
case METER_CONFIG:
break;
case METER_FEATURES:
break;
case PORT_DESC:
break;
case TABLE_FEATURES:
break;
default:
break;
}
}
private void processOFMessage(OFMessage m) throws IOException {
switch (m.getType()) {
case BARRIER_REPLY:
processBarrierReply(m);
break;
case ERROR:
processErrorMessage(m);
break;
case GET_ASYNC_REPLY:
OFAsyncGetReply asrep = (OFAsyncGetReply) m;
decodeAsyncGetReply(asrep);
break;
case PACKET_IN:
// not ready to handle packet-ins
break;
case QUEUE_GET_CONFIG_REPLY:
// not doing queue config yet
break;
case STATS_REPLY:
processStatsReply((OFStatsReply) m);
break;
case ROLE_REPLY: // channelHandler should handle this
case PORT_STATUS: // channelHandler should handle this
case FEATURES_REPLY: // don't care
case FLOW_REMOVED: // don't care
default:
log.debug("Received message {} during switch-driver subhandshake "
+ "from switch {} ... Ignoring message", m, getStringId());
}
}
}
builder.setTableId(getTableId(type));
builder.setInstructions(newInstructions);
OFMessage msgnew = builder.build();
channel.write(Collections.singletonList(msgnew));
log.trace("Installed {}", msgnew);
private void processBarrierReply(OFMessage m) throws IOException {
if (m.getXid() == barrierXidToWaitFor) {
// Driver state-machine progresses to the next state.
// If Barrier messages is not received, then eventually
// the ChannelHandler state machine will timeout, and the switch
// will be disconnected.
nextDriverState();
} else {
log.error("Received incorrect barrier-message xid {} (expected: {}) in "
+ "switch-driver state {} for switch {}", m, barrierXidToWaitFor,
driverState, getStringId());
channel.write(Collections.singletonList(msg));
}
}
private void processErrorMessage(OFMessage m) {
log.error("Switch {} Error {} in DriverState", getStringId(),
(OFErrorMsg) m, driverState);
}
private void processGroupFeatures(OFGroupFeaturesStatsReply gfsr) {
log.info("Sw: {} Group Features {}", getStringId(), gfsr);
}
private void processGroupDesc(OFGroupDescStatsReply gdsr) {
log.info("Sw: {} Group Desc {}", getStringId(), gdsr);
}
/*
* Utility functions
*/
private void decodeAsyncGetReply(OFAsyncGetReply rep) {
long frm = rep.getFlowRemovedMaskEqualMaster();
//long frs = rep.getFlowRemovedMaskSlave();
long pim = rep.getPacketInMaskEqualMaster();
//long pis = rep.getPacketInMaskSlave();
long psm = rep.getPortStatusMaskEqualMaster();
//long pss = rep.getPortStatusMaskSlave();
if (role == RoleState.MASTER || role == RoleState.EQUAL) { // should separate
log.info("FRM:{}", HexString.toHexString((frm & TEST_FLOW_REMOVED_MASK)));
log.info("PIM:{}", HexString.toHexString((pim & TEST_PACKET_IN_MASK)));
log.info("PSM:{}", HexString.toHexString((psm & TEST_PORT_STATUS_MASK)));
}
}
protected void setTableMissEntries() throws IOException {
// set all table-miss-entries
populateTableMissEntry(vlanTableId, true, false, false, -1);
populateTableMissEntry(tmacTableId, true, false, false, -1);
populateTableMissEntry(ipv4UnicastTableId, false, true, true,
aclTableId);
populateTableMissEntry(mplsTableId, false, true, true,
aclTableId);
populateTableMissEntry(aclTableId, false, false, false, -1);
log.debug("TableMissEntries are set");
}
/**
* Adds a table-miss-entry to a pipeline table.
* <p>
* The table-miss-entry can be added with 'write-actions' or
* 'apply-actions'. It can also add a 'goto-table' instruction. By default
* if none of the booleans in the call are set, then the table-miss entry is
* added with no instructions, which means that if a packet hits the
* table-miss-entry, pipeline execution will stop, and the action set
* associated with the packet will be executed.
*
* @param tableToAdd the table to where the table-miss-entry will be added
* @param toControllerNow as an APPLY_ACTION instruction
* @param toControllerWrite as a WRITE_ACTION instruction
* @param toTable as a GOTO_TABLE instruction
* @param tableToSend the table to send as per the GOTO_TABLE instruction it
* needs to be set if 'toTable' is true. Ignored of 'toTable' is
* false.
*/
protected void populateTableMissEntry(int tableToAdd, boolean toControllerNow,
boolean toControllerWrite,
boolean toTable, int tableToSend) {
OFOxmList oxmList = OFOxmList.EMPTY;
OFMatchV3 match = factory.buildMatchV3()
.setOxmList(oxmList)
.build();
OFAction outc = factory.actions()
.buildOutput()
.setPort(OFPort.CONTROLLER)
.setMaxLen(OFPCML_NO_BUFFER)
.build();
List<OFInstruction> instructions = new ArrayList<OFInstruction>();
if (toControllerNow) {
// table-miss instruction to send to controller immediately
OFInstruction instr = factory.instructions()
.buildApplyActions()
.setActions(Collections.singletonList(outc))
.build();
instructions.add(instr);
}
if (toControllerWrite) {
// table-miss instruction to write-action to send to controller
// this will be executed whenever the action-set gets executed
OFInstruction instr = factory.instructions()
.buildWriteActions()
.setActions(Collections.singletonList(outc))
.build();
instructions.add(instr);
}
if (toTable) {
// table-miss instruction to goto-table x
OFInstruction instr = factory.instructions()
.gotoTable(TableId.of(tableToSend));
instructions.add(instr);
}
if (!toControllerNow && !toControllerWrite && !toTable) {
// table-miss has no instruction - at which point action-set will be
// executed - if there is an action to output/group in the action
// set
// the packet will be sent there, otherwise it will be dropped.
instructions = Collections.<OFInstruction>emptyList();
}
OFMessage tableMissEntry = factory.buildFlowAdd()
.setTableId(TableId.of(tableToAdd))
.setMatch(match) // match everything
.setInstructions(instructions)
.setPriority(MIN_PRIORITY)
.setBufferId(OFBufferId.NO_BUFFER)
.setIdleTimeout(0)
.setHardTimeout(0)
.setXid(getNextTransactionId())
.build();
write(tableMissEntry);
}
private void populateTableVlan() throws IOException {
List<OFMessage> msglist = new ArrayList<OFMessage>();
for (OFPortDesc p : getPorts()) {
int pnum = p.getPortNo().getPortNumber();
if (U32.of(pnum).compareTo(U32.of(OFPort.MAX.getPortNumber())) < 1) {
OFOxmInPort oxp = factory.oxms().inPort(p.getPortNo());
OFOxmVlanVid oxv = factory.oxms()
.vlanVid(OFVlanVidMatch.UNTAGGED);
OFOxmList oxmList = OFOxmList.of(oxp, oxv);
OFMatchV3 match = factory.buildMatchV3()
.setOxmList(oxmList).build();
// TODO: match on vlan-tagged packets for vlans configured on
// subnet ports and strip-vlan
OFInstruction gotoTbl = factory.instructions().buildGotoTable()
.setTableId(TableId.of(tmacTableId)).build();
List<OFInstruction> instructions = new ArrayList<OFInstruction>();
instructions.add(gotoTbl);
OFMessage flowEntry = factory.buildFlowAdd()
.setTableId(TableId.of(vlanTableId))
.setMatch(match)
.setInstructions(instructions)
.setPriority(1000) // does not matter - all rules
// exclusive
.setBufferId(OFBufferId.NO_BUFFER)
.setIdleTimeout(0)
.setHardTimeout(0)
.setXid(getNextTransactionId())
.build();
msglist.add(flowEntry);
}
@Override
public TableType getTableType(TableId tid) {
switch (tid.getValue()) {
case TABLE_IPV4_UNICAST:
return TableType.IP;
case TABLE_MPLS:
return TableType.MPLS;
case TABLE_ACL:
return TableType.ACL;
case TABLE_VLAN:
return TableType.VLAN;
case TABLE_TMAC:
return TableType.ETHER;
default:
log.error("Table type for Table id {} is not supported in the driver", tid);
return TableType.NONE;
}
write(msglist);
log.debug("Adding {} port/vlan-rules in sw {}", msglist.size(), getStringId());
}
private void populateTableTMac() throws IOException {
// match for router-mac and ip-packets
OFOxmEthType oxe = factory.oxms().ethType(EthType.IPv4);
/* TODO: need to read network config and need to allow only
the packets with DMAC as the correspondent router MAC address
Until network configuration is implemented, all packets are allowed
OFOxmEthDst dmac = factory.oxms().ethDst(getRouterMacAddr());
OFOxmList oxmListIp = OFOxmList.of(dmac, oxe);
OFMatchV3 matchIp = factory.buildMatchV3()
.setOxmList(oxmListIp).build();
*/
OFOxmList oxmList = OFOxmList.EMPTY;
OFMatchV3 matchIp = factory.buildMatchV3()
.setOxmList(oxmList)
.build();
OFInstruction gotoTblIp = factory.instructions().buildGotoTable()
.setTableId(TableId.of(ipv4UnicastTableId)).build();
List<OFInstruction> instructionsIp = Collections.singletonList(gotoTblIp);
OFMessage ipEntry = factory.buildFlowAdd()
.setTableId(TableId.of(tmacTableId))
.setMatch(matchIp)
.setInstructions(instructionsIp)
.setPriority(1000) // strict priority required lower than
// multicastMac
.setBufferId(OFBufferId.NO_BUFFER)
.setIdleTimeout(0)
.setHardTimeout(0)
.setXid(getNextTransactionId())
.build();
// match for router-mac and mpls packets
OFOxmEthType oxmpls = factory.oxms().ethType(EthType.MPLS_UNICAST);
/* TODO: need to read network config and need to allow only
the packets with DMAC as the correspondent router MAC address
OFOxmList oxmListMpls = OFOxmList.of(dmac, oxmpls);
OFMatchV3 matchMpls = factory.buildMatchV3()
.setOxmList(oxmListMpls).build();
*/
OFOxmList oxmListMpls = OFOxmList.EMPTY;
OFMatchV3 matchMpls = factory.buildMatchV3()
.setOxmList(oxmList)
.build();
OFInstruction gotoTblMpls = factory.instructions().buildGotoTable()
.setTableId(TableId.of(mplsTableId)).build();
List<OFInstruction> instructionsMpls = Collections.singletonList(gotoTblMpls);
OFMessage mplsEntry = factory.buildFlowAdd()
.setTableId(TableId.of(tmacTableId))
.setMatch(matchMpls)
.setInstructions(instructionsMpls)
.setPriority(1001) // strict priority required lower than
// multicastMac
.setBufferId(OFBufferId.NO_BUFFER)
.setIdleTimeout(0)
.setHardTimeout(0)
.setXid(getNextTransactionId())
.build();
log.debug("Adding termination-mac-rules in sw {}", getStringId());
List<OFMessage> msglist = new ArrayList<OFMessage>(2);
msglist.add(ipEntry);
msglist.add(mplsEntry);
write(msglist);
}
private MacAddress getRouterMacAddr() {
// TODO: need to read network config : RouterIp
return MacAddress.of("00:00:00:00:00:00");
}
private TableId getTableId(TableType tableType) {
......@@ -556,6 +180,10 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch {
return TableId.of(mplsTableId);
case ACL:
return TableId.of(aclTableId);
case VLAN:
return TableId.of(vlanTableId);
case ETHER:
return TableId.of(tmacTableId);
default: {
log.error("Table type {} is not supported in the driver", tableType);
return TableId.NONE;
......@@ -563,19 +191,4 @@ public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch {
}
}
private void sendHandshakeBarrier() throws IOException {
long xid = getNextTransactionId();
barrierXidToWaitFor = xid;
OFBarrierRequest br = factory()
.buildBarrierRequest()
.setXid(xid)
.build();
write(br);
}
@Override
public TableType getTableType(TableId tid) {
return TableType.NONE; // XXX this needs to be fixed
}
}
......
package org.onlab.packet;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
public class MPLS extends BasePacket {
public static final int ADDRESS_LENGTH = 4;
public static final byte PROTOCOL_IPV4 = 0x1;
public static final byte PROTOCOL_MPLS = 0x6;
public static final Map<Byte, Class<? extends IPacket>> PROTOCOL_CLASS_MAP;
static {
PROTOCOL_CLASS_MAP = new HashMap<Byte, Class<? extends IPacket>>();
PROTOCOL_CLASS_MAP.put(PROTOCOL_IPV4, IPv4.class);
PROTOCOL_CLASS_MAP.put(PROTOCOL_MPLS, MPLS.class);
}
protected int label; //20bits
protected byte bos; //1bit
protected byte ttl; //8bits
protected byte protocol;
/**
* Default constructor that sets the version to 4.
*/
public MPLS() {
super();
this.bos = 1;
this.protocol = PROTOCOL_IPV4;
}
@Override
public byte[] serialize() {
byte[] payloadData = null;
if (payload != null) {
payload.setParent(this);
payloadData = payload.serialize();
}
byte[] data = new byte[(4 + ((payloadData != null) ? payloadData.length : 0)) ];
ByteBuffer bb = ByteBuffer.wrap(data);
bb.putInt(((this.label & 0x000fffff) << 12) | ((this.bos & 0x1) << 8 | (this.ttl & 0xff)));
if (payloadData != null) {
bb.put(payloadData);
}
return data;
}
@Override
public IPacket deserialize(byte[] data, int offset, int length) {
ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
int mplsheader = bb.getInt();
this.label = ((mplsheader & 0xfffff000) >> 12);
this.bos = (byte) ((mplsheader & 0x00000100) >> 8);
this.bos = (byte) (mplsheader & 0x000000ff);
this.protocol = (this.bos == 1) ? PROTOCOL_IPV4 : PROTOCOL_MPLS;
IPacket payload;
if (IPv4.PROTOCOL_CLASS_MAP.containsKey(this.protocol)) {
Class<? extends IPacket> clazz = IPv4.PROTOCOL_CLASS_MAP.get(this.protocol);
try {
payload = clazz.newInstance();
} catch (Exception e) {
throw new RuntimeException("Error parsing payload for MPLS packet", e);
}
} else {
payload = new Data();
}
this.payload = payload.deserialize(data, bb.position(), bb.limit() - bb.position());
this.payload.setParent(this);
return this;
}
/**
* Returns the MPLS label.
*
* @return MPLS label
*/
public int getLabel() {
return label;
}
/**
* Sets the MPLS label.
*
* @param label
*/
public void setLabel(int label) {
this.label = label;
}
/**
* Returns the MPLS TTL of the packet.
*
* @return MPLS TTL of the packet
*/
public byte getTtl() {
return ttl;
}
/**
* Sets the MPLS TTL of the packet.
*
* @param ttl MPLS TTL
*/
public void setTtl(byte ttl) {
this.ttl = ttl;
}
}