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 1344 additions and 11 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 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;
}
}
}
......@@ -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")) {
......
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;
}
}