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
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..3e541f0
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,38 @@
+<?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>
diff --git a/src/main/java/org/onosproject/segmentrouting/ArpHandler.java b/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
new file mode 100644
index 0000000..5b94518
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
@@ -0,0 +1,222 @@
+/*
+ * 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));
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
new file mode 100644
index 0000000..3684ec9
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -0,0 +1,228 @@
+/*
+ * 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();
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java b/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
new file mode 100644
index 0000000..17cfd96
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
@@ -0,0 +1,190 @@
+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;
+ }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java b/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java
new file mode 100644
index 0000000..cd28fcf
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java
@@ -0,0 +1,374 @@
+/*
+ * 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();
+ }
+}
+
diff --git a/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java b/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
new file mode 100644
index 0000000..3bfb888
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
@@ -0,0 +1,167 @@
+/*
+ * 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);
+ }
+ }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/IpHandler.java b/src/main/java/org/onosproject/segmentrouting/IpHandler.java
new file mode 100644
index 0000000..cca8692
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/IpHandler.java
@@ -0,0 +1,146 @@
+/*
+ * 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);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/NetworkConfigHandler.java b/src/main/java/org/onosproject/segmentrouting/NetworkConfigHandler.java
new file mode 100644
index 0000000..2cb5867
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/NetworkConfigHandler.java
@@ -0,0 +1,206 @@
+/*
+ * 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;
+ }
+ }
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
new file mode 100644
index 0000000..063517e
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -0,0 +1,402 @@
+/*
+ * 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;
+ }
+
+}
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
new file mode 100644
index 0000000..a7a3b11
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -0,0 +1,342 @@
+/*
+ * 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());
+ }
+ }
+}